blob 670dbd75 (568294B) - Raw
1 //! Ingests an AST and produces ZIR code. 2 const AstGen = @This(); 3 4 const std = @import("std"); 5 const Ast = std.zig.Ast; 6 const mem = std.mem; 7 const Allocator = std.mem.Allocator; 8 const assert = std.debug.assert; 9 const ArrayListUnmanaged = std.ArrayListUnmanaged; 10 const StringIndexAdapter = std.hash_map.StringIndexAdapter; 11 const StringIndexContext = std.hash_map.StringIndexContext; 12 13 const isPrimitive = std.zig.primitives.isPrimitive; 14 15 const Zir = std.zig.Zir; 16 const BuiltinFn = std.zig.BuiltinFn; 17 const AstRlAnnotate = std.zig.AstRlAnnotate; 18 19 gpa: Allocator, 20 tree: *const Ast, 21 /// The set of nodes which, given the choice, must expose a result pointer to 22 /// sub-expressions. See `AstRlAnnotate` for details. 23 nodes_need_rl: *const AstRlAnnotate.RlNeededSet, 24 instructions: std.MultiArrayList(Zir.Inst) = .{}, 25 extra: ArrayListUnmanaged(u32) = .empty, 26 string_bytes: ArrayListUnmanaged(u8) = .empty, 27 /// Tracks the current byte offset within the source file. 28 /// Used to populate line deltas in the ZIR. AstGen maintains 29 /// this "cursor" throughout the entire AST lowering process in order 30 /// to avoid starting over the line/column scan for every declaration, which 31 /// would be O(N^2). 32 source_offset: u32 = 0, 33 /// Tracks the corresponding line of `source_offset`. 34 /// This value is absolute. 35 source_line: u32 = 0, 36 /// Tracks the corresponding column of `source_offset`. 37 /// This value is absolute. 38 source_column: u32 = 0, 39 /// Used for temporary allocations; freed after AstGen is complete. 40 /// The resulting ZIR code has no references to anything in this arena. 41 arena: Allocator, 42 string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .empty, 43 compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .empty, 44 /// The topmost block of the current function. 45 fn_block: ?*GenZir = null, 46 fn_var_args: bool = false, 47 /// Whether we are somewhere within a function. If `true`, any container decls may be 48 /// generic and thus must be tunneled through closure. 49 within_fn: bool = false, 50 /// The return type of the current function. This may be a trivial `Ref`, or 51 /// otherwise it refers to a `ret_type` instruction. 52 fn_ret_ty: Zir.Inst.Ref = .none, 53 /// Maps string table indexes to the first `@import` ZIR instruction 54 /// that uses this string as the operand. 55 imports: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, Ast.TokenIndex) = .empty, 56 /// Used for temporary storage when building payloads. 57 scratch: std.ArrayListUnmanaged(u32) = .empty, 58 /// Whenever a `ref` instruction is needed, it is created and saved in this 59 /// table instead of being immediately appended to the current block body. 60 /// Then, when the instruction is being added to the parent block (typically from 61 /// setBlockBody), if it has a ref_table entry, then the ref instruction is added 62 /// there. This makes sure two properties are upheld: 63 /// 1. All pointers to the same locals return the same address. This is required 64 /// to be compliant with the language specification. 65 /// 2. `ref` instructions will dominate their uses. This is a required property 66 /// of ZIR. 67 /// The key is the ref operand; the value is the ref instruction. 68 ref_table: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .empty, 69 /// Any information which should trigger invalidation of incremental compilation 70 /// data should be used to update this hasher. The result is the final source 71 /// hash of the enclosing declaration/etc. 72 src_hasher: std.zig.SrcHasher, 73 74 const InnerError = error{ OutOfMemory, AnalysisFail }; 75 76 fn addExtra(astgen: *AstGen, extra: anytype) Allocator.Error!u32 { 77 const fields = std.meta.fields(@TypeOf(extra)); 78 try astgen.extra.ensureUnusedCapacity(astgen.gpa, fields.len); 79 return addExtraAssumeCapacity(astgen, extra); 80 } 81 82 fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 { 83 const fields = std.meta.fields(@TypeOf(extra)); 84 const extra_index: u32 = @intCast(astgen.extra.items.len); 85 astgen.extra.items.len += fields.len; 86 setExtra(astgen, extra_index, extra); 87 return extra_index; 88 } 89 90 fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void { 91 const fields = std.meta.fields(@TypeOf(extra)); 92 var i = index; 93 inline for (fields) |field| { 94 astgen.extra.items[i] = switch (field.type) { 95 u32 => @field(extra, field.name), 96 97 Zir.Inst.Ref, 98 Zir.Inst.Index, 99 Zir.Inst.Declaration.Name, 100 std.zig.SimpleComptimeReason, 101 Zir.NullTerminatedString, 102 // Ast.TokenIndex is missing because it is a u32. 103 Ast.OptionalTokenIndex, 104 Ast.Node.Index, 105 Ast.Node.OptionalIndex, 106 => @intFromEnum(@field(extra, field.name)), 107 108 Ast.TokenOffset, 109 Ast.OptionalTokenOffset, 110 Ast.Node.Offset, 111 Ast.Node.OptionalOffset, 112 => @bitCast(@intFromEnum(@field(extra, field.name))), 113 114 i32, 115 Zir.Inst.Call.Flags, 116 Zir.Inst.BuiltinCall.Flags, 117 Zir.Inst.SwitchBlock.Bits, 118 Zir.Inst.SwitchBlockErrUnion.Bits, 119 Zir.Inst.FuncFancy.Bits, 120 Zir.Inst.Param.Type, 121 Zir.Inst.Func.RetTy, 122 => @bitCast(@field(extra, field.name)), 123 124 else => @compileError("bad field type"), 125 }; 126 i += 1; 127 } 128 } 129 130 fn reserveExtra(astgen: *AstGen, size: usize) Allocator.Error!u32 { 131 const extra_index: u32 = @intCast(astgen.extra.items.len); 132 try astgen.extra.resize(astgen.gpa, extra_index + size); 133 return extra_index; 134 } 135 136 fn appendRefs(astgen: *AstGen, refs: []const Zir.Inst.Ref) !void { 137 return astgen.extra.appendSlice(astgen.gpa, @ptrCast(refs)); 138 } 139 140 fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void { 141 astgen.extra.appendSliceAssumeCapacity(@ptrCast(refs)); 142 } 143 144 pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { 145 assert(tree.mode == .zig); 146 147 var arena = std.heap.ArenaAllocator.init(gpa); 148 defer arena.deinit(); 149 150 var nodes_need_rl = try AstRlAnnotate.annotate(gpa, arena.allocator(), tree); 151 defer nodes_need_rl.deinit(gpa); 152 153 var astgen: AstGen = .{ 154 .gpa = gpa, 155 .arena = arena.allocator(), 156 .tree = &tree, 157 .nodes_need_rl = &nodes_need_rl, 158 .src_hasher = undefined, // `structDeclInner` for the root struct will set this 159 }; 160 defer astgen.deinit(gpa); 161 162 // String table index 0 is reserved for `NullTerminatedString.empty`. 163 try astgen.string_bytes.append(gpa, 0); 164 165 // We expect at least as many ZIR instructions and extra data items 166 // as AST nodes. 167 try astgen.instructions.ensureTotalCapacity(gpa, tree.nodes.len); 168 169 // First few indexes of extra are reserved and set at the end. 170 const reserved_count = @typeInfo(Zir.ExtraIndex).@"enum".fields.len; 171 try astgen.extra.ensureTotalCapacity(gpa, tree.nodes.len + reserved_count); 172 astgen.extra.items.len += reserved_count; 173 174 var top_scope: Scope.Top = .{}; 175 176 var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty; 177 var gen_scope: GenZir = .{ 178 .is_comptime = true, 179 .parent = &top_scope.base, 180 .decl_node_index = .root, 181 .decl_line = 0, 182 .astgen = &astgen, 183 .instructions = &gz_instructions, 184 .instructions_top = 0, 185 }; 186 defer gz_instructions.deinit(gpa); 187 188 // The AST -> ZIR lowering process assumes an AST that does not have any parse errors. 189 // Parse errors, or AstGen errors in the root struct, are considered "fatal", so we emit no ZIR. 190 const fatal = if (tree.errors.len == 0) fatal: { 191 if (AstGen.structDeclInner( 192 &gen_scope, 193 &gen_scope.base, 194 .root, 195 tree.containerDeclRoot(), 196 .auto, 197 .none, 198 .parent, 199 )) |struct_decl_ref| { 200 assert(struct_decl_ref.toIndex().? == .main_struct_inst); 201 break :fatal false; 202 } else |err| switch (err) { 203 error.OutOfMemory => return error.OutOfMemory, 204 error.AnalysisFail => break :fatal true, // Handled via compile_errors below. 205 } 206 } else fatal: { 207 try lowerAstErrors(&astgen); 208 break :fatal true; 209 }; 210 211 const err_index = @intFromEnum(Zir.ExtraIndex.compile_errors); 212 if (astgen.compile_errors.items.len == 0) { 213 astgen.extra.items[err_index] = 0; 214 } else { 215 try astgen.extra.ensureUnusedCapacity(gpa, 1 + astgen.compile_errors.items.len * 216 @typeInfo(Zir.Inst.CompileErrors.Item).@"struct".fields.len); 217 218 astgen.extra.items[err_index] = astgen.addExtraAssumeCapacity(Zir.Inst.CompileErrors{ 219 .items_len = @intCast(astgen.compile_errors.items.len), 220 }); 221 222 for (astgen.compile_errors.items) |item| { 223 _ = astgen.addExtraAssumeCapacity(item); 224 } 225 } 226 227 const imports_index = @intFromEnum(Zir.ExtraIndex.imports); 228 if (astgen.imports.count() == 0) { 229 astgen.extra.items[imports_index] = 0; 230 } else { 231 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Imports).@"struct".fields.len + 232 astgen.imports.count() * @typeInfo(Zir.Inst.Imports.Item).@"struct".fields.len); 233 234 astgen.extra.items[imports_index] = astgen.addExtraAssumeCapacity(Zir.Inst.Imports{ 235 .imports_len = @intCast(astgen.imports.count()), 236 }); 237 238 var it = astgen.imports.iterator(); 239 while (it.next()) |entry| { 240 _ = astgen.addExtraAssumeCapacity(Zir.Inst.Imports.Item{ 241 .name = entry.key_ptr.*, 242 .token = entry.value_ptr.*, 243 }); 244 } 245 } 246 247 return .{ 248 .instructions = if (fatal) .empty else astgen.instructions.toOwnedSlice(), 249 .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa), 250 .extra = try astgen.extra.toOwnedSlice(gpa), 251 }; 252 } 253 254 fn deinit(astgen: *AstGen, gpa: Allocator) void { 255 astgen.instructions.deinit(gpa); 256 astgen.extra.deinit(gpa); 257 astgen.string_table.deinit(gpa); 258 astgen.string_bytes.deinit(gpa); 259 astgen.compile_errors.deinit(gpa); 260 astgen.imports.deinit(gpa); 261 astgen.scratch.deinit(gpa); 262 astgen.ref_table.deinit(gpa); 263 } 264 265 const ResultInfo = struct { 266 /// The semantics requested for the result location 267 rl: Loc, 268 269 /// The "operator" consuming the result location 270 ctx: Context = .none, 271 272 /// Turns a `coerced_ty` back into a `ty`. Should be called at branch points 273 /// such as if and switch expressions. 274 fn br(ri: ResultInfo) ResultInfo { 275 return switch (ri.rl) { 276 .coerced_ty => |ty| .{ 277 .rl = .{ .ty = ty }, 278 .ctx = ri.ctx, 279 }, 280 else => ri, 281 }; 282 } 283 284 fn zirTag(ri: ResultInfo) Zir.Inst.Tag { 285 switch (ri.rl) { 286 .ty => return switch (ri.ctx) { 287 .shift_op => .as_shift_operand, 288 else => .as_node, 289 }, 290 else => unreachable, 291 } 292 } 293 294 const Loc = union(enum) { 295 /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the 296 /// expression should be generated. The result instruction from the expression must 297 /// be ignored. 298 discard, 299 /// The expression has an inferred type, and it will be evaluated as an rvalue. 300 none, 301 /// The expression will be coerced into this type, but it will be evaluated as an rvalue. 302 ty: Zir.Inst.Ref, 303 /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion, 304 /// so no `as` instruction needs to be emitted. 305 coerced_ty: Zir.Inst.Ref, 306 /// The expression must generate a pointer rather than a value. For example, the left hand side 307 /// of an assignment uses this kind of result location. 308 ref, 309 /// The expression must generate a pointer rather than a value, and the pointer will be coerced 310 /// by other code to this type, which is guaranteed by earlier instructions to be a pointer type. 311 ref_coerced_ty: Zir.Inst.Ref, 312 /// The expression must store its result into this typed pointer. The result instruction 313 /// from the expression must be ignored. 314 ptr: PtrResultLoc, 315 /// The expression must store its result into this allocation, which has an inferred type. 316 /// The result instruction from the expression must be ignored. 317 /// Always an instruction with tag `alloc_inferred`. 318 inferred_ptr: Zir.Inst.Ref, 319 /// The expression has a sequence of pointers to store its results into due to a destructure 320 /// operation. Each of these pointers may or may not have an inferred type. 321 destructure: struct { 322 /// The AST node of the destructure operation itself. 323 src_node: Ast.Node.Index, 324 /// The pointers to store results into. 325 components: []const DestructureComponent, 326 }, 327 328 const DestructureComponent = union(enum) { 329 typed_ptr: PtrResultLoc, 330 inferred_ptr: Zir.Inst.Ref, 331 discard, 332 }; 333 334 const PtrResultLoc = struct { 335 inst: Zir.Inst.Ref, 336 src_node: ?Ast.Node.Index = null, 337 }; 338 339 /// Find the result type for a cast builtin given the result location. 340 /// If the location does not have a known result type, returns `null`. 341 fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index) !?Zir.Inst.Ref { 342 return switch (rl) { 343 .discard, .none, .ref, .inferred_ptr, .destructure => null, 344 .ty, .coerced_ty => |ty_ref| ty_ref, 345 .ref_coerced_ty => |ptr_ty| try gz.addUnNode(.elem_type, ptr_ty, node), 346 .ptr => |ptr| { 347 const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node); 348 return try gz.addUnNode(.elem_type, ptr_ty, node); 349 }, 350 }; 351 } 352 353 /// Find the result type for a cast builtin given the result location. 354 /// If the location does not have a known result type, emits an error on 355 /// the given node. 356 fn resultTypeForCast(rl: Loc, gz: *GenZir, node: Ast.Node.Index, builtin_name: []const u8) !Zir.Inst.Ref { 357 const astgen = gz.astgen; 358 if (try rl.resultType(gz, node)) |ty| return ty; 359 switch (rl) { 360 .destructure => |destructure| return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ 361 try astgen.errNoteNode(destructure.src_node, "destructure expressions do not provide a single result type", .{}), 362 try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}), 363 }), 364 else => return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ 365 try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}), 366 }), 367 } 368 } 369 }; 370 371 const Context = enum { 372 /// The expression is the operand to a return expression. 373 @"return", 374 /// The expression is the input to an error-handling operator (if-else, try, or catch). 375 error_handling_expr, 376 /// The expression is the right-hand side of a shift operation. 377 shift_op, 378 /// The expression is an argument in a function call. 379 fn_arg, 380 /// The expression is the right-hand side of an initializer for a `const` variable 381 const_init, 382 /// The expression is the right-hand side of an assignment expression. 383 assignment, 384 /// No specific operator in particular. 385 none, 386 }; 387 }; 388 389 const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } }; 390 const coerced_linksection_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }; 391 const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } }; 392 const coerced_bool_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .bool_type } }; 393 394 fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 395 return comptimeExpr(gz, scope, coerced_type_ri, type_node, .type); 396 } 397 398 fn reachableTypeExpr( 399 gz: *GenZir, 400 scope: *Scope, 401 type_node: Ast.Node.Index, 402 reachable_node: Ast.Node.Index, 403 ) InnerError!Zir.Inst.Ref { 404 return reachableExprComptime(gz, scope, coerced_type_ri, type_node, reachable_node, .type); 405 } 406 407 /// Same as `expr` but fails with a compile error if the result type is `noreturn`. 408 fn reachableExpr( 409 gz: *GenZir, 410 scope: *Scope, 411 ri: ResultInfo, 412 node: Ast.Node.Index, 413 reachable_node: Ast.Node.Index, 414 ) InnerError!Zir.Inst.Ref { 415 return reachableExprComptime(gz, scope, ri, node, reachable_node, null); 416 } 417 418 fn reachableExprComptime( 419 gz: *GenZir, 420 scope: *Scope, 421 ri: ResultInfo, 422 node: Ast.Node.Index, 423 reachable_node: Ast.Node.Index, 424 /// If `null`, the expression is not evaluated in a comptime context. 425 comptime_reason: ?std.zig.SimpleComptimeReason, 426 ) InnerError!Zir.Inst.Ref { 427 const result_inst = if (comptime_reason) |r| 428 try comptimeExpr(gz, scope, ri, node, r) 429 else 430 try expr(gz, scope, ri, node); 431 432 if (gz.refIsNoReturn(result_inst)) { 433 try gz.astgen.appendErrorNodeNotes(reachable_node, "unreachable code", .{}, &[_]u32{ 434 try gz.astgen.errNoteNode(node, "control flow is diverted here", .{}), 435 }); 436 } 437 return result_inst; 438 } 439 440 fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 441 const astgen = gz.astgen; 442 const tree = astgen.tree; 443 switch (tree.nodeTag(node)) { 444 .root => unreachable, 445 .test_decl => unreachable, 446 .global_var_decl => unreachable, 447 .local_var_decl => unreachable, 448 .simple_var_decl => unreachable, 449 .aligned_var_decl => unreachable, 450 .switch_case => unreachable, 451 .switch_case_inline => unreachable, 452 .switch_case_one => unreachable, 453 .switch_case_inline_one => unreachable, 454 .container_field_init => unreachable, 455 .container_field_align => unreachable, 456 .container_field => unreachable, 457 .asm_output => unreachable, 458 .asm_input => unreachable, 459 460 .assign, 461 .assign_destructure, 462 .assign_bit_and, 463 .assign_bit_or, 464 .assign_shl, 465 .assign_shl_sat, 466 .assign_shr, 467 .assign_bit_xor, 468 .assign_div, 469 .assign_sub, 470 .assign_sub_wrap, 471 .assign_sub_sat, 472 .assign_mod, 473 .assign_add, 474 .assign_add_wrap, 475 .assign_add_sat, 476 .assign_mul, 477 .assign_mul_wrap, 478 .assign_mul_sat, 479 .add, 480 .add_wrap, 481 .add_sat, 482 .sub, 483 .sub_wrap, 484 .sub_sat, 485 .mul, 486 .mul_wrap, 487 .mul_sat, 488 .div, 489 .mod, 490 .bit_and, 491 .bit_or, 492 .shl, 493 .shl_sat, 494 .shr, 495 .bit_xor, 496 .bang_equal, 497 .equal_equal, 498 .greater_than, 499 .greater_or_equal, 500 .less_than, 501 .less_or_equal, 502 .array_cat, 503 .array_mult, 504 .bool_and, 505 .bool_or, 506 .@"asm", 507 .asm_simple, 508 .asm_legacy, 509 .string_literal, 510 .number_literal, 511 .call, 512 .call_comma, 513 .call_one, 514 .call_one_comma, 515 .unreachable_literal, 516 .@"return", 517 .@"if", 518 .if_simple, 519 .@"while", 520 .while_simple, 521 .while_cont, 522 .bool_not, 523 .address_of, 524 .optional_type, 525 .block, 526 .block_semicolon, 527 .block_two, 528 .block_two_semicolon, 529 .@"break", 530 .ptr_type_aligned, 531 .ptr_type_sentinel, 532 .ptr_type, 533 .ptr_type_bit_range, 534 .array_type, 535 .array_type_sentinel, 536 .enum_literal, 537 .multiline_string_literal, 538 .char_literal, 539 .@"defer", 540 .@"errdefer", 541 .@"catch", 542 .error_union, 543 .merge_error_sets, 544 .switch_range, 545 .for_range, 546 .bit_not, 547 .negation, 548 .negation_wrap, 549 .@"resume", 550 .@"try", 551 .slice, 552 .slice_open, 553 .slice_sentinel, 554 .array_init_one, 555 .array_init_one_comma, 556 .array_init_dot_two, 557 .array_init_dot_two_comma, 558 .array_init_dot, 559 .array_init_dot_comma, 560 .array_init, 561 .array_init_comma, 562 .struct_init_one, 563 .struct_init_one_comma, 564 .struct_init_dot_two, 565 .struct_init_dot_two_comma, 566 .struct_init_dot, 567 .struct_init_dot_comma, 568 .struct_init, 569 .struct_init_comma, 570 .@"switch", 571 .switch_comma, 572 .@"for", 573 .for_simple, 574 .@"suspend", 575 .@"continue", 576 .fn_proto_simple, 577 .fn_proto_multi, 578 .fn_proto_one, 579 .fn_proto, 580 .fn_decl, 581 .anyframe_type, 582 .anyframe_literal, 583 .error_set_decl, 584 .container_decl, 585 .container_decl_trailing, 586 .container_decl_two, 587 .container_decl_two_trailing, 588 .container_decl_arg, 589 .container_decl_arg_trailing, 590 .tagged_union, 591 .tagged_union_trailing, 592 .tagged_union_two, 593 .tagged_union_two_trailing, 594 .tagged_union_enum_tag, 595 .tagged_union_enum_tag_trailing, 596 .@"comptime", 597 .@"nosuspend", 598 .error_value, 599 => return astgen.failNode(node, "invalid left-hand side to assignment", .{}), 600 601 .builtin_call, 602 .builtin_call_comma, 603 .builtin_call_two, 604 .builtin_call_two_comma, 605 => { 606 const builtin_token = tree.nodeMainToken(node); 607 const builtin_name = tree.tokenSlice(builtin_token); 608 // If the builtin is an invalid name, we don't cause an error here; instead 609 // let it pass, and the error will be "invalid builtin function" later. 610 if (BuiltinFn.list.get(builtin_name)) |info| { 611 if (!info.allows_lvalue) { 612 return astgen.failNode(node, "invalid left-hand side to assignment", .{}); 613 } 614 } 615 }, 616 617 // These can be assigned to. 618 .unwrap_optional, 619 .deref, 620 .field_access, 621 .array_access, 622 .identifier, 623 .grouped_expression, 624 .@"orelse", 625 => {}, 626 } 627 return expr(gz, scope, .{ .rl = .ref }, node); 628 } 629 630 /// Turn Zig AST into untyped ZIR instructions. 631 /// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the 632 /// result instruction can be used to inspect whether it is isNoReturn() but that is it, 633 /// it must otherwise not be used. 634 fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 635 const astgen = gz.astgen; 636 const tree = astgen.tree; 637 638 switch (tree.nodeTag(node)) { 639 .root => unreachable, // Top-level declaration. 640 .test_decl => unreachable, // Top-level declaration. 641 .container_field_init => unreachable, // Top-level declaration. 642 .container_field_align => unreachable, // Top-level declaration. 643 .container_field => unreachable, // Top-level declaration. 644 .fn_decl => unreachable, // Top-level declaration. 645 646 .global_var_decl => unreachable, // Handled in `blockExpr`. 647 .local_var_decl => unreachable, // Handled in `blockExpr`. 648 .simple_var_decl => unreachable, // Handled in `blockExpr`. 649 .aligned_var_decl => unreachable, // Handled in `blockExpr`. 650 .@"defer" => unreachable, // Handled in `blockExpr`. 651 .@"errdefer" => unreachable, // Handled in `blockExpr`. 652 653 .switch_case => unreachable, // Handled in `switchExpr`. 654 .switch_case_inline => unreachable, // Handled in `switchExpr`. 655 .switch_case_one => unreachable, // Handled in `switchExpr`. 656 .switch_case_inline_one => unreachable, // Handled in `switchExpr`. 657 .switch_range => unreachable, // Handled in `switchExpr`. 658 659 .asm_output => unreachable, // Handled in `asmExpr`. 660 .asm_input => unreachable, // Handled in `asmExpr`. 661 662 .for_range => unreachable, // Handled in `forExpr`. 663 664 .assign => { 665 try assign(gz, scope, node); 666 return rvalue(gz, ri, .void_value, node); 667 }, 668 669 .assign_destructure => { 670 // Note that this variant does not declare any new var/const: that 671 // variant is handled by `blockExprStmts`. 672 try assignDestructure(gz, scope, node); 673 return rvalue(gz, ri, .void_value, node); 674 }, 675 676 .assign_shl => { 677 try assignShift(gz, scope, node, .shl); 678 return rvalue(gz, ri, .void_value, node); 679 }, 680 .assign_shl_sat => { 681 try assignShiftSat(gz, scope, node); 682 return rvalue(gz, ri, .void_value, node); 683 }, 684 .assign_shr => { 685 try assignShift(gz, scope, node, .shr); 686 return rvalue(gz, ri, .void_value, node); 687 }, 688 689 .assign_bit_and => { 690 try assignOp(gz, scope, node, .bit_and); 691 return rvalue(gz, ri, .void_value, node); 692 }, 693 .assign_bit_or => { 694 try assignOp(gz, scope, node, .bit_or); 695 return rvalue(gz, ri, .void_value, node); 696 }, 697 .assign_bit_xor => { 698 try assignOp(gz, scope, node, .xor); 699 return rvalue(gz, ri, .void_value, node); 700 }, 701 .assign_div => { 702 try assignOp(gz, scope, node, .div); 703 return rvalue(gz, ri, .void_value, node); 704 }, 705 .assign_sub => { 706 try assignOp(gz, scope, node, .sub); 707 return rvalue(gz, ri, .void_value, node); 708 }, 709 .assign_sub_wrap => { 710 try assignOp(gz, scope, node, .subwrap); 711 return rvalue(gz, ri, .void_value, node); 712 }, 713 .assign_sub_sat => { 714 try assignOp(gz, scope, node, .sub_sat); 715 return rvalue(gz, ri, .void_value, node); 716 }, 717 .assign_mod => { 718 try assignOp(gz, scope, node, .mod_rem); 719 return rvalue(gz, ri, .void_value, node); 720 }, 721 .assign_add => { 722 try assignOp(gz, scope, node, .add); 723 return rvalue(gz, ri, .void_value, node); 724 }, 725 .assign_add_wrap => { 726 try assignOp(gz, scope, node, .addwrap); 727 return rvalue(gz, ri, .void_value, node); 728 }, 729 .assign_add_sat => { 730 try assignOp(gz, scope, node, .add_sat); 731 return rvalue(gz, ri, .void_value, node); 732 }, 733 .assign_mul => { 734 try assignOp(gz, scope, node, .mul); 735 return rvalue(gz, ri, .void_value, node); 736 }, 737 .assign_mul_wrap => { 738 try assignOp(gz, scope, node, .mulwrap); 739 return rvalue(gz, ri, .void_value, node); 740 }, 741 .assign_mul_sat => { 742 try assignOp(gz, scope, node, .mul_sat); 743 return rvalue(gz, ri, .void_value, node); 744 }, 745 746 // zig fmt: off 747 .shl => return shiftOp(gz, scope, ri, node, tree.nodeData(node).node_and_node[0], tree.nodeData(node).node_and_node[1], .shl), 748 .shr => return shiftOp(gz, scope, ri, node, tree.nodeData(node).node_and_node[0], tree.nodeData(node).node_and_node[1], .shr), 749 750 .add => return simpleBinOp(gz, scope, ri, node, .add), 751 .add_wrap => return simpleBinOp(gz, scope, ri, node, .addwrap), 752 .add_sat => return simpleBinOp(gz, scope, ri, node, .add_sat), 753 .sub => return simpleBinOp(gz, scope, ri, node, .sub), 754 .sub_wrap => return simpleBinOp(gz, scope, ri, node, .subwrap), 755 .sub_sat => return simpleBinOp(gz, scope, ri, node, .sub_sat), 756 .mul => return simpleBinOp(gz, scope, ri, node, .mul), 757 .mul_wrap => return simpleBinOp(gz, scope, ri, node, .mulwrap), 758 .mul_sat => return simpleBinOp(gz, scope, ri, node, .mul_sat), 759 .div => return simpleBinOp(gz, scope, ri, node, .div), 760 .mod => return simpleBinOp(gz, scope, ri, node, .mod_rem), 761 .shl_sat => return simpleBinOp(gz, scope, ri, node, .shl_sat), 762 763 .bit_and => return simpleBinOp(gz, scope, ri, node, .bit_and), 764 .bit_or => return simpleBinOp(gz, scope, ri, node, .bit_or), 765 .bit_xor => return simpleBinOp(gz, scope, ri, node, .xor), 766 .bang_equal => return simpleBinOp(gz, scope, ri, node, .cmp_neq), 767 .equal_equal => return simpleBinOp(gz, scope, ri, node, .cmp_eq), 768 .greater_than => return simpleBinOp(gz, scope, ri, node, .cmp_gt), 769 .greater_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_gte), 770 .less_than => return simpleBinOp(gz, scope, ri, node, .cmp_lt), 771 .less_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_lte), 772 .array_cat => return simpleBinOp(gz, scope, ri, node, .array_cat), 773 774 .array_mult => { 775 // This syntax form does not currently use the result type in the language specification. 776 // However, the result type can be used to emit more optimal code for large multiplications by 777 // having Sema perform a coercion before the multiplication operation. 778 const lhs_node, const rhs_node = tree.nodeData(node).node_and_node; 779 const result = try gz.addPlNode(.array_mul, node, Zir.Inst.ArrayMul{ 780 .res_ty = if (try ri.rl.resultType(gz, node)) |t| t else .none, 781 .lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node), 782 .rhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, rhs_node, .array_mul_factor), 783 }); 784 return rvalue(gz, ri, result, node); 785 }, 786 787 .error_union, .merge_error_sets => |tag| { 788 const inst_tag: Zir.Inst.Tag = switch (tag) { 789 .error_union => .error_union_type, 790 .merge_error_sets => .merge_error_sets, 791 else => unreachable, 792 }; 793 const lhs_node, const rhs_node = tree.nodeData(node).node_and_node; 794 const lhs = try reachableTypeExpr(gz, scope, lhs_node, node); 795 const rhs = try reachableTypeExpr(gz, scope, rhs_node, node); 796 const result = try gz.addPlNode(inst_tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); 797 return rvalue(gz, ri, result, node); 798 }, 799 800 .bool_and => return boolBinOp(gz, scope, ri, node, .bool_br_and), 801 .bool_or => return boolBinOp(gz, scope, ri, node, .bool_br_or), 802 803 .bool_not => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, tree.nodeData(node).node, .bool_not), 804 .bit_not => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, tree.nodeData(node).node, .bit_not), 805 806 .negation => return negation(gz, scope, ri, node), 807 .negation_wrap => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, tree.nodeData(node).node, .negate_wrap), 808 809 .identifier => return identifier(gz, scope, ri, node, null), 810 811 .asm_simple, 812 .@"asm", 813 => return asmExpr(gz, scope, ri, node, tree.fullAsm(node).?), 814 815 .asm_legacy => { 816 return astgen.failNodeNotes(node, "legacy asm clobbers syntax", .{}, &[_]u32{ 817 try astgen.errNoteNode(node, "use 'zig fmt' to auto-upgrade", .{}), 818 }); 819 }, 820 821 .string_literal => return stringLiteral(gz, ri, node), 822 .multiline_string_literal => return multilineStringLiteral(gz, ri, node), 823 824 .number_literal => return numberLiteral(gz, ri, node, node, .positive), 825 // zig fmt: on 826 827 .builtin_call_two, 828 .builtin_call_two_comma, 829 .builtin_call, 830 .builtin_call_comma, 831 => { 832 var buf: [2]Ast.Node.Index = undefined; 833 const params = tree.builtinCallParams(&buf, node).?; 834 return builtinCall(gz, scope, ri, node, params, false); 835 }, 836 837 .call_one, 838 .call_one_comma, 839 .call, 840 .call_comma, 841 => { 842 var buf: [1]Ast.Node.Index = undefined; 843 return callExpr(gz, scope, ri, .none, node, tree.fullCall(&buf, node).?); 844 }, 845 846 .unreachable_literal => { 847 try emitDbgNode(gz, node); 848 _ = try gz.addAsIndex(.{ 849 .tag = .@"unreachable", 850 .data = .{ .@"unreachable" = .{ 851 .src_node = gz.nodeIndexToRelative(node), 852 } }, 853 }); 854 return Zir.Inst.Ref.unreachable_value; 855 }, 856 .@"return" => return ret(gz, scope, node), 857 .field_access => return fieldAccess(gz, scope, ri, node), 858 859 .if_simple, 860 .@"if", 861 => { 862 const if_full = tree.fullIf(node).?; 863 no_switch_on_err: { 864 const error_token = if_full.error_token orelse break :no_switch_on_err; 865 const else_node = if_full.ast.else_expr.unwrap() orelse break :no_switch_on_err; 866 const full_switch = tree.fullSwitch(else_node) orelse break :no_switch_on_err; 867 if (full_switch.label_token != null) break :no_switch_on_err; 868 if (tree.nodeTag(full_switch.ast.condition) != .identifier) break :no_switch_on_err; 869 if (!mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(tree.nodeMainToken(full_switch.ast.condition)))) break :no_switch_on_err; 870 return switchExprErrUnion(gz, scope, ri.br(), node, .@"if"); 871 } 872 return ifExpr(gz, scope, ri.br(), node, if_full); 873 }, 874 875 .while_simple, 876 .while_cont, 877 .@"while", 878 => return whileExpr(gz, scope, ri.br(), node, tree.fullWhile(node).?, false), 879 880 .for_simple, .@"for" => return forExpr(gz, scope, ri.br(), node, tree.fullFor(node).?, false), 881 882 .slice_open, 883 .slice, 884 .slice_sentinel, 885 => { 886 const full = tree.fullSlice(node).?; 887 if (full.ast.end != .none and 888 tree.nodeTag(full.ast.sliced) == .slice_open and 889 nodeIsTriviallyZero(tree, full.ast.start)) 890 { 891 const lhs_extra = tree.sliceOpen(full.ast.sliced).ast; 892 893 const lhs = try expr(gz, scope, .{ .rl = .ref }, lhs_extra.sliced); 894 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start); 895 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 896 const len = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.end.unwrap().?); 897 const sentinel = if (full.ast.sentinel.unwrap()) |sentinel| try expr(gz, scope, .{ .rl = .none }, sentinel) else .none; 898 try emitDbgStmt(gz, cursor); 899 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{ 900 .lhs = lhs, 901 .start = start, 902 .len = len, 903 .start_src_node_offset = gz.nodeIndexToRelative(full.ast.sliced), 904 .sentinel = sentinel, 905 }); 906 return rvalue(gz, ri, result, node); 907 } 908 const lhs = try expr(gz, scope, .{ .rl = .ref }, full.ast.sliced); 909 910 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 911 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.start); 912 const end = if (full.ast.end.unwrap()) |end| try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, end) else .none; 913 const sentinel = if (full.ast.sentinel.unwrap()) |sentinel| s: { 914 const sentinel_ty = try gz.addUnNode(.slice_sentinel_ty, lhs, node); 915 break :s try expr(gz, scope, .{ .rl = .{ .coerced_ty = sentinel_ty } }, sentinel); 916 } else .none; 917 try emitDbgStmt(gz, cursor); 918 if (sentinel != .none) { 919 const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{ 920 .lhs = lhs, 921 .start = start, 922 .end = end, 923 .sentinel = sentinel, 924 }); 925 return rvalue(gz, ri, result, node); 926 } else if (end != .none) { 927 const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{ 928 .lhs = lhs, 929 .start = start, 930 .end = end, 931 }); 932 return rvalue(gz, ri, result, node); 933 } else { 934 const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{ 935 .lhs = lhs, 936 .start = start, 937 }); 938 return rvalue(gz, ri, result, node); 939 } 940 }, 941 942 .deref => { 943 const lhs = try expr(gz, scope, .{ .rl = .none }, tree.nodeData(node).node); 944 _ = try gz.addUnNode(.validate_deref, lhs, node); 945 switch (ri.rl) { 946 .ref, .ref_coerced_ty => return lhs, 947 else => { 948 const result = try gz.addUnNode(.load, lhs, node); 949 return rvalue(gz, ri, result, node); 950 }, 951 } 952 }, 953 .address_of => { 954 const operand_rl: ResultInfo.Loc = if (try ri.rl.resultType(gz, node)) |res_ty_inst| rl: { 955 _ = try gz.addUnTok(.validate_ref_ty, res_ty_inst, tree.firstToken(node)); 956 break :rl .{ .ref_coerced_ty = res_ty_inst }; 957 } else .ref; 958 const result = try expr(gz, scope, .{ .rl = operand_rl }, tree.nodeData(node).node); 959 return rvalue(gz, ri, result, node); 960 }, 961 .optional_type => { 962 const operand = try typeExpr(gz, scope, tree.nodeData(node).node); 963 const result = try gz.addUnNode(.optional_type, operand, node); 964 return rvalue(gz, ri, result, node); 965 }, 966 .unwrap_optional => switch (ri.rl) { 967 .ref, .ref_coerced_ty => { 968 const lhs = try expr(gz, scope, .{ .rl = .ref }, tree.nodeData(node).node_and_token[0]); 969 970 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 971 try emitDbgStmt(gz, cursor); 972 973 return gz.addUnNode(.optional_payload_safe_ptr, lhs, node); 974 }, 975 else => { 976 const lhs = try expr(gz, scope, .{ .rl = .none }, tree.nodeData(node).node_and_token[0]); 977 978 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 979 try emitDbgStmt(gz, cursor); 980 981 return rvalue(gz, ri, try gz.addUnNode(.optional_payload_safe, lhs, node), node); 982 }, 983 }, 984 .block_two, 985 .block_two_semicolon, 986 .block, 987 .block_semicolon, 988 => { 989 var buf: [2]Ast.Node.Index = undefined; 990 const statements = tree.blockStatements(&buf, node).?; 991 return blockExpr(gz, scope, ri, node, statements, .normal); 992 }, 993 .enum_literal => if (try ri.rl.resultType(gz, node)) |res_ty| { 994 const str_index = try astgen.identAsString(tree.nodeMainToken(node)); 995 const res = try gz.addPlNode(.decl_literal, node, Zir.Inst.Field{ 996 .lhs = res_ty, 997 .field_name_start = str_index, 998 }); 999 switch (ri.rl) { 1000 .discard, .none, .ref => unreachable, // no result type 1001 .ty, .coerced_ty => return res, // `decl_literal` does the coercion for us 1002 .ref_coerced_ty, .ptr, .inferred_ptr, .destructure => return rvalue(gz, ri, res, node), 1003 } 1004 } else return simpleStrTok(gz, ri, tree.nodeMainToken(node), node, .enum_literal), 1005 .error_value => return simpleStrTok(gz, ri, tree.nodeMainToken(node) + 2, node, .error_value), 1006 // TODO restore this when implementing https://github.com/ziglang/zig/issues/6025 1007 // .anyframe_literal => return rvalue(gz, ri, .anyframe_type, node), 1008 .anyframe_literal => { 1009 const result = try gz.addUnNode(.anyframe_type, .void_type, node); 1010 return rvalue(gz, ri, result, node); 1011 }, 1012 .anyframe_type => { 1013 const return_type = try typeExpr(gz, scope, tree.nodeData(node).token_and_node[1]); 1014 const result = try gz.addUnNode(.anyframe_type, return_type, node); 1015 return rvalue(gz, ri, result, node); 1016 }, 1017 .@"catch" => { 1018 const catch_token = tree.nodeMainToken(node); 1019 const payload_token: ?Ast.TokenIndex = if (tree.tokenTag(catch_token + 1) == .pipe) 1020 catch_token + 2 1021 else 1022 null; 1023 no_switch_on_err: { 1024 const capture_token = payload_token orelse break :no_switch_on_err; 1025 const full_switch = tree.fullSwitch(tree.nodeData(node).node_and_node[1]) orelse break :no_switch_on_err; 1026 if (full_switch.label_token != null) break :no_switch_on_err; 1027 if (tree.nodeTag(full_switch.ast.condition) != .identifier) break :no_switch_on_err; 1028 if (!mem.eql(u8, tree.tokenSlice(capture_token), tree.tokenSlice(tree.nodeMainToken(full_switch.ast.condition)))) break :no_switch_on_err; 1029 return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch"); 1030 } 1031 switch (ri.rl) { 1032 .ref, .ref_coerced_ty => return orelseCatchExpr( 1033 gz, 1034 scope, 1035 ri, 1036 node, 1037 .is_non_err_ptr, 1038 .err_union_payload_unsafe_ptr, 1039 .err_union_code_ptr, 1040 payload_token, 1041 ), 1042 else => return orelseCatchExpr( 1043 gz, 1044 scope, 1045 ri, 1046 node, 1047 .is_non_err, 1048 .err_union_payload_unsafe, 1049 .err_union_code, 1050 payload_token, 1051 ), 1052 } 1053 }, 1054 .@"orelse" => switch (ri.rl) { 1055 .ref, .ref_coerced_ty => return orelseCatchExpr( 1056 gz, 1057 scope, 1058 ri, 1059 node, 1060 .is_non_null_ptr, 1061 .optional_payload_unsafe_ptr, 1062 undefined, 1063 null, 1064 ), 1065 else => return orelseCatchExpr( 1066 gz, 1067 scope, 1068 ri, 1069 node, 1070 .is_non_null, 1071 .optional_payload_unsafe, 1072 undefined, 1073 null, 1074 ), 1075 }, 1076 1077 .ptr_type_aligned, 1078 .ptr_type_sentinel, 1079 .ptr_type, 1080 .ptr_type_bit_range, 1081 => return ptrType(gz, scope, ri, node, tree.fullPtrType(node).?), 1082 1083 .container_decl, 1084 .container_decl_trailing, 1085 .container_decl_arg, 1086 .container_decl_arg_trailing, 1087 .container_decl_two, 1088 .container_decl_two_trailing, 1089 .tagged_union, 1090 .tagged_union_trailing, 1091 .tagged_union_enum_tag, 1092 .tagged_union_enum_tag_trailing, 1093 .tagged_union_two, 1094 .tagged_union_two_trailing, 1095 => { 1096 var buf: [2]Ast.Node.Index = undefined; 1097 return containerDecl(gz, scope, ri, node, tree.fullContainerDecl(&buf, node).?, .anon); 1098 }, 1099 1100 .@"break" => return breakExpr(gz, scope, node), 1101 .@"continue" => return continueExpr(gz, scope, node), 1102 .grouped_expression => return expr(gz, scope, ri, tree.nodeData(node).node_and_token[0]), 1103 .array_type => return arrayType(gz, scope, ri, node), 1104 .array_type_sentinel => return arrayTypeSentinel(gz, scope, ri, node), 1105 .char_literal => return charLiteral(gz, ri, node), 1106 .error_set_decl => return errorSetDecl(gz, ri, node), 1107 .array_access => return arrayAccess(gz, scope, ri, node), 1108 .@"comptime" => return comptimeExprAst(gz, scope, ri, node), 1109 .@"switch", .switch_comma => return switchExpr(gz, scope, ri.br(), node, tree.fullSwitch(node).?), 1110 1111 .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node), 1112 .@"suspend" => return suspendExpr(gz, scope, node), 1113 .@"resume" => return resumeExpr(gz, scope, ri, node), 1114 1115 .@"try" => return tryExpr(gz, scope, ri, node, tree.nodeData(node).node), 1116 1117 .array_init_one, 1118 .array_init_one_comma, 1119 .array_init_dot_two, 1120 .array_init_dot_two_comma, 1121 .array_init_dot, 1122 .array_init_dot_comma, 1123 .array_init, 1124 .array_init_comma, 1125 => { 1126 var buf: [2]Ast.Node.Index = undefined; 1127 return arrayInitExpr(gz, scope, ri, node, tree.fullArrayInit(&buf, node).?); 1128 }, 1129 1130 .struct_init_one, 1131 .struct_init_one_comma, 1132 .struct_init_dot_two, 1133 .struct_init_dot_two_comma, 1134 .struct_init_dot, 1135 .struct_init_dot_comma, 1136 .struct_init, 1137 .struct_init_comma, 1138 => { 1139 var buf: [2]Ast.Node.Index = undefined; 1140 return structInitExpr(gz, scope, ri, node, tree.fullStructInit(&buf, node).?); 1141 }, 1142 1143 .fn_proto_simple, 1144 .fn_proto_multi, 1145 .fn_proto_one, 1146 .fn_proto, 1147 => { 1148 var buf: [1]Ast.Node.Index = undefined; 1149 return fnProtoExpr(gz, scope, ri, node, tree.fullFnProto(&buf, node).?); 1150 }, 1151 } 1152 } 1153 1154 /// When a name strategy other than `.anon` is available, for instance when analyzing the init expr 1155 /// of a variable declaration, try this function before `expr`/`comptimeExpr`/etc, so that the name 1156 /// strategy can be applied if necessary. If `null` is returned, then `node` does not consume a name 1157 /// strategy, and a normal evaluation function like `expr` should be used instead. Otherwise, `node` 1158 /// does consume a name strategy; the expression has been evaluated like `expr`, but using the given 1159 /// name strategy. 1160 fn nameStratExpr( 1161 gz: *GenZir, 1162 scope: *Scope, 1163 ri: ResultInfo, 1164 node: Ast.Node.Index, 1165 name_strat: Zir.Inst.NameStrategy, 1166 ) InnerError!?Zir.Inst.Ref { 1167 const astgen = gz.astgen; 1168 const tree = astgen.tree; 1169 switch (tree.nodeTag(node)) { 1170 .container_decl, 1171 .container_decl_trailing, 1172 .container_decl_two, 1173 .container_decl_two_trailing, 1174 .container_decl_arg, 1175 .container_decl_arg_trailing, 1176 .tagged_union, 1177 .tagged_union_trailing, 1178 .tagged_union_two, 1179 .tagged_union_two_trailing, 1180 .tagged_union_enum_tag, 1181 .tagged_union_enum_tag_trailing, 1182 => { 1183 var buf: [2]Ast.Node.Index = undefined; 1184 return try containerDecl(gz, scope, ri, node, tree.fullContainerDecl(&buf, node).?, name_strat); 1185 }, 1186 .builtin_call_two, 1187 .builtin_call_two_comma, 1188 => { 1189 const builtin_token = tree.nodeMainToken(node); 1190 const builtin_name = tree.tokenSlice(builtin_token); 1191 if (!std.mem.eql(u8, builtin_name, "@Type")) return null; 1192 var buf: [2]Ast.Node.Index = undefined; 1193 const params = tree.builtinCallParams(&buf, node).?; 1194 if (params.len != 1) return null; // let `builtinCall` error 1195 return try builtinReify(gz, scope, ri, node, params[0], name_strat); 1196 }, 1197 else => return null, 1198 } 1199 } 1200 1201 fn nosuspendExpr( 1202 gz: *GenZir, 1203 scope: *Scope, 1204 ri: ResultInfo, 1205 node: Ast.Node.Index, 1206 ) InnerError!Zir.Inst.Ref { 1207 const astgen = gz.astgen; 1208 const tree = astgen.tree; 1209 const body_node = tree.nodeData(node).node; 1210 if (gz.nosuspend_node.unwrap()) |nosuspend_node| { 1211 try astgen.appendErrorNodeNotes(node, "redundant nosuspend block", .{}, &[_]u32{ 1212 try astgen.errNoteNode(nosuspend_node, "other nosuspend block here", .{}), 1213 }); 1214 } 1215 gz.nosuspend_node = node.toOptional(); 1216 defer gz.nosuspend_node = .none; 1217 return expr(gz, scope, ri, body_node); 1218 } 1219 1220 fn suspendExpr( 1221 gz: *GenZir, 1222 scope: *Scope, 1223 node: Ast.Node.Index, 1224 ) InnerError!Zir.Inst.Ref { 1225 const astgen = gz.astgen; 1226 const gpa = astgen.gpa; 1227 const tree = astgen.tree; 1228 const body_node = tree.nodeData(node).node; 1229 1230 if (gz.nosuspend_node.unwrap()) |nosuspend_node| { 1231 return astgen.failNodeNotes(node, "suspend inside nosuspend block", .{}, &[_]u32{ 1232 try astgen.errNoteNode(nosuspend_node, "nosuspend block here", .{}), 1233 }); 1234 } 1235 if (gz.suspend_node.unwrap()) |suspend_node| { 1236 return astgen.failNodeNotes(node, "cannot suspend inside suspend block", .{}, &[_]u32{ 1237 try astgen.errNoteNode(suspend_node, "other suspend block here", .{}), 1238 }); 1239 } 1240 1241 const suspend_inst = try gz.makeBlockInst(.suspend_block, node); 1242 try gz.instructions.append(gpa, suspend_inst); 1243 1244 var suspend_scope = gz.makeSubBlock(scope); 1245 suspend_scope.suspend_node = node.toOptional(); 1246 defer suspend_scope.unstack(); 1247 1248 const body_result = try fullBodyExpr(&suspend_scope, &suspend_scope.base, .{ .rl = .none }, body_node, .normal); 1249 if (!gz.refIsNoReturn(body_result)) { 1250 _ = try suspend_scope.addBreak(.break_inline, suspend_inst, .void_value); 1251 } 1252 try suspend_scope.setBlockBody(suspend_inst); 1253 1254 return suspend_inst.toRef(); 1255 } 1256 1257 fn resumeExpr( 1258 gz: *GenZir, 1259 scope: *Scope, 1260 ri: ResultInfo, 1261 node: Ast.Node.Index, 1262 ) InnerError!Zir.Inst.Ref { 1263 const astgen = gz.astgen; 1264 const tree = astgen.tree; 1265 const rhs_node = tree.nodeData(node).node; 1266 const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node); 1267 const result = try gz.addUnNode(.@"resume", operand, node); 1268 return rvalue(gz, ri, result, node); 1269 } 1270 1271 fn fnProtoExpr( 1272 gz: *GenZir, 1273 scope: *Scope, 1274 ri: ResultInfo, 1275 node: Ast.Node.Index, 1276 fn_proto: Ast.full.FnProto, 1277 ) InnerError!Zir.Inst.Ref { 1278 const astgen = gz.astgen; 1279 const tree = astgen.tree; 1280 1281 if (fn_proto.name_token) |some| { 1282 return astgen.failTok(some, "function type cannot have a name", .{}); 1283 } 1284 1285 if (fn_proto.ast.align_expr.unwrap()) |align_expr| { 1286 return astgen.failNode(align_expr, "function type cannot have an alignment", .{}); 1287 } 1288 1289 if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| { 1290 return astgen.failNode(addrspace_expr, "function type cannot have an addrspace", .{}); 1291 } 1292 1293 if (fn_proto.ast.section_expr.unwrap()) |section_expr| { 1294 return astgen.failNode(section_expr, "function type cannot have a linksection", .{}); 1295 } 1296 1297 const return_type = fn_proto.ast.return_type.unwrap().?; 1298 const maybe_bang = tree.firstToken(return_type) - 1; 1299 const is_inferred_error = tree.tokenTag(maybe_bang) == .bang; 1300 if (is_inferred_error) { 1301 return astgen.failTok(maybe_bang, "function type cannot have an inferred error set", .{}); 1302 } 1303 1304 const is_extern = blk: { 1305 const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false; 1306 break :blk tree.tokenTag(maybe_extern_token) == .keyword_extern; 1307 }; 1308 assert(!is_extern); 1309 1310 return fnProtoExprInner(gz, scope, ri, node, fn_proto, false); 1311 } 1312 1313 fn fnProtoExprInner( 1314 gz: *GenZir, 1315 scope: *Scope, 1316 ri: ResultInfo, 1317 node: Ast.Node.Index, 1318 fn_proto: Ast.full.FnProto, 1319 implicit_ccc: bool, 1320 ) InnerError!Zir.Inst.Ref { 1321 const astgen = gz.astgen; 1322 const tree = astgen.tree; 1323 1324 var block_scope = gz.makeSubBlock(scope); 1325 defer block_scope.unstack(); 1326 1327 const block_inst = try gz.makeBlockInst(.block_inline, node); 1328 1329 var noalias_bits: u32 = 0; 1330 const is_var_args = is_var_args: { 1331 var param_type_i: usize = 0; 1332 var it = fn_proto.iterate(tree); 1333 while (it.next()) |param| : (param_type_i += 1) { 1334 const is_comptime = if (param.comptime_noalias) |token| switch (tree.tokenTag(token)) { 1335 .keyword_noalias => is_comptime: { 1336 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse 1337 return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{})); 1338 break :is_comptime false; 1339 }, 1340 .keyword_comptime => true, 1341 else => false, 1342 } else false; 1343 1344 const is_anytype = if (param.anytype_ellipsis3) |token| blk: { 1345 switch (tree.tokenTag(token)) { 1346 .keyword_anytype => break :blk true, 1347 .ellipsis3 => break :is_var_args true, 1348 else => unreachable, 1349 } 1350 } else false; 1351 1352 const param_name = if (param.name_token) |name_token| blk: { 1353 if (mem.eql(u8, "_", tree.tokenSlice(name_token))) 1354 break :blk .empty; 1355 1356 break :blk try astgen.identAsString(name_token); 1357 } else .empty; 1358 1359 if (is_anytype) { 1360 const name_token = param.name_token orelse param.anytype_ellipsis3.?; 1361 1362 const tag: Zir.Inst.Tag = if (is_comptime) 1363 .param_anytype_comptime 1364 else 1365 .param_anytype; 1366 _ = try block_scope.addStrTok(tag, param_name, name_token); 1367 } else { 1368 const param_type_node = param.type_expr.?; 1369 var param_gz = block_scope.makeSubBlock(scope); 1370 defer param_gz.unstack(); 1371 param_gz.is_comptime = true; 1372 const param_type = try fullBodyExpr(¶m_gz, scope, coerced_type_ri, param_type_node, .normal); 1373 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1); 1374 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node); 1375 const name_token = param.name_token orelse tree.nodeMainToken(param_type_node); 1376 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param; 1377 // We pass `prev_param_insts` as `&.{}` here because a function prototype can't refer to previous 1378 // arguments (we haven't set up scopes here). 1379 const param_inst = try block_scope.addParam(¶m_gz, &.{}, false, tag, name_token, param_name); 1380 assert(param_inst_expected == param_inst); 1381 } 1382 } 1383 break :is_var_args false; 1384 }; 1385 1386 const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| 1387 try comptimeExpr( 1388 &block_scope, 1389 scope, 1390 .{ .rl = .{ .coerced_ty = try block_scope.addBuiltinValue(callconv_expr, .calling_convention) } }, 1391 callconv_expr, 1392 .@"callconv", 1393 ) 1394 else if (implicit_ccc) 1395 try block_scope.addBuiltinValue(node, .calling_convention_c) 1396 else 1397 .none; 1398 1399 const ret_ty_node = fn_proto.ast.return_type.unwrap().?; 1400 const ret_ty = try comptimeExpr(&block_scope, scope, coerced_type_ri, ret_ty_node, .function_ret_ty); 1401 1402 const result = try block_scope.addFunc(.{ 1403 .src_node = fn_proto.ast.proto_node, 1404 1405 .cc_ref = cc, 1406 .cc_gz = null, 1407 .ret_ref = ret_ty, 1408 .ret_gz = null, 1409 1410 .ret_param_refs = &.{}, 1411 .param_insts = &.{}, 1412 .ret_ty_is_generic = false, 1413 1414 .param_block = block_inst, 1415 .body_gz = null, 1416 .is_var_args = is_var_args, 1417 .is_inferred_error = false, 1418 .is_noinline = false, 1419 .noalias_bits = noalias_bits, 1420 1421 .proto_hash = undefined, // ignored for `body_gz == null` 1422 }); 1423 1424 _ = try block_scope.addBreak(.break_inline, block_inst, result); 1425 try block_scope.setBlockBody(block_inst); 1426 try gz.instructions.append(astgen.gpa, block_inst); 1427 1428 return rvalue(gz, ri, block_inst.toRef(), fn_proto.ast.proto_node); 1429 } 1430 1431 fn arrayInitExpr( 1432 gz: *GenZir, 1433 scope: *Scope, 1434 ri: ResultInfo, 1435 node: Ast.Node.Index, 1436 array_init: Ast.full.ArrayInit, 1437 ) InnerError!Zir.Inst.Ref { 1438 const astgen = gz.astgen; 1439 const tree = astgen.tree; 1440 1441 assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init. 1442 1443 const array_ty: Zir.Inst.Ref, const elem_ty: Zir.Inst.Ref = inst: { 1444 const type_expr = array_init.ast.type_expr.unwrap() orelse break :inst .{ .none, .none }; 1445 1446 infer: { 1447 const array_type: Ast.full.ArrayType = tree.fullArrayType(type_expr) orelse break :infer; 1448 // This intentionally does not support `@"_"` syntax. 1449 if (tree.nodeTag(array_type.ast.elem_count) == .identifier and 1450 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(array_type.ast.elem_count)), "_")) 1451 { 1452 const len_inst = try gz.addInt(array_init.ast.elements.len); 1453 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type); 1454 if (array_type.ast.sentinel == .none) { 1455 const array_type_inst = try gz.addPlNode(.array_type, type_expr, Zir.Inst.Bin{ 1456 .lhs = len_inst, 1457 .rhs = elem_type, 1458 }); 1459 break :inst .{ array_type_inst, elem_type }; 1460 } else { 1461 const sentinel_node = array_type.ast.sentinel.unwrap().?; 1462 const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, sentinel_node, .array_sentinel); 1463 const array_type_inst = try gz.addPlNode( 1464 .array_type_sentinel, 1465 type_expr, 1466 Zir.Inst.ArrayTypeSentinel{ 1467 .len = len_inst, 1468 .elem_type = elem_type, 1469 .sentinel = sentinel, 1470 }, 1471 ); 1472 break :inst .{ array_type_inst, elem_type }; 1473 } 1474 } 1475 } 1476 const array_type_inst = try typeExpr(gz, scope, type_expr); 1477 _ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{ 1478 .ty = array_type_inst, 1479 .init_count = @intCast(array_init.ast.elements.len), 1480 }); 1481 break :inst .{ array_type_inst, .none }; 1482 }; 1483 1484 if (array_ty != .none) { 1485 // Typed inits do not use RLS for language simplicity. 1486 switch (ri.rl) { 1487 .discard => { 1488 if (elem_ty != .none) { 1489 const elem_ri: ResultInfo = .{ .rl = .{ .ty = elem_ty } }; 1490 for (array_init.ast.elements) |elem_init| { 1491 _ = try expr(gz, scope, elem_ri, elem_init); 1492 } 1493 } else { 1494 for (array_init.ast.elements, 0..) |elem_init, i| { 1495 const this_elem_ty = try gz.add(.{ 1496 .tag = .array_init_elem_type, 1497 .data = .{ .bin = .{ 1498 .lhs = array_ty, 1499 .rhs = @enumFromInt(i), 1500 } }, 1501 }); 1502 _ = try expr(gz, scope, .{ .rl = .{ .ty = this_elem_ty } }, elem_init); 1503 } 1504 } 1505 return .void_value; 1506 }, 1507 .ref => return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, true), 1508 else => { 1509 const array_inst = try arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, false); 1510 return rvalue(gz, ri, array_inst, node); 1511 }, 1512 } 1513 } 1514 1515 switch (ri.rl) { 1516 .none => return arrayInitExprAnon(gz, scope, node, array_init.ast.elements), 1517 .discard => { 1518 for (array_init.ast.elements) |elem_init| { 1519 _ = try expr(gz, scope, .{ .rl = .discard }, elem_init); 1520 } 1521 return Zir.Inst.Ref.void_value; 1522 }, 1523 .ref => { 1524 const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements); 1525 return gz.addUnTok(.ref, result, tree.firstToken(node)); 1526 }, 1527 .ref_coerced_ty => |ptr_ty_inst| { 1528 const dest_arr_ty_inst = try gz.addPlNode(.validate_array_init_ref_ty, node, Zir.Inst.ArrayInitRefTy{ 1529 .ptr_ty = ptr_ty_inst, 1530 .elem_count = @intCast(array_init.ast.elements.len), 1531 }); 1532 return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, dest_arr_ty_inst, .none, true); 1533 }, 1534 .ty, .coerced_ty => |result_ty_inst| { 1535 _ = try gz.addPlNode(.validate_array_init_result_ty, node, Zir.Inst.ArrayInit{ 1536 .ty = result_ty_inst, 1537 .init_count = @intCast(array_init.ast.elements.len), 1538 }); 1539 return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, result_ty_inst, .none, false); 1540 }, 1541 .ptr => |ptr| { 1542 try arrayInitExprPtr(gz, scope, node, array_init.ast.elements, ptr.inst); 1543 return .void_value; 1544 }, 1545 .inferred_ptr => { 1546 // We can't get elem pointers of an untyped inferred alloc, so must perform a 1547 // standard anonymous initialization followed by an rvalue store. 1548 // See corresponding logic in structInitExpr. 1549 const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements); 1550 return rvalue(gz, ri, result, node); 1551 }, 1552 .destructure => |destructure| { 1553 // Untyped init - destructure directly into result pointers 1554 if (array_init.ast.elements.len != destructure.components.len) { 1555 return astgen.failNodeNotes(node, "expected {} elements for destructure, found {}", .{ 1556 destructure.components.len, 1557 array_init.ast.elements.len, 1558 }, &.{ 1559 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}), 1560 }); 1561 } 1562 for (array_init.ast.elements, destructure.components) |elem_init, ds_comp| { 1563 const elem_ri: ResultInfo = .{ .rl = switch (ds_comp) { 1564 .typed_ptr => |ptr_rl| .{ .ptr = ptr_rl }, 1565 .inferred_ptr => |ptr_inst| .{ .inferred_ptr = ptr_inst }, 1566 .discard => .discard, 1567 } }; 1568 _ = try expr(gz, scope, elem_ri, elem_init); 1569 } 1570 return .void_value; 1571 }, 1572 } 1573 } 1574 1575 /// An array initialization expression using an `array_init_anon` instruction. 1576 fn arrayInitExprAnon( 1577 gz: *GenZir, 1578 scope: *Scope, 1579 node: Ast.Node.Index, 1580 elements: []const Ast.Node.Index, 1581 ) InnerError!Zir.Inst.Ref { 1582 const astgen = gz.astgen; 1583 1584 const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{ 1585 .operands_len = @intCast(elements.len), 1586 }); 1587 var extra_index = try reserveExtra(astgen, elements.len); 1588 1589 for (elements) |elem_init| { 1590 const elem_ref = try expr(gz, scope, .{ .rl = .none }, elem_init); 1591 astgen.extra.items[extra_index] = @intFromEnum(elem_ref); 1592 extra_index += 1; 1593 } 1594 return try gz.addPlNodePayloadIndex(.array_init_anon, node, payload_index); 1595 } 1596 1597 /// An array initialization expression using an `array_init` or `array_init_ref` instruction. 1598 fn arrayInitExprTyped( 1599 gz: *GenZir, 1600 scope: *Scope, 1601 node: Ast.Node.Index, 1602 elements: []const Ast.Node.Index, 1603 ty_inst: Zir.Inst.Ref, 1604 maybe_elem_ty_inst: Zir.Inst.Ref, 1605 is_ref: bool, 1606 ) InnerError!Zir.Inst.Ref { 1607 const astgen = gz.astgen; 1608 1609 const len = elements.len + 1; // +1 for type 1610 const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{ 1611 .operands_len = @intCast(len), 1612 }); 1613 var extra_index = try reserveExtra(astgen, len); 1614 astgen.extra.items[extra_index] = @intFromEnum(ty_inst); 1615 extra_index += 1; 1616 1617 if (maybe_elem_ty_inst != .none) { 1618 const elem_ri: ResultInfo = .{ .rl = .{ .coerced_ty = maybe_elem_ty_inst } }; 1619 for (elements) |elem_init| { 1620 const elem_inst = try expr(gz, scope, elem_ri, elem_init); 1621 astgen.extra.items[extra_index] = @intFromEnum(elem_inst); 1622 extra_index += 1; 1623 } 1624 } else { 1625 for (elements, 0..) |elem_init, i| { 1626 const ri: ResultInfo = .{ .rl = .{ .coerced_ty = try gz.add(.{ 1627 .tag = .array_init_elem_type, 1628 .data = .{ .bin = .{ 1629 .lhs = ty_inst, 1630 .rhs = @enumFromInt(i), 1631 } }, 1632 }) } }; 1633 1634 const elem_inst = try expr(gz, scope, ri, elem_init); 1635 astgen.extra.items[extra_index] = @intFromEnum(elem_inst); 1636 extra_index += 1; 1637 } 1638 } 1639 1640 const tag: Zir.Inst.Tag = if (is_ref) .array_init_ref else .array_init; 1641 return try gz.addPlNodePayloadIndex(tag, node, payload_index); 1642 } 1643 1644 /// An array initialization expression using element pointers. 1645 fn arrayInitExprPtr( 1646 gz: *GenZir, 1647 scope: *Scope, 1648 node: Ast.Node.Index, 1649 elements: []const Ast.Node.Index, 1650 ptr_inst: Zir.Inst.Ref, 1651 ) InnerError!void { 1652 const astgen = gz.astgen; 1653 1654 const array_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node); 1655 1656 const payload_index = try addExtra(astgen, Zir.Inst.Block{ 1657 .body_len = @intCast(elements.len), 1658 }); 1659 var extra_index = try reserveExtra(astgen, elements.len); 1660 1661 for (elements, 0..) |elem_init, i| { 1662 const elem_ptr_inst = try gz.addPlNode(.array_init_elem_ptr, elem_init, Zir.Inst.ElemPtrImm{ 1663 .ptr = array_ptr_inst, 1664 .index = @intCast(i), 1665 }); 1666 astgen.extra.items[extra_index] = @intFromEnum(elem_ptr_inst.toIndex().?); 1667 extra_index += 1; 1668 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = elem_ptr_inst } } }, elem_init); 1669 } 1670 1671 _ = try gz.addPlNodePayloadIndex(.validate_ptr_array_init, node, payload_index); 1672 } 1673 1674 fn structInitExpr( 1675 gz: *GenZir, 1676 scope: *Scope, 1677 ri: ResultInfo, 1678 node: Ast.Node.Index, 1679 struct_init: Ast.full.StructInit, 1680 ) InnerError!Zir.Inst.Ref { 1681 const astgen = gz.astgen; 1682 const tree = astgen.tree; 1683 1684 if (struct_init.ast.type_expr == .none) { 1685 if (struct_init.ast.fields.len == 0) { 1686 // Anonymous init with no fields. 1687 switch (ri.rl) { 1688 .discard => return .void_value, 1689 .ref_coerced_ty => |ptr_ty_inst| return gz.addUnNode(.struct_init_empty_ref_result, ptr_ty_inst, node), 1690 .ty, .coerced_ty => |ty_inst| return gz.addUnNode(.struct_init_empty_result, ty_inst, node), 1691 .ptr => { 1692 // TODO: should we modify this to use RLS for the field stores here? 1693 const ty_inst = (try ri.rl.resultType(gz, node)).?; 1694 const val = try gz.addUnNode(.struct_init_empty_result, ty_inst, node); 1695 return rvalue(gz, ri, val, node); 1696 }, 1697 .none, .ref, .inferred_ptr => { 1698 return rvalue(gz, ri, .empty_tuple, node); 1699 }, 1700 .destructure => |destructure| { 1701 return astgen.failNodeNotes(node, "empty initializer cannot be destructured", .{}, &.{ 1702 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}), 1703 }); 1704 }, 1705 } 1706 } 1707 } else array: { 1708 const type_expr = struct_init.ast.type_expr.unwrap().?; 1709 const array_type: Ast.full.ArrayType = tree.fullArrayType(type_expr) orelse { 1710 if (struct_init.ast.fields.len == 0) { 1711 const ty_inst = try typeExpr(gz, scope, type_expr); 1712 const result = try gz.addUnNode(.struct_init_empty, ty_inst, node); 1713 return rvalue(gz, ri, result, node); 1714 } 1715 break :array; 1716 }; 1717 const is_inferred_array_len = tree.nodeTag(array_type.ast.elem_count) == .identifier and 1718 // This intentionally does not support `@"_"` syntax. 1719 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(array_type.ast.elem_count)), "_"); 1720 if (struct_init.ast.fields.len == 0) { 1721 if (is_inferred_array_len) { 1722 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type); 1723 const array_type_inst = if (array_type.ast.sentinel == .none) blk: { 1724 break :blk try gz.addPlNode(.array_type, type_expr, Zir.Inst.Bin{ 1725 .lhs = .zero_usize, 1726 .rhs = elem_type, 1727 }); 1728 } else blk: { 1729 const sentinel_node = array_type.ast.sentinel.unwrap().?; 1730 const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, sentinel_node, .array_sentinel); 1731 break :blk try gz.addPlNode( 1732 .array_type_sentinel, 1733 type_expr, 1734 Zir.Inst.ArrayTypeSentinel{ 1735 .len = .zero_usize, 1736 .elem_type = elem_type, 1737 .sentinel = sentinel, 1738 }, 1739 ); 1740 }; 1741 const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node); 1742 return rvalue(gz, ri, result, node); 1743 } 1744 const ty_inst = try typeExpr(gz, scope, type_expr); 1745 const result = try gz.addUnNode(.struct_init_empty, ty_inst, node); 1746 return rvalue(gz, ri, result, node); 1747 } else { 1748 return astgen.failNode( 1749 type_expr, 1750 "initializing array with struct syntax", 1751 .{}, 1752 ); 1753 } 1754 } 1755 1756 { 1757 var sfba = std.heap.stackFallback(256, astgen.arena); 1758 const sfba_allocator = sfba.get(); 1759 1760 var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator); 1761 try duplicate_names.ensureTotalCapacity(@intCast(struct_init.ast.fields.len)); 1762 1763 // When there aren't errors, use this to avoid a second iteration. 1764 var any_duplicate = false; 1765 1766 for (struct_init.ast.fields) |field| { 1767 const name_token = tree.firstToken(field) - 2; 1768 const name_index = try astgen.identAsString(name_token); 1769 1770 const gop = try duplicate_names.getOrPut(name_index); 1771 1772 if (gop.found_existing) { 1773 try gop.value_ptr.append(sfba_allocator, name_token); 1774 any_duplicate = true; 1775 } else { 1776 gop.value_ptr.* = .{}; 1777 try gop.value_ptr.append(sfba_allocator, name_token); 1778 } 1779 } 1780 1781 if (any_duplicate) { 1782 var it = duplicate_names.iterator(); 1783 1784 while (it.next()) |entry| { 1785 const record = entry.value_ptr.*; 1786 if (record.items.len > 1) { 1787 var error_notes = std.ArrayList(u32).init(astgen.arena); 1788 1789 for (record.items[1..]) |duplicate| { 1790 try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate name here", .{})); 1791 } 1792 1793 try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{})); 1794 1795 try astgen.appendErrorTokNotes( 1796 record.items[0], 1797 "duplicate struct field name", 1798 .{}, 1799 error_notes.items, 1800 ); 1801 } 1802 } 1803 1804 return error.AnalysisFail; 1805 } 1806 } 1807 1808 if (struct_init.ast.type_expr.unwrap()) |type_expr| { 1809 // Typed inits do not use RLS for language simplicity. 1810 const ty_inst = try typeExpr(gz, scope, type_expr); 1811 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); 1812 switch (ri.rl) { 1813 .ref => return structInitExprTyped(gz, scope, node, struct_init, ty_inst, true), 1814 else => { 1815 const struct_inst = try structInitExprTyped(gz, scope, node, struct_init, ty_inst, false); 1816 return rvalue(gz, ri, struct_inst, node); 1817 }, 1818 } 1819 } 1820 1821 switch (ri.rl) { 1822 .none => return structInitExprAnon(gz, scope, node, struct_init), 1823 .discard => { 1824 // Even if discarding we must perform side-effects. 1825 for (struct_init.ast.fields) |field_init| { 1826 _ = try expr(gz, scope, .{ .rl = .discard }, field_init); 1827 } 1828 return .void_value; 1829 }, 1830 .ref => { 1831 const result = try structInitExprAnon(gz, scope, node, struct_init); 1832 return gz.addUnTok(.ref, result, tree.firstToken(node)); 1833 }, 1834 .ref_coerced_ty => |ptr_ty_inst| { 1835 const result_ty_inst = try gz.addUnNode(.elem_type, ptr_ty_inst, node); 1836 _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node); 1837 return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, true); 1838 }, 1839 .ty, .coerced_ty => |result_ty_inst| { 1840 _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node); 1841 return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, false); 1842 }, 1843 .ptr => |ptr| { 1844 try structInitExprPtr(gz, scope, node, struct_init, ptr.inst); 1845 return .void_value; 1846 }, 1847 .inferred_ptr => { 1848 // We can't get field pointers of an untyped inferred alloc, so must perform a 1849 // standard anonymous initialization followed by an rvalue store. 1850 // See corresponding logic in arrayInitExpr. 1851 const struct_inst = try structInitExprAnon(gz, scope, node, struct_init); 1852 return rvalue(gz, ri, struct_inst, node); 1853 }, 1854 .destructure => |destructure| { 1855 // This is an untyped init, so is an actual struct, which does 1856 // not support destructuring. 1857 return astgen.failNodeNotes(node, "struct value cannot be destructured", .{}, &.{ 1858 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}), 1859 }); 1860 }, 1861 } 1862 } 1863 1864 /// A struct initialization expression using a `struct_init_anon` instruction. 1865 fn structInitExprAnon( 1866 gz: *GenZir, 1867 scope: *Scope, 1868 node: Ast.Node.Index, 1869 struct_init: Ast.full.StructInit, 1870 ) InnerError!Zir.Inst.Ref { 1871 const astgen = gz.astgen; 1872 const tree = astgen.tree; 1873 1874 const payload_index = try addExtra(astgen, Zir.Inst.StructInitAnon{ 1875 .abs_node = node, 1876 .abs_line = astgen.source_line, 1877 .fields_len = @intCast(struct_init.ast.fields.len), 1878 }); 1879 const field_size = @typeInfo(Zir.Inst.StructInitAnon.Item).@"struct".fields.len; 1880 var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size); 1881 1882 for (struct_init.ast.fields) |field_init| { 1883 const name_token = tree.firstToken(field_init) - 2; 1884 const str_index = try astgen.identAsString(name_token); 1885 setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{ 1886 .field_name = str_index, 1887 .init = try expr(gz, scope, .{ .rl = .none }, field_init), 1888 }); 1889 extra_index += field_size; 1890 } 1891 1892 return gz.addPlNodePayloadIndex(.struct_init_anon, node, payload_index); 1893 } 1894 1895 /// A struct initialization expression using a `struct_init` or `struct_init_ref` instruction. 1896 fn structInitExprTyped( 1897 gz: *GenZir, 1898 scope: *Scope, 1899 node: Ast.Node.Index, 1900 struct_init: Ast.full.StructInit, 1901 ty_inst: Zir.Inst.Ref, 1902 is_ref: bool, 1903 ) InnerError!Zir.Inst.Ref { 1904 const astgen = gz.astgen; 1905 const tree = astgen.tree; 1906 1907 const payload_index = try addExtra(astgen, Zir.Inst.StructInit{ 1908 .abs_node = node, 1909 .abs_line = astgen.source_line, 1910 .fields_len = @intCast(struct_init.ast.fields.len), 1911 }); 1912 const field_size = @typeInfo(Zir.Inst.StructInit.Item).@"struct".fields.len; 1913 var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size); 1914 1915 for (struct_init.ast.fields) |field_init| { 1916 const name_token = tree.firstToken(field_init) - 2; 1917 const str_index = try astgen.identAsString(name_token); 1918 const field_ty_inst = try gz.addPlNode(.struct_init_field_type, field_init, Zir.Inst.FieldType{ 1919 .container_type = ty_inst, 1920 .name_start = str_index, 1921 }); 1922 setExtra(astgen, extra_index, Zir.Inst.StructInit.Item{ 1923 .field_type = field_ty_inst.toIndex().?, 1924 .init = try expr(gz, scope, .{ .rl = .{ .coerced_ty = field_ty_inst } }, field_init), 1925 }); 1926 extra_index += field_size; 1927 } 1928 1929 const tag: Zir.Inst.Tag = if (is_ref) .struct_init_ref else .struct_init; 1930 return gz.addPlNodePayloadIndex(tag, node, payload_index); 1931 } 1932 1933 /// A struct initialization expression using field pointers. 1934 fn structInitExprPtr( 1935 gz: *GenZir, 1936 scope: *Scope, 1937 node: Ast.Node.Index, 1938 struct_init: Ast.full.StructInit, 1939 ptr_inst: Zir.Inst.Ref, 1940 ) InnerError!void { 1941 const astgen = gz.astgen; 1942 const tree = astgen.tree; 1943 1944 const struct_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node); 1945 1946 const payload_index = try addExtra(astgen, Zir.Inst.Block{ 1947 .body_len = @intCast(struct_init.ast.fields.len), 1948 }); 1949 var extra_index = try reserveExtra(astgen, struct_init.ast.fields.len); 1950 1951 for (struct_init.ast.fields) |field_init| { 1952 const name_token = tree.firstToken(field_init) - 2; 1953 const str_index = try astgen.identAsString(name_token); 1954 const field_ptr = try gz.addPlNode(.struct_init_field_ptr, field_init, Zir.Inst.Field{ 1955 .lhs = struct_ptr_inst, 1956 .field_name_start = str_index, 1957 }); 1958 astgen.extra.items[extra_index] = @intFromEnum(field_ptr.toIndex().?); 1959 extra_index += 1; 1960 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = field_ptr } } }, field_init); 1961 } 1962 1963 _ = try gz.addPlNodePayloadIndex(.validate_ptr_struct_init, node, payload_index); 1964 } 1965 1966 /// This explicitly calls expr in a comptime scope by wrapping it in a `block_comptime` if 1967 /// necessary. It should be used whenever we need to force compile-time evaluation of something, 1968 /// such as a type. 1969 /// The function corresponding to `comptime` expression syntax is `comptimeExprAst`. 1970 fn comptimeExpr( 1971 gz: *GenZir, 1972 scope: *Scope, 1973 ri: ResultInfo, 1974 node: Ast.Node.Index, 1975 reason: std.zig.SimpleComptimeReason, 1976 ) InnerError!Zir.Inst.Ref { 1977 return comptimeExpr2(gz, scope, ri, node, node, reason); 1978 } 1979 1980 /// Like `comptimeExpr`, but draws a distinction between `node`, the expression to evaluate at comptime, 1981 /// and `src_node`, the node to attach to the `block_comptime`. 1982 fn comptimeExpr2( 1983 gz: *GenZir, 1984 scope: *Scope, 1985 ri: ResultInfo, 1986 node: Ast.Node.Index, 1987 src_node: Ast.Node.Index, 1988 reason: std.zig.SimpleComptimeReason, 1989 ) InnerError!Zir.Inst.Ref { 1990 if (gz.is_comptime) { 1991 // No need to change anything! 1992 return expr(gz, scope, ri, node); 1993 } 1994 1995 // There's an optimization here: if the body will be evaluated at comptime regardless, there's 1996 // no need to wrap it in a block. This is hard to determine in general, but we can identify a 1997 // common subset of trivially comptime expressions to take down the size of the ZIR a bit. 1998 const tree = gz.astgen.tree; 1999 switch (tree.nodeTag(node)) { 2000 .identifier => { 2001 // Many identifiers can be handled without a `block_comptime`, so `AstGen.identifier` has 2002 // special handling for this case. 2003 return identifier(gz, scope, ri, node, .{ .src_node = src_node, .reason = reason }); 2004 }, 2005 2006 // These are leaf nodes which are always comptime-known. 2007 .number_literal, 2008 .char_literal, 2009 .string_literal, 2010 .multiline_string_literal, 2011 .enum_literal, 2012 .error_value, 2013 .anyframe_literal, 2014 .error_set_decl, 2015 // These nodes are not leaves, but will force comptime evaluation of all sub-expressions, and 2016 // hence behave the same regardless of whether they're in a comptime scope. 2017 .error_union, 2018 .merge_error_sets, 2019 .optional_type, 2020 .anyframe_type, 2021 .ptr_type_aligned, 2022 .ptr_type_sentinel, 2023 .ptr_type, 2024 .ptr_type_bit_range, 2025 .array_type, 2026 .array_type_sentinel, 2027 .fn_proto_simple, 2028 .fn_proto_multi, 2029 .fn_proto_one, 2030 .fn_proto, 2031 .container_decl, 2032 .container_decl_trailing, 2033 .container_decl_arg, 2034 .container_decl_arg_trailing, 2035 .container_decl_two, 2036 .container_decl_two_trailing, 2037 .tagged_union, 2038 .tagged_union_trailing, 2039 .tagged_union_enum_tag, 2040 .tagged_union_enum_tag_trailing, 2041 .tagged_union_two, 2042 .tagged_union_two_trailing, 2043 => { 2044 // No need to worry about result location here, we're not creating a comptime block! 2045 return expr(gz, scope, ri, node); 2046 }, 2047 2048 // Lastly, for labelled blocks, avoid emitting a labelled block directly inside this 2049 // comptime block, because that would be silly! Note that we don't bother doing this for 2050 // unlabelled blocks, since they don't generate blocks at comptime anyway (see `blockExpr`). 2051 .block_two, .block_two_semicolon, .block, .block_semicolon => { 2052 const lbrace = tree.nodeMainToken(node); 2053 // Careful! We can't pass in the real result location here, since it may 2054 // refer to runtime memory. A runtime-to-comptime boundary has to remove 2055 // result location information, compute the result, and copy it to the true 2056 // result location at runtime. We do this below as well. 2057 const ty_only_ri: ResultInfo = .{ 2058 .ctx = ri.ctx, 2059 .rl = if (try ri.rl.resultType(gz, node)) |res_ty| 2060 .{ .coerced_ty = res_ty } 2061 else 2062 .none, 2063 }; 2064 if (tree.isTokenPrecededByTags(lbrace, &.{ .identifier, .colon })) { 2065 var buf: [2]Ast.Node.Index = undefined; 2066 const stmts = tree.blockStatements(&buf, node).?; 2067 2068 // Replace result location and copy back later - see above. 2069 const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmts, true, .normal); 2070 return rvalue(gz, ri, block_ref, node); 2071 } 2072 }, 2073 2074 // In other cases, we don't optimize anything - we need a wrapper comptime block. 2075 else => {}, 2076 } 2077 2078 var block_scope = gz.makeSubBlock(scope); 2079 block_scope.is_comptime = true; 2080 defer block_scope.unstack(); 2081 2082 const block_inst = try gz.makeBlockInst(.block_comptime, src_node); 2083 // Replace result location and copy back later - see above. 2084 const ty_only_ri: ResultInfo = .{ 2085 .ctx = ri.ctx, 2086 .rl = if (try ri.rl.resultType(gz, src_node)) |res_ty| 2087 .{ .coerced_ty = res_ty } 2088 else 2089 .none, 2090 }; 2091 const block_result = try fullBodyExpr(&block_scope, scope, ty_only_ri, node, .normal); 2092 if (!gz.refIsNoReturn(block_result)) { 2093 _ = try block_scope.addBreak(.break_inline, block_inst, block_result); 2094 } 2095 try block_scope.setBlockComptimeBody(block_inst, reason); 2096 try gz.instructions.append(gz.astgen.gpa, block_inst); 2097 2098 return rvalue(gz, ri, block_inst.toRef(), src_node); 2099 } 2100 2101 /// This one is for an actual `comptime` syntax, and will emit a compile error if 2102 /// the scope is already known to be comptime-evaluated. 2103 /// See `comptimeExpr` for the helper function for calling expr in a comptime scope. 2104 fn comptimeExprAst( 2105 gz: *GenZir, 2106 scope: *Scope, 2107 ri: ResultInfo, 2108 node: Ast.Node.Index, 2109 ) InnerError!Zir.Inst.Ref { 2110 const astgen = gz.astgen; 2111 if (gz.is_comptime) { 2112 try astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{}); 2113 } 2114 const tree = astgen.tree; 2115 const body_node = tree.nodeData(node).node; 2116 return comptimeExpr2(gz, scope, ri, body_node, node, .comptime_keyword); 2117 } 2118 2119 /// Restore the error return trace index. Performs the restore only if the result is a non-error or 2120 /// if the result location is a non-error-handling expression. 2121 fn restoreErrRetIndex( 2122 gz: *GenZir, 2123 bt: GenZir.BranchTarget, 2124 ri: ResultInfo, 2125 node: Ast.Node.Index, 2126 result: Zir.Inst.Ref, 2127 ) !void { 2128 const op = switch (nodeMayEvalToError(gz.astgen.tree, node)) { 2129 .always => return, // never restore/pop 2130 .never => .none, // always restore/pop 2131 .maybe => switch (ri.ctx) { 2132 .error_handling_expr, .@"return", .fn_arg, .const_init => switch (ri.rl) { 2133 .ptr => |ptr_res| try gz.addUnNode(.load, ptr_res.inst, node), 2134 .inferred_ptr => blk: { 2135 // This is a terrible workaround for Sema's inability to load from a .alloc_inferred ptr 2136 // before its type has been resolved. There is no valid operand to use here, so error 2137 // traces will be popped prematurely. 2138 // TODO: Update this to do a proper load from the rl_ptr, once Sema can support it. 2139 break :blk .none; 2140 }, 2141 .destructure => return, // value must be a tuple or array, so never restore/pop 2142 else => result, 2143 }, 2144 else => .none, // always restore/pop 2145 }, 2146 }; 2147 _ = try gz.addRestoreErrRetIndex(bt, .{ .if_non_error = op }, node); 2148 } 2149 2150 fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 2151 const astgen = parent_gz.astgen; 2152 const tree = astgen.tree; 2153 const opt_break_label, const opt_rhs = tree.nodeData(node).opt_token_and_opt_node; 2154 2155 // Look for the label in the scope. 2156 var scope = parent_scope; 2157 while (true) { 2158 switch (scope.tag) { 2159 .gen_zir => { 2160 const block_gz = scope.cast(GenZir).?; 2161 2162 if (block_gz.cur_defer_node.unwrap()) |cur_defer_node| { 2163 // We are breaking out of a `defer` block. 2164 return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{ 2165 try astgen.errNoteNode( 2166 cur_defer_node, 2167 "defer expression here", 2168 .{}, 2169 ), 2170 }); 2171 } 2172 2173 const block_inst = blk: { 2174 if (opt_break_label.unwrap()) |break_label| { 2175 if (block_gz.label) |*label| { 2176 if (try astgen.tokenIdentEql(label.token, break_label)) { 2177 label.used = true; 2178 break :blk label.block_inst; 2179 } 2180 } 2181 } else if (block_gz.break_block.unwrap()) |i| { 2182 break :blk i; 2183 } 2184 // If not the target, start over with the parent 2185 scope = block_gz.parent; 2186 continue; 2187 }; 2188 // If we made it here, this block is the target of the break expr 2189 2190 const break_tag: Zir.Inst.Tag = if (block_gz.is_inline) 2191 .break_inline 2192 else 2193 .@"break"; 2194 2195 const rhs = opt_rhs.unwrap() orelse { 2196 _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node); 2197 2198 try genDefers(parent_gz, scope, parent_scope, .normal_only); 2199 2200 // As our last action before the break, "pop" the error trace if needed 2201 if (!block_gz.is_comptime) 2202 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, node); 2203 2204 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); 2205 return Zir.Inst.Ref.unreachable_value; 2206 }; 2207 2208 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node); 2209 2210 try genDefers(parent_gz, scope, parent_scope, .normal_only); 2211 2212 // As our last action before the break, "pop" the error trace if needed 2213 if (!block_gz.is_comptime) 2214 try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand); 2215 2216 switch (block_gz.break_result_info.rl) { 2217 .ptr => { 2218 // In this case we don't have any mechanism to intercept it; 2219 // we assume the result location is written, and we break with void. 2220 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); 2221 }, 2222 .discard => { 2223 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); 2224 }, 2225 else => { 2226 _ = try parent_gz.addBreakWithSrcNode(break_tag, block_inst, operand, rhs); 2227 }, 2228 } 2229 return Zir.Inst.Ref.unreachable_value; 2230 }, 2231 .local_val => scope = scope.cast(Scope.LocalVal).?.parent, 2232 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, 2233 .namespace => break, 2234 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, 2235 .top => unreachable, 2236 } 2237 } 2238 if (opt_break_label.unwrap()) |break_label| { 2239 const label_name = try astgen.identifierTokenString(break_label); 2240 return astgen.failTok(break_label, "label not found: '{s}'", .{label_name}); 2241 } else { 2242 return astgen.failNode(node, "break expression outside loop", .{}); 2243 } 2244 } 2245 2246 fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 2247 const astgen = parent_gz.astgen; 2248 const tree = astgen.tree; 2249 const opt_break_label, const opt_rhs = tree.nodeData(node).opt_token_and_opt_node; 2250 2251 if (opt_break_label == .none and opt_rhs != .none) { 2252 return astgen.failNode(node, "cannot continue with operand without label", .{}); 2253 } 2254 2255 // Look for the label in the scope. 2256 var scope = parent_scope; 2257 while (true) { 2258 switch (scope.tag) { 2259 .gen_zir => { 2260 const gen_zir = scope.cast(GenZir).?; 2261 2262 if (gen_zir.cur_defer_node.unwrap()) |cur_defer_node| { 2263 return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{ 2264 try astgen.errNoteNode( 2265 cur_defer_node, 2266 "defer expression here", 2267 .{}, 2268 ), 2269 }); 2270 } 2271 const continue_block = gen_zir.continue_block.unwrap() orelse { 2272 scope = gen_zir.parent; 2273 continue; 2274 }; 2275 if (opt_break_label.unwrap()) |break_label| blk: { 2276 if (gen_zir.label) |*label| { 2277 if (try astgen.tokenIdentEql(label.token, break_label)) { 2278 const maybe_switch_tag = astgen.instructions.items(.tag)[@intFromEnum(label.block_inst)]; 2279 if (opt_rhs != .none) switch (maybe_switch_tag) { 2280 .switch_block, .switch_block_ref => {}, 2281 else => return astgen.failNode(node, "cannot continue loop with operand", .{}), 2282 } else switch (maybe_switch_tag) { 2283 .switch_block, .switch_block_ref => return astgen.failNode(node, "cannot continue switch without operand", .{}), 2284 else => {}, 2285 } 2286 2287 label.used = true; 2288 label.used_for_continue = true; 2289 break :blk; 2290 } 2291 } 2292 // found continue but either it has a different label, or no label 2293 scope = gen_zir.parent; 2294 continue; 2295 } else if (gen_zir.label) |label| { 2296 // This `continue` is unlabeled. If the gz we've found corresponds to a labeled 2297 // `switch`, ignore it and continue to parent scopes. 2298 switch (astgen.instructions.items(.tag)[@intFromEnum(label.block_inst)]) { 2299 .switch_block, .switch_block_ref => { 2300 scope = gen_zir.parent; 2301 continue; 2302 }, 2303 else => {}, 2304 } 2305 } 2306 2307 if (opt_rhs.unwrap()) |rhs| { 2308 // We need to figure out the result info to use. 2309 // The type should match 2310 const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.continue_result_info, rhs, node); 2311 2312 try genDefers(parent_gz, scope, parent_scope, .normal_only); 2313 2314 // As our last action before the continue, "pop" the error trace if needed 2315 if (!gen_zir.is_comptime) 2316 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node); 2317 2318 _ = try parent_gz.addBreakWithSrcNode(.switch_continue, continue_block, operand, rhs); 2319 return Zir.Inst.Ref.unreachable_value; 2320 } 2321 2322 try genDefers(parent_gz, scope, parent_scope, .normal_only); 2323 2324 const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline) 2325 .break_inline 2326 else 2327 .@"break"; 2328 if (break_tag == .break_inline) { 2329 _ = try parent_gz.addUnNode(.check_comptime_control_flow, continue_block.toRef(), node); 2330 } 2331 2332 // As our last action before the continue, "pop" the error trace if needed 2333 if (!gen_zir.is_comptime) 2334 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node); 2335 2336 _ = try parent_gz.addBreak(break_tag, continue_block, .void_value); 2337 return Zir.Inst.Ref.unreachable_value; 2338 }, 2339 .local_val => scope = scope.cast(Scope.LocalVal).?.parent, 2340 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, 2341 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, 2342 .namespace => break, 2343 .top => unreachable, 2344 } 2345 } 2346 if (opt_break_label.unwrap()) |break_label| { 2347 const label_name = try astgen.identifierTokenString(break_label); 2348 return astgen.failTok(break_label, "label not found: '{s}'", .{label_name}); 2349 } else { 2350 return astgen.failNode(node, "continue expression outside loop", .{}); 2351 } 2352 } 2353 2354 /// Similar to `expr`, but intended for use when `gz` corresponds to a body 2355 /// which will contain only this node's code. Differs from `expr` in that if the 2356 /// root expression is an unlabeled block, does not emit an actual block. 2357 /// Instead, the block contents are emitted directly into `gz`. 2358 fn fullBodyExpr( 2359 gz: *GenZir, 2360 scope: *Scope, 2361 ri: ResultInfo, 2362 node: Ast.Node.Index, 2363 block_kind: BlockKind, 2364 ) InnerError!Zir.Inst.Ref { 2365 const tree = gz.astgen.tree; 2366 2367 var stmt_buf: [2]Ast.Node.Index = undefined; 2368 const statements = tree.blockStatements(&stmt_buf, node) orelse 2369 return expr(gz, scope, ri, node); 2370 2371 const lbrace = tree.nodeMainToken(node); 2372 2373 if (tree.isTokenPrecededByTags(lbrace, &.{ .identifier, .colon })) { 2374 // Labeled blocks are tricky - forwarding result location information properly is non-trivial, 2375 // plus if this block is exited with a `break_inline` we aren't allowed multiple breaks. This 2376 // case is rare, so just treat it as a normal expression and create a nested block. 2377 return blockExpr(gz, scope, ri, node, statements, block_kind); 2378 } 2379 2380 var sub_gz = gz.makeSubBlock(scope); 2381 try blockExprStmts(&sub_gz, &sub_gz.base, statements, block_kind); 2382 2383 return rvalue(gz, ri, .void_value, node); 2384 } 2385 2386 const BlockKind = enum { normal, allow_branch_hint }; 2387 2388 fn blockExpr( 2389 gz: *GenZir, 2390 scope: *Scope, 2391 ri: ResultInfo, 2392 block_node: Ast.Node.Index, 2393 statements: []const Ast.Node.Index, 2394 kind: BlockKind, 2395 ) InnerError!Zir.Inst.Ref { 2396 const astgen = gz.astgen; 2397 const tree = astgen.tree; 2398 2399 const lbrace = tree.nodeMainToken(block_node); 2400 if (tree.isTokenPrecededByTags(lbrace, &.{ .identifier, .colon })) { 2401 return labeledBlockExpr(gz, scope, ri, block_node, statements, false, kind); 2402 } 2403 2404 if (!gz.is_comptime) { 2405 // Since this block is unlabeled, its control flow is effectively linear and we 2406 // can *almost* get away with inlining the block here. However, we actually need 2407 // to preserve the .block for Sema, to properly pop the error return trace. 2408 2409 const block_tag: Zir.Inst.Tag = .block; 2410 const block_inst = try gz.makeBlockInst(block_tag, block_node); 2411 try gz.instructions.append(astgen.gpa, block_inst); 2412 2413 var block_scope = gz.makeSubBlock(scope); 2414 defer block_scope.unstack(); 2415 2416 try blockExprStmts(&block_scope, &block_scope.base, statements, kind); 2417 2418 if (!block_scope.endsWithNoReturn()) { 2419 // As our last action before the break, "pop" the error trace if needed 2420 _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node); 2421 // No `rvalue` call here, as the block result is always `void`, so we do that below. 2422 _ = try block_scope.addBreak(.@"break", block_inst, .void_value); 2423 } 2424 2425 try block_scope.setBlockBody(block_inst); 2426 } else { 2427 var sub_gz = gz.makeSubBlock(scope); 2428 try blockExprStmts(&sub_gz, &sub_gz.base, statements, kind); 2429 } 2430 2431 return rvalue(gz, ri, .void_value, block_node); 2432 } 2433 2434 fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.TokenIndex) !void { 2435 // Look for the label in the scope. 2436 var scope = parent_scope; 2437 while (true) { 2438 switch (scope.tag) { 2439 .gen_zir => { 2440 const gen_zir = scope.cast(GenZir).?; 2441 if (gen_zir.label) |prev_label| { 2442 if (try astgen.tokenIdentEql(label, prev_label.token)) { 2443 const label_name = try astgen.identifierTokenString(label); 2444 return astgen.failTokNotes(label, "redefinition of label '{s}'", .{ 2445 label_name, 2446 }, &[_]u32{ 2447 try astgen.errNoteTok( 2448 prev_label.token, 2449 "previous definition here", 2450 .{}, 2451 ), 2452 }); 2453 } 2454 } 2455 scope = gen_zir.parent; 2456 }, 2457 .local_val => scope = scope.cast(Scope.LocalVal).?.parent, 2458 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, 2459 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, 2460 .namespace => break, 2461 .top => unreachable, 2462 } 2463 } 2464 } 2465 2466 fn labeledBlockExpr( 2467 gz: *GenZir, 2468 parent_scope: *Scope, 2469 ri: ResultInfo, 2470 block_node: Ast.Node.Index, 2471 statements: []const Ast.Node.Index, 2472 force_comptime: bool, 2473 block_kind: BlockKind, 2474 ) InnerError!Zir.Inst.Ref { 2475 const astgen = gz.astgen; 2476 const tree = astgen.tree; 2477 2478 const lbrace = tree.nodeMainToken(block_node); 2479 const label_token = lbrace - 2; 2480 assert(tree.tokenTag(label_token) == .identifier); 2481 2482 try astgen.checkLabelRedefinition(parent_scope, label_token); 2483 2484 const need_rl = astgen.nodes_need_rl.contains(block_node); 2485 const block_ri: ResultInfo = if (need_rl) ri else .{ 2486 .rl = switch (ri.rl) { 2487 .ptr => .{ .ty = (try ri.rl.resultType(gz, block_node)).? }, 2488 .inferred_ptr => .none, 2489 else => ri.rl, 2490 }, 2491 .ctx = ri.ctx, 2492 }; 2493 // We need to call `rvalue` to write through to the pointer only if we had a 2494 // result pointer and aren't forwarding it. 2495 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 2496 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 2497 2498 // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct 2499 // so that break statements can reference it. 2500 const block_inst = try gz.makeBlockInst(if (force_comptime) .block_comptime else .block, block_node); 2501 try gz.instructions.append(astgen.gpa, block_inst); 2502 var block_scope = gz.makeSubBlock(parent_scope); 2503 block_scope.is_inline = force_comptime; 2504 block_scope.label = GenZir.Label{ 2505 .token = label_token, 2506 .block_inst = block_inst, 2507 }; 2508 block_scope.setBreakResultInfo(block_ri); 2509 if (force_comptime) block_scope.is_comptime = true; 2510 defer block_scope.unstack(); 2511 2512 try blockExprStmts(&block_scope, &block_scope.base, statements, block_kind); 2513 if (!block_scope.endsWithNoReturn()) { 2514 // As our last action before the return, "pop" the error trace if needed 2515 _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node); 2516 const result = try rvalue(gz, block_scope.break_result_info, .void_value, block_node); 2517 const break_tag: Zir.Inst.Tag = if (force_comptime) .break_inline else .@"break"; 2518 _ = try block_scope.addBreak(break_tag, block_inst, result); 2519 } 2520 2521 if (!block_scope.label.?.used) { 2522 try astgen.appendErrorTok(label_token, "unused block label", .{}); 2523 } 2524 2525 if (force_comptime) { 2526 try block_scope.setBlockComptimeBody(block_inst, .comptime_keyword); 2527 } else { 2528 try block_scope.setBlockBody(block_inst); 2529 } 2530 2531 if (need_result_rvalue) { 2532 return rvalue(gz, ri, block_inst.toRef(), block_node); 2533 } else { 2534 return block_inst.toRef(); 2535 } 2536 } 2537 2538 fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Node.Index, block_kind: BlockKind) !void { 2539 const astgen = gz.astgen; 2540 const tree = astgen.tree; 2541 2542 if (statements.len == 0) return; 2543 2544 var block_arena = std.heap.ArenaAllocator.init(gz.astgen.gpa); 2545 defer block_arena.deinit(); 2546 const block_arena_allocator = block_arena.allocator(); 2547 2548 var noreturn_src_node: Ast.Node.OptionalIndex = .none; 2549 var scope = parent_scope; 2550 for (statements, 0..) |statement, stmt_idx| { 2551 if (noreturn_src_node.unwrap()) |src_node| { 2552 try astgen.appendErrorNodeNotes( 2553 statement, 2554 "unreachable code", 2555 .{}, 2556 &[_]u32{ 2557 try astgen.errNoteNode( 2558 src_node, 2559 "control flow is diverted here", 2560 .{}, 2561 ), 2562 }, 2563 ); 2564 } 2565 const allow_branch_hint = switch (block_kind) { 2566 .normal => false, 2567 .allow_branch_hint => stmt_idx == 0, 2568 }; 2569 var inner_node = statement; 2570 while (true) { 2571 switch (tree.nodeTag(inner_node)) { 2572 // zig fmt: off 2573 .global_var_decl, 2574 .local_var_decl, 2575 .simple_var_decl, 2576 .aligned_var_decl, => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.fullVarDecl(statement).?), 2577 2578 .assign_destructure => scope = try assignDestructureMaybeDecls(gz, scope, statement, block_arena_allocator), 2579 2580 .@"defer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal), 2581 .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error), 2582 2583 .assign => try assign(gz, scope, statement), 2584 2585 .assign_shl => try assignShift(gz, scope, statement, .shl), 2586 .assign_shr => try assignShift(gz, scope, statement, .shr), 2587 2588 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and), 2589 .assign_bit_or => try assignOp(gz, scope, statement, .bit_or), 2590 .assign_bit_xor => try assignOp(gz, scope, statement, .xor), 2591 .assign_div => try assignOp(gz, scope, statement, .div), 2592 .assign_sub => try assignOp(gz, scope, statement, .sub), 2593 .assign_sub_wrap => try assignOp(gz, scope, statement, .subwrap), 2594 .assign_mod => try assignOp(gz, scope, statement, .mod_rem), 2595 .assign_add => try assignOp(gz, scope, statement, .add), 2596 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap), 2597 .assign_mul => try assignOp(gz, scope, statement, .mul), 2598 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap), 2599 2600 .grouped_expression => { 2601 inner_node = tree.nodeData(statement).node_and_token[0]; 2602 continue; 2603 }, 2604 2605 .while_simple, 2606 .while_cont, 2607 .@"while", => _ = try whileExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullWhile(inner_node).?, true), 2608 2609 .for_simple, 2610 .@"for", => _ = try forExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullFor(inner_node).?, true), 2611 // zig fmt: on 2612 2613 // These cases are here to allow branch hints. 2614 .builtin_call_two, 2615 .builtin_call_two_comma, 2616 .builtin_call, 2617 .builtin_call_comma, 2618 => { 2619 var buf: [2]Ast.Node.Index = undefined; 2620 const params = tree.builtinCallParams(&buf, inner_node).?; 2621 2622 try emitDbgNode(gz, inner_node); 2623 const result = try builtinCall(gz, scope, .{ .rl = .none }, inner_node, params, allow_branch_hint); 2624 noreturn_src_node = try addEnsureResult(gz, result, inner_node); 2625 }, 2626 2627 else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node), 2628 } 2629 break; 2630 } 2631 } 2632 2633 if (noreturn_src_node == .none) { 2634 try genDefers(gz, parent_scope, scope, .normal_only); 2635 } 2636 try checkUsed(gz, parent_scope, scope); 2637 } 2638 2639 /// Returns AST source node of the thing that is noreturn if the statement is 2640 /// definitely `noreturn`. Otherwise returns .none. 2641 fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.OptionalIndex { 2642 try emitDbgNode(gz, statement); 2643 // We need to emit an error if the result is not `noreturn` or `void`, but 2644 // we want to avoid adding the ZIR instruction if possible for performance. 2645 const maybe_unused_result = try expr(gz, scope, .{ .rl = .none }, statement); 2646 return addEnsureResult(gz, maybe_unused_result, statement); 2647 } 2648 2649 fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: Ast.Node.Index) InnerError!Ast.Node.OptionalIndex { 2650 var noreturn_src_node: Ast.Node.OptionalIndex = .none; 2651 const elide_check = if (maybe_unused_result.toIndex()) |inst| b: { 2652 // Note that this array becomes invalid after appending more items to it 2653 // in the above while loop. 2654 const zir_tags = gz.astgen.instructions.items(.tag); 2655 switch (zir_tags[@intFromEnum(inst)]) { 2656 // For some instructions, modify the zir data 2657 // so we can avoid a separate ensure_result_used instruction. 2658 .call, .field_call => { 2659 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index; 2660 comptime assert(std.meta.fieldIndex(Zir.Inst.Call, "flags") == 2661 std.meta.fieldIndex(Zir.Inst.FieldCall, "flags")); 2662 const flags: *Zir.Inst.Call.Flags = @ptrCast(&gz.astgen.extra.items[ 2663 break_extra + std.meta.fieldIndex(Zir.Inst.Call, "flags").? 2664 ]); 2665 flags.ensure_result_used = true; 2666 break :b true; 2667 }, 2668 .builtin_call => { 2669 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index; 2670 const flags: *Zir.Inst.BuiltinCall.Flags = @ptrCast(&gz.astgen.extra.items[ 2671 break_extra + std.meta.fieldIndex(Zir.Inst.BuiltinCall, "flags").? 2672 ]); 2673 flags.ensure_result_used = true; 2674 break :b true; 2675 }, 2676 2677 // ZIR instructions that might be a type other than `noreturn` or `void`. 2678 .add, 2679 .addwrap, 2680 .add_sat, 2681 .add_unsafe, 2682 .param, 2683 .param_comptime, 2684 .param_anytype, 2685 .param_anytype_comptime, 2686 .alloc, 2687 .alloc_mut, 2688 .alloc_comptime_mut, 2689 .alloc_inferred, 2690 .alloc_inferred_mut, 2691 .alloc_inferred_comptime, 2692 .alloc_inferred_comptime_mut, 2693 .make_ptr_const, 2694 .array_cat, 2695 .array_mul, 2696 .array_type, 2697 .array_type_sentinel, 2698 .elem_type, 2699 .indexable_ptr_elem_type, 2700 .vec_arr_elem_type, 2701 .vector_type, 2702 .indexable_ptr_len, 2703 .anyframe_type, 2704 .as_node, 2705 .as_shift_operand, 2706 .bit_and, 2707 .bitcast, 2708 .bit_or, 2709 .block, 2710 .block_comptime, 2711 .block_inline, 2712 .declaration, 2713 .suspend_block, 2714 .loop, 2715 .bool_br_and, 2716 .bool_br_or, 2717 .bool_not, 2718 .cmp_lt, 2719 .cmp_lte, 2720 .cmp_eq, 2721 .cmp_gte, 2722 .cmp_gt, 2723 .cmp_neq, 2724 .decl_ref, 2725 .decl_val, 2726 .load, 2727 .div, 2728 .elem_ptr, 2729 .elem_val, 2730 .elem_ptr_node, 2731 .elem_val_node, 2732 .elem_val_imm, 2733 .field_ptr, 2734 .field_val, 2735 .field_ptr_named, 2736 .field_val_named, 2737 .func, 2738 .func_inferred, 2739 .func_fancy, 2740 .int, 2741 .int_big, 2742 .float, 2743 .float128, 2744 .int_type, 2745 .is_non_null, 2746 .is_non_null_ptr, 2747 .is_non_err, 2748 .is_non_err_ptr, 2749 .ret_is_non_err, 2750 .mod_rem, 2751 .mul, 2752 .mulwrap, 2753 .mul_sat, 2754 .ref, 2755 .shl, 2756 .shl_sat, 2757 .shr, 2758 .str, 2759 .sub, 2760 .subwrap, 2761 .sub_sat, 2762 .negate, 2763 .negate_wrap, 2764 .typeof, 2765 .typeof_builtin, 2766 .xor, 2767 .optional_type, 2768 .optional_payload_safe, 2769 .optional_payload_unsafe, 2770 .optional_payload_safe_ptr, 2771 .optional_payload_unsafe_ptr, 2772 .err_union_payload_unsafe, 2773 .err_union_payload_unsafe_ptr, 2774 .err_union_code, 2775 .err_union_code_ptr, 2776 .ptr_type, 2777 .enum_literal, 2778 .decl_literal, 2779 .decl_literal_no_coerce, 2780 .merge_error_sets, 2781 .error_union_type, 2782 .bit_not, 2783 .error_value, 2784 .slice_start, 2785 .slice_end, 2786 .slice_sentinel, 2787 .slice_length, 2788 .slice_sentinel_ty, 2789 .import, 2790 .switch_block, 2791 .switch_block_ref, 2792 .switch_block_err_union, 2793 .union_init, 2794 .field_type_ref, 2795 .error_set_decl, 2796 .enum_from_int, 2797 .int_from_enum, 2798 .type_info, 2799 .size_of, 2800 .bit_size_of, 2801 .typeof_log2_int_type, 2802 .int_from_ptr, 2803 .align_of, 2804 .int_from_bool, 2805 .embed_file, 2806 .error_name, 2807 .sqrt, 2808 .sin, 2809 .cos, 2810 .tan, 2811 .exp, 2812 .exp2, 2813 .log, 2814 .log2, 2815 .log10, 2816 .abs, 2817 .floor, 2818 .ceil, 2819 .trunc, 2820 .round, 2821 .tag_name, 2822 .type_name, 2823 .frame_type, 2824 .int_from_float, 2825 .float_from_int, 2826 .ptr_from_int, 2827 .float_cast, 2828 .int_cast, 2829 .ptr_cast, 2830 .truncate, 2831 .has_decl, 2832 .has_field, 2833 .clz, 2834 .ctz, 2835 .pop_count, 2836 .byte_swap, 2837 .bit_reverse, 2838 .div_exact, 2839 .div_floor, 2840 .div_trunc, 2841 .mod, 2842 .rem, 2843 .shl_exact, 2844 .shr_exact, 2845 .bit_offset_of, 2846 .offset_of, 2847 .splat, 2848 .reduce, 2849 .shuffle, 2850 .atomic_load, 2851 .atomic_rmw, 2852 .mul_add, 2853 .max, 2854 .min, 2855 .c_import, 2856 .@"resume", 2857 .ret_err_value_code, 2858 .ret_ptr, 2859 .ret_type, 2860 .for_len, 2861 .@"try", 2862 .try_ptr, 2863 .opt_eu_base_ptr_init, 2864 .coerce_ptr_elem_ty, 2865 .struct_init_empty, 2866 .struct_init_empty_result, 2867 .struct_init_empty_ref_result, 2868 .struct_init_anon, 2869 .struct_init, 2870 .struct_init_ref, 2871 .struct_init_field_type, 2872 .struct_init_field_ptr, 2873 .array_init_anon, 2874 .array_init, 2875 .array_init_ref, 2876 .validate_array_init_ref_ty, 2877 .array_init_elem_type, 2878 .array_init_elem_ptr, 2879 => break :b false, 2880 2881 .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) { 2882 .breakpoint, 2883 .disable_instrumentation, 2884 .disable_intrinsics, 2885 .set_float_mode, 2886 .branch_hint, 2887 => break :b true, 2888 else => break :b false, 2889 }, 2890 2891 // ZIR instructions that are always `noreturn`. 2892 .@"break", 2893 .break_inline, 2894 .condbr, 2895 .condbr_inline, 2896 .compile_error, 2897 .ret_node, 2898 .ret_load, 2899 .ret_implicit, 2900 .ret_err_value, 2901 .@"unreachable", 2902 .repeat, 2903 .repeat_inline, 2904 .panic, 2905 .trap, 2906 .check_comptime_control_flow, 2907 .switch_continue, 2908 => { 2909 noreturn_src_node = statement.toOptional(); 2910 break :b true; 2911 }, 2912 2913 // ZIR instructions that are always `void`. 2914 .dbg_stmt, 2915 .dbg_var_ptr, 2916 .dbg_var_val, 2917 .ensure_result_used, 2918 .ensure_result_non_error, 2919 .ensure_err_union_payload_void, 2920 .@"export", 2921 .set_eval_branch_quota, 2922 .atomic_store, 2923 .store_node, 2924 .store_to_inferred_ptr, 2925 .resolve_inferred_alloc, 2926 .set_runtime_safety, 2927 .memcpy, 2928 .memset, 2929 .memmove, 2930 .validate_deref, 2931 .validate_destructure, 2932 .save_err_ret_index, 2933 .restore_err_ret_index_unconditional, 2934 .restore_err_ret_index_fn_entry, 2935 .validate_struct_init_ty, 2936 .validate_struct_init_result_ty, 2937 .validate_ptr_struct_init, 2938 .validate_array_init_ty, 2939 .validate_array_init_result_ty, 2940 .validate_ptr_array_init, 2941 .validate_ref_ty, 2942 .validate_const, 2943 => break :b true, 2944 2945 .@"defer" => unreachable, 2946 .defer_err_code => unreachable, 2947 } 2948 } else switch (maybe_unused_result) { 2949 .none => unreachable, 2950 2951 .unreachable_value => b: { 2952 noreturn_src_node = statement.toOptional(); 2953 break :b true; 2954 }, 2955 2956 .void_value => true, 2957 2958 else => false, 2959 }; 2960 if (!elide_check) { 2961 _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement); 2962 } 2963 return noreturn_src_node; 2964 } 2965 2966 fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct { 2967 have_any: bool, 2968 have_normal: bool, 2969 have_err: bool, 2970 need_err_code: bool, 2971 } { 2972 var have_normal = false; 2973 var have_err = false; 2974 var need_err_code = false; 2975 var scope = inner_scope; 2976 while (scope != outer_scope) { 2977 switch (scope.tag) { 2978 .gen_zir => scope = scope.cast(GenZir).?.parent, 2979 .local_val => scope = scope.cast(Scope.LocalVal).?.parent, 2980 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, 2981 .defer_normal => { 2982 const defer_scope = scope.cast(Scope.Defer).?; 2983 scope = defer_scope.parent; 2984 2985 have_normal = true; 2986 }, 2987 .defer_error => { 2988 const defer_scope = scope.cast(Scope.Defer).?; 2989 scope = defer_scope.parent; 2990 2991 have_err = true; 2992 2993 const have_err_payload = defer_scope.remapped_err_code != .none; 2994 need_err_code = need_err_code or have_err_payload; 2995 }, 2996 .namespace => unreachable, 2997 .top => unreachable, 2998 } 2999 } 3000 return .{ 3001 .have_any = have_normal or have_err, 3002 .have_normal = have_normal, 3003 .have_err = have_err, 3004 .need_err_code = need_err_code, 3005 }; 3006 } 3007 3008 const DefersToEmit = union(enum) { 3009 both: Zir.Inst.Ref, // err code 3010 both_sans_err, 3011 normal_only, 3012 }; 3013 3014 fn genDefers( 3015 gz: *GenZir, 3016 outer_scope: *Scope, 3017 inner_scope: *Scope, 3018 which_ones: DefersToEmit, 3019 ) InnerError!void { 3020 const gpa = gz.astgen.gpa; 3021 3022 var scope = inner_scope; 3023 while (scope != outer_scope) { 3024 switch (scope.tag) { 3025 .gen_zir => scope = scope.cast(GenZir).?.parent, 3026 .local_val => scope = scope.cast(Scope.LocalVal).?.parent, 3027 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, 3028 .defer_normal => { 3029 const defer_scope = scope.cast(Scope.Defer).?; 3030 scope = defer_scope.parent; 3031 try gz.addDefer(defer_scope.index, defer_scope.len); 3032 }, 3033 .defer_error => { 3034 const defer_scope = scope.cast(Scope.Defer).?; 3035 scope = defer_scope.parent; 3036 switch (which_ones) { 3037 .both_sans_err => { 3038 try gz.addDefer(defer_scope.index, defer_scope.len); 3039 }, 3040 .both => |err_code| { 3041 if (defer_scope.remapped_err_code.unwrap()) |remapped_err_code| { 3042 try gz.instructions.ensureUnusedCapacity(gpa, 1); 3043 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 3044 3045 const payload_index = try gz.astgen.addExtra(Zir.Inst.DeferErrCode{ 3046 .remapped_err_code = remapped_err_code, 3047 .index = defer_scope.index, 3048 .len = defer_scope.len, 3049 }); 3050 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 3051 gz.astgen.instructions.appendAssumeCapacity(.{ 3052 .tag = .defer_err_code, 3053 .data = .{ .defer_err_code = .{ 3054 .err_code = err_code, 3055 .payload_index = payload_index, 3056 } }, 3057 }); 3058 gz.instructions.appendAssumeCapacity(new_index); 3059 } else { 3060 try gz.addDefer(defer_scope.index, defer_scope.len); 3061 } 3062 }, 3063 .normal_only => continue, 3064 } 3065 }, 3066 .namespace => unreachable, 3067 .top => unreachable, 3068 } 3069 } 3070 } 3071 3072 fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!void { 3073 const astgen = gz.astgen; 3074 3075 var scope = inner_scope; 3076 while (scope != outer_scope) { 3077 switch (scope.tag) { 3078 .gen_zir => scope = scope.cast(GenZir).?.parent, 3079 .local_val => { 3080 const s = scope.cast(Scope.LocalVal).?; 3081 if (s.used == .none and s.discarded == .none) { 3082 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)}); 3083 } else if (s.used != .none and s.discarded != .none) { 3084 try astgen.appendErrorTokNotes(s.discarded.unwrap().?, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{ 3085 try gz.astgen.errNoteTok(s.used.unwrap().?, "used here", .{}), 3086 }); 3087 } 3088 scope = s.parent; 3089 }, 3090 .local_ptr => { 3091 const s = scope.cast(Scope.LocalPtr).?; 3092 if (s.used == .none and s.discarded == .none) { 3093 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)}); 3094 } else { 3095 if (s.used != .none and s.discarded != .none) { 3096 try astgen.appendErrorTokNotes(s.discarded.unwrap().?, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{ 3097 try astgen.errNoteTok(s.used.unwrap().?, "used here", .{}), 3098 }); 3099 } 3100 if (s.id_cat == .@"local variable" and !s.used_as_lvalue) { 3101 try astgen.appendErrorTokNotes(s.token_src, "local variable is never mutated", .{}, &.{ 3102 try astgen.errNoteTok(s.token_src, "consider using 'const'", .{}), 3103 }); 3104 } 3105 } 3106 3107 scope = s.parent; 3108 }, 3109 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, 3110 .namespace => unreachable, 3111 .top => unreachable, 3112 } 3113 } 3114 } 3115 3116 fn deferStmt( 3117 gz: *GenZir, 3118 scope: *Scope, 3119 node: Ast.Node.Index, 3120 block_arena: Allocator, 3121 scope_tag: Scope.Tag, 3122 ) InnerError!*Scope { 3123 var defer_gen = gz.makeSubBlock(scope); 3124 defer_gen.cur_defer_node = node.toOptional(); 3125 defer_gen.any_defer_node = node.toOptional(); 3126 defer defer_gen.unstack(); 3127 3128 const tree = gz.astgen.tree; 3129 var local_val_scope: Scope.LocalVal = undefined; 3130 var opt_remapped_err_code: Zir.Inst.OptionalIndex = .none; 3131 const sub_scope = if (scope_tag != .defer_error) &defer_gen.base else blk: { 3132 const payload_token = tree.nodeData(node).opt_token_and_node[0].unwrap() orelse break :blk &defer_gen.base; 3133 const ident_name = try gz.astgen.identAsString(payload_token); 3134 if (std.mem.eql(u8, tree.tokenSlice(payload_token), "_")) { 3135 try gz.astgen.appendErrorTok(payload_token, "discard of error capture; omit it instead", .{}); 3136 break :blk &defer_gen.base; 3137 } 3138 const remapped_err_code: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 3139 opt_remapped_err_code = remapped_err_code.toOptional(); 3140 try gz.astgen.instructions.append(gz.astgen.gpa, .{ 3141 .tag = .extended, 3142 .data = .{ .extended = .{ 3143 .opcode = .value_placeholder, 3144 .small = undefined, 3145 .operand = undefined, 3146 } }, 3147 }); 3148 const remapped_err_code_ref = remapped_err_code.toRef(); 3149 local_val_scope = .{ 3150 .parent = &defer_gen.base, 3151 .gen_zir = gz, 3152 .name = ident_name, 3153 .inst = remapped_err_code_ref, 3154 .token_src = payload_token, 3155 .id_cat = .capture, 3156 }; 3157 try gz.addDbgVar(.dbg_var_val, ident_name, remapped_err_code_ref); 3158 break :blk &local_val_scope.base; 3159 }; 3160 const expr_node = switch (scope_tag) { 3161 .defer_normal => tree.nodeData(node).node, 3162 .defer_error => tree.nodeData(node).opt_token_and_node[1], 3163 else => unreachable, 3164 }; 3165 _ = try unusedResultExpr(&defer_gen, sub_scope, expr_node); 3166 try checkUsed(gz, scope, sub_scope); 3167 _ = try defer_gen.addBreak(.break_inline, @enumFromInt(0), .void_value); 3168 3169 const body = defer_gen.instructionsSlice(); 3170 const extra_insts: []const Zir.Inst.Index = if (opt_remapped_err_code.unwrap()) |ec| &.{ec} else &.{}; 3171 const body_len = gz.astgen.countBodyLenAfterFixupsExtraRefs(body, extra_insts); 3172 3173 const index: u32 = @intCast(gz.astgen.extra.items.len); 3174 try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len); 3175 gz.astgen.appendBodyWithFixupsExtraRefsArrayList(&gz.astgen.extra, body, extra_insts); 3176 3177 const defer_scope = try block_arena.create(Scope.Defer); 3178 3179 defer_scope.* = .{ 3180 .base = .{ .tag = scope_tag }, 3181 .parent = scope, 3182 .index = index, 3183 .len = body_len, 3184 .remapped_err_code = opt_remapped_err_code, 3185 }; 3186 return &defer_scope.base; 3187 } 3188 3189 fn varDecl( 3190 gz: *GenZir, 3191 scope: *Scope, 3192 node: Ast.Node.Index, 3193 block_arena: Allocator, 3194 var_decl: Ast.full.VarDecl, 3195 ) InnerError!*Scope { 3196 try emitDbgNode(gz, node); 3197 const astgen = gz.astgen; 3198 const tree = astgen.tree; 3199 3200 const name_token = var_decl.ast.mut_token + 1; 3201 const ident_name_raw = tree.tokenSlice(name_token); 3202 if (mem.eql(u8, ident_name_raw, "_")) { 3203 return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{}); 3204 } 3205 const ident_name = try astgen.identAsString(name_token); 3206 3207 try astgen.detectLocalShadowing( 3208 scope, 3209 ident_name, 3210 name_token, 3211 ident_name_raw, 3212 if (tree.tokenTag(var_decl.ast.mut_token) == .keyword_const) .@"local constant" else .@"local variable", 3213 ); 3214 3215 const init_node = var_decl.ast.init_node.unwrap() orelse { 3216 return astgen.failNode(node, "variables must be initialized", .{}); 3217 }; 3218 3219 if (var_decl.ast.addrspace_node.unwrap()) |addrspace_node| { 3220 return astgen.failTok(tree.nodeMainToken(addrspace_node), "cannot set address space of local variable '{s}'", .{ident_name_raw}); 3221 } 3222 3223 if (var_decl.ast.section_node.unwrap()) |section_node| { 3224 return astgen.failTok(tree.nodeMainToken(section_node), "cannot set section of local variable '{s}'", .{ident_name_raw}); 3225 } 3226 3227 const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node.unwrap()) |align_node| 3228 try expr(gz, scope, coerced_align_ri, align_node) 3229 else 3230 .none; 3231 3232 switch (tree.tokenTag(var_decl.ast.mut_token)) { 3233 .keyword_const => { 3234 if (var_decl.comptime_token) |comptime_token| { 3235 try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{}); 3236 } 3237 3238 // `comptime const` is a non-fatal error; treat it like the init was marked `comptime`. 3239 const force_comptime = var_decl.comptime_token != null; 3240 3241 // Depending on the type of AST the initialization expression is, we may need an lvalue 3242 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as 3243 // the variable, no memory location needed. 3244 if (align_inst == .none and 3245 !astgen.nodes_need_rl.contains(node)) 3246 { 3247 const result_info: ResultInfo = if (var_decl.ast.type_node.unwrap()) |type_node| .{ 3248 .rl = .{ .ty = try typeExpr(gz, scope, type_node) }, 3249 .ctx = .const_init, 3250 } else .{ .rl = .none, .ctx = .const_init }; 3251 const init_inst: Zir.Inst.Ref = try nameStratExpr(gz, scope, result_info, init_node, .dbg_var) orelse 3252 try reachableExprComptime(gz, scope, result_info, init_node, node, if (force_comptime) .comptime_keyword else null); 3253 3254 _ = try gz.addUnNode(.validate_const, init_inst, init_node); 3255 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst); 3256 3257 // The const init expression may have modified the error return trace, so signal 3258 // to Sema that it should save the new index for restoring later. 3259 if (nodeMayAppendToErrorTrace(tree, init_node)) 3260 _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst }); 3261 3262 const sub_scope = try block_arena.create(Scope.LocalVal); 3263 sub_scope.* = .{ 3264 .parent = scope, 3265 .gen_zir = gz, 3266 .name = ident_name, 3267 .inst = init_inst, 3268 .token_src = name_token, 3269 .id_cat = .@"local constant", 3270 }; 3271 return &sub_scope.base; 3272 } 3273 3274 const is_comptime = gz.is_comptime or 3275 tree.nodeTag(init_node) == .@"comptime"; 3276 3277 const init_rl: ResultInfo.Loc = if (var_decl.ast.type_node.unwrap()) |type_node| init_rl: { 3278 const type_inst = try typeExpr(gz, scope, type_node); 3279 if (align_inst == .none) { 3280 break :init_rl .{ .ptr = .{ .inst = try gz.addUnNode(.alloc, type_inst, node) } }; 3281 } else { 3282 break :init_rl .{ .ptr = .{ .inst = try gz.addAllocExtended(.{ 3283 .node = node, 3284 .type_inst = type_inst, 3285 .align_inst = align_inst, 3286 .is_const = true, 3287 .is_comptime = is_comptime, 3288 }) } }; 3289 } 3290 } else init_rl: { 3291 const alloc_inst = if (align_inst == .none) ptr: { 3292 const tag: Zir.Inst.Tag = if (is_comptime) 3293 .alloc_inferred_comptime 3294 else 3295 .alloc_inferred; 3296 break :ptr try gz.addNode(tag, node); 3297 } else ptr: { 3298 break :ptr try gz.addAllocExtended(.{ 3299 .node = node, 3300 .type_inst = .none, 3301 .align_inst = align_inst, 3302 .is_const = true, 3303 .is_comptime = is_comptime, 3304 }); 3305 }; 3306 break :init_rl .{ .inferred_ptr = alloc_inst }; 3307 }; 3308 const var_ptr: Zir.Inst.Ref, const resolve_inferred: bool = switch (init_rl) { 3309 .ptr => |ptr| .{ ptr.inst, false }, 3310 .inferred_ptr => |inst| .{ inst, true }, 3311 else => unreachable, 3312 }; 3313 const init_result_info: ResultInfo = .{ .rl = init_rl, .ctx = .const_init }; 3314 3315 const init_inst: Zir.Inst.Ref = try nameStratExpr(gz, scope, init_result_info, init_node, .dbg_var) orelse 3316 try reachableExprComptime(gz, scope, init_result_info, init_node, node, if (force_comptime) .comptime_keyword else null); 3317 3318 // The const init expression may have modified the error return trace, so signal 3319 // to Sema that it should save the new index for restoring later. 3320 if (nodeMayAppendToErrorTrace(tree, init_node)) 3321 _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst }); 3322 3323 const const_ptr = if (resolve_inferred) 3324 try gz.addUnNode(.resolve_inferred_alloc, var_ptr, node) 3325 else 3326 try gz.addUnNode(.make_ptr_const, var_ptr, node); 3327 3328 try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr); 3329 3330 const sub_scope = try block_arena.create(Scope.LocalPtr); 3331 sub_scope.* = .{ 3332 .parent = scope, 3333 .gen_zir = gz, 3334 .name = ident_name, 3335 .ptr = const_ptr, 3336 .token_src = name_token, 3337 .maybe_comptime = true, 3338 .id_cat = .@"local constant", 3339 }; 3340 return &sub_scope.base; 3341 }, 3342 .keyword_var => { 3343 if (var_decl.comptime_token != null and gz.is_comptime) 3344 return astgen.failTok(var_decl.comptime_token.?, "'comptime var' is redundant in comptime scope", .{}); 3345 const is_comptime = var_decl.comptime_token != null or gz.is_comptime; 3346 const alloc: Zir.Inst.Ref, const resolve_inferred: bool, const result_info: ResultInfo = if (var_decl.ast.type_node.unwrap()) |type_node| a: { 3347 const type_inst = try typeExpr(gz, scope, type_node); 3348 const alloc = alloc: { 3349 if (align_inst == .none) { 3350 const tag: Zir.Inst.Tag = if (is_comptime) 3351 .alloc_comptime_mut 3352 else 3353 .alloc_mut; 3354 break :alloc try gz.addUnNode(tag, type_inst, node); 3355 } else { 3356 break :alloc try gz.addAllocExtended(.{ 3357 .node = node, 3358 .type_inst = type_inst, 3359 .align_inst = align_inst, 3360 .is_const = false, 3361 .is_comptime = is_comptime, 3362 }); 3363 } 3364 }; 3365 break :a .{ alloc, false, .{ .rl = .{ .ptr = .{ .inst = alloc } } } }; 3366 } else a: { 3367 const alloc = alloc: { 3368 if (align_inst == .none) { 3369 const tag: Zir.Inst.Tag = if (is_comptime) 3370 .alloc_inferred_comptime_mut 3371 else 3372 .alloc_inferred_mut; 3373 break :alloc try gz.addNode(tag, node); 3374 } else { 3375 break :alloc try gz.addAllocExtended(.{ 3376 .node = node, 3377 .type_inst = .none, 3378 .align_inst = align_inst, 3379 .is_const = false, 3380 .is_comptime = is_comptime, 3381 }); 3382 } 3383 }; 3384 break :a .{ alloc, true, .{ .rl = .{ .inferred_ptr = alloc } } }; 3385 }; 3386 _ = try nameStratExpr( 3387 gz, 3388 scope, 3389 result_info, 3390 init_node, 3391 .dbg_var, 3392 ) orelse try reachableExprComptime( 3393 gz, 3394 scope, 3395 result_info, 3396 init_node, 3397 node, 3398 if (var_decl.comptime_token != null) .comptime_keyword else null, 3399 ); 3400 const final_ptr: Zir.Inst.Ref = if (resolve_inferred) ptr: { 3401 break :ptr try gz.addUnNode(.resolve_inferred_alloc, alloc, node); 3402 } else alloc; 3403 3404 try gz.addDbgVar(.dbg_var_ptr, ident_name, final_ptr); 3405 3406 const sub_scope = try block_arena.create(Scope.LocalPtr); 3407 sub_scope.* = .{ 3408 .parent = scope, 3409 .gen_zir = gz, 3410 .name = ident_name, 3411 .ptr = final_ptr, 3412 .token_src = name_token, 3413 .maybe_comptime = is_comptime, 3414 .id_cat = .@"local variable", 3415 }; 3416 return &sub_scope.base; 3417 }, 3418 else => unreachable, 3419 } 3420 } 3421 3422 fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void { 3423 // The instruction emitted here is for debugging runtime code. 3424 // If the current block will be evaluated only during semantic analysis 3425 // then no dbg_stmt ZIR instruction is needed. 3426 if (gz.is_comptime) return; 3427 const astgen = gz.astgen; 3428 astgen.advanceSourceCursorToNode(node); 3429 const line = astgen.source_line - gz.decl_line; 3430 const column = astgen.source_column; 3431 try emitDbgStmt(gz, .{ line, column }); 3432 } 3433 3434 fn assign(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void { 3435 try emitDbgNode(gz, infix_node); 3436 const astgen = gz.astgen; 3437 const tree = astgen.tree; 3438 3439 const lhs, const rhs = tree.nodeData(infix_node).node_and_node; 3440 if (tree.nodeTag(lhs) == .identifier) { 3441 // This intentionally does not support `@"_"` syntax. 3442 const ident_name = tree.tokenSlice(tree.nodeMainToken(lhs)); 3443 if (mem.eql(u8, ident_name, "_")) { 3444 _ = try expr(gz, scope, .{ .rl = .discard, .ctx = .assignment }, rhs); 3445 return; 3446 } 3447 } 3448 const lvalue = try lvalExpr(gz, scope, lhs); 3449 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ 3450 .inst = lvalue, 3451 .src_node = infix_node, 3452 } } }, rhs); 3453 } 3454 3455 /// Handles destructure assignments where no LHS is a `const` or `var` decl. 3456 fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!void { 3457 try emitDbgNode(gz, node); 3458 const astgen = gz.astgen; 3459 const tree = astgen.tree; 3460 3461 const full = tree.assignDestructure(node); 3462 if (full.comptime_token != null and gz.is_comptime) { 3463 return astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{}); 3464 } 3465 3466 // If this expression is marked comptime, we must wrap the whole thing in a comptime block. 3467 var gz_buf: GenZir = undefined; 3468 const inner_gz = if (full.comptime_token) |_| bs: { 3469 gz_buf = gz.makeSubBlock(scope); 3470 gz_buf.is_comptime = true; 3471 break :bs &gz_buf; 3472 } else gz; 3473 defer if (full.comptime_token) |_| inner_gz.unstack(); 3474 3475 const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len); 3476 for (rl_components, full.ast.variables) |*variable_rl, variable_node| { 3477 if (tree.nodeTag(variable_node) == .identifier) { 3478 // This intentionally does not support `@"_"` syntax. 3479 const ident_name = tree.tokenSlice(tree.nodeMainToken(variable_node)); 3480 if (mem.eql(u8, ident_name, "_")) { 3481 variable_rl.* = .discard; 3482 continue; 3483 } 3484 } 3485 variable_rl.* = .{ .typed_ptr = .{ 3486 .inst = try lvalExpr(inner_gz, scope, variable_node), 3487 .src_node = variable_node, 3488 } }; 3489 } 3490 3491 const ri: ResultInfo = .{ .rl = .{ .destructure = .{ 3492 .src_node = node, 3493 .components = rl_components, 3494 } } }; 3495 3496 _ = try expr(inner_gz, scope, ri, full.ast.value_expr); 3497 3498 if (full.comptime_token) |_| { 3499 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node); 3500 _ = try inner_gz.addBreak(.break_inline, comptime_block_inst, .void_value); 3501 try inner_gz.setBlockComptimeBody(comptime_block_inst, .comptime_keyword); 3502 try gz.instructions.append(gz.astgen.gpa, comptime_block_inst); 3503 } 3504 } 3505 3506 /// Handles destructure assignments where the LHS may contain `const` or `var` decls. 3507 fn assignDestructureMaybeDecls( 3508 gz: *GenZir, 3509 scope: *Scope, 3510 node: Ast.Node.Index, 3511 block_arena: Allocator, 3512 ) InnerError!*Scope { 3513 try emitDbgNode(gz, node); 3514 const astgen = gz.astgen; 3515 const tree = astgen.tree; 3516 3517 const full = tree.assignDestructure(node); 3518 if (full.comptime_token != null and gz.is_comptime) { 3519 try astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{}); 3520 } 3521 3522 const is_comptime = full.comptime_token != null or gz.is_comptime; 3523 const value_is_comptime = tree.nodeTag(full.ast.value_expr) == .@"comptime"; 3524 3525 // When declaring consts via a destructure, we always use a result pointer. 3526 // This avoids the need to create tuple types, and is also likely easier to 3527 // optimize, since it's a bit tricky for the optimizer to "split up" the 3528 // value into individual pointer writes down the line. 3529 3530 // We know this rl information won't live past the evaluation of this 3531 // expression, so it may as well go in the block arena. 3532 const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len); 3533 var any_non_const_variables = false; 3534 var any_lvalue_expr = false; 3535 for (rl_components, full.ast.variables) |*variable_rl, variable_node| { 3536 switch (tree.nodeTag(variable_node)) { 3537 .identifier => { 3538 // This intentionally does not support `@"_"` syntax. 3539 const ident_name = tree.tokenSlice(tree.nodeMainToken(variable_node)); 3540 if (mem.eql(u8, ident_name, "_")) { 3541 any_non_const_variables = true; 3542 variable_rl.* = .discard; 3543 continue; 3544 } 3545 }, 3546 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => { 3547 const full_var_decl = tree.fullVarDecl(variable_node).?; 3548 3549 const name_token = full_var_decl.ast.mut_token + 1; 3550 const ident_name_raw = tree.tokenSlice(name_token); 3551 if (mem.eql(u8, ident_name_raw, "_")) { 3552 return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{}); 3553 } 3554 3555 // We detect shadowing in the second pass over these, while we're creating scopes. 3556 3557 if (full_var_decl.ast.addrspace_node.unwrap()) |addrspace_node| { 3558 return astgen.failTok(tree.nodeMainToken(addrspace_node), "cannot set address space of local variable '{s}'", .{ident_name_raw}); 3559 } 3560 if (full_var_decl.ast.section_node.unwrap()) |section_node| { 3561 return astgen.failTok(tree.nodeMainToken(section_node), "cannot set section of local variable '{s}'", .{ident_name_raw}); 3562 } 3563 3564 const is_const = switch (tree.tokenTag(full_var_decl.ast.mut_token)) { 3565 .keyword_var => false, 3566 .keyword_const => true, 3567 else => unreachable, 3568 }; 3569 if (!is_const) any_non_const_variables = true; 3570 3571 // We also mark `const`s as comptime if the RHS is definitely comptime-known. 3572 const this_variable_comptime = is_comptime or (is_const and value_is_comptime); 3573 3574 const align_inst: Zir.Inst.Ref = if (full_var_decl.ast.align_node.unwrap()) |align_node| 3575 try expr(gz, scope, coerced_align_ri, align_node) 3576 else 3577 .none; 3578 3579 if (full_var_decl.ast.type_node.unwrap()) |type_node| { 3580 // Typed alloc 3581 const type_inst = try typeExpr(gz, scope, type_node); 3582 const ptr = if (align_inst == .none) ptr: { 3583 const tag: Zir.Inst.Tag = if (is_const) 3584 .alloc 3585 else if (this_variable_comptime) 3586 .alloc_comptime_mut 3587 else 3588 .alloc_mut; 3589 break :ptr try gz.addUnNode(tag, type_inst, node); 3590 } else try gz.addAllocExtended(.{ 3591 .node = node, 3592 .type_inst = type_inst, 3593 .align_inst = align_inst, 3594 .is_const = is_const, 3595 .is_comptime = this_variable_comptime, 3596 }); 3597 variable_rl.* = .{ .typed_ptr = .{ .inst = ptr } }; 3598 } else { 3599 // Inferred alloc 3600 const ptr = if (align_inst == .none) ptr: { 3601 const tag: Zir.Inst.Tag = if (is_const) tag: { 3602 break :tag if (this_variable_comptime) .alloc_inferred_comptime else .alloc_inferred; 3603 } else tag: { 3604 break :tag if (this_variable_comptime) .alloc_inferred_comptime_mut else .alloc_inferred_mut; 3605 }; 3606 break :ptr try gz.addNode(tag, node); 3607 } else try gz.addAllocExtended(.{ 3608 .node = node, 3609 .type_inst = .none, 3610 .align_inst = align_inst, 3611 .is_const = is_const, 3612 .is_comptime = this_variable_comptime, 3613 }); 3614 variable_rl.* = .{ .inferred_ptr = ptr }; 3615 } 3616 3617 continue; 3618 }, 3619 else => {}, 3620 } 3621 // This variable is just an lvalue expression. 3622 // We will fill in its result pointer later, inside a comptime block. 3623 any_non_const_variables = true; 3624 any_lvalue_expr = true; 3625 variable_rl.* = .{ .typed_ptr = .{ 3626 .inst = undefined, 3627 .src_node = variable_node, 3628 } }; 3629 } 3630 3631 if (full.comptime_token != null and !any_non_const_variables) { 3632 try astgen.appendErrorTok(full.comptime_token.?, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{}); 3633 // Note that this is non-fatal; we will still evaluate at comptime. 3634 } 3635 3636 // If this expression is marked comptime, we must wrap it in a comptime block. 3637 var gz_buf: GenZir = undefined; 3638 const inner_gz = if (full.comptime_token) |_| bs: { 3639 gz_buf = gz.makeSubBlock(scope); 3640 gz_buf.is_comptime = true; 3641 break :bs &gz_buf; 3642 } else gz; 3643 defer if (full.comptime_token) |_| inner_gz.unstack(); 3644 3645 if (any_lvalue_expr) { 3646 // At least one variable was an lvalue expr. Iterate again in order to 3647 // evaluate the lvalues from within the possible block_comptime. 3648 for (rl_components, full.ast.variables) |*variable_rl, variable_node| { 3649 if (variable_rl.* != .typed_ptr) continue; 3650 switch (tree.nodeTag(variable_node)) { 3651 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue, 3652 else => {}, 3653 } 3654 variable_rl.typed_ptr.inst = try lvalExpr(inner_gz, scope, variable_node); 3655 } 3656 } 3657 3658 // We can't give a reasonable anon name strategy for destructured inits, so 3659 // leave it at its default of `.anon`. 3660 _ = try reachableExpr(inner_gz, scope, .{ .rl = .{ .destructure = .{ 3661 .src_node = node, 3662 .components = rl_components, 3663 } } }, full.ast.value_expr, node); 3664 3665 if (full.comptime_token) |_| { 3666 // Finish the block_comptime. Inferred alloc resolution etc will occur 3667 // in the parent block. 3668 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node); 3669 _ = try inner_gz.addBreak(.break_inline, comptime_block_inst, .void_value); 3670 try inner_gz.setBlockComptimeBody(comptime_block_inst, .comptime_keyword); 3671 try gz.instructions.append(gz.astgen.gpa, comptime_block_inst); 3672 } 3673 3674 // Now, iterate over the variable exprs to construct any new scopes. 3675 // If there were any inferred allocations, resolve them. 3676 // If there were any `const` decls, make the pointer constant. 3677 var cur_scope = scope; 3678 for (rl_components, full.ast.variables) |variable_rl, variable_node| { 3679 switch (tree.nodeTag(variable_node)) { 3680 .local_var_decl, .simple_var_decl, .aligned_var_decl => {}, 3681 else => continue, // We were mutating an existing lvalue - nothing to do 3682 } 3683 const full_var_decl = tree.fullVarDecl(variable_node).?; 3684 const raw_ptr, const resolve_inferred = switch (variable_rl) { 3685 .discard => unreachable, 3686 .typed_ptr => |typed_ptr| .{ typed_ptr.inst, false }, 3687 .inferred_ptr => |ptr_inst| .{ ptr_inst, true }, 3688 }; 3689 const is_const = switch (tree.tokenTag(full_var_decl.ast.mut_token)) { 3690 .keyword_var => false, 3691 .keyword_const => true, 3692 else => unreachable, 3693 }; 3694 3695 // If the alloc was inferred, resolve it. If the alloc was const, make it const. 3696 const final_ptr = if (resolve_inferred) 3697 try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, variable_node) 3698 else if (is_const) 3699 try gz.addUnNode(.make_ptr_const, raw_ptr, node) 3700 else 3701 raw_ptr; 3702 3703 const name_token = full_var_decl.ast.mut_token + 1; 3704 const ident_name_raw = tree.tokenSlice(name_token); 3705 const ident_name = try astgen.identAsString(name_token); 3706 try astgen.detectLocalShadowing( 3707 cur_scope, 3708 ident_name, 3709 name_token, 3710 ident_name_raw, 3711 if (is_const) .@"local constant" else .@"local variable", 3712 ); 3713 try gz.addDbgVar(.dbg_var_ptr, ident_name, final_ptr); 3714 // Finally, create the scope. 3715 const sub_scope = try block_arena.create(Scope.LocalPtr); 3716 sub_scope.* = .{ 3717 .parent = cur_scope, 3718 .gen_zir = gz, 3719 .name = ident_name, 3720 .ptr = final_ptr, 3721 .token_src = name_token, 3722 .maybe_comptime = is_const or is_comptime, 3723 .id_cat = if (is_const) .@"local constant" else .@"local variable", 3724 }; 3725 cur_scope = &sub_scope.base; 3726 } 3727 3728 return cur_scope; 3729 } 3730 3731 fn assignOp( 3732 gz: *GenZir, 3733 scope: *Scope, 3734 infix_node: Ast.Node.Index, 3735 op_inst_tag: Zir.Inst.Tag, 3736 ) InnerError!void { 3737 try emitDbgNode(gz, infix_node); 3738 const astgen = gz.astgen; 3739 const tree = astgen.tree; 3740 3741 const lhs_node, const rhs_node = tree.nodeData(infix_node).node_and_node; 3742 const lhs_ptr = try lvalExpr(gz, scope, lhs_node); 3743 3744 const cursor = switch (op_inst_tag) { 3745 .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, infix_node), 3746 else => undefined, 3747 }; 3748 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); 3749 3750 const rhs_res_ty = switch (op_inst_tag) { 3751 .add, 3752 .sub, 3753 => try gz.add(.{ 3754 .tag = .extended, 3755 .data = .{ .extended = .{ 3756 .opcode = .inplace_arith_result_ty, 3757 .small = @intFromEnum(@as(Zir.Inst.InplaceOp, switch (op_inst_tag) { 3758 .add => .add_eq, 3759 .sub => .sub_eq, 3760 else => unreachable, 3761 })), 3762 .operand = @intFromEnum(lhs), 3763 } }, 3764 }), 3765 else => try gz.addUnNode(.typeof, lhs, infix_node), // same as LHS type 3766 }; 3767 // Not `coerced_ty` since `add`/etc won't coerce to this type. 3768 const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_res_ty } }, rhs_node); 3769 3770 switch (op_inst_tag) { 3771 .add, .sub, .mul, .div, .mod_rem => { 3772 try emitDbgStmt(gz, cursor); 3773 }, 3774 else => {}, 3775 } 3776 const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{ 3777 .lhs = lhs, 3778 .rhs = rhs, 3779 }); 3780 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{ 3781 .lhs = lhs_ptr, 3782 .rhs = result, 3783 }); 3784 } 3785 3786 fn assignShift( 3787 gz: *GenZir, 3788 scope: *Scope, 3789 infix_node: Ast.Node.Index, 3790 op_inst_tag: Zir.Inst.Tag, 3791 ) InnerError!void { 3792 try emitDbgNode(gz, infix_node); 3793 const astgen = gz.astgen; 3794 const tree = astgen.tree; 3795 3796 const lhs_node, const rhs_node = tree.nodeData(infix_node).node_and_node; 3797 const lhs_ptr = try lvalExpr(gz, scope, lhs_node); 3798 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); 3799 const rhs_type = try gz.addUnNode(.typeof_log2_int_type, lhs, infix_node); 3800 const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_type } }, rhs_node); 3801 3802 const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{ 3803 .lhs = lhs, 3804 .rhs = rhs, 3805 }); 3806 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{ 3807 .lhs = lhs_ptr, 3808 .rhs = result, 3809 }); 3810 } 3811 3812 fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void { 3813 try emitDbgNode(gz, infix_node); 3814 const astgen = gz.astgen; 3815 const tree = astgen.tree; 3816 3817 const lhs_node, const rhs_node = tree.nodeData(infix_node).node_and_node; 3818 const lhs_ptr = try lvalExpr(gz, scope, lhs_node); 3819 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); 3820 // Saturating shift-left allows any integer type for both the LHS and RHS. 3821 const rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node); 3822 3823 const result = try gz.addPlNode(.shl_sat, infix_node, Zir.Inst.Bin{ 3824 .lhs = lhs, 3825 .rhs = rhs, 3826 }); 3827 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{ 3828 .lhs = lhs_ptr, 3829 .rhs = result, 3830 }); 3831 } 3832 3833 fn ptrType( 3834 gz: *GenZir, 3835 scope: *Scope, 3836 ri: ResultInfo, 3837 node: Ast.Node.Index, 3838 ptr_info: Ast.full.PtrType, 3839 ) InnerError!Zir.Inst.Ref { 3840 if (ptr_info.size == .c and ptr_info.allowzero_token != null) { 3841 return gz.astgen.failTok(ptr_info.allowzero_token.?, "C pointers always allow address zero", .{}); 3842 } 3843 3844 const source_offset = gz.astgen.source_offset; 3845 const source_line = gz.astgen.source_line; 3846 const source_column = gz.astgen.source_column; 3847 const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type); 3848 3849 var sentinel_ref: Zir.Inst.Ref = .none; 3850 var align_ref: Zir.Inst.Ref = .none; 3851 var addrspace_ref: Zir.Inst.Ref = .none; 3852 var bit_start_ref: Zir.Inst.Ref = .none; 3853 var bit_end_ref: Zir.Inst.Ref = .none; 3854 var trailing_count: u32 = 0; 3855 3856 if (ptr_info.ast.sentinel.unwrap()) |sentinel| { 3857 // These attributes can appear in any order and they all come before the 3858 // element type so we need to reset the source cursor before generating them. 3859 gz.astgen.source_offset = source_offset; 3860 gz.astgen.source_line = source_line; 3861 gz.astgen.source_column = source_column; 3862 3863 sentinel_ref = try comptimeExpr( 3864 gz, 3865 scope, 3866 .{ .rl = .{ .ty = elem_type } }, 3867 sentinel, 3868 switch (ptr_info.size) { 3869 .slice => .slice_sentinel, 3870 else => .pointer_sentinel, 3871 }, 3872 ); 3873 trailing_count += 1; 3874 } 3875 if (ptr_info.ast.addrspace_node.unwrap()) |addrspace_node| { 3876 gz.astgen.source_offset = source_offset; 3877 gz.astgen.source_line = source_line; 3878 gz.astgen.source_column = source_column; 3879 3880 const addrspace_ty = try gz.addBuiltinValue(addrspace_node, .address_space); 3881 addrspace_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = addrspace_ty } }, addrspace_node, .@"addrspace"); 3882 trailing_count += 1; 3883 } 3884 if (ptr_info.ast.align_node.unwrap()) |align_node| { 3885 gz.astgen.source_offset = source_offset; 3886 gz.astgen.source_line = source_line; 3887 gz.astgen.source_column = source_column; 3888 3889 align_ref = try comptimeExpr(gz, scope, coerced_align_ri, align_node, .@"align"); 3890 trailing_count += 1; 3891 } 3892 if (ptr_info.ast.bit_range_start.unwrap()) |bit_range_start| { 3893 const bit_range_end = ptr_info.ast.bit_range_end.unwrap().?; 3894 bit_start_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, bit_range_start, .type); 3895 bit_end_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, bit_range_end, .type); 3896 trailing_count += 2; 3897 } 3898 3899 const gpa = gz.astgen.gpa; 3900 try gz.instructions.ensureUnusedCapacity(gpa, 1); 3901 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 3902 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.PtrType).@"struct".fields.len + 3903 trailing_count); 3904 3905 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{ 3906 .elem_type = elem_type, 3907 .src_node = gz.nodeIndexToRelative(node), 3908 }); 3909 if (sentinel_ref != .none) { 3910 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(sentinel_ref)); 3911 } 3912 if (align_ref != .none) { 3913 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(align_ref)); 3914 } 3915 if (addrspace_ref != .none) { 3916 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(addrspace_ref)); 3917 } 3918 if (bit_start_ref != .none) { 3919 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_start_ref)); 3920 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_end_ref)); 3921 } 3922 3923 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 3924 const result = new_index.toRef(); 3925 gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{ 3926 .ptr_type = .{ 3927 .flags = .{ 3928 .is_allowzero = ptr_info.allowzero_token != null, 3929 .is_mutable = ptr_info.const_token == null, 3930 .is_volatile = ptr_info.volatile_token != null, 3931 .has_sentinel = sentinel_ref != .none, 3932 .has_align = align_ref != .none, 3933 .has_addrspace = addrspace_ref != .none, 3934 .has_bit_range = bit_start_ref != .none, 3935 }, 3936 .size = ptr_info.size, 3937 .payload_index = payload_index, 3938 }, 3939 } }); 3940 gz.instructions.appendAssumeCapacity(new_index); 3941 3942 return rvalue(gz, ri, result, node); 3943 } 3944 3945 fn arrayType(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref { 3946 const astgen = gz.astgen; 3947 const tree = astgen.tree; 3948 3949 const len_node, const elem_type_node = tree.nodeData(node).node_and_node; 3950 if (tree.nodeTag(len_node) == .identifier and 3951 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(len_node)), "_")) 3952 { 3953 return astgen.failNode(len_node, "unable to infer array size", .{}); 3954 } 3955 const len = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node, node, .type); 3956 const elem_type = try typeExpr(gz, scope, elem_type_node); 3957 3958 const result = try gz.addPlNode(.array_type, node, Zir.Inst.Bin{ 3959 .lhs = len, 3960 .rhs = elem_type, 3961 }); 3962 return rvalue(gz, ri, result, node); 3963 } 3964 3965 fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref { 3966 const astgen = gz.astgen; 3967 const tree = astgen.tree; 3968 3969 const len_node, const extra_index = tree.nodeData(node).node_and_extra; 3970 const extra = tree.extraData(extra_index, Ast.Node.ArrayTypeSentinel); 3971 3972 if (tree.nodeTag(len_node) == .identifier and 3973 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(len_node)), "_")) 3974 { 3975 return astgen.failNode(len_node, "unable to infer array size", .{}); 3976 } 3977 const len = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node, node, .array_length); 3978 const elem_type = try typeExpr(gz, scope, extra.elem_type); 3979 const sentinel = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = elem_type } }, extra.sentinel, node, .array_sentinel); 3980 3981 const result = try gz.addPlNode(.array_type_sentinel, node, Zir.Inst.ArrayTypeSentinel{ 3982 .len = len, 3983 .elem_type = elem_type, 3984 .sentinel = sentinel, 3985 }); 3986 return rvalue(gz, ri, result, node); 3987 } 3988 3989 const WipMembers = struct { 3990 payload: *ArrayListUnmanaged(u32), 3991 payload_top: usize, 3992 field_bits_start: u32, 3993 fields_start: u32, 3994 fields_end: u32, 3995 decl_index: u32 = 0, 3996 field_index: u32 = 0, 3997 3998 const Self = @This(); 3999 4000 fn init(gpa: Allocator, payload: *ArrayListUnmanaged(u32), decl_count: u32, field_count: u32, comptime bits_per_field: u32, comptime max_field_size: u32) Allocator.Error!Self { 4001 const payload_top: u32 = @intCast(payload.items.len); 4002 const field_bits_start = payload_top + decl_count; 4003 const fields_start = field_bits_start + if (bits_per_field > 0) blk: { 4004 const fields_per_u32 = 32 / bits_per_field; 4005 break :blk (field_count + fields_per_u32 - 1) / fields_per_u32; 4006 } else 0; 4007 const payload_end = fields_start + field_count * max_field_size; 4008 try payload.resize(gpa, payload_end); 4009 return .{ 4010 .payload = payload, 4011 .payload_top = payload_top, 4012 .field_bits_start = field_bits_start, 4013 .fields_start = fields_start, 4014 .fields_end = fields_start, 4015 }; 4016 } 4017 4018 fn nextDecl(self: *Self, decl_inst: Zir.Inst.Index) void { 4019 self.payload.items[self.payload_top + self.decl_index] = @intFromEnum(decl_inst); 4020 self.decl_index += 1; 4021 } 4022 4023 fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void { 4024 const fields_per_u32 = 32 / bits_per_field; 4025 const index = self.field_bits_start + self.field_index / fields_per_u32; 4026 assert(index < self.fields_start); 4027 var bit_bag: u32 = if (self.field_index % fields_per_u32 == 0) 0 else self.payload.items[index]; 4028 bit_bag >>= bits_per_field; 4029 comptime var i = 0; 4030 inline while (i < bits_per_field) : (i += 1) { 4031 bit_bag |= @as(u32, @intFromBool(bits[i])) << (32 - bits_per_field + i); 4032 } 4033 self.payload.items[index] = bit_bag; 4034 self.field_index += 1; 4035 } 4036 4037 fn appendToField(self: *Self, data: u32) void { 4038 assert(self.fields_end < self.payload.items.len); 4039 self.payload.items[self.fields_end] = data; 4040 self.fields_end += 1; 4041 } 4042 4043 fn finishBits(self: *Self, comptime bits_per_field: u32) void { 4044 if (bits_per_field > 0) { 4045 const fields_per_u32 = 32 / bits_per_field; 4046 const empty_field_slots = fields_per_u32 - (self.field_index % fields_per_u32); 4047 if (self.field_index > 0 and empty_field_slots < fields_per_u32) { 4048 const index = self.field_bits_start + self.field_index / fields_per_u32; 4049 self.payload.items[index] >>= @intCast(empty_field_slots * bits_per_field); 4050 } 4051 } 4052 } 4053 4054 fn declsSlice(self: *Self) []u32 { 4055 return self.payload.items[self.payload_top..][0..self.decl_index]; 4056 } 4057 4058 fn fieldsSlice(self: *Self) []u32 { 4059 return self.payload.items[self.field_bits_start..self.fields_end]; 4060 } 4061 4062 fn deinit(self: *Self) void { 4063 self.payload.items.len = self.payload_top; 4064 } 4065 }; 4066 4067 fn fnDecl( 4068 astgen: *AstGen, 4069 gz: *GenZir, 4070 scope: *Scope, 4071 wip_members: *WipMembers, 4072 decl_node: Ast.Node.Index, 4073 body_node: Ast.Node.OptionalIndex, 4074 fn_proto: Ast.full.FnProto, 4075 ) InnerError!void { 4076 const tree = astgen.tree; 4077 4078 const old_hasher = astgen.src_hasher; 4079 defer astgen.src_hasher = old_hasher; 4080 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 4081 // We don't add the full source yet, because we also need the prototype hash! 4082 // The source slice is added towards the *end* of this function. 4083 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); 4084 4085 // missing function name already checked in scanContainer() 4086 const fn_name_token = fn_proto.name_token.?; 4087 4088 // We insert this at the beginning so that its instruction index marks the 4089 // start of the top level declaration. 4090 const decl_inst = try gz.makeDeclaration(fn_proto.ast.proto_node); 4091 astgen.advanceSourceCursorToNode(decl_node); 4092 4093 const saved_cursor = astgen.saveSourceCursor(); 4094 4095 const decl_column = astgen.source_column; 4096 4097 // Set this now, since parameter types, return type, etc may be generic. 4098 const prev_within_fn = astgen.within_fn; 4099 defer astgen.within_fn = prev_within_fn; 4100 astgen.within_fn = true; 4101 4102 const is_pub = fn_proto.visib_token != null; 4103 const is_export = blk: { 4104 const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false; 4105 break :blk tree.tokenTag(maybe_export_token) == .keyword_export; 4106 }; 4107 const is_extern = blk: { 4108 const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false; 4109 break :blk tree.tokenTag(maybe_extern_token) == .keyword_extern; 4110 }; 4111 const has_inline_keyword = blk: { 4112 const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false; 4113 break :blk tree.tokenTag(maybe_inline_token) == .keyword_inline; 4114 }; 4115 const lib_name = if (fn_proto.lib_name) |lib_name_token| blk: { 4116 const lib_name_str = try astgen.strLitAsString(lib_name_token); 4117 const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len]; 4118 if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) { 4119 return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{}); 4120 } else if (lib_name_str.len == 0) { 4121 return astgen.failTok(lib_name_token, "library name cannot be empty", .{}); 4122 } 4123 break :blk lib_name_str.index; 4124 } else .empty; 4125 if (fn_proto.ast.callconv_expr != .none and has_inline_keyword) { 4126 return astgen.failNode( 4127 fn_proto.ast.callconv_expr.unwrap().?, 4128 "explicit callconv incompatible with inline keyword", 4129 .{}, 4130 ); 4131 } 4132 4133 const return_type = fn_proto.ast.return_type.unwrap().?; 4134 const maybe_bang = tree.firstToken(return_type) - 1; 4135 const is_inferred_error = tree.tokenTag(maybe_bang) == .bang; 4136 if (body_node == .none) { 4137 if (!is_extern) { 4138 return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{}); 4139 } 4140 if (is_inferred_error) { 4141 return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{}); 4142 } 4143 } else { 4144 assert(!is_extern); // validated by parser (TODO why???) 4145 } 4146 4147 wip_members.nextDecl(decl_inst); 4148 4149 var type_gz: GenZir = .{ 4150 .is_comptime = true, 4151 .decl_node_index = fn_proto.ast.proto_node, 4152 .decl_line = astgen.source_line, 4153 .parent = scope, 4154 .astgen = astgen, 4155 .instructions = gz.instructions, 4156 .instructions_top = gz.instructions.items.len, 4157 }; 4158 defer type_gz.unstack(); 4159 4160 if (is_extern) { 4161 // We include a function *type*, not a value. 4162 const type_inst = try fnProtoExprInner(&type_gz, &type_gz.base, .{ .rl = .none }, decl_node, fn_proto, true); 4163 _ = try type_gz.addBreakWithSrcNode(.break_inline, decl_inst, type_inst, decl_node); 4164 } 4165 4166 var align_gz = type_gz.makeSubBlock(scope); 4167 defer align_gz.unstack(); 4168 4169 if (fn_proto.ast.align_expr.unwrap()) |align_expr| { 4170 astgen.restoreSourceCursor(saved_cursor); 4171 const inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, align_expr); 4172 _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node); 4173 } 4174 4175 var linksection_gz = align_gz.makeSubBlock(scope); 4176 defer linksection_gz.unstack(); 4177 4178 if (fn_proto.ast.section_expr.unwrap()) |section_expr| { 4179 astgen.restoreSourceCursor(saved_cursor); 4180 const inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_expr); 4181 _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node); 4182 } 4183 4184 var addrspace_gz = linksection_gz.makeSubBlock(scope); 4185 defer addrspace_gz.unstack(); 4186 4187 if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| { 4188 astgen.restoreSourceCursor(saved_cursor); 4189 const addrspace_ty = try addrspace_gz.addBuiltinValue(addrspace_expr, .address_space); 4190 const inst = try expr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, addrspace_expr); 4191 _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node); 4192 } 4193 4194 var value_gz = addrspace_gz.makeSubBlock(scope); 4195 defer value_gz.unstack(); 4196 4197 if (!is_extern) { 4198 // We include a function *value*, not a type. 4199 astgen.restoreSourceCursor(saved_cursor); 4200 try astgen.fnDeclInner(&value_gz, &value_gz.base, saved_cursor, decl_inst, decl_node, body_node.unwrap().?, fn_proto); 4201 } 4202 4203 // *Now* we can incorporate the full source code into the hasher. 4204 astgen.src_hasher.update(tree.getNodeSource(decl_node)); 4205 4206 var hash: std.zig.SrcHash = undefined; 4207 astgen.src_hasher.final(&hash); 4208 try setDeclaration(decl_inst, .{ 4209 .src_hash = hash, 4210 .src_line = type_gz.decl_line, 4211 .src_column = decl_column, 4212 4213 .kind = .@"const", 4214 .name = try astgen.identAsString(fn_name_token), 4215 .is_pub = is_pub, 4216 .is_threadlocal = false, 4217 .linkage = if (is_extern) .@"extern" else if (is_export) .@"export" else .normal, 4218 .lib_name = lib_name, 4219 4220 .type_gz = &type_gz, 4221 .align_gz = &align_gz, 4222 .linksection_gz = &linksection_gz, 4223 .addrspace_gz = &addrspace_gz, 4224 .value_gz = &value_gz, 4225 }); 4226 } 4227 4228 fn fnDeclInner( 4229 astgen: *AstGen, 4230 decl_gz: *GenZir, 4231 scope: *Scope, 4232 saved_cursor: SourceCursor, 4233 decl_inst: Zir.Inst.Index, 4234 decl_node: Ast.Node.Index, 4235 body_node: Ast.Node.Index, 4236 fn_proto: Ast.full.FnProto, 4237 ) InnerError!void { 4238 const tree = astgen.tree; 4239 4240 const is_noinline = blk: { 4241 const maybe_noinline_token = fn_proto.extern_export_inline_token orelse break :blk false; 4242 break :blk tree.tokenTag(maybe_noinline_token) == .keyword_noinline; 4243 }; 4244 const has_inline_keyword = blk: { 4245 const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false; 4246 break :blk tree.tokenTag(maybe_inline_token) == .keyword_inline; 4247 }; 4248 4249 const return_type = fn_proto.ast.return_type.unwrap().?; 4250 const maybe_bang = tree.firstToken(return_type) - 1; 4251 const is_inferred_error = tree.tokenTag(maybe_bang) == .bang; 4252 4253 // Note that the capacity here may not be sufficient, as this does not include `anytype` parameters. 4254 var param_insts: std.ArrayListUnmanaged(Zir.Inst.Index) = try .initCapacity(astgen.arena, fn_proto.ast.params.len); 4255 4256 // We use this as `is_used_or_discarded` to figure out if parameters / return types are generic. 4257 var any_param_used = false; 4258 4259 var noalias_bits: u32 = 0; 4260 var params_scope = scope; 4261 const is_var_args = is_var_args: { 4262 var param_type_i: usize = 0; 4263 var it = fn_proto.iterate(tree); 4264 while (it.next()) |param| : (param_type_i += 1) { 4265 const is_comptime = if (param.comptime_noalias) |token| switch (tree.tokenTag(token)) { 4266 .keyword_noalias => is_comptime: { 4267 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse 4268 return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{})); 4269 break :is_comptime false; 4270 }, 4271 .keyword_comptime => true, 4272 else => false, 4273 } else false; 4274 4275 const is_anytype = if (param.anytype_ellipsis3) |token| blk: { 4276 switch (tree.tokenTag(token)) { 4277 .keyword_anytype => break :blk true, 4278 .ellipsis3 => break :is_var_args true, 4279 else => unreachable, 4280 } 4281 } else false; 4282 4283 const param_name: Zir.NullTerminatedString = if (param.name_token) |name_token| blk: { 4284 const name_bytes = tree.tokenSlice(name_token); 4285 if (mem.eql(u8, "_", name_bytes)) 4286 break :blk .empty; 4287 4288 const param_name = try astgen.identAsString(name_token); 4289 try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes, .@"function parameter"); 4290 break :blk param_name; 4291 } else { 4292 if (param.anytype_ellipsis3) |tok| { 4293 return astgen.failTok(tok, "missing parameter name", .{}); 4294 } else { 4295 const type_expr = param.type_expr.?; 4296 ambiguous: { 4297 if (tree.nodeTag(type_expr) != .identifier) break :ambiguous; 4298 const main_token = tree.nodeMainToken(type_expr); 4299 const identifier_str = tree.tokenSlice(main_token); 4300 if (isPrimitive(identifier_str)) break :ambiguous; 4301 return astgen.failNodeNotes( 4302 type_expr, 4303 "missing parameter name or type", 4304 .{}, 4305 &[_]u32{ 4306 try astgen.errNoteNode( 4307 type_expr, 4308 "if this is a name, annotate its type: '{s}: T'", 4309 .{identifier_str}, 4310 ), 4311 try astgen.errNoteNode( 4312 type_expr, 4313 "if this is a type, give it a name: 'name: {s}'", 4314 .{identifier_str}, 4315 ), 4316 }, 4317 ); 4318 } 4319 return astgen.failNode(type_expr, "missing parameter name", .{}); 4320 } 4321 }; 4322 4323 const param_inst = if (is_anytype) param: { 4324 const name_token = param.name_token orelse param.anytype_ellipsis3.?; 4325 const tag: Zir.Inst.Tag = if (is_comptime) 4326 .param_anytype_comptime 4327 else 4328 .param_anytype; 4329 break :param try decl_gz.addStrTok(tag, param_name, name_token); 4330 } else param: { 4331 const param_type_node = param.type_expr.?; 4332 any_param_used = false; // we will check this later 4333 var param_gz = decl_gz.makeSubBlock(scope); 4334 defer param_gz.unstack(); 4335 const param_type = try fullBodyExpr(¶m_gz, params_scope, coerced_type_ri, param_type_node, .normal); 4336 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1); 4337 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node); 4338 const param_type_is_generic = any_param_used; 4339 4340 const name_token = param.name_token orelse tree.nodeMainToken(param_type_node); 4341 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param; 4342 const param_inst = try decl_gz.addParam(¶m_gz, param_insts.items, param_type_is_generic, tag, name_token, param_name); 4343 assert(param_inst_expected == param_inst); 4344 break :param param_inst.toRef(); 4345 }; 4346 4347 if (param_name == .empty) continue; 4348 4349 const sub_scope = try astgen.arena.create(Scope.LocalVal); 4350 sub_scope.* = .{ 4351 .parent = params_scope, 4352 .gen_zir = decl_gz, 4353 .name = param_name, 4354 .inst = param_inst, 4355 .token_src = param.name_token.?, 4356 .id_cat = .@"function parameter", 4357 .is_used_or_discarded = &any_param_used, 4358 }; 4359 params_scope = &sub_scope.base; 4360 try param_insts.append(astgen.arena, param_inst.toIndex().?); 4361 } 4362 break :is_var_args false; 4363 }; 4364 4365 // After creating the function ZIR instruction, it will need to update the break 4366 // instructions inside the expression blocks for cc and ret_ty to use the function 4367 // instruction as the body to break from. 4368 4369 var ret_gz = decl_gz.makeSubBlock(params_scope); 4370 defer ret_gz.unstack(); 4371 any_param_used = false; // we will check this later 4372 const ret_ref: Zir.Inst.Ref = inst: { 4373 // Parameters are in scope for the return type, so we use `params_scope` here. 4374 // The calling convention will not have parameters in scope, so we'll just use `scope`. 4375 // See #22263 for a proposal to solve the inconsistency here. 4376 const inst = try fullBodyExpr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type.unwrap().?, .normal); 4377 if (ret_gz.instructionsSlice().len == 0) { 4378 // In this case we will send a len=0 body which can be encoded more efficiently. 4379 break :inst inst; 4380 } 4381 _ = try ret_gz.addBreak(.break_inline, @enumFromInt(0), inst); 4382 break :inst inst; 4383 }; 4384 const ret_body_param_refs = try astgen.fetchRemoveRefEntries(param_insts.items); 4385 const ret_ty_is_generic = any_param_used; 4386 4387 // We're jumping back in source, so restore the cursor. 4388 astgen.restoreSourceCursor(saved_cursor); 4389 4390 var cc_gz = decl_gz.makeSubBlock(scope); 4391 defer cc_gz.unstack(); 4392 const cc_ref: Zir.Inst.Ref = blk: { 4393 if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| { 4394 const inst = try expr( 4395 &cc_gz, 4396 scope, 4397 .{ .rl = .{ .coerced_ty = try cc_gz.addBuiltinValue(callconv_expr, .calling_convention) } }, 4398 callconv_expr, 4399 ); 4400 if (cc_gz.instructionsSlice().len == 0) { 4401 // In this case we will send a len=0 body which can be encoded more efficiently. 4402 break :blk inst; 4403 } 4404 _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst); 4405 break :blk inst; 4406 } else if (has_inline_keyword) { 4407 const inst = try cc_gz.addBuiltinValue(decl_node, .calling_convention_inline); 4408 _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst); 4409 break :blk inst; 4410 } else { 4411 break :blk .none; 4412 } 4413 }; 4414 4415 var body_gz: GenZir = .{ 4416 .is_comptime = false, 4417 .decl_node_index = fn_proto.ast.proto_node, 4418 .decl_line = decl_gz.decl_line, 4419 .parent = params_scope, 4420 .astgen = astgen, 4421 .instructions = decl_gz.instructions, 4422 .instructions_top = decl_gz.instructions.items.len, 4423 }; 4424 defer body_gz.unstack(); 4425 4426 // The scope stack looks like this: 4427 // body_gz (top) 4428 // param2 4429 // param1 4430 // param0 4431 // decl_gz (bottom) 4432 4433 // Construct the prototype hash. 4434 // Leave `astgen.src_hasher` unmodified; this will be used for hashing 4435 // the *whole* function declaration, including its body. 4436 var proto_hasher = astgen.src_hasher; 4437 const proto_node = tree.nodeData(decl_node).node_and_node[0]; 4438 proto_hasher.update(tree.getNodeSource(proto_node)); 4439 var proto_hash: std.zig.SrcHash = undefined; 4440 proto_hasher.final(&proto_hash); 4441 4442 const prev_fn_block = astgen.fn_block; 4443 const prev_fn_ret_ty = astgen.fn_ret_ty; 4444 defer { 4445 astgen.fn_block = prev_fn_block; 4446 astgen.fn_ret_ty = prev_fn_ret_ty; 4447 } 4448 astgen.fn_block = &body_gz; 4449 astgen.fn_ret_ty = if (is_inferred_error or ret_ref.toIndex() != null) r: { 4450 // We're essentially guaranteed to need the return type at some point, 4451 // since the return type is likely not `void` or `noreturn` so there 4452 // will probably be an explicit return requiring RLS. Fetch this 4453 // return type now so the rest of the function can use it. 4454 break :r try body_gz.addNode(.ret_type, decl_node); 4455 } else ret_ref; 4456 4457 const prev_var_args = astgen.fn_var_args; 4458 astgen.fn_var_args = is_var_args; 4459 defer astgen.fn_var_args = prev_var_args; 4460 4461 astgen.advanceSourceCursorToNode(body_node); 4462 const lbrace_line = astgen.source_line - decl_gz.decl_line; 4463 const lbrace_column = astgen.source_column; 4464 4465 _ = try fullBodyExpr(&body_gz, &body_gz.base, .{ .rl = .none }, body_node, .allow_branch_hint); 4466 try checkUsed(decl_gz, scope, params_scope); 4467 4468 if (!body_gz.endsWithNoReturn()) { 4469 // As our last action before the return, "pop" the error trace if needed 4470 _ = try body_gz.addRestoreErrRetIndex(.ret, .always, decl_node); 4471 4472 // Add implicit return at end of function. 4473 _ = try body_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node)); 4474 } 4475 4476 const func_inst = try decl_gz.addFunc(.{ 4477 .src_node = decl_node, 4478 .cc_ref = cc_ref, 4479 .cc_gz = &cc_gz, 4480 .ret_ref = ret_ref, 4481 .ret_gz = &ret_gz, 4482 .ret_param_refs = ret_body_param_refs, 4483 .ret_ty_is_generic = ret_ty_is_generic, 4484 .lbrace_line = lbrace_line, 4485 .lbrace_column = lbrace_column, 4486 .param_block = decl_inst, 4487 .param_insts = param_insts.items, 4488 .body_gz = &body_gz, 4489 .is_var_args = is_var_args, 4490 .is_inferred_error = is_inferred_error, 4491 .is_noinline = is_noinline, 4492 .noalias_bits = noalias_bits, 4493 .proto_hash = proto_hash, 4494 }); 4495 _ = try decl_gz.addBreakWithSrcNode(.break_inline, decl_inst, func_inst, decl_node); 4496 } 4497 4498 fn globalVarDecl( 4499 astgen: *AstGen, 4500 gz: *GenZir, 4501 scope: *Scope, 4502 wip_members: *WipMembers, 4503 node: Ast.Node.Index, 4504 var_decl: Ast.full.VarDecl, 4505 ) InnerError!void { 4506 const tree = astgen.tree; 4507 4508 const old_hasher = astgen.src_hasher; 4509 defer astgen.src_hasher = old_hasher; 4510 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 4511 astgen.src_hasher.update(tree.getNodeSource(node)); 4512 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); 4513 4514 const is_mutable = tree.tokenTag(var_decl.ast.mut_token) == .keyword_var; 4515 const name_token = var_decl.ast.mut_token + 1; 4516 const is_pub = var_decl.visib_token != null; 4517 const is_export = blk: { 4518 const maybe_export_token = var_decl.extern_export_token orelse break :blk false; 4519 break :blk tree.tokenTag(maybe_export_token) == .keyword_export; 4520 }; 4521 const is_extern = blk: { 4522 const maybe_extern_token = var_decl.extern_export_token orelse break :blk false; 4523 break :blk tree.tokenTag(maybe_extern_token) == .keyword_extern; 4524 }; 4525 const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: { 4526 if (!is_mutable) { 4527 return astgen.failTok(tok, "threadlocal variable cannot be constant", .{}); 4528 } 4529 break :blk true; 4530 } else false; 4531 const lib_name = if (var_decl.lib_name) |lib_name_token| blk: { 4532 const lib_name_str = try astgen.strLitAsString(lib_name_token); 4533 const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len]; 4534 if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) { 4535 return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{}); 4536 } else if (lib_name_str.len == 0) { 4537 return astgen.failTok(lib_name_token, "library name cannot be empty", .{}); 4538 } 4539 break :blk lib_name_str.index; 4540 } else .empty; 4541 4542 astgen.advanceSourceCursorToNode(node); 4543 4544 const decl_column = astgen.source_column; 4545 4546 const decl_inst = try gz.makeDeclaration(node); 4547 wip_members.nextDecl(decl_inst); 4548 4549 if (var_decl.ast.init_node.unwrap()) |init_node| { 4550 if (is_extern) { 4551 return astgen.failNode( 4552 init_node, 4553 "extern variables have no initializers", 4554 .{}, 4555 ); 4556 } 4557 } else { 4558 if (!is_extern) { 4559 return astgen.failNode(node, "variables must be initialized", .{}); 4560 } 4561 } 4562 4563 if (is_extern and var_decl.ast.type_node == .none) { 4564 return astgen.failNode(node, "unable to infer variable type", .{}); 4565 } 4566 4567 assert(var_decl.comptime_token == null); // handled by parser 4568 4569 var type_gz: GenZir = .{ 4570 .parent = scope, 4571 .decl_node_index = node, 4572 .decl_line = astgen.source_line, 4573 .astgen = astgen, 4574 .is_comptime = true, 4575 .instructions = gz.instructions, 4576 .instructions_top = gz.instructions.items.len, 4577 }; 4578 defer type_gz.unstack(); 4579 4580 if (var_decl.ast.type_node.unwrap()) |type_node| { 4581 const type_inst = try expr(&type_gz, &type_gz.base, coerced_type_ri, type_node); 4582 _ = try type_gz.addBreakWithSrcNode(.break_inline, decl_inst, type_inst, node); 4583 } 4584 4585 var align_gz = type_gz.makeSubBlock(scope); 4586 defer align_gz.unstack(); 4587 4588 if (var_decl.ast.align_node.unwrap()) |align_node| { 4589 const align_inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, align_node); 4590 _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node); 4591 } 4592 4593 var linksection_gz = type_gz.makeSubBlock(scope); 4594 defer linksection_gz.unstack(); 4595 4596 if (var_decl.ast.section_node.unwrap()) |section_node| { 4597 const linksection_inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_node); 4598 _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node); 4599 } 4600 4601 var addrspace_gz = type_gz.makeSubBlock(scope); 4602 defer addrspace_gz.unstack(); 4603 4604 if (var_decl.ast.addrspace_node.unwrap()) |addrspace_node| { 4605 const addrspace_ty = try addrspace_gz.addBuiltinValue(addrspace_node, .address_space); 4606 const addrspace_inst = try expr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, addrspace_node); 4607 _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node); 4608 } 4609 4610 var init_gz = type_gz.makeSubBlock(scope); 4611 defer init_gz.unstack(); 4612 4613 if (var_decl.ast.init_node.unwrap()) |init_node| { 4614 const init_ri: ResultInfo = if (var_decl.ast.type_node != .none) .{ 4615 .rl = .{ .coerced_ty = decl_inst.toRef() }, 4616 } else .{ .rl = .none }; 4617 const init_inst: Zir.Inst.Ref = try nameStratExpr(&init_gz, &init_gz.base, init_ri, init_node, .parent) orelse init: { 4618 break :init try expr(&init_gz, &init_gz.base, init_ri, init_node); 4619 }; 4620 _ = try init_gz.addBreakWithSrcNode(.break_inline, decl_inst, init_inst, node); 4621 } 4622 4623 var hash: std.zig.SrcHash = undefined; 4624 astgen.src_hasher.final(&hash); 4625 try setDeclaration(decl_inst, .{ 4626 .src_hash = hash, 4627 .src_line = type_gz.decl_line, 4628 .src_column = decl_column, 4629 4630 .kind = if (is_mutable) .@"var" else .@"const", 4631 .name = try astgen.identAsString(name_token), 4632 .is_pub = is_pub, 4633 .is_threadlocal = is_threadlocal, 4634 .linkage = if (is_extern) .@"extern" else if (is_export) .@"export" else .normal, 4635 .lib_name = lib_name, 4636 4637 .type_gz = &type_gz, 4638 .align_gz = &align_gz, 4639 .linksection_gz = &linksection_gz, 4640 .addrspace_gz = &addrspace_gz, 4641 .value_gz = &init_gz, 4642 }); 4643 } 4644 4645 fn comptimeDecl( 4646 astgen: *AstGen, 4647 gz: *GenZir, 4648 scope: *Scope, 4649 wip_members: *WipMembers, 4650 node: Ast.Node.Index, 4651 ) InnerError!void { 4652 const tree = astgen.tree; 4653 const body_node = tree.nodeData(node).node; 4654 4655 const old_hasher = astgen.src_hasher; 4656 defer astgen.src_hasher = old_hasher; 4657 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 4658 astgen.src_hasher.update(tree.getNodeSource(node)); 4659 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); 4660 4661 // Up top so the ZIR instruction index marks the start range of this 4662 // top-level declaration. 4663 const decl_inst = try gz.makeDeclaration(node); 4664 wip_members.nextDecl(decl_inst); 4665 astgen.advanceSourceCursorToNode(node); 4666 4667 // This is just needed for the `setDeclaration` call. 4668 var dummy_gz = gz.makeSubBlock(scope); 4669 defer dummy_gz.unstack(); 4670 4671 var comptime_gz: GenZir = .{ 4672 .is_comptime = true, 4673 .decl_node_index = node, 4674 .decl_line = astgen.source_line, 4675 .parent = scope, 4676 .astgen = astgen, 4677 .instructions = dummy_gz.instructions, 4678 .instructions_top = dummy_gz.instructions.items.len, 4679 }; 4680 defer comptime_gz.unstack(); 4681 4682 const decl_column = astgen.source_column; 4683 4684 const block_result = try fullBodyExpr(&comptime_gz, &comptime_gz.base, .{ .rl = .none }, body_node, .normal); 4685 if (comptime_gz.isEmpty() or !comptime_gz.refIsNoReturn(block_result)) { 4686 _ = try comptime_gz.addBreak(.break_inline, decl_inst, .void_value); 4687 } 4688 4689 var hash: std.zig.SrcHash = undefined; 4690 astgen.src_hasher.final(&hash); 4691 try setDeclaration(decl_inst, .{ 4692 .src_hash = hash, 4693 .src_line = comptime_gz.decl_line, 4694 .src_column = decl_column, 4695 .kind = .@"comptime", 4696 .name = .empty, 4697 .is_pub = false, 4698 .is_threadlocal = false, 4699 .linkage = .normal, 4700 .type_gz = &dummy_gz, 4701 .align_gz = &dummy_gz, 4702 .linksection_gz = &dummy_gz, 4703 .addrspace_gz = &dummy_gz, 4704 .value_gz = &comptime_gz, 4705 }); 4706 } 4707 4708 fn testDecl( 4709 astgen: *AstGen, 4710 gz: *GenZir, 4711 scope: *Scope, 4712 wip_members: *WipMembers, 4713 node: Ast.Node.Index, 4714 ) InnerError!void { 4715 const tree = astgen.tree; 4716 _, const body_node = tree.nodeData(node).opt_token_and_node; 4717 4718 const old_hasher = astgen.src_hasher; 4719 defer astgen.src_hasher = old_hasher; 4720 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 4721 astgen.src_hasher.update(tree.getNodeSource(node)); 4722 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); 4723 4724 // Up top so the ZIR instruction index marks the start range of this 4725 // top-level declaration. 4726 const decl_inst = try gz.makeDeclaration(node); 4727 4728 wip_members.nextDecl(decl_inst); 4729 astgen.advanceSourceCursorToNode(node); 4730 4731 // This is just needed for the `setDeclaration` call. 4732 var dummy_gz: GenZir = gz.makeSubBlock(scope); 4733 defer dummy_gz.unstack(); 4734 4735 var decl_block: GenZir = .{ 4736 .is_comptime = true, 4737 .decl_node_index = node, 4738 .decl_line = astgen.source_line, 4739 .parent = scope, 4740 .astgen = astgen, 4741 .instructions = dummy_gz.instructions, 4742 .instructions_top = dummy_gz.instructions.items.len, 4743 }; 4744 defer decl_block.unstack(); 4745 4746 const decl_column = astgen.source_column; 4747 4748 const test_token = tree.nodeMainToken(node); 4749 4750 const test_name_token = test_token + 1; 4751 const test_name: Zir.NullTerminatedString = switch (tree.tokenTag(test_name_token)) { 4752 else => .empty, 4753 .string_literal => name: { 4754 const name = try astgen.strLitAsString(test_name_token); 4755 const slice = astgen.string_bytes.items[@intFromEnum(name.index)..][0..name.len]; 4756 if (mem.indexOfScalar(u8, slice, 0) != null) { 4757 return astgen.failTok(test_name_token, "test name cannot contain null bytes", .{}); 4758 } else if (slice.len == 0) { 4759 return astgen.failTok(test_name_token, "empty test name must be omitted", .{}); 4760 } 4761 break :name name.index; 4762 }, 4763 .identifier => name: { 4764 const ident_name_raw = tree.tokenSlice(test_name_token); 4765 4766 if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{}); 4767 4768 // if not @"" syntax, just use raw token slice 4769 if (ident_name_raw[0] != '@') { 4770 if (isPrimitive(ident_name_raw)) return astgen.failTok(test_name_token, "cannot test a primitive", .{}); 4771 } 4772 4773 // Local variables, including function parameters. 4774 const name_str_index = try astgen.identAsString(test_name_token); 4775 var s = scope; 4776 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already 4777 var num_namespaces_out: u32 = 0; 4778 var capturing_namespace: ?*Scope.Namespace = null; 4779 while (true) switch (s.tag) { 4780 .local_val => { 4781 const local_val = s.cast(Scope.LocalVal).?; 4782 if (local_val.name == name_str_index) { 4783 local_val.used = .fromToken(test_name_token); 4784 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{ 4785 @tagName(local_val.id_cat), 4786 }, &[_]u32{ 4787 try astgen.errNoteTok(local_val.token_src, "{s} declared here", .{ 4788 @tagName(local_val.id_cat), 4789 }), 4790 }); 4791 } 4792 s = local_val.parent; 4793 }, 4794 .local_ptr => { 4795 const local_ptr = s.cast(Scope.LocalPtr).?; 4796 if (local_ptr.name == name_str_index) { 4797 local_ptr.used = .fromToken(test_name_token); 4798 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{ 4799 @tagName(local_ptr.id_cat), 4800 }, &[_]u32{ 4801 try astgen.errNoteTok(local_ptr.token_src, "{s} declared here", .{ 4802 @tagName(local_ptr.id_cat), 4803 }), 4804 }); 4805 } 4806 s = local_ptr.parent; 4807 }, 4808 .gen_zir => s = s.cast(GenZir).?.parent, 4809 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, 4810 .namespace => { 4811 const ns = s.cast(Scope.Namespace).?; 4812 if (ns.decls.get(name_str_index)) |i| { 4813 if (found_already) |f| { 4814 return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{ 4815 try astgen.errNoteNode(f, "declared here", .{}), 4816 try astgen.errNoteNode(i, "also declared here", .{}), 4817 }); 4818 } 4819 // We found a match but must continue looking for ambiguous references to decls. 4820 found_already = i; 4821 } 4822 num_namespaces_out += 1; 4823 capturing_namespace = ns; 4824 s = ns.parent; 4825 }, 4826 .top => break, 4827 }; 4828 if (found_already == null) { 4829 const ident_name = try astgen.identifierTokenString(test_name_token); 4830 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name}); 4831 } 4832 4833 break :name try astgen.identAsString(test_name_token); 4834 }, 4835 }; 4836 4837 var fn_block: GenZir = .{ 4838 .is_comptime = false, 4839 .decl_node_index = node, 4840 .decl_line = decl_block.decl_line, 4841 .parent = &decl_block.base, 4842 .astgen = astgen, 4843 .instructions = decl_block.instructions, 4844 .instructions_top = decl_block.instructions.items.len, 4845 }; 4846 defer fn_block.unstack(); 4847 4848 const prev_within_fn = astgen.within_fn; 4849 const prev_fn_block = astgen.fn_block; 4850 const prev_fn_ret_ty = astgen.fn_ret_ty; 4851 astgen.within_fn = true; 4852 astgen.fn_block = &fn_block; 4853 astgen.fn_ret_ty = .anyerror_void_error_union_type; 4854 defer { 4855 astgen.within_fn = prev_within_fn; 4856 astgen.fn_block = prev_fn_block; 4857 astgen.fn_ret_ty = prev_fn_ret_ty; 4858 } 4859 4860 astgen.advanceSourceCursorToNode(body_node); 4861 const lbrace_line = astgen.source_line - decl_block.decl_line; 4862 const lbrace_column = astgen.source_column; 4863 4864 const block_result = try fullBodyExpr(&fn_block, &fn_block.base, .{ .rl = .none }, body_node, .normal); 4865 if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) { 4866 4867 // As our last action before the return, "pop" the error trace if needed 4868 _ = try fn_block.addRestoreErrRetIndex(.ret, .always, node); 4869 4870 // Add implicit return at end of function. 4871 _ = try fn_block.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node)); 4872 } 4873 4874 const func_inst = try decl_block.addFunc(.{ 4875 .src_node = node, 4876 4877 .cc_ref = .none, 4878 .cc_gz = null, 4879 .ret_ref = .anyerror_void_error_union_type, 4880 .ret_gz = null, 4881 4882 .ret_param_refs = &.{}, 4883 .param_insts = &.{}, 4884 .ret_ty_is_generic = false, 4885 4886 .lbrace_line = lbrace_line, 4887 .lbrace_column = lbrace_column, 4888 .param_block = decl_inst, 4889 .body_gz = &fn_block, 4890 .is_var_args = false, 4891 .is_inferred_error = false, 4892 .is_noinline = false, 4893 .noalias_bits = 0, 4894 4895 // Tests don't have a prototype that needs hashing 4896 .proto_hash = .{0} ** 16, 4897 }); 4898 4899 _ = try decl_block.addBreak(.break_inline, decl_inst, func_inst); 4900 4901 var hash: std.zig.SrcHash = undefined; 4902 astgen.src_hasher.final(&hash); 4903 try setDeclaration(decl_inst, .{ 4904 .src_hash = hash, 4905 .src_line = decl_block.decl_line, 4906 .src_column = decl_column, 4907 4908 .kind = switch (tree.tokenTag(test_name_token)) { 4909 .string_literal => .@"test", 4910 .identifier => .decltest, 4911 else => .unnamed_test, 4912 }, 4913 .name = test_name, 4914 .is_pub = false, 4915 .is_threadlocal = false, 4916 .linkage = .normal, 4917 4918 .type_gz = &dummy_gz, 4919 .align_gz = &dummy_gz, 4920 .linksection_gz = &dummy_gz, 4921 .addrspace_gz = &dummy_gz, 4922 .value_gz = &decl_block, 4923 }); 4924 } 4925 4926 fn structDeclInner( 4927 gz: *GenZir, 4928 scope: *Scope, 4929 node: Ast.Node.Index, 4930 container_decl: Ast.full.ContainerDecl, 4931 layout: std.builtin.Type.ContainerLayout, 4932 backing_int_node: Ast.Node.OptionalIndex, 4933 name_strat: Zir.Inst.NameStrategy, 4934 ) InnerError!Zir.Inst.Ref { 4935 const astgen = gz.astgen; 4936 const gpa = astgen.gpa; 4937 const tree = astgen.tree; 4938 4939 is_tuple: { 4940 const tuple_field_node = for (container_decl.ast.members) |member_node| { 4941 const container_field = tree.fullContainerField(member_node) orelse continue; 4942 if (container_field.ast.tuple_like) break member_node; 4943 } else break :is_tuple; 4944 4945 if (node == .root) { 4946 return astgen.failNode(tuple_field_node, "file cannot be a tuple", .{}); 4947 } else { 4948 return tupleDecl(gz, scope, node, container_decl, layout, backing_int_node); 4949 } 4950 } 4951 4952 const decl_inst = try gz.reserveInstructionIndex(); 4953 4954 if (container_decl.ast.members.len == 0 and backing_int_node == .none) { 4955 try gz.setStruct(decl_inst, .{ 4956 .src_node = node, 4957 .layout = layout, 4958 .captures_len = 0, 4959 .fields_len = 0, 4960 .decls_len = 0, 4961 .has_backing_int = false, 4962 .known_non_opv = false, 4963 .known_comptime_only = false, 4964 .any_comptime_fields = false, 4965 .any_default_inits = false, 4966 .any_aligned_fields = false, 4967 .fields_hash = std.zig.hashSrc(@tagName(layout)), 4968 .name_strat = name_strat, 4969 }); 4970 return decl_inst.toRef(); 4971 } 4972 4973 var namespace: Scope.Namespace = .{ 4974 .parent = scope, 4975 .node = node, 4976 .inst = decl_inst, 4977 .declaring_gz = gz, 4978 .maybe_generic = astgen.within_fn, 4979 }; 4980 defer namespace.deinit(gpa); 4981 4982 // The struct_decl instruction introduces a scope in which the decls of the struct 4983 // are in scope, so that field types, alignments, and default value expressions 4984 // can refer to decls within the struct itself. 4985 astgen.advanceSourceCursorToNode(node); 4986 var block_scope: GenZir = .{ 4987 .parent = &namespace.base, 4988 .decl_node_index = node, 4989 .decl_line = gz.decl_line, 4990 .astgen = astgen, 4991 .is_comptime = true, 4992 .instructions = gz.instructions, 4993 .instructions_top = gz.instructions.items.len, 4994 }; 4995 defer block_scope.unstack(); 4996 4997 const scratch_top = astgen.scratch.items.len; 4998 defer astgen.scratch.items.len = scratch_top; 4999 5000 var backing_int_body_len: usize = 0; 5001 const backing_int_ref: Zir.Inst.Ref = blk: { 5002 if (backing_int_node.unwrap()) |arg| { 5003 if (layout != .@"packed") { 5004 return astgen.failNode(arg, "non-packed struct does not support backing integer type", .{}); 5005 } else { 5006 const backing_int_ref = try typeExpr(&block_scope, &namespace.base, arg); 5007 if (!block_scope.isEmpty()) { 5008 if (!block_scope.endsWithNoReturn()) { 5009 _ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref); 5010 } 5011 5012 const body = block_scope.instructionsSlice(); 5013 const old_scratch_len = astgen.scratch.items.len; 5014 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); 5015 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 5016 backing_int_body_len = astgen.scratch.items.len - old_scratch_len; 5017 block_scope.instructions.items.len = block_scope.instructions_top; 5018 } 5019 break :blk backing_int_ref; 5020 } 5021 } else { 5022 break :blk .none; 5023 } 5024 }; 5025 5026 const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"struct"); 5027 const field_count: u32 = @intCast(container_decl.ast.members.len - decl_count); 5028 5029 const bits_per_field = 4; 5030 const max_field_size = 5; 5031 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size); 5032 defer wip_members.deinit(); 5033 5034 // We will use the scratch buffer, starting here, for the bodies: 5035 // bodies: { // for every fields_len 5036 // field_type_body_inst: Inst, // for each field_type_body_len 5037 // align_body_inst: Inst, // for each align_body_len 5038 // init_body_inst: Inst, // for each init_body_len 5039 // } 5040 // Note that the scratch buffer is simultaneously being used by WipMembers, however 5041 // it will not access any elements beyond this point in the ArrayList. It also 5042 // accesses via the ArrayList items field so it can handle the scratch buffer being 5043 // reallocated. 5044 // No defer needed here because it is handled by `wip_members.deinit()` above. 5045 const bodies_start = astgen.scratch.items.len; 5046 5047 const old_hasher = astgen.src_hasher; 5048 defer astgen.src_hasher = old_hasher; 5049 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 5050 astgen.src_hasher.update(@tagName(layout)); 5051 if (backing_int_node.unwrap()) |arg| { 5052 astgen.src_hasher.update(tree.getNodeSource(arg)); 5053 } 5054 5055 var known_non_opv = false; 5056 var known_comptime_only = false; 5057 var any_comptime_fields = false; 5058 var any_aligned_fields = false; 5059 var any_default_inits = false; 5060 for (container_decl.ast.members) |member_node| { 5061 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { 5062 .decl => continue, 5063 .field => |field| field, 5064 }; 5065 5066 astgen.src_hasher.update(tree.getNodeSource(member_node)); 5067 5068 const field_name = try astgen.identAsString(member.ast.main_token); 5069 member.convertToNonTupleLike(astgen.tree); 5070 assert(!member.ast.tuple_like); 5071 wip_members.appendToField(@intFromEnum(field_name)); 5072 5073 const type_expr = member.ast.type_expr.unwrap() orelse { 5074 return astgen.failTok(member.ast.main_token, "struct field missing type", .{}); 5075 }; 5076 5077 const field_type = try typeExpr(&block_scope, &namespace.base, type_expr); 5078 const have_type_body = !block_scope.isEmpty(); 5079 const have_align = member.ast.align_expr != .none; 5080 const have_value = member.ast.value_expr != .none; 5081 const is_comptime = member.comptime_token != null; 5082 5083 if (is_comptime) { 5084 switch (layout) { 5085 .@"packed", .@"extern" => return astgen.failTok(member.comptime_token.?, "{s} struct fields cannot be marked comptime", .{@tagName(layout)}), 5086 .auto => any_comptime_fields = true, 5087 } 5088 } else { 5089 known_non_opv = known_non_opv or 5090 nodeImpliesMoreThanOnePossibleValue(tree, type_expr); 5091 known_comptime_only = known_comptime_only or 5092 nodeImpliesComptimeOnly(tree, type_expr); 5093 } 5094 wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, have_type_body }); 5095 5096 if (have_type_body) { 5097 if (!block_scope.endsWithNoReturn()) { 5098 _ = try block_scope.addBreak(.break_inline, decl_inst, field_type); 5099 } 5100 const body = block_scope.instructionsSlice(); 5101 const old_scratch_len = astgen.scratch.items.len; 5102 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); 5103 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 5104 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len)); 5105 block_scope.instructions.items.len = block_scope.instructions_top; 5106 } else { 5107 wip_members.appendToField(@intFromEnum(field_type)); 5108 } 5109 5110 if (member.ast.align_expr.unwrap()) |align_expr| { 5111 if (layout == .@"packed") { 5112 return astgen.failNode(align_expr, "unable to override alignment of packed struct fields", .{}); 5113 } 5114 any_aligned_fields = true; 5115 const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, align_expr); 5116 if (!block_scope.endsWithNoReturn()) { 5117 _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref); 5118 } 5119 const body = block_scope.instructionsSlice(); 5120 const old_scratch_len = astgen.scratch.items.len; 5121 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); 5122 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 5123 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len)); 5124 block_scope.instructions.items.len = block_scope.instructions_top; 5125 } 5126 5127 if (member.ast.value_expr.unwrap()) |value_expr| { 5128 any_default_inits = true; 5129 5130 // The decl_inst is used as here so that we can easily reconstruct a mapping 5131 // between it and the field type when the fields inits are analyzed. 5132 const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = decl_inst.toRef() } }; 5133 5134 const default_inst = try expr(&block_scope, &namespace.base, ri, value_expr); 5135 if (!block_scope.endsWithNoReturn()) { 5136 _ = try block_scope.addBreak(.break_inline, decl_inst, default_inst); 5137 } 5138 const body = block_scope.instructionsSlice(); 5139 const old_scratch_len = astgen.scratch.items.len; 5140 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); 5141 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 5142 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len)); 5143 block_scope.instructions.items.len = block_scope.instructions_top; 5144 } else if (member.comptime_token) |comptime_token| { 5145 return astgen.failTok(comptime_token, "comptime field without default initialization value", .{}); 5146 } 5147 } 5148 5149 var fields_hash: std.zig.SrcHash = undefined; 5150 astgen.src_hasher.final(&fields_hash); 5151 5152 try gz.setStruct(decl_inst, .{ 5153 .src_node = node, 5154 .layout = layout, 5155 .captures_len = @intCast(namespace.captures.count()), 5156 .fields_len = field_count, 5157 .decls_len = decl_count, 5158 .has_backing_int = backing_int_ref != .none, 5159 .known_non_opv = known_non_opv, 5160 .known_comptime_only = known_comptime_only, 5161 .any_comptime_fields = any_comptime_fields, 5162 .any_default_inits = any_default_inits, 5163 .any_aligned_fields = any_aligned_fields, 5164 .fields_hash = fields_hash, 5165 .name_strat = name_strat, 5166 }); 5167 5168 wip_members.finishBits(bits_per_field); 5169 const decls_slice = wip_members.declsSlice(); 5170 const fields_slice = wip_members.fieldsSlice(); 5171 const bodies_slice = astgen.scratch.items[bodies_start..]; 5172 try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len + 2 + 5173 decls_slice.len + namespace.captures.count() * 2 + fields_slice.len + bodies_slice.len); 5174 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); 5175 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values())); 5176 if (backing_int_ref != .none) { 5177 astgen.extra.appendAssumeCapacity(@intCast(backing_int_body_len)); 5178 if (backing_int_body_len == 0) { 5179 astgen.extra.appendAssumeCapacity(@intFromEnum(backing_int_ref)); 5180 } else { 5181 astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]); 5182 } 5183 } 5184 astgen.extra.appendSliceAssumeCapacity(decls_slice); 5185 astgen.extra.appendSliceAssumeCapacity(fields_slice); 5186 astgen.extra.appendSliceAssumeCapacity(bodies_slice); 5187 5188 block_scope.unstack(); 5189 return decl_inst.toRef(); 5190 } 5191 5192 fn tupleDecl( 5193 gz: *GenZir, 5194 scope: *Scope, 5195 node: Ast.Node.Index, 5196 container_decl: Ast.full.ContainerDecl, 5197 layout: std.builtin.Type.ContainerLayout, 5198 backing_int_node: Ast.Node.OptionalIndex, 5199 ) InnerError!Zir.Inst.Ref { 5200 const astgen = gz.astgen; 5201 const gpa = astgen.gpa; 5202 const tree = astgen.tree; 5203 5204 switch (layout) { 5205 .auto => {}, 5206 .@"extern", .@"packed" => return astgen.failNode(node, "{s} tuples are not supported", .{@tagName(layout)}), 5207 } 5208 5209 if (backing_int_node.unwrap()) |arg| { 5210 return astgen.failNode(arg, "tuple does not support backing integer type", .{}); 5211 } 5212 5213 // We will use the scratch buffer, starting here, for the field data: 5214 // 1. fields: { // for every `fields_len` (stored in `extended.small`) 5215 // type: Inst.Ref, 5216 // init: Inst.Ref, // `.none` for non-`comptime` fields 5217 // } 5218 const fields_start = astgen.scratch.items.len; 5219 defer astgen.scratch.items.len = fields_start; 5220 5221 try astgen.scratch.ensureUnusedCapacity(gpa, container_decl.ast.members.len * 2); 5222 5223 for (container_decl.ast.members) |member_node| { 5224 const field = tree.fullContainerField(member_node) orelse { 5225 const tuple_member = for (container_decl.ast.members) |maybe_tuple| switch (tree.nodeTag(maybe_tuple)) { 5226 .container_field_init, 5227 .container_field_align, 5228 .container_field, 5229 => break maybe_tuple, 5230 else => {}, 5231 } else unreachable; 5232 return astgen.failNodeNotes( 5233 member_node, 5234 "tuple declarations cannot contain declarations", 5235 .{}, 5236 &.{try astgen.errNoteNode(tuple_member, "tuple field here", .{})}, 5237 ); 5238 }; 5239 5240 if (!field.ast.tuple_like) { 5241 return astgen.failTok(field.ast.main_token, "tuple field has a name", .{}); 5242 } 5243 5244 if (field.ast.align_expr != .none) { 5245 return astgen.failTok(field.ast.main_token, "tuple field has alignment", .{}); 5246 } 5247 5248 if (field.ast.value_expr != .none and field.comptime_token == null) { 5249 return astgen.failTok(field.ast.main_token, "non-comptime tuple field has default initialization value", .{}); 5250 } 5251 5252 if (field.ast.value_expr == .none and field.comptime_token != null) { 5253 return astgen.failTok(field.comptime_token.?, "comptime field without default initialization value", .{}); 5254 } 5255 5256 const field_type_ref = try typeExpr(gz, scope, field.ast.type_expr.unwrap().?); 5257 astgen.scratch.appendAssumeCapacity(@intFromEnum(field_type_ref)); 5258 5259 if (field.ast.value_expr.unwrap()) |value_expr| { 5260 const field_init_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_type_ref } }, value_expr, .tuple_field_default_value); 5261 astgen.scratch.appendAssumeCapacity(@intFromEnum(field_init_ref)); 5262 } else { 5263 astgen.scratch.appendAssumeCapacity(@intFromEnum(Zir.Inst.Ref.none)); 5264 } 5265 } 5266 5267 const fields_len = std.math.cast(u16, container_decl.ast.members.len) orelse { 5268 return astgen.failNode(node, "this compiler implementation only supports 65535 tuple fields", .{}); 5269 }; 5270 5271 const extra_trail = astgen.scratch.items[fields_start..]; 5272 assert(extra_trail.len == fields_len * 2); 5273 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.TupleDecl).@"struct".fields.len + extra_trail.len); 5274 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.TupleDecl{ 5275 .src_node = gz.nodeIndexToRelative(node), 5276 }); 5277 astgen.extra.appendSliceAssumeCapacity(extra_trail); 5278 5279 return gz.add(.{ 5280 .tag = .extended, 5281 .data = .{ .extended = .{ 5282 .opcode = .tuple_decl, 5283 .small = fields_len, 5284 .operand = payload_index, 5285 } }, 5286 }); 5287 } 5288 5289 fn unionDeclInner( 5290 gz: *GenZir, 5291 scope: *Scope, 5292 node: Ast.Node.Index, 5293 members: []const Ast.Node.Index, 5294 layout: std.builtin.Type.ContainerLayout, 5295 opt_arg_node: Ast.Node.OptionalIndex, 5296 auto_enum_tok: ?Ast.TokenIndex, 5297 name_strat: Zir.Inst.NameStrategy, 5298 ) InnerError!Zir.Inst.Ref { 5299 const decl_inst = try gz.reserveInstructionIndex(); 5300 5301 const astgen = gz.astgen; 5302 const gpa = astgen.gpa; 5303 5304 var namespace: Scope.Namespace = .{ 5305 .parent = scope, 5306 .node = node, 5307 .inst = decl_inst, 5308 .declaring_gz = gz, 5309 .maybe_generic = astgen.within_fn, 5310 }; 5311 defer namespace.deinit(gpa); 5312 5313 // The union_decl instruction introduces a scope in which the decls of the union 5314 // are in scope, so that field types, alignments, and default value expressions 5315 // can refer to decls within the union itself. 5316 astgen.advanceSourceCursorToNode(node); 5317 var block_scope: GenZir = .{ 5318 .parent = &namespace.base, 5319 .decl_node_index = node, 5320 .decl_line = gz.decl_line, 5321 .astgen = astgen, 5322 .is_comptime = true, 5323 .instructions = gz.instructions, 5324 .instructions_top = gz.instructions.items.len, 5325 }; 5326 defer block_scope.unstack(); 5327 5328 const decl_count = try astgen.scanContainer(&namespace, members, .@"union"); 5329 const field_count: u32 = @intCast(members.len - decl_count); 5330 5331 if (layout != .auto and (auto_enum_tok != null or opt_arg_node != .none)) { 5332 if (opt_arg_node.unwrap()) |arg_node| { 5333 return astgen.failNode(arg_node, "{s} union does not support enum tag type", .{@tagName(layout)}); 5334 } else { 5335 return astgen.failTok(auto_enum_tok.?, "{s} union does not support enum tag type", .{@tagName(layout)}); 5336 } 5337 } 5338 5339 const arg_inst: Zir.Inst.Ref = if (opt_arg_node.unwrap()) |arg_node| 5340 try typeExpr(&block_scope, &namespace.base, arg_node) 5341 else 5342 .none; 5343 5344 const bits_per_field = 4; 5345 const max_field_size = 4; 5346 var any_aligned_fields = false; 5347 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size); 5348 defer wip_members.deinit(); 5349 5350 const old_hasher = astgen.src_hasher; 5351 defer astgen.src_hasher = old_hasher; 5352 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 5353 astgen.src_hasher.update(@tagName(layout)); 5354 astgen.src_hasher.update(&.{@intFromBool(auto_enum_tok != null)}); 5355 if (opt_arg_node.unwrap()) |arg_node| { 5356 astgen.src_hasher.update(astgen.tree.getNodeSource(arg_node)); 5357 } 5358 5359 for (members) |member_node| { 5360 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { 5361 .decl => continue, 5362 .field => |field| field, 5363 }; 5364 astgen.src_hasher.update(astgen.tree.getNodeSource(member_node)); 5365 member.convertToNonTupleLike(astgen.tree); 5366 if (member.ast.tuple_like) { 5367 return astgen.failTok(member.ast.main_token, "union field missing name", .{}); 5368 } 5369 if (member.comptime_token) |comptime_token| { 5370 return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{}); 5371 } 5372 5373 const field_name = try astgen.identAsString(member.ast.main_token); 5374 wip_members.appendToField(@intFromEnum(field_name)); 5375 5376 const have_type = member.ast.type_expr != .none; 5377 const have_align = member.ast.align_expr != .none; 5378 const have_value = member.ast.value_expr != .none; 5379 const unused = false; 5380 wip_members.nextField(bits_per_field, .{ have_type, have_align, have_value, unused }); 5381 5382 if (member.ast.type_expr.unwrap()) |type_expr| { 5383 const field_type = try typeExpr(&block_scope, &namespace.base, type_expr); 5384 wip_members.appendToField(@intFromEnum(field_type)); 5385 } else if (arg_inst == .none and auto_enum_tok == null) { 5386 return astgen.failNode(member_node, "union field missing type", .{}); 5387 } 5388 if (member.ast.align_expr.unwrap()) |align_expr| { 5389 if (layout == .@"packed") { 5390 return astgen.failNode(align_expr, "unable to override alignment of packed union fields", .{}); 5391 } 5392 const align_inst = try expr(&block_scope, &block_scope.base, coerced_align_ri, align_expr); 5393 wip_members.appendToField(@intFromEnum(align_inst)); 5394 any_aligned_fields = true; 5395 } 5396 if (member.ast.value_expr.unwrap()) |value_expr| { 5397 if (arg_inst == .none) { 5398 return astgen.failNodeNotes( 5399 node, 5400 "explicitly valued tagged union missing integer tag type", 5401 .{}, 5402 &[_]u32{ 5403 try astgen.errNoteNode( 5404 value_expr, 5405 "tag value specified here", 5406 .{}, 5407 ), 5408 }, 5409 ); 5410 } 5411 if (auto_enum_tok == null) { 5412 return astgen.failNodeNotes( 5413 node, 5414 "explicitly valued tagged union requires inferred enum tag type", 5415 .{}, 5416 &[_]u32{ 5417 try astgen.errNoteNode( 5418 value_expr, 5419 "tag value specified here", 5420 .{}, 5421 ), 5422 }, 5423 ); 5424 } 5425 const tag_value = try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = arg_inst } }, value_expr); 5426 wip_members.appendToField(@intFromEnum(tag_value)); 5427 } 5428 } 5429 5430 var fields_hash: std.zig.SrcHash = undefined; 5431 astgen.src_hasher.final(&fields_hash); 5432 5433 if (!block_scope.isEmpty()) { 5434 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); 5435 } 5436 5437 const body = block_scope.instructionsSlice(); 5438 const body_len = astgen.countBodyLenAfterFixups(body); 5439 5440 try gz.setUnion(decl_inst, .{ 5441 .src_node = node, 5442 .layout = layout, 5443 .tag_type = arg_inst, 5444 .captures_len = @intCast(namespace.captures.count()), 5445 .body_len = body_len, 5446 .fields_len = field_count, 5447 .decls_len = decl_count, 5448 .auto_enum_tag = auto_enum_tok != null, 5449 .any_aligned_fields = any_aligned_fields, 5450 .fields_hash = fields_hash, 5451 .name_strat = name_strat, 5452 }); 5453 5454 wip_members.finishBits(bits_per_field); 5455 const decls_slice = wip_members.declsSlice(); 5456 const fields_slice = wip_members.fieldsSlice(); 5457 try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() * 2 + decls_slice.len + body_len + fields_slice.len); 5458 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); 5459 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values())); 5460 astgen.extra.appendSliceAssumeCapacity(decls_slice); 5461 astgen.appendBodyWithFixups(body); 5462 astgen.extra.appendSliceAssumeCapacity(fields_slice); 5463 5464 block_scope.unstack(); 5465 return decl_inst.toRef(); 5466 } 5467 5468 fn containerDecl( 5469 gz: *GenZir, 5470 scope: *Scope, 5471 ri: ResultInfo, 5472 node: Ast.Node.Index, 5473 container_decl: Ast.full.ContainerDecl, 5474 name_strat: Zir.Inst.NameStrategy, 5475 ) InnerError!Zir.Inst.Ref { 5476 const astgen = gz.astgen; 5477 const gpa = astgen.gpa; 5478 const tree = astgen.tree; 5479 5480 const prev_fn_block = astgen.fn_block; 5481 astgen.fn_block = null; 5482 defer astgen.fn_block = prev_fn_block; 5483 5484 // We must not create any types until Sema. Here the goal is only to generate 5485 // ZIR for all the field types, alignments, and default value expressions. 5486 5487 switch (tree.tokenTag(container_decl.ast.main_token)) { 5488 .keyword_struct => { 5489 const layout: std.builtin.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (tree.tokenTag(t)) { 5490 .keyword_packed => .@"packed", 5491 .keyword_extern => .@"extern", 5492 else => unreachable, 5493 } else .auto; 5494 5495 const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg, name_strat); 5496 return rvalue(gz, ri, result, node); 5497 }, 5498 .keyword_union => { 5499 const layout: std.builtin.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (tree.tokenTag(t)) { 5500 .keyword_packed => .@"packed", 5501 .keyword_extern => .@"extern", 5502 else => unreachable, 5503 } else .auto; 5504 5505 const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, container_decl.ast.enum_token, name_strat); 5506 return rvalue(gz, ri, result, node); 5507 }, 5508 .keyword_enum => { 5509 if (container_decl.layout_token) |t| { 5510 return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{}); 5511 } 5512 // Count total fields as well as how many have explicitly provided tag values. 5513 const counts = blk: { 5514 var values: usize = 0; 5515 var total_fields: usize = 0; 5516 var decls: usize = 0; 5517 var opt_nonexhaustive_node: Ast.Node.OptionalIndex = .none; 5518 var nonfinal_nonexhaustive = false; 5519 for (container_decl.ast.members) |member_node| { 5520 var member = tree.fullContainerField(member_node) orelse { 5521 decls += 1; 5522 continue; 5523 }; 5524 member.convertToNonTupleLike(astgen.tree); 5525 if (member.ast.tuple_like) { 5526 return astgen.failTok(member.ast.main_token, "enum field missing name", .{}); 5527 } 5528 if (member.comptime_token) |comptime_token| { 5529 return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{}); 5530 } 5531 if (member.ast.type_expr.unwrap()) |type_expr| { 5532 return astgen.failNodeNotes( 5533 type_expr, 5534 "enum fields do not have types", 5535 .{}, 5536 &[_]u32{ 5537 try astgen.errNoteNode( 5538 node, 5539 "consider 'union(enum)' here to make it a tagged union", 5540 .{}, 5541 ), 5542 }, 5543 ); 5544 } 5545 if (member.ast.align_expr.unwrap()) |align_expr| { 5546 return astgen.failNode(align_expr, "enum fields cannot be aligned", .{}); 5547 } 5548 5549 const name_token = member.ast.main_token; 5550 if (mem.eql(u8, tree.tokenSlice(name_token), "_")) { 5551 if (opt_nonexhaustive_node.unwrap()) |nonexhaustive_node| { 5552 return astgen.failNodeNotes( 5553 member_node, 5554 "redundant non-exhaustive enum mark", 5555 .{}, 5556 &[_]u32{ 5557 try astgen.errNoteNode( 5558 nonexhaustive_node, 5559 "other mark here", 5560 .{}, 5561 ), 5562 }, 5563 ); 5564 } 5565 opt_nonexhaustive_node = member_node.toOptional(); 5566 if (member.ast.value_expr.unwrap()) |value_expr| { 5567 return astgen.failNode(value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{}); 5568 } 5569 continue; 5570 } else if (opt_nonexhaustive_node != .none) { 5571 nonfinal_nonexhaustive = true; 5572 } 5573 total_fields += 1; 5574 if (member.ast.value_expr.unwrap()) |value_expr| { 5575 if (container_decl.ast.arg == .none) { 5576 return astgen.failNode(value_expr, "value assigned to enum tag with inferred tag type", .{}); 5577 } 5578 values += 1; 5579 } 5580 } 5581 if (nonfinal_nonexhaustive) { 5582 return astgen.failNode(opt_nonexhaustive_node.unwrap().?, "'_' field of non-exhaustive enum must be last", .{}); 5583 } 5584 break :blk .{ 5585 .total_fields = total_fields, 5586 .values = values, 5587 .decls = decls, 5588 .nonexhaustive_node = opt_nonexhaustive_node, 5589 }; 5590 }; 5591 if (counts.nonexhaustive_node != .none and container_decl.ast.arg == .none) { 5592 const nonexhaustive_node = counts.nonexhaustive_node.unwrap().?; 5593 return astgen.failNodeNotes( 5594 node, 5595 "non-exhaustive enum missing integer tag type", 5596 .{}, 5597 &[_]u32{ 5598 try astgen.errNoteNode( 5599 nonexhaustive_node, 5600 "marked non-exhaustive here", 5601 .{}, 5602 ), 5603 }, 5604 ); 5605 } 5606 // In this case we must generate ZIR code for the tag values, similar to 5607 // how structs are handled above. 5608 const nonexhaustive = counts.nonexhaustive_node != .none; 5609 5610 const decl_inst = try gz.reserveInstructionIndex(); 5611 5612 var namespace: Scope.Namespace = .{ 5613 .parent = scope, 5614 .node = node, 5615 .inst = decl_inst, 5616 .declaring_gz = gz, 5617 .maybe_generic = astgen.within_fn, 5618 }; 5619 defer namespace.deinit(gpa); 5620 5621 // The enum_decl instruction introduces a scope in which the decls of the enum 5622 // are in scope, so that tag values can refer to decls within the enum itself. 5623 astgen.advanceSourceCursorToNode(node); 5624 var block_scope: GenZir = .{ 5625 .parent = &namespace.base, 5626 .decl_node_index = node, 5627 .decl_line = gz.decl_line, 5628 .astgen = astgen, 5629 .is_comptime = true, 5630 .instructions = gz.instructions, 5631 .instructions_top = gz.instructions.items.len, 5632 }; 5633 defer block_scope.unstack(); 5634 5635 _ = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"enum"); 5636 namespace.base.tag = .namespace; 5637 5638 const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg.unwrap()) |arg| 5639 try comptimeExpr(&block_scope, &namespace.base, coerced_type_ri, arg, .type) 5640 else 5641 .none; 5642 5643 const bits_per_field = 1; 5644 const max_field_size = 2; 5645 var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(counts.decls), @intCast(counts.total_fields), bits_per_field, max_field_size); 5646 defer wip_members.deinit(); 5647 5648 const old_hasher = astgen.src_hasher; 5649 defer astgen.src_hasher = old_hasher; 5650 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 5651 if (container_decl.ast.arg.unwrap()) |arg| { 5652 astgen.src_hasher.update(tree.getNodeSource(arg)); 5653 } 5654 astgen.src_hasher.update(&.{@intFromBool(nonexhaustive)}); 5655 5656 for (container_decl.ast.members) |member_node| { 5657 if (member_node.toOptional() == counts.nonexhaustive_node) 5658 continue; 5659 astgen.src_hasher.update(tree.getNodeSource(member_node)); 5660 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { 5661 .decl => continue, 5662 .field => |field| field, 5663 }; 5664 member.convertToNonTupleLike(astgen.tree); 5665 assert(member.comptime_token == null); 5666 assert(member.ast.type_expr == .none); 5667 assert(member.ast.align_expr == .none); 5668 5669 const field_name = try astgen.identAsString(member.ast.main_token); 5670 wip_members.appendToField(@intFromEnum(field_name)); 5671 5672 const have_value = member.ast.value_expr != .none; 5673 wip_members.nextField(bits_per_field, .{have_value}); 5674 5675 if (member.ast.value_expr.unwrap()) |value_expr| { 5676 if (arg_inst == .none) { 5677 return astgen.failNodeNotes( 5678 node, 5679 "explicitly valued enum missing integer tag type", 5680 .{}, 5681 &[_]u32{ 5682 try astgen.errNoteNode( 5683 value_expr, 5684 "tag value specified here", 5685 .{}, 5686 ), 5687 }, 5688 ); 5689 } 5690 const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .rl = .{ .ty = arg_inst } }, value_expr); 5691 wip_members.appendToField(@intFromEnum(tag_value_inst)); 5692 } 5693 } 5694 5695 if (!block_scope.isEmpty()) { 5696 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); 5697 } 5698 5699 var fields_hash: std.zig.SrcHash = undefined; 5700 astgen.src_hasher.final(&fields_hash); 5701 5702 const body = block_scope.instructionsSlice(); 5703 const body_len = astgen.countBodyLenAfterFixups(body); 5704 5705 try gz.setEnum(decl_inst, .{ 5706 .src_node = node, 5707 .nonexhaustive = nonexhaustive, 5708 .tag_type = arg_inst, 5709 .captures_len = @intCast(namespace.captures.count()), 5710 .body_len = body_len, 5711 .fields_len = @intCast(counts.total_fields), 5712 .decls_len = @intCast(counts.decls), 5713 .fields_hash = fields_hash, 5714 .name_strat = name_strat, 5715 }); 5716 5717 wip_members.finishBits(bits_per_field); 5718 const decls_slice = wip_members.declsSlice(); 5719 const fields_slice = wip_members.fieldsSlice(); 5720 try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() * 2 + decls_slice.len + body_len + fields_slice.len); 5721 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); 5722 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values())); 5723 astgen.extra.appendSliceAssumeCapacity(decls_slice); 5724 astgen.appendBodyWithFixups(body); 5725 astgen.extra.appendSliceAssumeCapacity(fields_slice); 5726 5727 block_scope.unstack(); 5728 return rvalue(gz, ri, decl_inst.toRef(), node); 5729 }, 5730 .keyword_opaque => { 5731 assert(container_decl.ast.arg == .none); 5732 5733 const decl_inst = try gz.reserveInstructionIndex(); 5734 5735 var namespace: Scope.Namespace = .{ 5736 .parent = scope, 5737 .node = node, 5738 .inst = decl_inst, 5739 .declaring_gz = gz, 5740 .maybe_generic = astgen.within_fn, 5741 }; 5742 defer namespace.deinit(gpa); 5743 5744 astgen.advanceSourceCursorToNode(node); 5745 var block_scope: GenZir = .{ 5746 .parent = &namespace.base, 5747 .decl_node_index = node, 5748 .decl_line = gz.decl_line, 5749 .astgen = astgen, 5750 .is_comptime = true, 5751 .instructions = gz.instructions, 5752 .instructions_top = gz.instructions.items.len, 5753 }; 5754 defer block_scope.unstack(); 5755 5756 const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"opaque"); 5757 5758 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0); 5759 defer wip_members.deinit(); 5760 5761 if (container_decl.layout_token) |layout_token| { 5762 return astgen.failTok(layout_token, "opaque types do not support 'packed' or 'extern'", .{}); 5763 } 5764 5765 for (container_decl.ast.members) |member_node| { 5766 const res = try containerMember(&block_scope, &namespace.base, &wip_members, member_node); 5767 if (res == .field) { 5768 return astgen.failNode(member_node, "opaque types cannot have fields", .{}); 5769 } 5770 } 5771 5772 try gz.setOpaque(decl_inst, .{ 5773 .src_node = node, 5774 .captures_len = @intCast(namespace.captures.count()), 5775 .decls_len = decl_count, 5776 .name_strat = name_strat, 5777 }); 5778 5779 wip_members.finishBits(0); 5780 const decls_slice = wip_members.declsSlice(); 5781 try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() * 2 + decls_slice.len); 5782 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); 5783 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values())); 5784 astgen.extra.appendSliceAssumeCapacity(decls_slice); 5785 5786 block_scope.unstack(); 5787 return rvalue(gz, ri, decl_inst.toRef(), node); 5788 }, 5789 else => unreachable, 5790 } 5791 } 5792 5793 const ContainerMemberResult = union(enum) { decl, field: Ast.full.ContainerField }; 5794 5795 fn containerMember( 5796 gz: *GenZir, 5797 scope: *Scope, 5798 wip_members: *WipMembers, 5799 member_node: Ast.Node.Index, 5800 ) InnerError!ContainerMemberResult { 5801 const astgen = gz.astgen; 5802 const tree = astgen.tree; 5803 switch (tree.nodeTag(member_node)) { 5804 .container_field_init, 5805 .container_field_align, 5806 .container_field, 5807 => return ContainerMemberResult{ .field = tree.fullContainerField(member_node).? }, 5808 5809 .fn_proto, 5810 .fn_proto_multi, 5811 .fn_proto_one, 5812 .fn_proto_simple, 5813 .fn_decl, 5814 => { 5815 var buf: [1]Ast.Node.Index = undefined; 5816 const full = tree.fullFnProto(&buf, member_node).?; 5817 5818 const body: Ast.Node.OptionalIndex = if (tree.nodeTag(member_node) == .fn_decl) 5819 tree.nodeData(member_node).node_and_node[1].toOptional() 5820 else 5821 .none; 5822 5823 const prev_decl_index = wip_members.decl_index; 5824 astgen.fnDecl(gz, scope, wip_members, member_node, body, full) catch |err| switch (err) { 5825 error.OutOfMemory => return error.OutOfMemory, 5826 error.AnalysisFail => { 5827 wip_members.decl_index = prev_decl_index; 5828 try addFailedDeclaration( 5829 wip_members, 5830 gz, 5831 .@"const", 5832 try astgen.identAsString(full.name_token.?), 5833 full.ast.proto_node, 5834 full.visib_token != null, 5835 ); 5836 }, 5837 }; 5838 }, 5839 5840 .global_var_decl, 5841 .local_var_decl, 5842 .simple_var_decl, 5843 .aligned_var_decl, 5844 => { 5845 const full = tree.fullVarDecl(member_node).?; 5846 const prev_decl_index = wip_members.decl_index; 5847 astgen.globalVarDecl(gz, scope, wip_members, member_node, full) catch |err| switch (err) { 5848 error.OutOfMemory => return error.OutOfMemory, 5849 error.AnalysisFail => { 5850 wip_members.decl_index = prev_decl_index; 5851 try addFailedDeclaration( 5852 wip_members, 5853 gz, 5854 .@"const", // doesn't really matter 5855 try astgen.identAsString(full.ast.mut_token + 1), 5856 member_node, 5857 full.visib_token != null, 5858 ); 5859 }, 5860 }; 5861 }, 5862 5863 .@"comptime" => { 5864 const prev_decl_index = wip_members.decl_index; 5865 astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) { 5866 error.OutOfMemory => return error.OutOfMemory, 5867 error.AnalysisFail => { 5868 wip_members.decl_index = prev_decl_index; 5869 try addFailedDeclaration( 5870 wip_members, 5871 gz, 5872 .@"comptime", 5873 .empty, 5874 member_node, 5875 false, 5876 ); 5877 }, 5878 }; 5879 }, 5880 .test_decl => { 5881 const prev_decl_index = wip_members.decl_index; 5882 // We need to have *some* decl here so that the decl count matches what's expected. 5883 // Since it doesn't strictly matter *what* this is, let's save ourselves the trouble 5884 // of duplicating the test name logic, and just assume this is an unnamed test. 5885 astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) { 5886 error.OutOfMemory => return error.OutOfMemory, 5887 error.AnalysisFail => { 5888 wip_members.decl_index = prev_decl_index; 5889 try addFailedDeclaration( 5890 wip_members, 5891 gz, 5892 .unnamed_test, 5893 .empty, 5894 member_node, 5895 false, 5896 ); 5897 }, 5898 }; 5899 }, 5900 else => unreachable, 5901 } 5902 return .decl; 5903 } 5904 5905 fn errorSetDecl(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 5906 const astgen = gz.astgen; 5907 const gpa = astgen.gpa; 5908 const tree = astgen.tree; 5909 5910 const payload_index = try reserveExtra(astgen, @typeInfo(Zir.Inst.ErrorSetDecl).@"struct".fields.len); 5911 var fields_len: usize = 0; 5912 { 5913 var idents: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.TokenIndex) = .empty; 5914 defer idents.deinit(gpa); 5915 5916 const lbrace, const rbrace = tree.nodeData(node).token_and_token; 5917 for (lbrace + 1..rbrace) |i| { 5918 const tok_i: Ast.TokenIndex = @intCast(i); 5919 switch (tree.tokenTag(tok_i)) { 5920 .doc_comment, .comma => {}, 5921 .identifier => { 5922 const str_index = try astgen.identAsString(tok_i); 5923 const gop = try idents.getOrPut(gpa, str_index); 5924 if (gop.found_existing) { 5925 const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(str_index))); 5926 defer gpa.free(name); 5927 return astgen.failTokNotes( 5928 tok_i, 5929 "duplicate error set field '{s}'", 5930 .{name}, 5931 &[_]u32{ 5932 try astgen.errNoteTok( 5933 gop.value_ptr.*, 5934 "previous declaration here", 5935 .{}, 5936 ), 5937 }, 5938 ); 5939 } 5940 gop.value_ptr.* = tok_i; 5941 5942 try astgen.extra.append(gpa, @intFromEnum(str_index)); 5943 fields_len += 1; 5944 }, 5945 else => unreachable, 5946 } 5947 } 5948 } 5949 5950 setExtra(astgen, payload_index, Zir.Inst.ErrorSetDecl{ 5951 .fields_len = @intCast(fields_len), 5952 }); 5953 const result = try gz.addPlNodePayloadIndex(.error_set_decl, node, payload_index); 5954 return rvalue(gz, ri, result, node); 5955 } 5956 5957 fn tryExpr( 5958 parent_gz: *GenZir, 5959 scope: *Scope, 5960 ri: ResultInfo, 5961 node: Ast.Node.Index, 5962 operand_node: Ast.Node.Index, 5963 ) InnerError!Zir.Inst.Ref { 5964 const astgen = parent_gz.astgen; 5965 5966 const fn_block = astgen.fn_block orelse { 5967 return astgen.failNode(node, "'try' outside function scope", .{}); 5968 }; 5969 5970 if (parent_gz.any_defer_node.unwrap()) |any_defer_node| { 5971 return astgen.failNodeNotes(node, "'try' not allowed inside defer expression", .{}, &.{ 5972 try astgen.errNoteNode( 5973 any_defer_node, 5974 "defer expression here", 5975 .{}, 5976 ), 5977 }); 5978 } 5979 5980 // Ensure debug line/column information is emitted for this try expression. 5981 // Then we will save the line/column so that we can emit another one that goes 5982 // "backwards" because we want to evaluate the operand, but then put the debug 5983 // info back at the try keyword for error return tracing. 5984 if (!parent_gz.is_comptime) { 5985 try emitDbgNode(parent_gz, node); 5986 } 5987 const try_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; 5988 5989 const operand_rl: ResultInfo.Loc, const block_tag: Zir.Inst.Tag = switch (ri.rl) { 5990 .ref, .ref_coerced_ty => .{ .ref, .try_ptr }, 5991 else => .{ .none, .@"try" }, 5992 }; 5993 const operand_ri: ResultInfo = .{ .rl = operand_rl, .ctx = .error_handling_expr }; 5994 const operand = operand: { 5995 // As a special case, we need to detect this form: 5996 // `try .foo(...)` 5997 // This is a decl literal form, even though we don't propagate a result type through `try`. 5998 var buf: [1]Ast.Node.Index = undefined; 5999 if (astgen.tree.fullCall(&buf, operand_node)) |full_call| { 6000 const res_ty: Zir.Inst.Ref = try ri.rl.resultType(parent_gz, operand_node) orelse .none; 6001 break :operand try callExpr(parent_gz, scope, operand_ri, res_ty, operand_node, full_call); 6002 } 6003 6004 // This could be a pointer or value depending on the `ri` parameter. 6005 break :operand try reachableExpr(parent_gz, scope, operand_ri, operand_node, node); 6006 }; 6007 6008 const try_inst = try parent_gz.makeBlockInst(block_tag, node); 6009 try parent_gz.instructions.append(astgen.gpa, try_inst); 6010 6011 var else_scope = parent_gz.makeSubBlock(scope); 6012 defer else_scope.unstack(); 6013 6014 const err_tag = switch (ri.rl) { 6015 .ref, .ref_coerced_ty => Zir.Inst.Tag.err_union_code_ptr, 6016 else => Zir.Inst.Tag.err_union_code, 6017 }; 6018 const err_code = try else_scope.addUnNode(err_tag, operand, node); 6019 try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code }); 6020 try emitDbgStmt(&else_scope, try_lc); 6021 _ = try else_scope.addUnNode(.ret_node, err_code, node); 6022 6023 try else_scope.setTryBody(try_inst, operand); 6024 const result = try_inst.toRef(); 6025 switch (ri.rl) { 6026 .ref, .ref_coerced_ty => return result, 6027 else => return rvalue(parent_gz, ri, result, node), 6028 } 6029 } 6030 6031 fn orelseCatchExpr( 6032 parent_gz: *GenZir, 6033 scope: *Scope, 6034 ri: ResultInfo, 6035 node: Ast.Node.Index, 6036 cond_op: Zir.Inst.Tag, 6037 unwrap_op: Zir.Inst.Tag, 6038 unwrap_code_op: Zir.Inst.Tag, 6039 payload_token: ?Ast.TokenIndex, 6040 ) InnerError!Zir.Inst.Ref { 6041 const astgen = parent_gz.astgen; 6042 const tree = astgen.tree; 6043 6044 const lhs, const rhs = tree.nodeData(node).node_and_node; 6045 6046 const need_rl = astgen.nodes_need_rl.contains(node); 6047 const block_ri: ResultInfo = if (need_rl) ri else .{ 6048 .rl = switch (ri.rl) { 6049 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 6050 .inferred_ptr => .none, 6051 else => ri.rl, 6052 }, 6053 .ctx = ri.ctx, 6054 }; 6055 // We need to call `rvalue` to write through to the pointer only if we had a 6056 // result pointer and aren't forwarding it. 6057 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 6058 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 6059 6060 const do_err_trace = astgen.fn_block != null and (cond_op == .is_non_err or cond_op == .is_non_err_ptr); 6061 6062 var block_scope = parent_gz.makeSubBlock(scope); 6063 block_scope.setBreakResultInfo(block_ri); 6064 defer block_scope.unstack(); 6065 6066 const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) { 6067 .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none }, 6068 else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none }, 6069 }; 6070 // This could be a pointer or value depending on the `operand_ri` parameter. 6071 // We cannot use `block_scope.break_result_info` because that has the bare 6072 // type, whereas this expression has the optional type. Later we make 6073 // up for this fact by calling rvalue on the else branch. 6074 const operand = try reachableExpr(&block_scope, &block_scope.base, operand_ri, lhs, rhs); 6075 const cond = try block_scope.addUnNode(cond_op, operand, node); 6076 const condbr = try block_scope.addCondBr(.condbr, node); 6077 6078 const block = try parent_gz.makeBlockInst(.block, node); 6079 try block_scope.setBlockBody(block); 6080 // block_scope unstacked now, can add new instructions to parent_gz 6081 try parent_gz.instructions.append(astgen.gpa, block); 6082 6083 var then_scope = block_scope.makeSubBlock(scope); 6084 defer then_scope.unstack(); 6085 6086 // This could be a pointer or value depending on `unwrap_op`. 6087 const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node); 6088 const then_result = switch (ri.rl) { 6089 .ref, .ref_coerced_ty => unwrapped_payload, 6090 else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node), 6091 }; 6092 _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node); 6093 6094 var else_scope = block_scope.makeSubBlock(scope); 6095 defer else_scope.unstack(); 6096 6097 // We know that the operand (almost certainly) modified the error return trace, 6098 // so signal to Sema that it should save the new index for restoring later. 6099 if (do_err_trace and nodeMayAppendToErrorTrace(tree, lhs)) 6100 _ = try else_scope.addSaveErrRetIndex(.always); 6101 6102 var err_val_scope: Scope.LocalVal = undefined; 6103 const else_sub_scope = blk: { 6104 const payload = payload_token orelse break :blk &else_scope.base; 6105 const err_str = tree.tokenSlice(payload); 6106 if (mem.eql(u8, err_str, "_")) { 6107 try astgen.appendErrorTok(payload, "discard of error capture; omit it instead", .{}); 6108 break :blk &else_scope.base; 6109 } 6110 const err_name = try astgen.identAsString(payload); 6111 6112 try astgen.detectLocalShadowing(scope, err_name, payload, err_str, .capture); 6113 6114 err_val_scope = .{ 6115 .parent = &else_scope.base, 6116 .gen_zir = &else_scope, 6117 .name = err_name, 6118 .inst = try else_scope.addUnNode(unwrap_code_op, operand, node), 6119 .token_src = payload, 6120 .id_cat = .capture, 6121 }; 6122 break :blk &err_val_scope.base; 6123 }; 6124 6125 const else_result = try fullBodyExpr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs, .allow_branch_hint); 6126 if (!else_scope.endsWithNoReturn()) { 6127 // As our last action before the break, "pop" the error trace if needed 6128 if (do_err_trace) 6129 try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, rhs, else_result); 6130 6131 _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, rhs); 6132 } 6133 try checkUsed(parent_gz, &else_scope.base, else_sub_scope); 6134 6135 try setCondBrPayload(condbr, cond, &then_scope, &else_scope); 6136 6137 if (need_result_rvalue) { 6138 return rvalue(parent_gz, ri, block.toRef(), node); 6139 } else { 6140 return block.toRef(); 6141 } 6142 } 6143 6144 /// Return whether the identifier names of two tokens are equal. Resolves @"" 6145 /// tokens without allocating. 6146 /// OK in theory it could do it without allocating. This implementation 6147 /// allocates when the @"" form is used. 6148 fn tokenIdentEql(astgen: *AstGen, token1: Ast.TokenIndex, token2: Ast.TokenIndex) !bool { 6149 const ident_name_1 = try astgen.identifierTokenString(token1); 6150 const ident_name_2 = try astgen.identifierTokenString(token2); 6151 return mem.eql(u8, ident_name_1, ident_name_2); 6152 } 6153 6154 fn fieldAccess( 6155 gz: *GenZir, 6156 scope: *Scope, 6157 ri: ResultInfo, 6158 node: Ast.Node.Index, 6159 ) InnerError!Zir.Inst.Ref { 6160 switch (ri.rl) { 6161 .ref, .ref_coerced_ty => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node), 6162 else => { 6163 const access = try addFieldAccess(.field_val, gz, scope, .{ .rl = .none }, node); 6164 return rvalue(gz, ri, access, node); 6165 }, 6166 } 6167 } 6168 6169 fn addFieldAccess( 6170 tag: Zir.Inst.Tag, 6171 gz: *GenZir, 6172 scope: *Scope, 6173 lhs_ri: ResultInfo, 6174 node: Ast.Node.Index, 6175 ) InnerError!Zir.Inst.Ref { 6176 const astgen = gz.astgen; 6177 const tree = astgen.tree; 6178 6179 const object_node, const field_ident = tree.nodeData(node).node_and_token; 6180 const str_index = try astgen.identAsString(field_ident); 6181 const lhs = try expr(gz, scope, lhs_ri, object_node); 6182 6183 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 6184 try emitDbgStmt(gz, cursor); 6185 6186 return gz.addPlNode(tag, node, Zir.Inst.Field{ 6187 .lhs = lhs, 6188 .field_name_start = str_index, 6189 }); 6190 } 6191 6192 fn arrayAccess( 6193 gz: *GenZir, 6194 scope: *Scope, 6195 ri: ResultInfo, 6196 node: Ast.Node.Index, 6197 ) InnerError!Zir.Inst.Ref { 6198 const tree = gz.astgen.tree; 6199 switch (ri.rl) { 6200 .ref, .ref_coerced_ty => { 6201 const lhs_node, const rhs_node = tree.nodeData(node).node_and_node; 6202 const lhs = try expr(gz, scope, .{ .rl = .ref }, lhs_node); 6203 6204 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 6205 6206 const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, rhs_node); 6207 try emitDbgStmt(gz, cursor); 6208 6209 return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); 6210 }, 6211 else => { 6212 const lhs_node, const rhs_node = tree.nodeData(node).node_and_node; 6213 const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node); 6214 6215 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 6216 6217 const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, rhs_node); 6218 try emitDbgStmt(gz, cursor); 6219 6220 return rvalue(gz, ri, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }), node); 6221 }, 6222 } 6223 } 6224 6225 fn simpleBinOp( 6226 gz: *GenZir, 6227 scope: *Scope, 6228 ri: ResultInfo, 6229 node: Ast.Node.Index, 6230 op_inst_tag: Zir.Inst.Tag, 6231 ) InnerError!Zir.Inst.Ref { 6232 const astgen = gz.astgen; 6233 const tree = astgen.tree; 6234 6235 const lhs_node, const rhs_node = tree.nodeData(node).node_and_node; 6236 6237 if (op_inst_tag == .cmp_neq or op_inst_tag == .cmp_eq) { 6238 const str = if (op_inst_tag == .cmp_eq) "==" else "!="; 6239 if (tree.nodeTag(lhs_node) == .string_literal or 6240 tree.nodeTag(rhs_node) == .string_literal) 6241 return astgen.failNode(node, "cannot compare strings with {s}", .{str}); 6242 } 6243 6244 const lhs = try reachableExpr(gz, scope, .{ .rl = .none }, lhs_node, node); 6245 const cursor = switch (op_inst_tag) { 6246 .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, node), 6247 else => undefined, 6248 }; 6249 const rhs = try reachableExpr(gz, scope, .{ .rl = .none }, rhs_node, node); 6250 6251 switch (op_inst_tag) { 6252 .add, .sub, .mul, .div, .mod_rem => { 6253 try emitDbgStmt(gz, cursor); 6254 }, 6255 else => {}, 6256 } 6257 const result = try gz.addPlNode(op_inst_tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); 6258 return rvalue(gz, ri, result, node); 6259 } 6260 6261 fn simpleStrTok( 6262 gz: *GenZir, 6263 ri: ResultInfo, 6264 ident_token: Ast.TokenIndex, 6265 node: Ast.Node.Index, 6266 op_inst_tag: Zir.Inst.Tag, 6267 ) InnerError!Zir.Inst.Ref { 6268 const astgen = gz.astgen; 6269 const str_index = try astgen.identAsString(ident_token); 6270 const result = try gz.addStrTok(op_inst_tag, str_index, ident_token); 6271 return rvalue(gz, ri, result, node); 6272 } 6273 6274 fn boolBinOp( 6275 gz: *GenZir, 6276 scope: *Scope, 6277 ri: ResultInfo, 6278 node: Ast.Node.Index, 6279 zir_tag: Zir.Inst.Tag, 6280 ) InnerError!Zir.Inst.Ref { 6281 const astgen = gz.astgen; 6282 const tree = astgen.tree; 6283 6284 const lhs_node, const rhs_node = tree.nodeData(node).node_and_node; 6285 const lhs = try expr(gz, scope, coerced_bool_ri, lhs_node); 6286 const bool_br = (try gz.addPlNodePayloadIndex(zir_tag, node, undefined)).toIndex().?; 6287 6288 var rhs_scope = gz.makeSubBlock(scope); 6289 defer rhs_scope.unstack(); 6290 const rhs = try fullBodyExpr(&rhs_scope, &rhs_scope.base, coerced_bool_ri, rhs_node, .allow_branch_hint); 6291 if (!gz.refIsNoReturn(rhs)) { 6292 _ = try rhs_scope.addBreakWithSrcNode(.break_inline, bool_br, rhs, rhs_node); 6293 } 6294 try rhs_scope.setBoolBrBody(bool_br, lhs); 6295 6296 const block_ref = bool_br.toRef(); 6297 return rvalue(gz, ri, block_ref, node); 6298 } 6299 6300 fn ifExpr( 6301 parent_gz: *GenZir, 6302 scope: *Scope, 6303 ri: ResultInfo, 6304 node: Ast.Node.Index, 6305 if_full: Ast.full.If, 6306 ) InnerError!Zir.Inst.Ref { 6307 const astgen = parent_gz.astgen; 6308 const tree = astgen.tree; 6309 6310 const do_err_trace = astgen.fn_block != null and if_full.error_token != null; 6311 6312 const need_rl = astgen.nodes_need_rl.contains(node); 6313 const block_ri: ResultInfo = if (need_rl) ri else .{ 6314 .rl = switch (ri.rl) { 6315 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 6316 .inferred_ptr => .none, 6317 else => ri.rl, 6318 }, 6319 .ctx = ri.ctx, 6320 }; 6321 // We need to call `rvalue` to write through to the pointer only if we had a 6322 // result pointer and aren't forwarding it. 6323 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 6324 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 6325 6326 var block_scope = parent_gz.makeSubBlock(scope); 6327 block_scope.setBreakResultInfo(block_ri); 6328 defer block_scope.unstack(); 6329 6330 const payload_is_ref = if (if_full.payload_token) |payload_token| 6331 tree.tokenTag(payload_token) == .asterisk 6332 else 6333 false; 6334 6335 try emitDbgNode(parent_gz, if_full.ast.cond_expr); 6336 const cond: struct { 6337 inst: Zir.Inst.Ref, 6338 bool_bit: Zir.Inst.Ref, 6339 } = c: { 6340 if (if_full.error_token) |_| { 6341 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none, .ctx = .error_handling_expr }; 6342 const err_union = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr); 6343 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err; 6344 break :c .{ 6345 .inst = err_union, 6346 .bool_bit = try block_scope.addUnNode(tag, err_union, if_full.ast.cond_expr), 6347 }; 6348 } else if (if_full.payload_token) |_| { 6349 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none }; 6350 const optional = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr); 6351 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null; 6352 break :c .{ 6353 .inst = optional, 6354 .bool_bit = try block_scope.addUnNode(tag, optional, if_full.ast.cond_expr), 6355 }; 6356 } else { 6357 const cond = try expr(&block_scope, &block_scope.base, coerced_bool_ri, if_full.ast.cond_expr); 6358 break :c .{ 6359 .inst = cond, 6360 .bool_bit = cond, 6361 }; 6362 } 6363 }; 6364 6365 const condbr = try block_scope.addCondBr(.condbr, node); 6366 6367 const block = try parent_gz.makeBlockInst(.block, node); 6368 try block_scope.setBlockBody(block); 6369 // block_scope unstacked now, can add new instructions to parent_gz 6370 try parent_gz.instructions.append(astgen.gpa, block); 6371 6372 var then_scope = parent_gz.makeSubBlock(scope); 6373 defer then_scope.unstack(); 6374 6375 var payload_val_scope: Scope.LocalVal = undefined; 6376 6377 const then_node = if_full.ast.then_expr; 6378 const then_sub_scope = s: { 6379 if (if_full.error_token != null) { 6380 if (if_full.payload_token) |payload_token| { 6381 const tag: Zir.Inst.Tag = if (payload_is_ref) 6382 .err_union_payload_unsafe_ptr 6383 else 6384 .err_union_payload_unsafe; 6385 const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node); 6386 const token_name_index = payload_token + @intFromBool(payload_is_ref); 6387 const ident_name = try astgen.identAsString(token_name_index); 6388 const token_name_str = tree.tokenSlice(token_name_index); 6389 if (mem.eql(u8, "_", token_name_str)) { 6390 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 6391 break :s &then_scope.base; 6392 } 6393 try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str, .capture); 6394 payload_val_scope = .{ 6395 .parent = &then_scope.base, 6396 .gen_zir = &then_scope, 6397 .name = ident_name, 6398 .inst = payload_inst, 6399 .token_src = token_name_index, 6400 .id_cat = .capture, 6401 }; 6402 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst); 6403 break :s &payload_val_scope.base; 6404 } else { 6405 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node); 6406 break :s &then_scope.base; 6407 } 6408 } else if (if_full.payload_token) |payload_token| { 6409 const ident_token = payload_token + @intFromBool(payload_is_ref); 6410 const tag: Zir.Inst.Tag = if (payload_is_ref) 6411 .optional_payload_unsafe_ptr 6412 else 6413 .optional_payload_unsafe; 6414 const ident_bytes = tree.tokenSlice(ident_token); 6415 if (mem.eql(u8, "_", ident_bytes)) { 6416 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 6417 break :s &then_scope.base; 6418 } 6419 const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node); 6420 const ident_name = try astgen.identAsString(ident_token); 6421 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture); 6422 payload_val_scope = .{ 6423 .parent = &then_scope.base, 6424 .gen_zir = &then_scope, 6425 .name = ident_name, 6426 .inst = payload_inst, 6427 .token_src = ident_token, 6428 .id_cat = .capture, 6429 }; 6430 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst); 6431 break :s &payload_val_scope.base; 6432 } else { 6433 break :s &then_scope.base; 6434 } 6435 }; 6436 6437 const then_result = try fullBodyExpr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node, .allow_branch_hint); 6438 try checkUsed(parent_gz, &then_scope.base, then_sub_scope); 6439 if (!then_scope.endsWithNoReturn()) { 6440 _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, then_node); 6441 } 6442 6443 var else_scope = parent_gz.makeSubBlock(scope); 6444 defer else_scope.unstack(); 6445 6446 // We know that the operand (almost certainly) modified the error return trace, 6447 // so signal to Sema that it should save the new index for restoring later. 6448 if (do_err_trace and nodeMayAppendToErrorTrace(tree, if_full.ast.cond_expr)) 6449 _ = try else_scope.addSaveErrRetIndex(.always); 6450 6451 if (if_full.ast.else_expr.unwrap()) |else_node| { 6452 const sub_scope = s: { 6453 if (if_full.error_token) |error_token| { 6454 const tag: Zir.Inst.Tag = if (payload_is_ref) 6455 .err_union_code_ptr 6456 else 6457 .err_union_code; 6458 const payload_inst = try else_scope.addUnNode(tag, cond.inst, if_full.ast.cond_expr); 6459 const ident_name = try astgen.identAsString(error_token); 6460 const error_token_str = tree.tokenSlice(error_token); 6461 if (mem.eql(u8, "_", error_token_str)) 6462 break :s &else_scope.base; 6463 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str, .capture); 6464 payload_val_scope = .{ 6465 .parent = &else_scope.base, 6466 .gen_zir = &else_scope, 6467 .name = ident_name, 6468 .inst = payload_inst, 6469 .token_src = error_token, 6470 .id_cat = .capture, 6471 }; 6472 try else_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst); 6473 break :s &payload_val_scope.base; 6474 } else { 6475 break :s &else_scope.base; 6476 } 6477 }; 6478 const else_result = try fullBodyExpr(&else_scope, sub_scope, block_scope.break_result_info, else_node, .allow_branch_hint); 6479 if (!else_scope.endsWithNoReturn()) { 6480 // As our last action before the break, "pop" the error trace if needed 6481 if (do_err_trace) 6482 try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, else_node, else_result); 6483 _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, else_node); 6484 } 6485 try checkUsed(parent_gz, &else_scope.base, sub_scope); 6486 } else { 6487 const result = try rvalue(&else_scope, ri, .void_value, node); 6488 _ = try else_scope.addBreak(.@"break", block, result); 6489 } 6490 6491 try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope); 6492 6493 if (need_result_rvalue) { 6494 return rvalue(parent_gz, ri, block.toRef(), node); 6495 } else { 6496 return block.toRef(); 6497 } 6498 } 6499 6500 /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`. 6501 fn setCondBrPayload( 6502 condbr: Zir.Inst.Index, 6503 cond: Zir.Inst.Ref, 6504 then_scope: *GenZir, 6505 else_scope: *GenZir, 6506 ) !void { 6507 defer then_scope.unstack(); 6508 defer else_scope.unstack(); 6509 const astgen = then_scope.astgen; 6510 const then_body = then_scope.instructionsSliceUpto(else_scope); 6511 const else_body = else_scope.instructionsSlice(); 6512 const then_body_len = astgen.countBodyLenAfterFixups(then_body); 6513 const else_body_len = astgen.countBodyLenAfterFixups(else_body); 6514 try astgen.extra.ensureUnusedCapacity( 6515 astgen.gpa, 6516 @typeInfo(Zir.Inst.CondBr).@"struct".fields.len + then_body_len + else_body_len, 6517 ); 6518 6519 const zir_datas = astgen.instructions.items(.data); 6520 zir_datas[@intFromEnum(condbr)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{ 6521 .condition = cond, 6522 .then_body_len = then_body_len, 6523 .else_body_len = else_body_len, 6524 }); 6525 astgen.appendBodyWithFixups(then_body); 6526 astgen.appendBodyWithFixups(else_body); 6527 } 6528 6529 fn whileExpr( 6530 parent_gz: *GenZir, 6531 scope: *Scope, 6532 ri: ResultInfo, 6533 node: Ast.Node.Index, 6534 while_full: Ast.full.While, 6535 is_statement: bool, 6536 ) InnerError!Zir.Inst.Ref { 6537 const astgen = parent_gz.astgen; 6538 const tree = astgen.tree; 6539 6540 const need_rl = astgen.nodes_need_rl.contains(node); 6541 const block_ri: ResultInfo = if (need_rl) ri else .{ 6542 .rl = switch (ri.rl) { 6543 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 6544 .inferred_ptr => .none, 6545 else => ri.rl, 6546 }, 6547 .ctx = ri.ctx, 6548 }; 6549 // We need to call `rvalue` to write through to the pointer only if we had a 6550 // result pointer and aren't forwarding it. 6551 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 6552 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 6553 6554 if (while_full.label_token) |label_token| { 6555 try astgen.checkLabelRedefinition(scope, label_token); 6556 } 6557 6558 const is_inline = while_full.inline_token != null; 6559 if (parent_gz.is_comptime and is_inline) { 6560 try astgen.appendErrorTok(while_full.inline_token.?, "redundant inline keyword in comptime scope", .{}); 6561 } 6562 const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop; 6563 const loop_block = try parent_gz.makeBlockInst(loop_tag, node); 6564 try parent_gz.instructions.append(astgen.gpa, loop_block); 6565 6566 var loop_scope = parent_gz.makeSubBlock(scope); 6567 loop_scope.is_inline = is_inline; 6568 loop_scope.setBreakResultInfo(block_ri); 6569 defer loop_scope.unstack(); 6570 6571 var cond_scope = parent_gz.makeSubBlock(&loop_scope.base); 6572 defer cond_scope.unstack(); 6573 6574 const payload_is_ref = if (while_full.payload_token) |payload_token| 6575 tree.tokenTag(payload_token) == .asterisk 6576 else 6577 false; 6578 6579 try emitDbgNode(parent_gz, while_full.ast.cond_expr); 6580 const cond: struct { 6581 inst: Zir.Inst.Ref, 6582 bool_bit: Zir.Inst.Ref, 6583 } = c: { 6584 if (while_full.error_token) |_| { 6585 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none }; 6586 const err_union = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr, .normal); 6587 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err; 6588 break :c .{ 6589 .inst = err_union, 6590 .bool_bit = try cond_scope.addUnNode(tag, err_union, while_full.ast.cond_expr), 6591 }; 6592 } else if (while_full.payload_token) |_| { 6593 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none }; 6594 const optional = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr, .normal); 6595 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null; 6596 break :c .{ 6597 .inst = optional, 6598 .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.cond_expr), 6599 }; 6600 } else { 6601 const cond = try fullBodyExpr(&cond_scope, &cond_scope.base, coerced_bool_ri, while_full.ast.cond_expr, .normal); 6602 break :c .{ 6603 .inst = cond, 6604 .bool_bit = cond, 6605 }; 6606 } 6607 }; 6608 6609 const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; 6610 const condbr = try cond_scope.addCondBr(condbr_tag, node); 6611 const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block; 6612 const cond_block = try loop_scope.makeBlockInst(block_tag, node); 6613 try cond_scope.setBlockBody(cond_block); 6614 // cond_scope unstacked now, can add new instructions to loop_scope 6615 try loop_scope.instructions.append(astgen.gpa, cond_block); 6616 6617 // make scope now but don't stack on parent_gz until loop_scope 6618 // gets unstacked after cont_expr is emitted and added below 6619 var then_scope = parent_gz.makeSubBlock(&cond_scope.base); 6620 then_scope.instructions_top = GenZir.unstacked_top; 6621 defer then_scope.unstack(); 6622 6623 var dbg_var_name: Zir.NullTerminatedString = .empty; 6624 var dbg_var_inst: Zir.Inst.Ref = undefined; 6625 var opt_payload_inst: Zir.Inst.OptionalIndex = .none; 6626 var payload_val_scope: Scope.LocalVal = undefined; 6627 const then_sub_scope = s: { 6628 if (while_full.error_token != null) { 6629 if (while_full.payload_token) |payload_token| { 6630 const tag: Zir.Inst.Tag = if (payload_is_ref) 6631 .err_union_payload_unsafe_ptr 6632 else 6633 .err_union_payload_unsafe; 6634 // will add this instruction to then_scope.instructions below 6635 const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr); 6636 opt_payload_inst = payload_inst.toOptional(); 6637 const ident_token = payload_token + @intFromBool(payload_is_ref); 6638 const ident_bytes = tree.tokenSlice(ident_token); 6639 if (mem.eql(u8, "_", ident_bytes)) { 6640 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 6641 break :s &then_scope.base; 6642 } 6643 const ident_name = try astgen.identAsString(ident_token); 6644 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture); 6645 payload_val_scope = .{ 6646 .parent = &then_scope.base, 6647 .gen_zir = &then_scope, 6648 .name = ident_name, 6649 .inst = payload_inst.toRef(), 6650 .token_src = ident_token, 6651 .id_cat = .capture, 6652 }; 6653 dbg_var_name = ident_name; 6654 dbg_var_inst = payload_inst.toRef(); 6655 break :s &payload_val_scope.base; 6656 } else { 6657 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node); 6658 break :s &then_scope.base; 6659 } 6660 } else if (while_full.payload_token) |payload_token| { 6661 const tag: Zir.Inst.Tag = if (payload_is_ref) 6662 .optional_payload_unsafe_ptr 6663 else 6664 .optional_payload_unsafe; 6665 // will add this instruction to then_scope.instructions below 6666 const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr); 6667 opt_payload_inst = payload_inst.toOptional(); 6668 const ident_token = payload_token + @intFromBool(payload_is_ref); 6669 const ident_name = try astgen.identAsString(ident_token); 6670 const ident_bytes = tree.tokenSlice(ident_token); 6671 if (mem.eql(u8, "_", ident_bytes)) { 6672 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 6673 break :s &then_scope.base; 6674 } 6675 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture); 6676 payload_val_scope = .{ 6677 .parent = &then_scope.base, 6678 .gen_zir = &then_scope, 6679 .name = ident_name, 6680 .inst = payload_inst.toRef(), 6681 .token_src = ident_token, 6682 .id_cat = .capture, 6683 }; 6684 dbg_var_name = ident_name; 6685 dbg_var_inst = payload_inst.toRef(); 6686 break :s &payload_val_scope.base; 6687 } else { 6688 break :s &then_scope.base; 6689 } 6690 }; 6691 6692 var continue_scope = parent_gz.makeSubBlock(then_sub_scope); 6693 continue_scope.instructions_top = GenZir.unstacked_top; 6694 defer continue_scope.unstack(); 6695 const continue_block = try then_scope.makeBlockInst(block_tag, node); 6696 6697 const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; 6698 _ = try loop_scope.addNode(repeat_tag, node); 6699 6700 try loop_scope.setBlockBody(loop_block); 6701 loop_scope.break_block = loop_block.toOptional(); 6702 loop_scope.continue_block = continue_block.toOptional(); 6703 if (while_full.label_token) |label_token| { 6704 loop_scope.label = .{ 6705 .token = label_token, 6706 .block_inst = loop_block, 6707 }; 6708 } 6709 6710 // done adding instructions to loop_scope, can now stack then_scope 6711 then_scope.instructions_top = then_scope.instructions.items.len; 6712 6713 const then_node = while_full.ast.then_expr; 6714 if (opt_payload_inst.unwrap()) |payload_inst| { 6715 try then_scope.instructions.append(astgen.gpa, payload_inst); 6716 } 6717 if (dbg_var_name != .empty) try then_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst); 6718 try then_scope.instructions.append(astgen.gpa, continue_block); 6719 // This code could be improved to avoid emitting the continue expr when there 6720 // are no jumps to it. This happens when the last statement of a while body is noreturn 6721 // and there are no `continue` statements. 6722 // Tracking issue: https://github.com/ziglang/zig/issues/9185 6723 if (while_full.ast.cont_expr.unwrap()) |cont_expr| { 6724 _ = try unusedResultExpr(&then_scope, then_sub_scope, cont_expr); 6725 } 6726 6727 continue_scope.instructions_top = continue_scope.instructions.items.len; 6728 { 6729 try emitDbgNode(&continue_scope, then_node); 6730 const unused_result = try fullBodyExpr(&continue_scope, &continue_scope.base, .{ .rl = .none }, then_node, .allow_branch_hint); 6731 _ = try addEnsureResult(&continue_scope, unused_result, then_node); 6732 } 6733 try checkUsed(parent_gz, &then_scope.base, then_sub_scope); 6734 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; 6735 if (!continue_scope.endsWithNoReturn()) { 6736 astgen.advanceSourceCursor(tree.tokenStart(tree.lastToken(then_node))); 6737 try emitDbgStmt(parent_gz, .{ astgen.source_line - parent_gz.decl_line, astgen.source_column }); 6738 _ = try parent_gz.add(.{ 6739 .tag = .extended, 6740 .data = .{ .extended = .{ 6741 .opcode = .dbg_empty_stmt, 6742 .small = undefined, 6743 .operand = undefined, 6744 } }, 6745 }); 6746 _ = try continue_scope.addBreak(break_tag, continue_block, .void_value); 6747 } 6748 try continue_scope.setBlockBody(continue_block); 6749 _ = try then_scope.addBreak(break_tag, cond_block, .void_value); 6750 6751 var else_scope = parent_gz.makeSubBlock(&cond_scope.base); 6752 defer else_scope.unstack(); 6753 6754 if (while_full.ast.else_expr.unwrap()) |else_node| { 6755 const sub_scope = s: { 6756 if (while_full.error_token) |error_token| { 6757 const tag: Zir.Inst.Tag = if (payload_is_ref) 6758 .err_union_code_ptr 6759 else 6760 .err_union_code; 6761 const else_payload_inst = try else_scope.addUnNode(tag, cond.inst, while_full.ast.cond_expr); 6762 const ident_name = try astgen.identAsString(error_token); 6763 const ident_bytes = tree.tokenSlice(error_token); 6764 if (mem.eql(u8, ident_bytes, "_")) 6765 break :s &else_scope.base; 6766 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, ident_bytes, .capture); 6767 payload_val_scope = .{ 6768 .parent = &else_scope.base, 6769 .gen_zir = &else_scope, 6770 .name = ident_name, 6771 .inst = else_payload_inst, 6772 .token_src = error_token, 6773 .id_cat = .capture, 6774 }; 6775 try else_scope.addDbgVar(.dbg_var_val, ident_name, else_payload_inst); 6776 break :s &payload_val_scope.base; 6777 } else { 6778 break :s &else_scope.base; 6779 } 6780 }; 6781 // Remove the continue block and break block so that `continue` and `break` 6782 // control flow apply to outer loops; not this one. 6783 loop_scope.continue_block = .none; 6784 loop_scope.break_block = .none; 6785 const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint); 6786 if (is_statement) { 6787 _ = try addEnsureResult(&else_scope, else_result, else_node); 6788 } 6789 6790 try checkUsed(parent_gz, &else_scope.base, sub_scope); 6791 if (!else_scope.endsWithNoReturn()) { 6792 _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node); 6793 } 6794 } else { 6795 const result = try rvalue(&else_scope, ri, .void_value, node); 6796 _ = try else_scope.addBreak(break_tag, loop_block, result); 6797 } 6798 6799 if (loop_scope.label) |some| { 6800 if (!some.used) { 6801 try astgen.appendErrorTok(some.token, "unused while loop label", .{}); 6802 } 6803 } 6804 6805 try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope); 6806 6807 const result = if (need_result_rvalue) 6808 try rvalue(parent_gz, ri, loop_block.toRef(), node) 6809 else 6810 loop_block.toRef(); 6811 6812 if (is_statement) { 6813 _ = try parent_gz.addUnNode(.ensure_result_used, result, node); 6814 } 6815 6816 return result; 6817 } 6818 6819 fn forExpr( 6820 parent_gz: *GenZir, 6821 scope: *Scope, 6822 ri: ResultInfo, 6823 node: Ast.Node.Index, 6824 for_full: Ast.full.For, 6825 is_statement: bool, 6826 ) InnerError!Zir.Inst.Ref { 6827 const astgen = parent_gz.astgen; 6828 6829 if (for_full.label_token) |label_token| { 6830 try astgen.checkLabelRedefinition(scope, label_token); 6831 } 6832 6833 const need_rl = astgen.nodes_need_rl.contains(node); 6834 const block_ri: ResultInfo = if (need_rl) ri else .{ 6835 .rl = switch (ri.rl) { 6836 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 6837 .inferred_ptr => .none, 6838 else => ri.rl, 6839 }, 6840 .ctx = ri.ctx, 6841 }; 6842 // We need to call `rvalue` to write through to the pointer only if we had a 6843 // result pointer and aren't forwarding it. 6844 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 6845 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 6846 6847 const is_inline = for_full.inline_token != null; 6848 if (parent_gz.is_comptime and is_inline) { 6849 try astgen.appendErrorTok(for_full.inline_token.?, "redundant inline keyword in comptime scope", .{}); 6850 } 6851 const tree = astgen.tree; 6852 const gpa = astgen.gpa; 6853 6854 // For counters, this is the start value; for indexables, this is the base 6855 // pointer that can be used with elem_ptr and similar instructions. 6856 // Special value `none` means that this is a counter and its start value is 6857 // zero, indicating that the main index counter can be used directly. 6858 const indexables = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len); 6859 defer gpa.free(indexables); 6860 // elements of this array can be `none`, indicating no length check. 6861 const lens = try gpa.alloc([2]Zir.Inst.Ref, for_full.ast.inputs.len); 6862 defer gpa.free(lens); 6863 6864 // We will use a single zero-based counter no matter how many indexables there are. 6865 const index_ptr = blk: { 6866 const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc; 6867 const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node); 6868 // initialize to zero 6869 _ = try parent_gz.addPlNode(.store_node, node, Zir.Inst.Bin{ 6870 .lhs = index_ptr, 6871 .rhs = .zero_usize, 6872 }); 6873 break :blk index_ptr; 6874 }; 6875 6876 var any_len_checks = false; 6877 6878 { 6879 var capture_token = for_full.payload_token; 6880 for (for_full.ast.inputs, indexables, lens) |input, *indexable_ref, *len_refs| { 6881 const capture_is_ref = tree.tokenTag(capture_token) == .asterisk; 6882 const ident_tok = capture_token + @intFromBool(capture_is_ref); 6883 const is_discard = mem.eql(u8, tree.tokenSlice(ident_tok), "_"); 6884 6885 if (is_discard and capture_is_ref) { 6886 return astgen.failTok(capture_token, "pointer modifier invalid on discard", .{}); 6887 } 6888 // Skip over the comma, and on to the next capture (or the ending pipe character). 6889 capture_token = ident_tok + 2; 6890 6891 try emitDbgNode(parent_gz, input); 6892 if (tree.nodeTag(input) == .for_range) { 6893 if (capture_is_ref) { 6894 return astgen.failTok(ident_tok, "cannot capture reference to range", .{}); 6895 } 6896 const start_node, const end_node = tree.nodeData(input).node_and_opt_node; 6897 const start_val = try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, start_node); 6898 6899 const end_val = if (end_node.unwrap()) |end| 6900 try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, end) 6901 else 6902 .none; 6903 6904 if (end_val == .none and is_discard) { 6905 try astgen.appendErrorTok(ident_tok, "discard of unbounded counter", .{}); 6906 } 6907 6908 if (end_val == .none) { 6909 len_refs.* = .{ .none, .none }; 6910 } else { 6911 any_len_checks = true; 6912 len_refs.* = .{ start_val, end_val }; 6913 } 6914 6915 const start_is_zero = nodeIsTriviallyZero(tree, start_node); 6916 indexable_ref.* = if (start_is_zero) .none else start_val; 6917 } else { 6918 const indexable = try expr(parent_gz, scope, .{ .rl = .none }, input); 6919 6920 any_len_checks = true; 6921 indexable_ref.* = indexable; 6922 len_refs.* = .{ indexable, .none }; 6923 } 6924 } 6925 } 6926 6927 if (!any_len_checks) { 6928 return astgen.failNode(node, "unbounded for loop", .{}); 6929 } 6930 6931 // We use a dedicated ZIR instruction to assert the lengths to assist with 6932 // nicer error reporting as well as fewer ZIR bytes emitted. 6933 const len: Zir.Inst.Ref = len: { 6934 const all_lens = @as([*]Zir.Inst.Ref, @ptrCast(lens))[0 .. lens.len * 2]; 6935 const lens_len: u32 = @intCast(all_lens.len); 6936 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).@"struct".fields.len + lens_len); 6937 const len = try parent_gz.addPlNode(.for_len, node, Zir.Inst.MultiOp{ 6938 .operands_len = lens_len, 6939 }); 6940 appendRefsAssumeCapacity(astgen, all_lens); 6941 break :len len; 6942 }; 6943 6944 const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop; 6945 const loop_block = try parent_gz.makeBlockInst(loop_tag, node); 6946 try parent_gz.instructions.append(gpa, loop_block); 6947 6948 var loop_scope = parent_gz.makeSubBlock(scope); 6949 loop_scope.is_inline = is_inline; 6950 loop_scope.setBreakResultInfo(block_ri); 6951 defer loop_scope.unstack(); 6952 6953 // We need to finish loop_scope later once we have the deferred refs from then_scope. However, the 6954 // load must be removed from instructions in the meantime or it appears to be part of parent_gz. 6955 const index = try loop_scope.addUnNode(.load, index_ptr, node); 6956 _ = loop_scope.instructions.pop(); 6957 6958 var cond_scope = parent_gz.makeSubBlock(&loop_scope.base); 6959 defer cond_scope.unstack(); 6960 6961 // Check the condition. 6962 const cond = try cond_scope.addPlNode(.cmp_lt, node, Zir.Inst.Bin{ 6963 .lhs = index, 6964 .rhs = len, 6965 }); 6966 6967 const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; 6968 const condbr = try cond_scope.addCondBr(condbr_tag, node); 6969 const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block; 6970 const cond_block = try loop_scope.makeBlockInst(block_tag, node); 6971 try cond_scope.setBlockBody(cond_block); 6972 6973 loop_scope.break_block = loop_block.toOptional(); 6974 loop_scope.continue_block = cond_block.toOptional(); 6975 if (for_full.label_token) |label_token| { 6976 loop_scope.label = .{ 6977 .token = label_token, 6978 .block_inst = loop_block, 6979 }; 6980 } 6981 6982 const then_node = for_full.ast.then_expr; 6983 var then_scope = parent_gz.makeSubBlock(&cond_scope.base); 6984 defer then_scope.unstack(); 6985 6986 const capture_scopes = try gpa.alloc(Scope.LocalVal, for_full.ast.inputs.len); 6987 defer gpa.free(capture_scopes); 6988 6989 const then_sub_scope = blk: { 6990 var capture_token = for_full.payload_token; 6991 var capture_sub_scope: *Scope = &then_scope.base; 6992 for (for_full.ast.inputs, indexables, capture_scopes) |input, indexable_ref, *capture_scope| { 6993 const capture_is_ref = tree.tokenTag(capture_token) == .asterisk; 6994 const ident_tok = capture_token + @intFromBool(capture_is_ref); 6995 const capture_name = tree.tokenSlice(ident_tok); 6996 // Skip over the comma, and on to the next capture (or the ending pipe character). 6997 capture_token = ident_tok + 2; 6998 6999 if (mem.eql(u8, capture_name, "_")) continue; 7000 7001 const name_str_index = try astgen.identAsString(ident_tok); 7002 try astgen.detectLocalShadowing(capture_sub_scope, name_str_index, ident_tok, capture_name, .capture); 7003 7004 const capture_inst = inst: { 7005 const is_counter = tree.nodeTag(input) == .for_range; 7006 7007 if (indexable_ref == .none) { 7008 // Special case: the main index can be used directly. 7009 assert(is_counter); 7010 assert(!capture_is_ref); 7011 break :inst index; 7012 } 7013 7014 // For counters, we add the index variable to the start value; for 7015 // indexables, we use it as an element index. This is so similar 7016 // that they can share the same code paths, branching only on the 7017 // ZIR tag. 7018 const switch_cond = (@as(u2, @intFromBool(capture_is_ref)) << 1) | @intFromBool(is_counter); 7019 const tag: Zir.Inst.Tag = switch (switch_cond) { 7020 0b00 => .elem_val, 7021 0b01 => .add, 7022 0b10 => .elem_ptr, 7023 0b11 => unreachable, // compile error emitted already 7024 }; 7025 break :inst try then_scope.addPlNode(tag, input, Zir.Inst.Bin{ 7026 .lhs = indexable_ref, 7027 .rhs = index, 7028 }); 7029 }; 7030 7031 capture_scope.* = .{ 7032 .parent = capture_sub_scope, 7033 .gen_zir = &then_scope, 7034 .name = name_str_index, 7035 .inst = capture_inst, 7036 .token_src = ident_tok, 7037 .id_cat = .capture, 7038 }; 7039 7040 try then_scope.addDbgVar(.dbg_var_val, name_str_index, capture_inst); 7041 capture_sub_scope = &capture_scope.base; 7042 } 7043 7044 break :blk capture_sub_scope; 7045 }; 7046 7047 const then_result = try fullBodyExpr(&then_scope, then_sub_scope, .{ .rl = .none }, then_node, .allow_branch_hint); 7048 _ = try addEnsureResult(&then_scope, then_result, then_node); 7049 7050 try checkUsed(parent_gz, &then_scope.base, then_sub_scope); 7051 7052 astgen.advanceSourceCursor(tree.tokenStart(tree.lastToken(then_node))); 7053 try emitDbgStmt(parent_gz, .{ astgen.source_line - parent_gz.decl_line, astgen.source_column }); 7054 _ = try parent_gz.add(.{ 7055 .tag = .extended, 7056 .data = .{ .extended = .{ 7057 .opcode = .dbg_empty_stmt, 7058 .small = undefined, 7059 .operand = undefined, 7060 } }, 7061 }); 7062 7063 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; 7064 _ = try then_scope.addBreak(break_tag, cond_block, .void_value); 7065 7066 var else_scope = parent_gz.makeSubBlock(&cond_scope.base); 7067 defer else_scope.unstack(); 7068 7069 if (for_full.ast.else_expr.unwrap()) |else_node| { 7070 const sub_scope = &else_scope.base; 7071 // Remove the continue block and break block so that `continue` and `break` 7072 // control flow apply to outer loops; not this one. 7073 loop_scope.continue_block = .none; 7074 loop_scope.break_block = .none; 7075 const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint); 7076 if (is_statement) { 7077 _ = try addEnsureResult(&else_scope, else_result, else_node); 7078 } 7079 if (!else_scope.endsWithNoReturn()) { 7080 _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node); 7081 } 7082 } else { 7083 const result = try rvalue(&else_scope, ri, .void_value, node); 7084 _ = try else_scope.addBreak(break_tag, loop_block, result); 7085 } 7086 7087 if (loop_scope.label) |some| { 7088 if (!some.used) { 7089 try astgen.appendErrorTok(some.token, "unused for loop label", .{}); 7090 } 7091 } 7092 7093 try setCondBrPayload(condbr, cond, &then_scope, &else_scope); 7094 7095 // then_block and else_block unstacked now, can resurrect loop_scope to finally finish it 7096 { 7097 loop_scope.instructions_top = loop_scope.instructions.items.len; 7098 try loop_scope.instructions.appendSlice(gpa, &.{ index.toIndex().?, cond_block }); 7099 7100 // Increment the index variable. 7101 const index_plus_one = try loop_scope.addPlNode(.add_unsafe, node, Zir.Inst.Bin{ 7102 .lhs = index, 7103 .rhs = .one_usize, 7104 }); 7105 _ = try loop_scope.addPlNode(.store_node, node, Zir.Inst.Bin{ 7106 .lhs = index_ptr, 7107 .rhs = index_plus_one, 7108 }); 7109 7110 const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; 7111 _ = try loop_scope.addNode(repeat_tag, node); 7112 7113 try loop_scope.setBlockBody(loop_block); 7114 } 7115 7116 const result = if (need_result_rvalue) 7117 try rvalue(parent_gz, ri, loop_block.toRef(), node) 7118 else 7119 loop_block.toRef(); 7120 7121 if (is_statement) { 7122 _ = try parent_gz.addUnNode(.ensure_result_used, result, node); 7123 } 7124 return result; 7125 } 7126 7127 fn switchExprErrUnion( 7128 parent_gz: *GenZir, 7129 scope: *Scope, 7130 ri: ResultInfo, 7131 catch_or_if_node: Ast.Node.Index, 7132 node_ty: enum { @"catch", @"if" }, 7133 ) InnerError!Zir.Inst.Ref { 7134 const astgen = parent_gz.astgen; 7135 const gpa = astgen.gpa; 7136 const tree = astgen.tree; 7137 7138 const if_full = switch (node_ty) { 7139 .@"catch" => undefined, 7140 .@"if" => tree.fullIf(catch_or_if_node).?, 7141 }; 7142 7143 const switch_node, const operand_node, const error_payload = switch (node_ty) { 7144 .@"catch" => .{ 7145 tree.nodeData(catch_or_if_node).node_and_node[1], 7146 tree.nodeData(catch_or_if_node).node_and_node[0], 7147 tree.nodeMainToken(catch_or_if_node) + 2, 7148 }, 7149 .@"if" => .{ 7150 if_full.ast.else_expr.unwrap().?, 7151 if_full.ast.cond_expr, 7152 if_full.error_token.?, 7153 }, 7154 }; 7155 const switch_full = tree.fullSwitch(switch_node).?; 7156 7157 const do_err_trace = astgen.fn_block != null; 7158 const need_rl = astgen.nodes_need_rl.contains(catch_or_if_node); 7159 const block_ri: ResultInfo = if (need_rl) ri else .{ 7160 .rl = switch (ri.rl) { 7161 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, catch_or_if_node)).? }, 7162 .inferred_ptr => .none, 7163 else => ri.rl, 7164 }, 7165 .ctx = ri.ctx, 7166 }; 7167 7168 const payload_is_ref = switch (node_ty) { 7169 .@"if" => if_full.payload_token != null and tree.tokenTag(if_full.payload_token.?) == .asterisk, 7170 .@"catch" => ri.rl == .ref or ri.rl == .ref_coerced_ty, 7171 }; 7172 7173 // We need to call `rvalue` to write through to the pointer only if we had a 7174 // result pointer and aren't forwarding it. 7175 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 7176 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 7177 var scalar_cases_len: u32 = 0; 7178 var multi_cases_len: u32 = 0; 7179 var inline_cases_len: u32 = 0; 7180 var has_else = false; 7181 var else_node: Ast.Node.OptionalIndex = .none; 7182 var else_src: ?Ast.TokenIndex = null; 7183 for (switch_full.ast.cases) |case_node| { 7184 const case = tree.fullSwitchCase(case_node).?; 7185 7186 if (case.ast.values.len == 0) { 7187 const case_src = case.ast.arrow_token - 1; 7188 if (else_src) |src| { 7189 return astgen.failTokNotes( 7190 case_src, 7191 "multiple else prongs in switch expression", 7192 .{}, 7193 &[_]u32{ 7194 try astgen.errNoteTok( 7195 src, 7196 "previous else prong here", 7197 .{}, 7198 ), 7199 }, 7200 ); 7201 } 7202 has_else = true; 7203 else_node = case_node.toOptional(); 7204 else_src = case_src; 7205 continue; 7206 } else if (case.ast.values.len == 1 and 7207 tree.nodeTag(case.ast.values[0]) == .identifier and 7208 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(case.ast.values[0])), "_")) 7209 { 7210 const case_src = case.ast.arrow_token - 1; 7211 return astgen.failTokNotes( 7212 case_src, 7213 "'_' prong is not allowed when switching on errors", 7214 .{}, 7215 &[_]u32{ 7216 try astgen.errNoteTok( 7217 case_src, 7218 "consider using 'else'", 7219 .{}, 7220 ), 7221 }, 7222 ); 7223 } 7224 7225 for (case.ast.values) |val| { 7226 if (tree.nodeTag(val) == .string_literal) 7227 return astgen.failNode(val, "cannot switch on strings", .{}); 7228 } 7229 7230 if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) != .switch_range) { 7231 scalar_cases_len += 1; 7232 } else { 7233 multi_cases_len += 1; 7234 } 7235 if (case.inline_token != null) { 7236 inline_cases_len += 1; 7237 } 7238 } 7239 7240 const operand_ri: ResultInfo = .{ 7241 .rl = if (payload_is_ref) .ref else .none, 7242 .ctx = .error_handling_expr, 7243 }; 7244 7245 astgen.advanceSourceCursorToNode(operand_node); 7246 const operand_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; 7247 7248 const raw_operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, switch_node); 7249 const item_ri: ResultInfo = .{ .rl = .none }; 7250 7251 // This contains the data that goes into the `extra` array for the SwitchBlockErrUnion, except 7252 // the first cases_nodes.len slots are a table that indexes payloads later in the array, 7253 // with the non-error and else case indices coming first, then scalar_cases_len indexes, then 7254 // multi_cases_len indexes 7255 const payloads = &astgen.scratch; 7256 const scratch_top = astgen.scratch.items.len; 7257 const case_table_start = scratch_top; 7258 const scalar_case_table = case_table_start + 1 + @intFromBool(has_else); 7259 const multi_case_table = scalar_case_table + scalar_cases_len; 7260 const case_table_end = multi_case_table + multi_cases_len; 7261 7262 try astgen.scratch.resize(gpa, case_table_end); 7263 defer astgen.scratch.items.len = scratch_top; 7264 7265 var block_scope = parent_gz.makeSubBlock(scope); 7266 // block_scope not used for collecting instructions 7267 block_scope.instructions_top = GenZir.unstacked_top; 7268 block_scope.setBreakResultInfo(block_ri); 7269 7270 // Sema expects a dbg_stmt immediately before switch_block_err_union 7271 try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc); 7272 // This gets added to the parent block later, after the item expressions. 7273 const switch_block = try parent_gz.makeBlockInst(.switch_block_err_union, switch_node); 7274 7275 // We re-use this same scope for all cases, including the special prong, if any. 7276 var case_scope = parent_gz.makeSubBlock(&block_scope.base); 7277 case_scope.instructions_top = GenZir.unstacked_top; 7278 7279 { 7280 const body_len_index: u32 = @intCast(payloads.items.len); 7281 payloads.items[case_table_start] = body_len_index; 7282 try payloads.resize(gpa, body_len_index + 1); // body_len 7283 7284 case_scope.instructions_top = parent_gz.instructions.items.len; 7285 defer case_scope.unstack(); 7286 7287 const unwrap_payload_tag: Zir.Inst.Tag = if (payload_is_ref) 7288 .err_union_payload_unsafe_ptr 7289 else 7290 .err_union_payload_unsafe; 7291 7292 const unwrapped_payload = try case_scope.addUnNode( 7293 unwrap_payload_tag, 7294 raw_operand, 7295 catch_or_if_node, 7296 ); 7297 7298 switch (node_ty) { 7299 .@"catch" => { 7300 const case_result = switch (ri.rl) { 7301 .ref, .ref_coerced_ty => unwrapped_payload, 7302 else => try rvalue( 7303 &case_scope, 7304 block_scope.break_result_info, 7305 unwrapped_payload, 7306 catch_or_if_node, 7307 ), 7308 }; 7309 _ = try case_scope.addBreakWithSrcNode( 7310 .@"break", 7311 switch_block, 7312 case_result, 7313 catch_or_if_node, 7314 ); 7315 }, 7316 .@"if" => { 7317 var payload_val_scope: Scope.LocalVal = undefined; 7318 7319 const then_node = if_full.ast.then_expr; 7320 const then_sub_scope = s: { 7321 assert(if_full.error_token != null); 7322 if (if_full.payload_token) |payload_token| { 7323 const token_name_index = payload_token + @intFromBool(payload_is_ref); 7324 const ident_name = try astgen.identAsString(token_name_index); 7325 const token_name_str = tree.tokenSlice(token_name_index); 7326 if (mem.eql(u8, "_", token_name_str)) 7327 break :s &case_scope.base; 7328 try astgen.detectLocalShadowing( 7329 &case_scope.base, 7330 ident_name, 7331 token_name_index, 7332 token_name_str, 7333 .capture, 7334 ); 7335 payload_val_scope = .{ 7336 .parent = &case_scope.base, 7337 .gen_zir = &case_scope, 7338 .name = ident_name, 7339 .inst = unwrapped_payload, 7340 .token_src = token_name_index, 7341 .id_cat = .capture, 7342 }; 7343 try case_scope.addDbgVar(.dbg_var_val, ident_name, unwrapped_payload); 7344 break :s &payload_val_scope.base; 7345 } else { 7346 _ = try case_scope.addUnNode( 7347 .ensure_err_union_payload_void, 7348 raw_operand, 7349 catch_or_if_node, 7350 ); 7351 break :s &case_scope.base; 7352 } 7353 }; 7354 const then_result = try expr( 7355 &case_scope, 7356 then_sub_scope, 7357 block_scope.break_result_info, 7358 then_node, 7359 ); 7360 try checkUsed(parent_gz, &case_scope.base, then_sub_scope); 7361 if (!case_scope.endsWithNoReturn()) { 7362 _ = try case_scope.addBreakWithSrcNode( 7363 .@"break", 7364 switch_block, 7365 then_result, 7366 then_node, 7367 ); 7368 } 7369 }, 7370 } 7371 7372 const case_slice = case_scope.instructionsSlice(); 7373 const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, &.{switch_block}); 7374 try payloads.ensureUnusedCapacity(gpa, body_len); 7375 const capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = switch (node_ty) { 7376 .@"catch" => .none, 7377 .@"if" => if (if_full.payload_token == null) 7378 .none 7379 else if (payload_is_ref) 7380 .by_ref 7381 else 7382 .by_val, 7383 }; 7384 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{ 7385 .body_len = @intCast(body_len), 7386 .capture = capture, 7387 .is_inline = false, 7388 .has_tag_capture = false, 7389 }); 7390 appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, &.{switch_block}); 7391 } 7392 7393 const err_name = blk: { 7394 const err_str = tree.tokenSlice(error_payload); 7395 if (mem.eql(u8, err_str, "_")) { 7396 // This is fatal because we already know we're switching on the captured error. 7397 return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{}); 7398 } 7399 const err_name = try astgen.identAsString(error_payload); 7400 try astgen.detectLocalShadowing(scope, err_name, error_payload, err_str, .capture); 7401 7402 break :blk err_name; 7403 }; 7404 7405 // allocate a shared dummy instruction for the error capture 7406 const err_inst = err_inst: { 7407 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 7408 try astgen.instructions.append(astgen.gpa, .{ 7409 .tag = .extended, 7410 .data = .{ .extended = .{ 7411 .opcode = .value_placeholder, 7412 .small = undefined, 7413 .operand = undefined, 7414 } }, 7415 }); 7416 break :err_inst inst; 7417 }; 7418 7419 // In this pass we generate all the item and prong expressions for error cases. 7420 var multi_case_index: u32 = 0; 7421 var scalar_case_index: u32 = 0; 7422 var any_uses_err_capture = false; 7423 for (switch_full.ast.cases) |case_node| { 7424 const case = tree.fullSwitchCase(case_node).?; 7425 7426 const is_multi_case = case.ast.values.len > 1 or 7427 (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) == .switch_range); 7428 7429 var dbg_var_name: Zir.NullTerminatedString = .empty; 7430 var dbg_var_inst: Zir.Inst.Ref = undefined; 7431 var err_scope: Scope.LocalVal = undefined; 7432 var capture_scope: Scope.LocalVal = undefined; 7433 7434 const sub_scope = blk: { 7435 err_scope = .{ 7436 .parent = &case_scope.base, 7437 .gen_zir = &case_scope, 7438 .name = err_name, 7439 .inst = err_inst.toRef(), 7440 .token_src = error_payload, 7441 .id_cat = .capture, 7442 }; 7443 7444 const capture_token = case.payload_token orelse break :blk &err_scope.base; 7445 if (tree.tokenTag(capture_token) != .identifier) { 7446 return astgen.failTok(capture_token + 1, "error set cannot be captured by reference", .{}); 7447 } 7448 7449 const capture_slice = tree.tokenSlice(capture_token); 7450 if (mem.eql(u8, capture_slice, "_")) { 7451 try astgen.appendErrorTok(capture_token, "discard of error capture; omit it instead", .{}); 7452 } 7453 const tag_name = try astgen.identAsString(capture_token); 7454 try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture); 7455 7456 capture_scope = .{ 7457 .parent = &case_scope.base, 7458 .gen_zir = &case_scope, 7459 .name = tag_name, 7460 .inst = switch_block.toRef(), 7461 .token_src = capture_token, 7462 .id_cat = .capture, 7463 }; 7464 dbg_var_name = tag_name; 7465 dbg_var_inst = switch_block.toRef(); 7466 7467 err_scope.parent = &capture_scope.base; 7468 7469 break :blk &err_scope.base; 7470 }; 7471 7472 const header_index: u32 = @intCast(payloads.items.len); 7473 const body_len_index = if (is_multi_case) blk: { 7474 payloads.items[multi_case_table + multi_case_index] = header_index; 7475 multi_case_index += 1; 7476 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len 7477 7478 // items 7479 var items_len: u32 = 0; 7480 for (case.ast.values) |item_node| { 7481 if (tree.nodeTag(item_node) == .switch_range) continue; 7482 items_len += 1; 7483 7484 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item); 7485 try payloads.append(gpa, @intFromEnum(item_inst)); 7486 } 7487 7488 // ranges 7489 var ranges_len: u32 = 0; 7490 for (case.ast.values) |range| { 7491 if (tree.nodeTag(range) != .switch_range) continue; 7492 ranges_len += 1; 7493 7494 const first_node, const last_node = tree.nodeData(range).node_and_node; 7495 const first = try comptimeExpr(parent_gz, scope, item_ri, first_node, .switch_item); 7496 const last = try comptimeExpr(parent_gz, scope, item_ri, last_node, .switch_item); 7497 try payloads.appendSlice(gpa, &[_]u32{ 7498 @intFromEnum(first), @intFromEnum(last), 7499 }); 7500 } 7501 7502 payloads.items[header_index] = items_len; 7503 payloads.items[header_index + 1] = ranges_len; 7504 break :blk header_index + 2; 7505 } else if (case_node.toOptional() == else_node) blk: { 7506 payloads.items[case_table_start + 1] = header_index; 7507 try payloads.resize(gpa, header_index + 1); // body_len 7508 break :blk header_index; 7509 } else blk: { 7510 payloads.items[scalar_case_table + scalar_case_index] = header_index; 7511 scalar_case_index += 1; 7512 try payloads.resize(gpa, header_index + 2); // item, body_len 7513 const item_node = case.ast.values[0]; 7514 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item); 7515 payloads.items[header_index] = @intFromEnum(item_inst); 7516 break :blk header_index + 1; 7517 }; 7518 7519 { 7520 // temporarily stack case_scope on parent_gz 7521 case_scope.instructions_top = parent_gz.instructions.items.len; 7522 defer case_scope.unstack(); 7523 7524 if (do_err_trace and nodeMayAppendToErrorTrace(tree, operand_node)) 7525 _ = try case_scope.addSaveErrRetIndex(.always); 7526 7527 if (dbg_var_name != .empty) { 7528 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst); 7529 } 7530 7531 const target_expr_node = case.ast.target_expr; 7532 const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint); 7533 // check capture_scope, not err_scope to avoid false positive unused error capture 7534 try checkUsed(parent_gz, &case_scope.base, err_scope.parent); 7535 const uses_err = err_scope.used != .none or err_scope.discarded != .none; 7536 if (uses_err) { 7537 try case_scope.addDbgVar(.dbg_var_val, err_name, err_inst.toRef()); 7538 any_uses_err_capture = true; 7539 } 7540 7541 if (!parent_gz.refIsNoReturn(case_result)) { 7542 if (do_err_trace) 7543 try restoreErrRetIndex( 7544 &case_scope, 7545 .{ .block = switch_block }, 7546 block_scope.break_result_info, 7547 target_expr_node, 7548 case_result, 7549 ); 7550 7551 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node); 7552 } 7553 7554 const case_slice = case_scope.instructionsSlice(); 7555 const extra_insts: []const Zir.Inst.Index = if (uses_err) &.{ switch_block, err_inst } else &.{switch_block}; 7556 const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, extra_insts); 7557 try payloads.ensureUnusedCapacity(gpa, body_len); 7558 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{ 7559 .body_len = @intCast(body_len), 7560 .capture = if (case.payload_token != null) .by_val else .none, 7561 .is_inline = case.inline_token != null, 7562 .has_tag_capture = false, 7563 }); 7564 appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, extra_insts); 7565 } 7566 } 7567 // Now that the item expressions are generated we can add this. 7568 try parent_gz.instructions.append(gpa, switch_block); 7569 7570 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlockErrUnion).@"struct".fields.len + 7571 @intFromBool(multi_cases_len != 0) + 7572 payloads.items.len - case_table_end + 7573 (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len); 7574 7575 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlockErrUnion{ 7576 .operand = raw_operand, 7577 .bits = Zir.Inst.SwitchBlockErrUnion.Bits{ 7578 .has_multi_cases = multi_cases_len != 0, 7579 .has_else = has_else, 7580 .scalar_cases_len = @intCast(scalar_cases_len), 7581 .any_uses_err_capture = any_uses_err_capture, 7582 .payload_is_ref = payload_is_ref, 7583 }, 7584 .main_src_node_offset = parent_gz.nodeIndexToRelative(catch_or_if_node), 7585 }); 7586 7587 if (multi_cases_len != 0) { 7588 astgen.extra.appendAssumeCapacity(multi_cases_len); 7589 } 7590 7591 if (any_uses_err_capture) { 7592 astgen.extra.appendAssumeCapacity(@intFromEnum(err_inst)); 7593 } 7594 7595 const zir_datas = astgen.instructions.items(.data); 7596 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index; 7597 7598 for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| { 7599 var body_len_index = start_index; 7600 var end_index = start_index; 7601 const table_index = case_table_start + i; 7602 if (table_index < scalar_case_table) { 7603 end_index += 1; 7604 } else if (table_index < multi_case_table) { 7605 body_len_index += 1; 7606 end_index += 2; 7607 } else { 7608 body_len_index += 2; 7609 const items_len = payloads.items[start_index]; 7610 const ranges_len = payloads.items[start_index + 1]; 7611 end_index += 3 + items_len + 2 * ranges_len; 7612 } 7613 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]); 7614 end_index += prong_info.body_len; 7615 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]); 7616 } 7617 7618 if (need_result_rvalue) { 7619 return rvalue(parent_gz, ri, switch_block.toRef(), switch_node); 7620 } else { 7621 return switch_block.toRef(); 7622 } 7623 } 7624 7625 fn switchExpr( 7626 parent_gz: *GenZir, 7627 scope: *Scope, 7628 ri: ResultInfo, 7629 node: Ast.Node.Index, 7630 switch_full: Ast.full.Switch, 7631 ) InnerError!Zir.Inst.Ref { 7632 const astgen = parent_gz.astgen; 7633 const gpa = astgen.gpa; 7634 const tree = astgen.tree; 7635 const operand_node = switch_full.ast.condition; 7636 const case_nodes = switch_full.ast.cases; 7637 7638 const need_rl = astgen.nodes_need_rl.contains(node); 7639 const block_ri: ResultInfo = if (need_rl) ri else .{ 7640 .rl = switch (ri.rl) { 7641 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 7642 .inferred_ptr => .none, 7643 else => ri.rl, 7644 }, 7645 .ctx = ri.ctx, 7646 }; 7647 // We need to call `rvalue` to write through to the pointer only if we had a 7648 // result pointer and aren't forwarding it. 7649 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 7650 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 7651 7652 if (switch_full.label_token) |label_token| { 7653 try astgen.checkLabelRedefinition(scope, label_token); 7654 } 7655 7656 // We perform two passes over the AST. This first pass is to collect information 7657 // for the following variables, make note of the special prong AST node index, 7658 // and bail out with a compile error if there are multiple special prongs present. 7659 var any_payload_is_ref = false; 7660 var any_has_tag_capture = false; 7661 var any_non_inline_capture = false; 7662 var scalar_cases_len: u32 = 0; 7663 var multi_cases_len: u32 = 0; 7664 var inline_cases_len: u32 = 0; 7665 var else_case_node: Ast.Node.OptionalIndex = .none; 7666 var else_src: ?Ast.TokenIndex = null; 7667 var underscore_case_node: Ast.Node.OptionalIndex = .none; 7668 var underscore_node: Ast.Node.OptionalIndex = .none; 7669 var underscore_src: ?Ast.TokenIndex = null; 7670 var underscore_additional_items: Zir.SpecialProngs.AdditionalItems = .none; 7671 for (case_nodes) |case_node| { 7672 const case = tree.fullSwitchCase(case_node).?; 7673 if (case.payload_token) |payload_token| { 7674 const ident = if (tree.tokenTag(payload_token) == .asterisk) blk: { 7675 any_payload_is_ref = true; 7676 break :blk payload_token + 1; 7677 } else payload_token; 7678 if (tree.tokenTag(ident + 1) == .comma) { 7679 any_has_tag_capture = true; 7680 } 7681 7682 // If the first capture is ignored, then there is no runtime-known 7683 // capture, as the tag capture must be for an inline prong. 7684 // This check isn't perfect, because for things like enums, the 7685 // first prong *is* comptime-known for inline prongs! But such 7686 // knowledge requires semantic analysis. 7687 if (!mem.eql(u8, tree.tokenSlice(ident), "_")) { 7688 any_non_inline_capture = true; 7689 } 7690 } 7691 7692 // Check for else prong. 7693 if (case.ast.values.len == 0) { 7694 const case_src = case.ast.arrow_token - 1; 7695 if (else_src) |src| { 7696 return astgen.failTokNotes( 7697 case_src, 7698 "multiple else prongs in switch expression", 7699 .{}, 7700 &[_]u32{ 7701 try astgen.errNoteTok( 7702 src, 7703 "previous else prong here", 7704 .{}, 7705 ), 7706 }, 7707 ); 7708 } 7709 else_case_node = case_node.toOptional(); 7710 else_src = case_src; 7711 continue; 7712 } 7713 7714 // Check for '_' prong. 7715 var case_has_underscore = false; 7716 for (case.ast.values) |val| { 7717 switch (tree.nodeTag(val)) { 7718 .identifier => if (mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_")) { 7719 const val_src = tree.nodeMainToken(val); 7720 if (underscore_src) |src| { 7721 return astgen.failTokNotes( 7722 val_src, 7723 "multiple '_' prongs in switch expression", 7724 .{}, 7725 &[_]u32{ 7726 try astgen.errNoteTok( 7727 src, 7728 "previous '_' prong here", 7729 .{}, 7730 ), 7731 }, 7732 ); 7733 } 7734 if (case.inline_token != null) { 7735 return astgen.failTok(val_src, "cannot inline '_' prong", .{}); 7736 } 7737 underscore_case_node = case_node.toOptional(); 7738 underscore_src = val_src; 7739 underscore_node = val.toOptional(); 7740 underscore_additional_items = switch (case.ast.values.len) { 7741 0 => unreachable, 7742 1 => .none, 7743 2 => .one, 7744 else => .many, 7745 }; 7746 case_has_underscore = true; 7747 }, 7748 .string_literal => return astgen.failNode(val, "cannot switch on strings", .{}), 7749 else => {}, 7750 } 7751 } 7752 if (case_has_underscore) continue; 7753 7754 if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) != .switch_range) { 7755 scalar_cases_len += 1; 7756 } else { 7757 multi_cases_len += 1; 7758 } 7759 if (case.inline_token != null) { 7760 inline_cases_len += 1; 7761 } 7762 } 7763 7764 const special_prongs: Zir.SpecialProngs = .init( 7765 else_src != null, 7766 underscore_src != null, 7767 underscore_additional_items, 7768 ); 7769 const has_else = special_prongs.hasElse(); 7770 const has_under = special_prongs.hasUnder(); 7771 7772 const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none }; 7773 7774 astgen.advanceSourceCursorToNode(operand_node); 7775 const operand_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; 7776 7777 const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node); 7778 const item_ri: ResultInfo = .{ .rl = .none }; 7779 7780 // If this switch is labeled, it may have `continue`s targeting it, and thus we need the operand type 7781 // to provide a result type. 7782 const raw_operand_ty_ref = if (switch_full.label_token != null) t: { 7783 break :t try parent_gz.addUnNode(.typeof, raw_operand, operand_node); 7784 } else undefined; 7785 7786 // This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti, 7787 // except the first cases_nodes.len slots are a table that indexes payloads later in the array, with 7788 // the special case index coming first, then scalar_case_len indexes, then multi_cases_len indexes 7789 const payloads = &astgen.scratch; 7790 const scratch_top = astgen.scratch.items.len; 7791 const case_table_start = scratch_top; 7792 const else_case_index = if (has_else) case_table_start else undefined; 7793 const under_case_index = if (has_under) case_table_start + @intFromBool(has_else) else undefined; 7794 const scalar_case_table = case_table_start + @intFromBool(has_else) + @intFromBool(has_under); 7795 const multi_case_table = scalar_case_table + scalar_cases_len; 7796 const case_table_end = multi_case_table + multi_cases_len; 7797 try astgen.scratch.resize(gpa, case_table_end); 7798 defer astgen.scratch.items.len = scratch_top; 7799 7800 var block_scope = parent_gz.makeSubBlock(scope); 7801 // block_scope not used for collecting instructions 7802 block_scope.instructions_top = GenZir.unstacked_top; 7803 block_scope.setBreakResultInfo(block_ri); 7804 7805 // Sema expects a dbg_stmt immediately before switch_block(_ref) 7806 try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc); 7807 // This gets added to the parent block later, after the item expressions. 7808 const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block; 7809 const switch_block = try parent_gz.makeBlockInst(switch_tag, node); 7810 7811 if (switch_full.label_token) |label_token| { 7812 block_scope.continue_block = switch_block.toOptional(); 7813 block_scope.continue_result_info = .{ 7814 .rl = if (any_payload_is_ref) 7815 .{ .ref_coerced_ty = raw_operand_ty_ref } 7816 else 7817 .{ .coerced_ty = raw_operand_ty_ref }, 7818 }; 7819 7820 block_scope.label = .{ 7821 .token = label_token, 7822 .block_inst = switch_block, 7823 }; 7824 // `break` can target this via `label.block_inst` 7825 // `break_result_info` already set by `setBreakResultInfo` 7826 } 7827 7828 // We re-use this same scope for all cases, including the special prong, if any. 7829 var case_scope = parent_gz.makeSubBlock(&block_scope.base); 7830 case_scope.instructions_top = GenZir.unstacked_top; 7831 7832 // If any prong has an inline tag capture, allocate a shared dummy instruction for it 7833 const tag_inst = if (any_has_tag_capture) tag_inst: { 7834 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 7835 try astgen.instructions.append(astgen.gpa, .{ 7836 .tag = .extended, 7837 .data = .{ .extended = .{ 7838 .opcode = .value_placeholder, 7839 .small = undefined, 7840 .operand = undefined, 7841 } }, 7842 }); 7843 break :tag_inst inst; 7844 } else undefined; 7845 7846 // In this pass we generate all the item and prong expressions. 7847 var multi_case_index: u32 = 0; 7848 var scalar_case_index: u32 = 0; 7849 for (case_nodes) |case_node| { 7850 const case = tree.fullSwitchCase(case_node).?; 7851 7852 const is_multi_case = case.ast.values.len > 1 or 7853 (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) == .switch_range); 7854 7855 var dbg_var_name: Zir.NullTerminatedString = .empty; 7856 var dbg_var_inst: Zir.Inst.Ref = undefined; 7857 var dbg_var_tag_name: Zir.NullTerminatedString = .empty; 7858 var dbg_var_tag_inst: Zir.Inst.Ref = undefined; 7859 var has_tag_capture = false; 7860 var capture_val_scope: Scope.LocalVal = undefined; 7861 var tag_scope: Scope.LocalVal = undefined; 7862 7863 var capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = .none; 7864 7865 const sub_scope = blk: { 7866 const payload_token = case.payload_token orelse break :blk &case_scope.base; 7867 const capture_is_ref = tree.tokenTag(payload_token) == .asterisk; 7868 const ident = payload_token + @intFromBool(capture_is_ref); 7869 7870 capture = if (capture_is_ref) .by_ref else .by_val; 7871 7872 const ident_slice = tree.tokenSlice(ident); 7873 var payload_sub_scope: *Scope = undefined; 7874 if (mem.eql(u8, ident_slice, "_")) { 7875 if (capture_is_ref) { 7876 return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 7877 } 7878 payload_sub_scope = &case_scope.base; 7879 } else { 7880 const capture_name = try astgen.identAsString(ident); 7881 try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture); 7882 capture_val_scope = .{ 7883 .parent = &case_scope.base, 7884 .gen_zir = &case_scope, 7885 .name = capture_name, 7886 .inst = switch_block.toRef(), 7887 .token_src = ident, 7888 .id_cat = .capture, 7889 }; 7890 dbg_var_name = capture_name; 7891 dbg_var_inst = switch_block.toRef(); 7892 payload_sub_scope = &capture_val_scope.base; 7893 } 7894 7895 const tag_token = if (tree.tokenTag(ident + 1) == .comma) 7896 ident + 2 7897 else 7898 break :blk payload_sub_scope; 7899 const tag_slice = tree.tokenSlice(tag_token); 7900 if (mem.eql(u8, tag_slice, "_")) { 7901 try astgen.appendErrorTok(tag_token, "discard of tag capture; omit it instead", .{}); 7902 } else if (case.inline_token == null) { 7903 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{}); 7904 } 7905 const tag_name = try astgen.identAsString(tag_token); 7906 try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture"); 7907 7908 assert(any_has_tag_capture); 7909 has_tag_capture = true; 7910 7911 tag_scope = .{ 7912 .parent = payload_sub_scope, 7913 .gen_zir = &case_scope, 7914 .name = tag_name, 7915 .inst = tag_inst.toRef(), 7916 .token_src = tag_token, 7917 .id_cat = .@"switch tag capture", 7918 }; 7919 dbg_var_tag_name = tag_name; 7920 dbg_var_tag_inst = tag_inst.toRef(); 7921 break :blk &tag_scope.base; 7922 }; 7923 7924 const header_index: u32 = @intCast(payloads.items.len); 7925 const body_len_index = if (is_multi_case) blk: { 7926 if (case_node.toOptional() == underscore_case_node) { 7927 payloads.items[under_case_index] = header_index; 7928 if (special_prongs.hasOneAdditionalItem()) { 7929 try payloads.resize(gpa, header_index + 2); // item, body_len 7930 const maybe_item_node = case.ast.values[0]; 7931 const item_node = if (maybe_item_node.toOptional() == underscore_node) 7932 case.ast.values[1] 7933 else 7934 maybe_item_node; 7935 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item); 7936 payloads.items[header_index] = @intFromEnum(item_inst); 7937 break :blk header_index + 1; 7938 } 7939 } else { 7940 payloads.items[multi_case_table + multi_case_index] = header_index; 7941 multi_case_index += 1; 7942 } 7943 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len 7944 7945 // items 7946 var items_len: u32 = 0; 7947 for (case.ast.values) |item_node| { 7948 if (item_node.toOptional() == underscore_node or 7949 tree.nodeTag(item_node) == .switch_range) 7950 { 7951 continue; 7952 } 7953 items_len += 1; 7954 7955 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item); 7956 try payloads.append(gpa, @intFromEnum(item_inst)); 7957 } 7958 7959 // ranges 7960 var ranges_len: u32 = 0; 7961 for (case.ast.values) |range| { 7962 if (tree.nodeTag(range) != .switch_range) { 7963 continue; 7964 } 7965 ranges_len += 1; 7966 7967 const first_node, const last_node = tree.nodeData(range).node_and_node; 7968 const first = try comptimeExpr(parent_gz, scope, item_ri, first_node, .switch_item); 7969 const last = try comptimeExpr(parent_gz, scope, item_ri, last_node, .switch_item); 7970 try payloads.appendSlice(gpa, &[_]u32{ 7971 @intFromEnum(first), @intFromEnum(last), 7972 }); 7973 } 7974 7975 payloads.items[header_index] = items_len; 7976 payloads.items[header_index + 1] = ranges_len; 7977 break :blk header_index + 2; 7978 } else if (case_node.toOptional() == else_case_node) blk: { 7979 payloads.items[else_case_index] = header_index; 7980 try payloads.resize(gpa, header_index + 1); // body_len 7981 break :blk header_index; 7982 } else if (case_node.toOptional() == underscore_case_node) blk: { 7983 assert(!special_prongs.hasAdditionalItems()); 7984 payloads.items[under_case_index] = header_index; 7985 try payloads.resize(gpa, header_index + 1); // body_len 7986 break :blk header_index; 7987 } else blk: { 7988 payloads.items[scalar_case_table + scalar_case_index] = header_index; 7989 scalar_case_index += 1; 7990 try payloads.resize(gpa, header_index + 2); // item, body_len 7991 const item_node = case.ast.values[0]; 7992 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item); 7993 payloads.items[header_index] = @intFromEnum(item_inst); 7994 break :blk header_index + 1; 7995 }; 7996 7997 { 7998 // temporarily stack case_scope on parent_gz 7999 case_scope.instructions_top = parent_gz.instructions.items.len; 8000 defer case_scope.unstack(); 8001 8002 if (dbg_var_name != .empty) { 8003 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst); 8004 } 8005 if (dbg_var_tag_name != .empty) { 8006 try case_scope.addDbgVar(.dbg_var_val, dbg_var_tag_name, dbg_var_tag_inst); 8007 } 8008 const target_expr_node = case.ast.target_expr; 8009 const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint); 8010 try checkUsed(parent_gz, &case_scope.base, sub_scope); 8011 if (!parent_gz.refIsNoReturn(case_result)) { 8012 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node); 8013 } 8014 8015 const case_slice = case_scope.instructionsSlice(); 8016 const extra_insts: []const Zir.Inst.Index = if (has_tag_capture) &.{ switch_block, tag_inst } else &.{switch_block}; 8017 const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, extra_insts); 8018 try payloads.ensureUnusedCapacity(gpa, body_len); 8019 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{ 8020 .body_len = @intCast(body_len), 8021 .capture = capture, 8022 .is_inline = case.inline_token != null, 8023 .has_tag_capture = has_tag_capture, 8024 }); 8025 appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, extra_insts); 8026 } 8027 } 8028 8029 if (switch_full.label_token) |label_token| if (!block_scope.label.?.used) { 8030 try astgen.appendErrorTok(label_token, "unused switch label", .{}); 8031 }; 8032 8033 // Now that the item expressions are generated we can add this. 8034 try parent_gz.instructions.append(gpa, switch_block); 8035 8036 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).@"struct".fields.len + 8037 @intFromBool(multi_cases_len != 0) + 8038 @intFromBool(any_has_tag_capture) + 8039 payloads.items.len - scratch_top); 8040 8041 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{ 8042 .operand = raw_operand, 8043 .bits = Zir.Inst.SwitchBlock.Bits{ 8044 .has_multi_cases = multi_cases_len != 0, 8045 .special_prongs = special_prongs, 8046 .any_has_tag_capture = any_has_tag_capture, 8047 .any_non_inline_capture = any_non_inline_capture, 8048 .has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue, 8049 .scalar_cases_len = @intCast(scalar_cases_len), 8050 }, 8051 }); 8052 8053 if (multi_cases_len != 0) { 8054 astgen.extra.appendAssumeCapacity(multi_cases_len); 8055 } 8056 8057 if (any_has_tag_capture) { 8058 astgen.extra.appendAssumeCapacity(@intFromEnum(tag_inst)); 8059 } 8060 8061 const zir_datas = astgen.instructions.items(.data); 8062 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index; 8063 8064 if (has_else) { 8065 const start_index = payloads.items[else_case_index]; 8066 var end_index = start_index + 1; 8067 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[start_index]); 8068 end_index += prong_info.body_len; 8069 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]); 8070 } 8071 if (has_under) { 8072 const start_index = payloads.items[under_case_index]; 8073 var body_len_index = start_index; 8074 var end_index = start_index; 8075 switch (underscore_additional_items) { 8076 .none => { 8077 end_index += 1; 8078 }, 8079 .one => { 8080 body_len_index += 1; 8081 end_index += 2; 8082 }, 8083 .many => { 8084 body_len_index += 2; 8085 const items_len = payloads.items[start_index]; 8086 const ranges_len = payloads.items[start_index + 1]; 8087 end_index += 3 + items_len + 2 * ranges_len; 8088 }, 8089 } 8090 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]); 8091 end_index += prong_info.body_len; 8092 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]); 8093 } 8094 for (payloads.items[scalar_case_table..case_table_end], 0..) |start_index, i| { 8095 var body_len_index = start_index; 8096 var end_index = start_index; 8097 const table_index = scalar_case_table + i; 8098 if (table_index < multi_case_table) { 8099 body_len_index += 1; 8100 end_index += 2; 8101 } else { 8102 body_len_index += 2; 8103 const items_len = payloads.items[start_index]; 8104 const ranges_len = payloads.items[start_index + 1]; 8105 end_index += 3 + items_len + 2 * ranges_len; 8106 } 8107 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]); 8108 end_index += prong_info.body_len; 8109 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]); 8110 } 8111 8112 if (need_result_rvalue) { 8113 return rvalue(parent_gz, ri, switch_block.toRef(), node); 8114 } else { 8115 return switch_block.toRef(); 8116 } 8117 } 8118 8119 fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 8120 const astgen = gz.astgen; 8121 const tree = astgen.tree; 8122 8123 if (astgen.fn_block == null) { 8124 return astgen.failNode(node, "'return' outside function scope", .{}); 8125 } 8126 8127 if (gz.any_defer_node.unwrap()) |any_defer_node| { 8128 return astgen.failNodeNotes(node, "cannot return from defer expression", .{}, &.{ 8129 try astgen.errNoteNode( 8130 any_defer_node, 8131 "defer expression here", 8132 .{}, 8133 ), 8134 }); 8135 } 8136 8137 // Ensure debug line/column information is emitted for this return expression. 8138 // Then we will save the line/column so that we can emit another one that goes 8139 // "backwards" because we want to evaluate the operand, but then put the debug 8140 // info back at the return keyword for error return tracing. 8141 if (!gz.is_comptime) { 8142 try emitDbgNode(gz, node); 8143 } 8144 const ret_lc: LineColumn = .{ astgen.source_line - gz.decl_line, astgen.source_column }; 8145 8146 const defer_outer = &astgen.fn_block.?.base; 8147 8148 const operand_node = tree.nodeData(node).opt_node.unwrap() orelse { 8149 // Returning a void value; skip error defers. 8150 try genDefers(gz, defer_outer, scope, .normal_only); 8151 8152 // As our last action before the return, "pop" the error trace if needed 8153 _ = try gz.addRestoreErrRetIndex(.ret, .always, node); 8154 8155 _ = try gz.addUnNode(.ret_node, .void_value, node); 8156 return Zir.Inst.Ref.unreachable_value; 8157 }; 8158 8159 if (tree.nodeTag(operand_node) == .error_value) { 8160 // Hot path for `return error.Foo`. This bypasses result location logic as well as logic 8161 // for detecting whether to add something to the function's inferred error set. 8162 const ident_token = tree.nodeMainToken(operand_node) + 2; 8163 const err_name_str_index = try astgen.identAsString(ident_token); 8164 const defer_counts = countDefers(defer_outer, scope); 8165 if (!defer_counts.need_err_code) { 8166 try genDefers(gz, defer_outer, scope, .both_sans_err); 8167 try emitDbgStmt(gz, ret_lc); 8168 _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token); 8169 return Zir.Inst.Ref.unreachable_value; 8170 } 8171 const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token); 8172 try genDefers(gz, defer_outer, scope, .{ .both = err_code }); 8173 try emitDbgStmt(gz, ret_lc); 8174 _ = try gz.addUnNode(.ret_node, err_code, node); 8175 return Zir.Inst.Ref.unreachable_value; 8176 } 8177 8178 const ri: ResultInfo = if (astgen.nodes_need_rl.contains(node)) .{ 8179 .rl = .{ .ptr = .{ .inst = try gz.addNode(.ret_ptr, node) } }, 8180 .ctx = .@"return", 8181 } else .{ 8182 .rl = .{ .coerced_ty = astgen.fn_ret_ty }, 8183 .ctx = .@"return", 8184 }; 8185 const operand: Zir.Inst.Ref = try nameStratExpr(gz, scope, ri, operand_node, .func) orelse 8186 try reachableExpr(gz, scope, ri, operand_node, node); 8187 8188 switch (nodeMayEvalToError(tree, operand_node)) { 8189 .never => { 8190 // Returning a value that cannot be an error; skip error defers. 8191 try genDefers(gz, defer_outer, scope, .normal_only); 8192 8193 // As our last action before the return, "pop" the error trace if needed 8194 _ = try gz.addRestoreErrRetIndex(.ret, .always, node); 8195 8196 try emitDbgStmt(gz, ret_lc); 8197 try gz.addRet(ri, operand, node); 8198 return Zir.Inst.Ref.unreachable_value; 8199 }, 8200 .always => { 8201 // Value is always an error. Emit both error defers and regular defers. 8202 const err_code = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand; 8203 try genDefers(gz, defer_outer, scope, .{ .both = err_code }); 8204 try emitDbgStmt(gz, ret_lc); 8205 try gz.addRet(ri, operand, node); 8206 return Zir.Inst.Ref.unreachable_value; 8207 }, 8208 .maybe => { 8209 const defer_counts = countDefers(defer_outer, scope); 8210 if (!defer_counts.have_err) { 8211 // Only regular defers; no branch needed. 8212 try genDefers(gz, defer_outer, scope, .normal_only); 8213 try emitDbgStmt(gz, ret_lc); 8214 8215 // As our last action before the return, "pop" the error trace if needed 8216 const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand; 8217 _ = try gz.addRestoreErrRetIndex(.ret, .{ .if_non_error = result }, node); 8218 8219 try gz.addRet(ri, operand, node); 8220 return Zir.Inst.Ref.unreachable_value; 8221 } 8222 8223 // Emit conditional branch for generating errdefers. 8224 const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand; 8225 const is_non_err = try gz.addUnNode(.ret_is_non_err, result, node); 8226 const condbr = try gz.addCondBr(.condbr, node); 8227 8228 var then_scope = gz.makeSubBlock(scope); 8229 defer then_scope.unstack(); 8230 8231 try genDefers(&then_scope, defer_outer, scope, .normal_only); 8232 8233 // As our last action before the return, "pop" the error trace if needed 8234 _ = try then_scope.addRestoreErrRetIndex(.ret, .always, node); 8235 8236 try emitDbgStmt(&then_scope, ret_lc); 8237 try then_scope.addRet(ri, operand, node); 8238 8239 var else_scope = gz.makeSubBlock(scope); 8240 defer else_scope.unstack(); 8241 8242 const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{ 8243 .both = try else_scope.addUnNode(.err_union_code, result, node), 8244 }; 8245 try genDefers(&else_scope, defer_outer, scope, which_ones); 8246 try emitDbgStmt(&else_scope, ret_lc); 8247 try else_scope.addRet(ri, operand, node); 8248 8249 try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope); 8250 8251 return Zir.Inst.Ref.unreachable_value; 8252 }, 8253 } 8254 } 8255 8256 /// Parses the string `buf` as a base 10 integer of type `u16`. 8257 /// 8258 /// Unlike std.fmt.parseInt, does not allow the '_' character in `buf`. 8259 fn parseBitCount(buf: []const u8) std.fmt.ParseIntError!u16 { 8260 if (buf.len == 0) return error.InvalidCharacter; 8261 8262 var x: u16 = 0; 8263 8264 for (buf) |c| { 8265 const digit = switch (c) { 8266 '0'...'9' => c - '0', 8267 else => return error.InvalidCharacter, 8268 }; 8269 8270 if (x != 0) x = try std.math.mul(u16, x, 10); 8271 x = try std.math.add(u16, x, digit); 8272 } 8273 8274 return x; 8275 } 8276 8277 const ComptimeBlockInfo = struct { 8278 src_node: Ast.Node.Index, 8279 reason: std.zig.SimpleComptimeReason, 8280 }; 8281 8282 fn identifier( 8283 gz: *GenZir, 8284 scope: *Scope, 8285 ri: ResultInfo, 8286 ident: Ast.Node.Index, 8287 force_comptime: ?ComptimeBlockInfo, 8288 ) InnerError!Zir.Inst.Ref { 8289 const astgen = gz.astgen; 8290 const tree = astgen.tree; 8291 8292 const ident_token = tree.nodeMainToken(ident); 8293 const ident_name_raw = tree.tokenSlice(ident_token); 8294 if (mem.eql(u8, ident_name_raw, "_")) { 8295 return astgen.failNode(ident, "'_' used as an identifier without @\"_\" syntax", .{}); 8296 } 8297 8298 // if not @"" syntax, just use raw token slice 8299 if (ident_name_raw[0] != '@') { 8300 if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| { 8301 return rvalue(gz, ri, zir_const_ref, ident); 8302 } 8303 8304 if (ident_name_raw.len >= 2) integer: { 8305 // Keep in sync with logic in `comptimeExpr2`. 8306 const first_c = ident_name_raw[0]; 8307 if (first_c == 'i' or first_c == 'u') { 8308 const signedness: std.builtin.Signedness = switch (first_c == 'i') { 8309 true => .signed, 8310 false => .unsigned, 8311 }; 8312 if (ident_name_raw.len >= 3 and ident_name_raw[1] == '0') { 8313 return astgen.failNode( 8314 ident, 8315 "primitive integer type '{s}' has leading zero", 8316 .{ident_name_raw}, 8317 ); 8318 } 8319 const bit_count = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) { 8320 error.Overflow => return astgen.failNode( 8321 ident, 8322 "primitive integer type '{s}' exceeds maximum bit width of 65535", 8323 .{ident_name_raw}, 8324 ), 8325 error.InvalidCharacter => break :integer, 8326 }; 8327 const result = try gz.add(.{ 8328 .tag = .int_type, 8329 .data = .{ .int_type = .{ 8330 .src_node = gz.nodeIndexToRelative(ident), 8331 .signedness = signedness, 8332 .bit_count = bit_count, 8333 } }, 8334 }); 8335 return rvalue(gz, ri, result, ident); 8336 } 8337 } 8338 } 8339 8340 // Local variables, including function parameters, and container-level declarations. 8341 8342 if (force_comptime) |fc| { 8343 // Mirrors the logic at the end of `comptimeExpr2`. 8344 const block_inst = try gz.makeBlockInst(.block_comptime, fc.src_node); 8345 8346 var comptime_gz = gz.makeSubBlock(scope); 8347 comptime_gz.is_comptime = true; 8348 defer comptime_gz.unstack(); 8349 8350 const sub_ri: ResultInfo = .{ 8351 .ctx = ri.ctx, 8352 .rl = .none, // no point providing a result type, it won't change anything 8353 }; 8354 const block_result = try localVarRef(&comptime_gz, scope, sub_ri, ident, ident_token); 8355 assert(!comptime_gz.endsWithNoReturn()); 8356 _ = try comptime_gz.addBreak(.break_inline, block_inst, block_result); 8357 8358 try comptime_gz.setBlockComptimeBody(block_inst, fc.reason); 8359 try gz.instructions.append(astgen.gpa, block_inst); 8360 8361 return rvalue(gz, ri, block_inst.toRef(), fc.src_node); 8362 } else { 8363 return localVarRef(gz, scope, ri, ident, ident_token); 8364 } 8365 } 8366 8367 fn localVarRef( 8368 gz: *GenZir, 8369 scope: *Scope, 8370 ri: ResultInfo, 8371 ident: Ast.Node.Index, 8372 ident_token: Ast.TokenIndex, 8373 ) InnerError!Zir.Inst.Ref { 8374 const astgen = gz.astgen; 8375 const name_str_index = try astgen.identAsString(ident_token); 8376 var s = scope; 8377 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already 8378 var found_needs_tunnel: bool = undefined; // defined when `found_already != null` 8379 var found_namespaces_out: u32 = undefined; // defined when `found_already != null` 8380 8381 // The number of namespaces above `gz` we currently are 8382 var num_namespaces_out: u32 = 0; 8383 // defined by `num_namespaces_out != 0` 8384 var capturing_namespace: *Scope.Namespace = undefined; 8385 8386 while (true) switch (s.tag) { 8387 .local_val => { 8388 const local_val = s.cast(Scope.LocalVal).?; 8389 8390 if (local_val.name == name_str_index) { 8391 // Locals cannot shadow anything, so we do not need to look for ambiguous 8392 // references in this case. 8393 if (ri.rl == .discard and ri.ctx == .assignment) { 8394 local_val.discarded = .fromToken(ident_token); 8395 } else { 8396 local_val.used = .fromToken(ident_token); 8397 } 8398 8399 if (local_val.is_used_or_discarded) |ptr| ptr.* = true; 8400 8401 const value_inst = if (num_namespaces_out != 0) try tunnelThroughClosure( 8402 gz, 8403 ident, 8404 num_namespaces_out, 8405 .{ .ref = local_val.inst }, 8406 .{ .token = local_val.token_src }, 8407 name_str_index, 8408 ) else local_val.inst; 8409 8410 return rvalueNoCoercePreRef(gz, ri, value_inst, ident); 8411 } 8412 s = local_val.parent; 8413 }, 8414 .local_ptr => { 8415 const local_ptr = s.cast(Scope.LocalPtr).?; 8416 if (local_ptr.name == name_str_index) { 8417 if (ri.rl == .discard and ri.ctx == .assignment) { 8418 local_ptr.discarded = .fromToken(ident_token); 8419 } else { 8420 local_ptr.used = .fromToken(ident_token); 8421 } 8422 8423 // Can't close over a runtime variable 8424 if (num_namespaces_out != 0 and !local_ptr.maybe_comptime and !gz.is_typeof) { 8425 const ident_name = try astgen.identifierTokenString(ident_token); 8426 return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{ 8427 try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}), 8428 try astgen.errNoteNode(capturing_namespace.node, "crosses namespace boundary here", .{}), 8429 }); 8430 } 8431 8432 switch (ri.rl) { 8433 .ref, .ref_coerced_ty => { 8434 const ptr_inst = if (num_namespaces_out != 0) try tunnelThroughClosure( 8435 gz, 8436 ident, 8437 num_namespaces_out, 8438 .{ .ref = local_ptr.ptr }, 8439 .{ .token = local_ptr.token_src }, 8440 name_str_index, 8441 ) else local_ptr.ptr; 8442 local_ptr.used_as_lvalue = true; 8443 return ptr_inst; 8444 }, 8445 else => { 8446 const val_inst = if (num_namespaces_out != 0) try tunnelThroughClosure( 8447 gz, 8448 ident, 8449 num_namespaces_out, 8450 .{ .ref_load = local_ptr.ptr }, 8451 .{ .token = local_ptr.token_src }, 8452 name_str_index, 8453 ) else try gz.addUnNode(.load, local_ptr.ptr, ident); 8454 return rvalueNoCoercePreRef(gz, ri, val_inst, ident); 8455 }, 8456 } 8457 } 8458 s = local_ptr.parent; 8459 }, 8460 .gen_zir => s = s.cast(GenZir).?.parent, 8461 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, 8462 .namespace => { 8463 const ns = s.cast(Scope.Namespace).?; 8464 if (ns.decls.get(name_str_index)) |i| { 8465 if (found_already) |f| { 8466 return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{ 8467 try astgen.errNoteNode(f, "declared here", .{}), 8468 try astgen.errNoteNode(i, "also declared here", .{}), 8469 }); 8470 } 8471 // We found a match but must continue looking for ambiguous references to decls. 8472 found_already = i; 8473 found_needs_tunnel = ns.maybe_generic; 8474 found_namespaces_out = num_namespaces_out; 8475 } 8476 num_namespaces_out += 1; 8477 capturing_namespace = ns; 8478 s = ns.parent; 8479 }, 8480 .top => break, 8481 }; 8482 if (found_already == null) { 8483 const ident_name = try astgen.identifierTokenString(ident_token); 8484 return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name}); 8485 } 8486 8487 // Decl references happen by name rather than ZIR index so that when unrelated 8488 // decls are modified, ZIR code containing references to them can be unmodified. 8489 8490 if (found_namespaces_out > 0 and found_needs_tunnel) { 8491 switch (ri.rl) { 8492 .ref, .ref_coerced_ty => return tunnelThroughClosure( 8493 gz, 8494 ident, 8495 found_namespaces_out, 8496 .{ .decl_ref = name_str_index }, 8497 .{ .node = found_already.? }, 8498 name_str_index, 8499 ), 8500 else => { 8501 const result = try tunnelThroughClosure( 8502 gz, 8503 ident, 8504 found_namespaces_out, 8505 .{ .decl_val = name_str_index }, 8506 .{ .node = found_already.? }, 8507 name_str_index, 8508 ); 8509 return rvalueNoCoercePreRef(gz, ri, result, ident); 8510 }, 8511 } 8512 } 8513 8514 switch (ri.rl) { 8515 .ref, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token), 8516 else => { 8517 const result = try gz.addStrTok(.decl_val, name_str_index, ident_token); 8518 return rvalueNoCoercePreRef(gz, ri, result, ident); 8519 }, 8520 } 8521 } 8522 8523 /// Access a ZIR instruction through closure. May tunnel through arbitrarily 8524 /// many namespaces, adding closure captures as required. 8525 /// Returns the index of the `closure_get` instruction added to `gz`. 8526 fn tunnelThroughClosure( 8527 gz: *GenZir, 8528 /// The node which references the value to be captured. 8529 inner_ref_node: Ast.Node.Index, 8530 /// The number of namespaces being tunnelled through. At least 1. 8531 num_tunnels: u32, 8532 /// The value being captured. 8533 value: union(enum) { 8534 ref: Zir.Inst.Ref, 8535 ref_load: Zir.Inst.Ref, 8536 decl_val: Zir.NullTerminatedString, 8537 decl_ref: Zir.NullTerminatedString, 8538 }, 8539 /// The location of the value's declaration. 8540 decl_src: union(enum) { 8541 token: Ast.TokenIndex, 8542 node: Ast.Node.Index, 8543 }, 8544 name_str_index: Zir.NullTerminatedString, 8545 ) !Zir.Inst.Ref { 8546 switch (value) { 8547 .ref => |v| if (v.toIndex() == null) return v, // trivial value; do not need tunnel 8548 .ref_load => |v| assert(v.toIndex() != null), // there are no constant pointer refs 8549 .decl_val, .decl_ref => {}, 8550 } 8551 8552 const astgen = gz.astgen; 8553 const gpa = astgen.gpa; 8554 8555 // Otherwise we need a tunnel. First, figure out the path of namespaces we 8556 // are tunneling through. This is usually only going to be one or two, so 8557 // use an SFBA to optimize for the common case. 8558 var sfba = std.heap.stackFallback(@sizeOf(usize) * 2, astgen.arena); 8559 var intermediate_tunnels = try sfba.get().alloc(*Scope.Namespace, num_tunnels - 1); 8560 8561 const root_ns = ns: { 8562 var i: usize = num_tunnels - 1; 8563 var scope: *Scope = gz.parent; 8564 while (i > 0) { 8565 if (scope.cast(Scope.Namespace)) |mid_ns| { 8566 i -= 1; 8567 intermediate_tunnels[i] = mid_ns; 8568 } 8569 scope = scope.parent().?; 8570 } 8571 while (true) { 8572 if (scope.cast(Scope.Namespace)) |ns| break :ns ns; 8573 scope = scope.parent().?; 8574 } 8575 }; 8576 8577 // Now that we know the scopes we're tunneling through, begin adding 8578 // captures as required, starting with the outermost namespace. 8579 const root_capture: Zir.Inst.Capture = .wrap(switch (value) { 8580 .ref => |v| .{ .instruction = v.toIndex().? }, 8581 .ref_load => |v| .{ .instruction_load = v.toIndex().? }, 8582 .decl_val => |str| .{ .decl_val = str }, 8583 .decl_ref => |str| .{ .decl_ref = str }, 8584 }); 8585 8586 const root_gop = try root_ns.captures.getOrPut(gpa, root_capture); 8587 root_gop.value_ptr.* = name_str_index; 8588 var cur_capture_index = std.math.cast(u16, root_gop.index) orelse return astgen.failNodeNotes( 8589 root_ns.node, 8590 "this compiler implementation only supports up to 65536 captures per namespace", 8591 .{}, 8592 &.{ 8593 switch (decl_src) { 8594 .token => |t| try astgen.errNoteTok(t, "captured value here", .{}), 8595 .node => |n| try astgen.errNoteNode(n, "captured value here", .{}), 8596 }, 8597 try astgen.errNoteNode(inner_ref_node, "value used here", .{}), 8598 }, 8599 ); 8600 8601 for (intermediate_tunnels) |tunnel_ns| { 8602 const tunnel_gop = try tunnel_ns.captures.getOrPut(gpa, .wrap(.{ .nested = cur_capture_index })); 8603 tunnel_gop.value_ptr.* = name_str_index; 8604 cur_capture_index = std.math.cast(u16, tunnel_gop.index) orelse return astgen.failNodeNotes( 8605 tunnel_ns.node, 8606 "this compiler implementation only supports up to 65536 captures per namespace", 8607 .{}, 8608 &.{ 8609 switch (decl_src) { 8610 .token => |t| try astgen.errNoteTok(t, "captured value here", .{}), 8611 .node => |n| try astgen.errNoteNode(n, "captured value here", .{}), 8612 }, 8613 try astgen.errNoteNode(inner_ref_node, "value used here", .{}), 8614 }, 8615 ); 8616 } 8617 8618 // Incorporate the capture index into the source hash, so that changes in 8619 // the order of captures cause suitable re-analysis. 8620 astgen.src_hasher.update(std.mem.asBytes(&cur_capture_index)); 8621 8622 // Add an instruction to get the value from the closure. 8623 return gz.addExtendedNodeSmall(.closure_get, inner_ref_node, cur_capture_index); 8624 } 8625 8626 fn stringLiteral( 8627 gz: *GenZir, 8628 ri: ResultInfo, 8629 node: Ast.Node.Index, 8630 ) InnerError!Zir.Inst.Ref { 8631 const astgen = gz.astgen; 8632 const tree = astgen.tree; 8633 const str_lit_token = tree.nodeMainToken(node); 8634 const str = try astgen.strLitAsString(str_lit_token); 8635 const result = try gz.add(.{ 8636 .tag = .str, 8637 .data = .{ .str = .{ 8638 .start = str.index, 8639 .len = str.len, 8640 } }, 8641 }); 8642 return rvalue(gz, ri, result, node); 8643 } 8644 8645 fn multilineStringLiteral( 8646 gz: *GenZir, 8647 ri: ResultInfo, 8648 node: Ast.Node.Index, 8649 ) InnerError!Zir.Inst.Ref { 8650 const astgen = gz.astgen; 8651 const str = try astgen.strLitNodeAsString(node); 8652 const result = try gz.add(.{ 8653 .tag = .str, 8654 .data = .{ .str = .{ 8655 .start = str.index, 8656 .len = str.len, 8657 } }, 8658 }); 8659 return rvalue(gz, ri, result, node); 8660 } 8661 8662 fn charLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 8663 const astgen = gz.astgen; 8664 const tree = astgen.tree; 8665 const main_token = tree.nodeMainToken(node); 8666 const slice = tree.tokenSlice(main_token); 8667 8668 switch (std.zig.parseCharLiteral(slice)) { 8669 .success => |codepoint| { 8670 const result = try gz.addInt(codepoint); 8671 return rvalue(gz, ri, result, node); 8672 }, 8673 .failure => |err| return astgen.failWithStrLitError(err, main_token, slice, 0), 8674 } 8675 } 8676 8677 const Sign = enum { negative, positive }; 8678 8679 fn numberLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index, source_node: Ast.Node.Index, sign: Sign) InnerError!Zir.Inst.Ref { 8680 const astgen = gz.astgen; 8681 const tree = astgen.tree; 8682 const num_token = tree.nodeMainToken(node); 8683 const bytes = tree.tokenSlice(num_token); 8684 8685 const result: Zir.Inst.Ref = switch (std.zig.parseNumberLiteral(bytes)) { 8686 .int => |num| switch (num) { 8687 0 => if (sign == .positive) .zero else return astgen.failTokNotes( 8688 num_token, 8689 "integer literal '-0' is ambiguous", 8690 .{}, 8691 &.{ 8692 try astgen.errNoteTok(num_token, "use '0' for an integer zero", .{}), 8693 try astgen.errNoteTok(num_token, "use '-0.0' for a floating-point signed zero", .{}), 8694 }, 8695 ), 8696 1 => { 8697 // Handle the negation here! 8698 const result: Zir.Inst.Ref = switch (sign) { 8699 .positive => .one, 8700 .negative => .negative_one, 8701 }; 8702 return rvalue(gz, ri, result, source_node); 8703 }, 8704 else => try gz.addInt(num), 8705 }, 8706 .big_int => |base| big: { 8707 const gpa = astgen.gpa; 8708 var big_int = try std.math.big.int.Managed.init(gpa); 8709 defer big_int.deinit(); 8710 const prefix_offset: usize = if (base == .decimal) 0 else 2; 8711 big_int.setString(@intFromEnum(base), bytes[prefix_offset..]) catch |err| switch (err) { 8712 error.InvalidCharacter => unreachable, // caught in `parseNumberLiteral` 8713 error.InvalidBase => unreachable, // we only pass 16, 8, 2, see above 8714 error.OutOfMemory => return error.OutOfMemory, 8715 }; 8716 8717 const limbs = big_int.limbs[0..big_int.len()]; 8718 assert(big_int.isPositive()); 8719 break :big try gz.addIntBig(limbs); 8720 }, 8721 .float => { 8722 const unsigned_float_number = std.fmt.parseFloat(f128, bytes) catch |err| switch (err) { 8723 error.InvalidCharacter => unreachable, // validated by tokenizer 8724 }; 8725 const float_number = switch (sign) { 8726 .negative => -unsigned_float_number, 8727 .positive => unsigned_float_number, 8728 }; 8729 // If the value fits into a f64 without losing any precision, store it that way. 8730 @setFloatMode(.strict); 8731 const smaller_float: f64 = @floatCast(float_number); 8732 const bigger_again: f128 = smaller_float; 8733 if (bigger_again == float_number) { 8734 const result = try gz.addFloat(smaller_float); 8735 return rvalue(gz, ri, result, source_node); 8736 } 8737 // We need to use 128 bits. Break the float into 4 u32 values so we can 8738 // put it into the `extra` array. 8739 const int_bits: u128 = @bitCast(float_number); 8740 const result = try gz.addPlNode(.float128, node, Zir.Inst.Float128{ 8741 .piece0 = @truncate(int_bits), 8742 .piece1 = @truncate(int_bits >> 32), 8743 .piece2 = @truncate(int_bits >> 64), 8744 .piece3 = @truncate(int_bits >> 96), 8745 }); 8746 return rvalue(gz, ri, result, source_node); 8747 }, 8748 .failure => |err| return astgen.failWithNumberError(err, num_token, bytes), 8749 }; 8750 8751 if (sign == .positive) { 8752 return rvalue(gz, ri, result, source_node); 8753 } else { 8754 const negated = try gz.addUnNode(.negate, result, source_node); 8755 return rvalue(gz, ri, negated, source_node); 8756 } 8757 } 8758 8759 fn failWithNumberError(astgen: *AstGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) InnerError { 8760 const is_float = std.mem.indexOfScalar(u8, bytes, '.') != null; 8761 switch (err) { 8762 .leading_zero => if (is_float) { 8763 return astgen.failTok(token, "number '{s}' has leading zero", .{bytes}); 8764 } else { 8765 return astgen.failTokNotes(token, "number '{s}' has leading zero", .{bytes}, &.{ 8766 try astgen.errNoteTok(token, "use '0o' prefix for octal literals", .{}), 8767 }); 8768 }, 8769 .digit_after_base => return astgen.failTok(token, "expected a digit after base prefix", .{}), 8770 .upper_case_base => |i| return astgen.failOff(token, @intCast(i), "base prefix must be lowercase", .{}), 8771 .invalid_float_base => |i| return astgen.failOff(token, @intCast(i), "invalid base for float literal", .{}), 8772 .repeated_underscore => |i| return astgen.failOff(token, @intCast(i), "repeated digit separator", .{}), 8773 .invalid_underscore_after_special => |i| return astgen.failOff(token, @intCast(i), "expected digit before digit separator", .{}), 8774 .invalid_digit => |info| return astgen.failOff(token, @intCast(info.i), "invalid digit '{c}' for {s} base", .{ bytes[info.i], @tagName(info.base) }), 8775 .invalid_digit_exponent => |i| return astgen.failOff(token, @intCast(i), "invalid digit '{c}' in exponent", .{bytes[i]}), 8776 .duplicate_exponent => |i| return astgen.failOff(token, @intCast(i), "duplicate exponent", .{}), 8777 .exponent_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before exponent", .{}), 8778 .special_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before '{c}'", .{bytes[i]}), 8779 .trailing_special => |i| return astgen.failOff(token, @intCast(i), "expected digit after '{c}'", .{bytes[i - 1]}), 8780 .trailing_underscore => |i| return astgen.failOff(token, @intCast(i), "trailing digit separator", .{}), 8781 .duplicate_period => unreachable, // Validated by tokenizer 8782 .invalid_character => unreachable, // Validated by tokenizer 8783 .invalid_exponent_sign => |i| { 8784 assert(bytes.len >= 2 and bytes[0] == '0' and bytes[1] == 'x'); // Validated by tokenizer 8785 return astgen.failOff(token, @intCast(i), "sign '{c}' cannot follow digit '{c}' in hex base", .{ bytes[i], bytes[i - 1] }); 8786 }, 8787 .period_after_exponent => |i| return astgen.failOff(token, @intCast(i), "unexpected period after exponent", .{}), 8788 } 8789 } 8790 8791 fn asmExpr( 8792 gz: *GenZir, 8793 scope: *Scope, 8794 ri: ResultInfo, 8795 node: Ast.Node.Index, 8796 full: Ast.full.Asm, 8797 ) InnerError!Zir.Inst.Ref { 8798 const astgen = gz.astgen; 8799 const tree = astgen.tree; 8800 8801 const TagAndTmpl = struct { tag: Zir.Inst.Extended, tmpl: Zir.NullTerminatedString }; 8802 const tag_and_tmpl: TagAndTmpl = switch (tree.nodeTag(full.ast.template)) { 8803 .string_literal => .{ 8804 .tag = .@"asm", 8805 .tmpl = (try astgen.strLitAsString(tree.nodeMainToken(full.ast.template))).index, 8806 }, 8807 .multiline_string_literal => .{ 8808 .tag = .@"asm", 8809 .tmpl = (try astgen.strLitNodeAsString(full.ast.template)).index, 8810 }, 8811 else => .{ 8812 .tag = .asm_expr, 8813 .tmpl = @enumFromInt(@intFromEnum(try comptimeExpr(gz, scope, .{ .rl = .none }, full.ast.template, .inline_assembly_code))), 8814 }, 8815 }; 8816 8817 // See https://github.com/ziglang/zig/issues/215 and related issues discussing 8818 // possible inline assembly improvements. Until then here is status quo AstGen 8819 // for assembly syntax. It's used by std lib crypto aesni.zig. 8820 const is_container_asm = astgen.fn_block == null; 8821 if (is_container_asm) { 8822 if (full.volatile_token) |t| 8823 return astgen.failTok(t, "volatile is meaningless on global assembly", .{}); 8824 if (full.outputs.len != 0 or full.inputs.len != 0 or full.ast.clobbers != .none) 8825 return astgen.failNode(node, "global assembly cannot have inputs, outputs, or clobbers", .{}); 8826 } else { 8827 if (full.outputs.len == 0 and full.volatile_token == null) { 8828 return astgen.failNode(node, "assembly expression with no output must be marked volatile", .{}); 8829 } 8830 } 8831 if (full.outputs.len >= 16) { 8832 return astgen.failNode(full.outputs[16], "too many asm outputs", .{}); 8833 } 8834 var outputs_buffer: [15]Zir.Inst.Asm.Output = undefined; 8835 const outputs = outputs_buffer[0..full.outputs.len]; 8836 8837 var output_type_bits: u32 = 0; 8838 8839 for (full.outputs, 0..) |output_node, i| { 8840 const symbolic_name = tree.nodeMainToken(output_node); 8841 const name = try astgen.identAsString(symbolic_name); 8842 const constraint_token = symbolic_name + 2; 8843 const constraint = (try astgen.strLitAsString(constraint_token)).index; 8844 const has_arrow = tree.tokenTag(symbolic_name + 4) == .arrow; 8845 if (has_arrow) { 8846 if (output_type_bits != 0) { 8847 return astgen.failNode(output_node, "inline assembly allows up to one output value", .{}); 8848 } 8849 output_type_bits |= @as(u32, 1) << @intCast(i); 8850 const out_type_node = tree.nodeData(output_node).opt_node_and_token[0].unwrap().?; 8851 const out_type_inst = try typeExpr(gz, scope, out_type_node); 8852 outputs[i] = .{ 8853 .name = name, 8854 .constraint = constraint, 8855 .operand = out_type_inst, 8856 }; 8857 } else { 8858 const ident_token = symbolic_name + 4; 8859 // TODO have a look at #215 and related issues and decide how to 8860 // handle outputs. Do we want this to be identifiers? 8861 // Or maybe we want to force this to be expressions with a pointer type. 8862 outputs[i] = .{ 8863 .name = name, 8864 .constraint = constraint, 8865 .operand = try localVarRef(gz, scope, .{ .rl = .ref }, node, ident_token), 8866 }; 8867 } 8868 } 8869 8870 if (full.inputs.len >= 32) { 8871 return astgen.failNode(full.inputs[32], "too many asm inputs", .{}); 8872 } 8873 var inputs_buffer: [31]Zir.Inst.Asm.Input = undefined; 8874 const inputs = inputs_buffer[0..full.inputs.len]; 8875 8876 for (full.inputs, 0..) |input_node, i| { 8877 const symbolic_name = tree.nodeMainToken(input_node); 8878 const name = try astgen.identAsString(symbolic_name); 8879 const constraint_token = symbolic_name + 2; 8880 const constraint = (try astgen.strLitAsString(constraint_token)).index; 8881 const operand = try expr(gz, scope, .{ .rl = .none }, tree.nodeData(input_node).node_and_token[0]); 8882 inputs[i] = .{ 8883 .name = name, 8884 .constraint = constraint, 8885 .operand = operand, 8886 }; 8887 } 8888 8889 const clobbers: Zir.Inst.Ref = if (full.ast.clobbers.unwrap()) |clobbers_node| 8890 try comptimeExpr(gz, scope, .{ .rl = .{ 8891 .coerced_ty = try gz.addBuiltinValue(clobbers_node, .clobbers), 8892 } }, clobbers_node, .clobber) 8893 else 8894 .none; 8895 8896 const result = try gz.addAsm(.{ 8897 .tag = tag_and_tmpl.tag, 8898 .node = node, 8899 .asm_source = tag_and_tmpl.tmpl, 8900 .is_volatile = full.volatile_token != null, 8901 .output_type_bits = output_type_bits, 8902 .outputs = outputs, 8903 .inputs = inputs, 8904 .clobbers = clobbers, 8905 }); 8906 return rvalue(gz, ri, result, node); 8907 } 8908 8909 fn as( 8910 gz: *GenZir, 8911 scope: *Scope, 8912 ri: ResultInfo, 8913 node: Ast.Node.Index, 8914 lhs: Ast.Node.Index, 8915 rhs: Ast.Node.Index, 8916 ) InnerError!Zir.Inst.Ref { 8917 const dest_type = try typeExpr(gz, scope, lhs); 8918 const result = try reachableExpr(gz, scope, .{ .rl = .{ .ty = dest_type } }, rhs, node); 8919 return rvalue(gz, ri, result, node); 8920 } 8921 8922 fn unionInit( 8923 gz: *GenZir, 8924 scope: *Scope, 8925 ri: ResultInfo, 8926 node: Ast.Node.Index, 8927 params: []const Ast.Node.Index, 8928 ) InnerError!Zir.Inst.Ref { 8929 const union_type = try typeExpr(gz, scope, params[0]); 8930 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .union_field_name); 8931 const field_type = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{ 8932 .container_type = union_type, 8933 .field_name = field_name, 8934 }); 8935 const init = try reachableExpr(gz, scope, .{ .rl = .{ .ty = field_type } }, params[2], node); 8936 const result = try gz.addPlNode(.union_init, node, Zir.Inst.UnionInit{ 8937 .union_type = union_type, 8938 .init = init, 8939 .field_name = field_name, 8940 }); 8941 return rvalue(gz, ri, result, node); 8942 } 8943 8944 fn bitCast( 8945 gz: *GenZir, 8946 scope: *Scope, 8947 ri: ResultInfo, 8948 node: Ast.Node.Index, 8949 operand_node: Ast.Node.Index, 8950 ) InnerError!Zir.Inst.Ref { 8951 const dest_type = try ri.rl.resultTypeForCast(gz, node, "@bitCast"); 8952 const operand = try reachableExpr(gz, scope, .{ .rl = .none }, operand_node, node); 8953 const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{ 8954 .lhs = dest_type, 8955 .rhs = operand, 8956 }); 8957 return rvalue(gz, ri, result, node); 8958 } 8959 8960 /// Handle one or more nested pointer cast builtins: 8961 /// * @ptrCast 8962 /// * @alignCast 8963 /// * @addrSpaceCast 8964 /// * @constCast 8965 /// * @volatileCast 8966 /// Any sequence of such builtins is treated as a single operation. This allowed 8967 /// for sequences like `@ptrCast(@alignCast(ptr))` to work correctly despite the 8968 /// intermediate result type being unknown. 8969 fn ptrCast( 8970 gz: *GenZir, 8971 scope: *Scope, 8972 ri: ResultInfo, 8973 root_node: Ast.Node.Index, 8974 ) InnerError!Zir.Inst.Ref { 8975 const astgen = gz.astgen; 8976 const tree = astgen.tree; 8977 8978 const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?; 8979 var flags: Zir.Inst.FullPtrCastFlags = .{}; 8980 8981 // Note that all pointer cast builtins have one parameter, so we only need 8982 // to handle `builtin_call_two`. 8983 var node = root_node; 8984 while (true) { 8985 switch (tree.nodeTag(node)) { 8986 .builtin_call_two, .builtin_call_two_comma => {}, 8987 .grouped_expression => { 8988 // Handle the chaining even with redundant parentheses 8989 node = tree.nodeData(node).node_and_token[0]; 8990 continue; 8991 }, 8992 else => break, 8993 } 8994 8995 var buf: [2]Ast.Node.Index = undefined; 8996 const args = tree.builtinCallParams(&buf, node).?; 8997 std.debug.assert(args.len <= 2); 8998 8999 if (args.len == 0) break; // 0 args 9000 9001 const builtin_token = tree.nodeMainToken(node); 9002 const builtin_name = tree.tokenSlice(builtin_token); 9003 const info = BuiltinFn.list.get(builtin_name) orelse break; 9004 if (args.len == 1) { 9005 if (info.param_count != 1) break; 9006 9007 switch (info.tag) { 9008 else => break, 9009 inline .ptr_cast, 9010 .align_cast, 9011 .addrspace_cast, 9012 .const_cast, 9013 .volatile_cast, 9014 => |tag| { 9015 if (@field(flags, @tagName(tag))) { 9016 return astgen.failNode(node, "redundant {s}", .{builtin_name}); 9017 } 9018 @field(flags, @tagName(tag)) = true; 9019 }, 9020 } 9021 9022 node = args[0]; 9023 } else { 9024 std.debug.assert(args.len == 2); 9025 if (info.param_count != 2) break; 9026 9027 switch (info.tag) { 9028 else => break, 9029 .field_parent_ptr => { 9030 if (flags.ptr_cast) break; 9031 9032 const flags_int: FlagsInt = @bitCast(flags); 9033 const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node); 9034 const parent_ptr_type = try ri.rl.resultTypeForCast(gz, root_node, "@alignCast"); 9035 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, args[0], .field_name); 9036 const field_ptr = try expr(gz, scope, .{ .rl = .none }, args[1]); 9037 try emitDbgStmt(gz, cursor); 9038 const result = try gz.addExtendedPayloadSmall(.field_parent_ptr, flags_int, Zir.Inst.FieldParentPtr{ 9039 .src_node = gz.nodeIndexToRelative(node), 9040 .parent_ptr_type = parent_ptr_type, 9041 .field_name = field_name, 9042 .field_ptr = field_ptr, 9043 }); 9044 return rvalue(gz, ri, result, root_node); 9045 }, 9046 } 9047 } 9048 } 9049 9050 const flags_int: FlagsInt = @bitCast(flags); 9051 assert(flags_int != 0); 9052 9053 const ptr_only: Zir.Inst.FullPtrCastFlags = .{ .ptr_cast = true }; 9054 if (flags_int == @as(FlagsInt, @bitCast(ptr_only))) { 9055 // Special case: simpler representation 9056 return typeCast(gz, scope, ri, root_node, node, .ptr_cast, "@ptrCast"); 9057 } 9058 9059 const no_result_ty_flags: Zir.Inst.FullPtrCastFlags = .{ 9060 .const_cast = true, 9061 .volatile_cast = true, 9062 }; 9063 if ((flags_int & ~@as(FlagsInt, @bitCast(no_result_ty_flags))) == 0) { 9064 // Result type not needed 9065 const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node); 9066 const operand = try expr(gz, scope, .{ .rl = .none }, node); 9067 try emitDbgStmt(gz, cursor); 9068 const result = try gz.addExtendedPayloadSmall(.ptr_cast_no_dest, flags_int, Zir.Inst.UnNode{ 9069 .node = gz.nodeIndexToRelative(root_node), 9070 .operand = operand, 9071 }); 9072 return rvalue(gz, ri, result, root_node); 9073 } 9074 9075 // Full cast including result type 9076 9077 const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node); 9078 const result_type = try ri.rl.resultTypeForCast(gz, root_node, flags.needResultTypeBuiltinName()); 9079 const operand = try expr(gz, scope, .{ .rl = .none }, node); 9080 try emitDbgStmt(gz, cursor); 9081 const result = try gz.addExtendedPayloadSmall(.ptr_cast_full, flags_int, Zir.Inst.BinNode{ 9082 .node = gz.nodeIndexToRelative(root_node), 9083 .lhs = result_type, 9084 .rhs = operand, 9085 }); 9086 return rvalue(gz, ri, result, root_node); 9087 } 9088 9089 fn typeOf( 9090 gz: *GenZir, 9091 scope: *Scope, 9092 ri: ResultInfo, 9093 node: Ast.Node.Index, 9094 args: []const Ast.Node.Index, 9095 ) InnerError!Zir.Inst.Ref { 9096 const astgen = gz.astgen; 9097 if (args.len < 1) { 9098 return astgen.failNode(node, "expected at least 1 argument, found 0", .{}); 9099 } 9100 const gpa = astgen.gpa; 9101 if (args.len == 1) { 9102 const typeof_inst = try gz.makeBlockInst(.typeof_builtin, node); 9103 9104 var typeof_scope = gz.makeSubBlock(scope); 9105 typeof_scope.is_comptime = false; 9106 typeof_scope.is_typeof = true; 9107 typeof_scope.c_import = false; 9108 defer typeof_scope.unstack(); 9109 9110 const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, args[0], node); 9111 if (!gz.refIsNoReturn(ty_expr)) { 9112 _ = try typeof_scope.addBreak(.break_inline, typeof_inst, ty_expr); 9113 } 9114 try typeof_scope.setBlockBody(typeof_inst); 9115 9116 // typeof_scope unstacked now, can add new instructions to gz 9117 try gz.instructions.append(gpa, typeof_inst); 9118 return rvalue(gz, ri, typeof_inst.toRef(), node); 9119 } 9120 const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len; 9121 const payload_index = try reserveExtra(astgen, payload_size + args.len); 9122 const args_index = payload_index + payload_size; 9123 9124 const typeof_inst = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, args.len); 9125 9126 var typeof_scope = gz.makeSubBlock(scope); 9127 typeof_scope.is_comptime = false; 9128 9129 for (args, 0..) |arg, i| { 9130 const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, arg, node); 9131 astgen.extra.items[args_index + i] = @intFromEnum(param_ref); 9132 } 9133 _ = try typeof_scope.addBreak(.break_inline, typeof_inst.toIndex().?, .void_value); 9134 9135 const body = typeof_scope.instructionsSlice(); 9136 const body_len = astgen.countBodyLenAfterFixups(body); 9137 astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{ 9138 .body_len = @intCast(body_len), 9139 .body_index = @intCast(astgen.extra.items.len), 9140 .src_node = gz.nodeIndexToRelative(node), 9141 }); 9142 try astgen.extra.ensureUnusedCapacity(gpa, body_len); 9143 astgen.appendBodyWithFixups(body); 9144 typeof_scope.unstack(); 9145 9146 return rvalue(gz, ri, typeof_inst, node); 9147 } 9148 9149 fn minMax( 9150 gz: *GenZir, 9151 scope: *Scope, 9152 ri: ResultInfo, 9153 node: Ast.Node.Index, 9154 args: []const Ast.Node.Index, 9155 comptime op: enum { min, max }, 9156 ) InnerError!Zir.Inst.Ref { 9157 const astgen = gz.astgen; 9158 if (args.len < 2) { 9159 return astgen.failNode(node, "expected at least 2 arguments, found {}", .{args.len}); 9160 } 9161 if (args.len == 2) { 9162 const tag: Zir.Inst.Tag = switch (op) { 9163 .min => .min, 9164 .max => .max, 9165 }; 9166 const a = try expr(gz, scope, .{ .rl = .none }, args[0]); 9167 const b = try expr(gz, scope, .{ .rl = .none }, args[1]); 9168 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 9169 .lhs = a, 9170 .rhs = b, 9171 }); 9172 return rvalue(gz, ri, result, node); 9173 } 9174 const payload_index = try addExtra(astgen, Zir.Inst.NodeMultiOp{ 9175 .src_node = gz.nodeIndexToRelative(node), 9176 }); 9177 var extra_index = try reserveExtra(gz.astgen, args.len); 9178 for (args) |arg| { 9179 const arg_ref = try expr(gz, scope, .{ .rl = .none }, arg); 9180 astgen.extra.items[extra_index] = @intFromEnum(arg_ref); 9181 extra_index += 1; 9182 } 9183 const tag: Zir.Inst.Extended = switch (op) { 9184 .min => .min_multi, 9185 .max => .max_multi, 9186 }; 9187 const result = try gz.addExtendedMultiOpPayloadIndex(tag, payload_index, args.len); 9188 return rvalue(gz, ri, result, node); 9189 } 9190 9191 fn builtinCall( 9192 gz: *GenZir, 9193 scope: *Scope, 9194 ri: ResultInfo, 9195 node: Ast.Node.Index, 9196 params: []const Ast.Node.Index, 9197 allow_branch_hint: bool, 9198 ) InnerError!Zir.Inst.Ref { 9199 const astgen = gz.astgen; 9200 const tree = astgen.tree; 9201 9202 const builtin_token = tree.nodeMainToken(node); 9203 const builtin_name = tree.tokenSlice(builtin_token); 9204 9205 // We handle the different builtins manually because they have different semantics depending 9206 // on the function. For example, `@as` and others participate in result location semantics, 9207 // and `@cImport` creates a special scope that collects a .c source code text buffer. 9208 // Also, some builtins have a variable number of parameters. 9209 9210 const info = BuiltinFn.list.get(builtin_name) orelse { 9211 return astgen.failNode(node, "invalid builtin function: '{s}'", .{ 9212 builtin_name, 9213 }); 9214 }; 9215 if (info.param_count) |expected| { 9216 if (expected != params.len) { 9217 const s = if (expected == 1) "" else "s"; 9218 return astgen.failNode(node, "expected {d} argument{s}, found {d}", .{ 9219 expected, s, params.len, 9220 }); 9221 } 9222 } 9223 9224 // Check function scope-only builtins 9225 9226 if (astgen.fn_block == null and info.illegal_outside_function) 9227 return astgen.failNode(node, "'{s}' outside function scope", .{builtin_name}); 9228 9229 switch (info.tag) { 9230 .branch_hint => { 9231 if (!allow_branch_hint) { 9232 return astgen.failNode(node, "'@branchHint' must appear as the first statement in a function or conditional branch", .{}); 9233 } 9234 const hint_ty = try gz.addBuiltinValue(node, .branch_hint); 9235 const hint_val = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = hint_ty } }, params[0], .operand_branchHint); 9236 _ = try gz.addExtendedPayload(.branch_hint, Zir.Inst.UnNode{ 9237 .node = gz.nodeIndexToRelative(node), 9238 .operand = hint_val, 9239 }); 9240 return rvalue(gz, ri, .void_value, node); 9241 }, 9242 .import => { 9243 const operand_node = params[0]; 9244 9245 if (tree.nodeTag(operand_node) != .string_literal) { 9246 // Spec reference: https://github.com/ziglang/zig/issues/2206 9247 return astgen.failNode(operand_node, "@import operand must be a string literal", .{}); 9248 } 9249 const str_lit_token = tree.nodeMainToken(operand_node); 9250 const str = try astgen.strLitAsString(str_lit_token); 9251 const str_slice = astgen.string_bytes.items[@intFromEnum(str.index)..][0..str.len]; 9252 if (mem.indexOfScalar(u8, str_slice, 0) != null) { 9253 return astgen.failTok(str_lit_token, "import path cannot contain null bytes", .{}); 9254 } else if (str.len == 0) { 9255 return astgen.failTok(str_lit_token, "import path cannot be empty", .{}); 9256 } 9257 const res_ty = try ri.rl.resultType(gz, node) orelse .none; 9258 const payload_index = try addExtra(gz.astgen, Zir.Inst.Import{ 9259 .res_ty = res_ty, 9260 .path = str.index, 9261 }); 9262 const result = try gz.add(.{ 9263 .tag = .import, 9264 .data = .{ .pl_tok = .{ 9265 .src_tok = gz.tokenIndexToRelative(str_lit_token), 9266 .payload_index = payload_index, 9267 } }, 9268 }); 9269 const gop = try astgen.imports.getOrPut(astgen.gpa, str.index); 9270 if (!gop.found_existing) { 9271 gop.value_ptr.* = str_lit_token; 9272 } 9273 return rvalue(gz, ri, result, node); 9274 }, 9275 .compile_log => { 9276 const payload_index = try addExtra(gz.astgen, Zir.Inst.NodeMultiOp{ 9277 .src_node = gz.nodeIndexToRelative(node), 9278 }); 9279 var extra_index = try reserveExtra(gz.astgen, params.len); 9280 for (params) |param| { 9281 const param_ref = try expr(gz, scope, .{ .rl = .none }, param); 9282 astgen.extra.items[extra_index] = @intFromEnum(param_ref); 9283 extra_index += 1; 9284 } 9285 const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log, payload_index, params.len); 9286 return rvalue(gz, ri, result, node); 9287 }, 9288 .field => { 9289 if (ri.rl == .ref or ri.rl == .ref_coerced_ty) { 9290 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{ 9291 .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]), 9292 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name), 9293 }); 9294 } 9295 const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{ 9296 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), 9297 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name), 9298 }); 9299 return rvalue(gz, ri, result, node); 9300 }, 9301 .FieldType => { 9302 const ty_inst = try typeExpr(gz, scope, params[0]); 9303 const name_inst = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name); 9304 const result = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{ 9305 .container_type = ty_inst, 9306 .field_name = name_inst, 9307 }); 9308 return rvalue(gz, ri, result, node); 9309 }, 9310 9311 // zig fmt: off 9312 .as => return as( gz, scope, ri, node, params[0], params[1]), 9313 .bit_cast => return bitCast( gz, scope, ri, node, params[0]), 9314 .TypeOf => return typeOf( gz, scope, ri, node, params), 9315 .union_init => return unionInit(gz, scope, ri, node, params), 9316 .c_import => return cImport( gz, scope, node, params[0]), 9317 .min => return minMax( gz, scope, ri, node, params, .min), 9318 .max => return minMax( gz, scope, ri, node, params, .max), 9319 // zig fmt: on 9320 9321 .@"export" => { 9322 const exported = try expr(gz, scope, .{ .rl = .none }, params[0]); 9323 const export_options_ty = try gz.addBuiltinValue(node, .export_options); 9324 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = export_options_ty } }, params[1], .export_options); 9325 _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{ 9326 .exported = exported, 9327 .options = options, 9328 }); 9329 return rvalue(gz, ri, .void_value, node); 9330 }, 9331 .@"extern" => { 9332 const type_inst = try typeExpr(gz, scope, params[0]); 9333 const extern_options_ty = try gz.addBuiltinValue(node, .extern_options); 9334 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = extern_options_ty } }, params[1], .extern_options); 9335 const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{ 9336 .node = gz.nodeIndexToRelative(node), 9337 .lhs = type_inst, 9338 .rhs = options, 9339 }); 9340 return rvalue(gz, ri, result, node); 9341 }, 9342 .set_float_mode => { 9343 const float_mode_ty = try gz.addBuiltinValue(node, .float_mode); 9344 const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_mode_ty } }, params[0]); 9345 _ = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{ 9346 .node = gz.nodeIndexToRelative(node), 9347 .operand = order, 9348 }); 9349 return rvalue(gz, ri, .void_value, node); 9350 }, 9351 9352 .src => { 9353 // Incorporate the source location into the source hash, so that 9354 // changes in the source location of `@src()` result in re-analysis. 9355 astgen.src_hasher.update( 9356 std.mem.asBytes(&astgen.source_line) ++ 9357 std.mem.asBytes(&astgen.source_column), 9358 ); 9359 9360 const node_start = tree.tokenStart(tree.firstToken(node)); 9361 astgen.advanceSourceCursor(node_start); 9362 const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.Src{ 9363 .node = gz.nodeIndexToRelative(node), 9364 .line = astgen.source_line, 9365 .column = astgen.source_column, 9366 }); 9367 return rvalue(gz, ri, result, node); 9368 }, 9369 9370 // zig fmt: off 9371 .This => return rvalue(gz, ri, try gz.addNodeExtended(.this, node), node), 9372 .return_address => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr, node), node), 9373 .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node), 9374 .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node), 9375 .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node), 9376 .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node), 9377 .disable_instrumentation => return rvalue(gz, ri, try gz.addNodeExtended(.disable_instrumentation, node), node), 9378 .disable_intrinsics => return rvalue(gz, ri, try gz.addNodeExtended(.disable_intrinsics, node), node), 9379 9380 .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info), 9381 .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of), 9382 .bit_size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .bit_size_of), 9383 .align_of => return simpleUnOpType(gz, scope, ri, node, params[0], .align_of), 9384 9385 .int_from_ptr => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_ptr), 9386 .compile_error => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .compile_error), 9387 .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota), 9388 .int_from_enum => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_enum), 9389 .int_from_bool => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_bool), 9390 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file), 9391 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name), 9392 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, params[0], .set_runtime_safety), 9393 .sqrt => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sqrt), 9394 .sin => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sin), 9395 .cos => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .cos), 9396 .tan => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tan), 9397 .exp => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .exp), 9398 .exp2 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .exp2), 9399 .log => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log), 9400 .log2 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log2), 9401 .log10 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log10), 9402 .abs => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .abs), 9403 .floor => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .floor), 9404 .ceil => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .ceil), 9405 .trunc => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .trunc), 9406 .round => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .round), 9407 .tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name), 9408 .type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name), 9409 .Frame => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_type), 9410 9411 .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name), 9412 .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name), 9413 .ptr_from_int => return typeCast(gz, scope, ri, node, params[0], .ptr_from_int, builtin_name), 9414 .enum_from_int => return typeCast(gz, scope, ri, node, params[0], .enum_from_int, builtin_name), 9415 .float_cast => return typeCast(gz, scope, ri, node, params[0], .float_cast, builtin_name), 9416 .int_cast => return typeCast(gz, scope, ri, node, params[0], .int_cast, builtin_name), 9417 .truncate => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name), 9418 // zig fmt: on 9419 9420 .in_comptime => if (gz.is_comptime) { 9421 return astgen.failNode(node, "redundant '@inComptime' in comptime scope", .{}); 9422 } else { 9423 return rvalue(gz, ri, try gz.addNodeExtended(.in_comptime, node), node); 9424 }, 9425 9426 .Type => { 9427 return builtinReify(gz, scope, ri, node, params[0], .anon); 9428 }, 9429 .panic => { 9430 try emitDbgNode(gz, node); 9431 return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .panic); 9432 }, 9433 .trap => { 9434 try emitDbgNode(gz, node); 9435 _ = try gz.addNode(.trap, node); 9436 return rvalue(gz, ri, .unreachable_value, node); 9437 }, 9438 .int_from_error => { 9439 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]); 9440 const result = try gz.addExtendedPayload(.int_from_error, Zir.Inst.UnNode{ 9441 .node = gz.nodeIndexToRelative(node), 9442 .operand = operand, 9443 }); 9444 return rvalue(gz, ri, result, node); 9445 }, 9446 .error_from_int => { 9447 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]); 9448 const result = try gz.addExtendedPayload(.error_from_int, Zir.Inst.UnNode{ 9449 .node = gz.nodeIndexToRelative(node), 9450 .operand = operand, 9451 }); 9452 return rvalue(gz, ri, result, node); 9453 }, 9454 .error_cast => { 9455 try emitDbgNode(gz, node); 9456 9457 const result = try gz.addExtendedPayload(.error_cast, Zir.Inst.BinNode{ 9458 .lhs = try ri.rl.resultTypeForCast(gz, node, builtin_name), 9459 .rhs = try expr(gz, scope, .{ .rl = .none }, params[0]), 9460 .node = gz.nodeIndexToRelative(node), 9461 }); 9462 return rvalue(gz, ri, result, node); 9463 }, 9464 .ptr_cast, 9465 .align_cast, 9466 .addrspace_cast, 9467 .const_cast, 9468 .volatile_cast, 9469 => return ptrCast(gz, scope, ri, node), 9470 9471 // zig fmt: off 9472 .has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl), 9473 .has_field => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_field), 9474 9475 .clz => return bitBuiltin(gz, scope, ri, node, params[0], .clz), 9476 .ctz => return bitBuiltin(gz, scope, ri, node, params[0], .ctz), 9477 .pop_count => return bitBuiltin(gz, scope, ri, node, params[0], .pop_count), 9478 .byte_swap => return bitBuiltin(gz, scope, ri, node, params[0], .byte_swap), 9479 .bit_reverse => return bitBuiltin(gz, scope, ri, node, params[0], .bit_reverse), 9480 9481 .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact), 9482 .div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor), 9483 .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc), 9484 .mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod), 9485 .rem => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem), 9486 9487 .shl_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shl_exact), 9488 .shr_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shr_exact), 9489 9490 .bit_offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .bit_offset_of), 9491 .offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .offset_of), 9492 9493 .c_undef => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_undef), 9494 .c_include => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_include), 9495 9496 .cmpxchg_strong => return cmpxchg(gz, scope, ri, node, params, 1), 9497 .cmpxchg_weak => return cmpxchg(gz, scope, ri, node, params, 0), 9498 // zig fmt: on 9499 9500 .wasm_memory_size => { 9501 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .wasm_memory_index); 9502 const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{ 9503 .node = gz.nodeIndexToRelative(node), 9504 .operand = operand, 9505 }); 9506 return rvalue(gz, ri, result, node); 9507 }, 9508 .wasm_memory_grow => { 9509 const index_arg = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .wasm_memory_index); 9510 const delta_arg = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[1]); 9511 const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{ 9512 .node = gz.nodeIndexToRelative(node), 9513 .lhs = index_arg, 9514 .rhs = delta_arg, 9515 }); 9516 return rvalue(gz, ri, result, node); 9517 }, 9518 .c_define => { 9519 if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{}); 9520 const name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .operand_cDefine_macro_name); 9521 const value = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1], .operand_cDefine_macro_value); 9522 const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{ 9523 .node = gz.nodeIndexToRelative(node), 9524 .lhs = name, 9525 .rhs = value, 9526 }); 9527 return rvalue(gz, ri, result, node); 9528 }, 9529 9530 .splat => { 9531 const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name); 9532 const elem_type = try gz.addUnNode(.vec_arr_elem_type, result_type, node); 9533 const scalar = try expr(gz, scope, .{ .rl = .{ .ty = elem_type } }, params[0]); 9534 const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{ 9535 .lhs = result_type, 9536 .rhs = scalar, 9537 }); 9538 return rvalue(gz, ri, result, node); 9539 }, 9540 .reduce => { 9541 const reduce_op_ty = try gz.addBuiltinValue(node, .reduce_op); 9542 const op = try expr(gz, scope, .{ .rl = .{ .coerced_ty = reduce_op_ty } }, params[0]); 9543 const scalar = try expr(gz, scope, .{ .rl = .none }, params[1]); 9544 const result = try gz.addPlNode(.reduce, node, Zir.Inst.Bin{ 9545 .lhs = op, 9546 .rhs = scalar, 9547 }); 9548 return rvalue(gz, ri, result, node); 9549 }, 9550 9551 .add_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .add_with_overflow), 9552 .sub_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .sub_with_overflow), 9553 .mul_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .mul_with_overflow), 9554 .shl_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .shl_with_overflow), 9555 9556 .atomic_load => { 9557 const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order); 9558 const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{ 9559 // zig fmt: off 9560 .elem_type = try typeExpr(gz, scope, params[0]), 9561 .ptr = try expr (gz, scope, .{ .rl = .none }, params[1]), 9562 .ordering = try expr (gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[2]), 9563 // zig fmt: on 9564 }); 9565 return rvalue(gz, ri, result, node); 9566 }, 9567 .atomic_rmw => { 9568 const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order); 9569 const atomic_rmw_op_type = try gz.addBuiltinValue(node, .atomic_rmw_op); 9570 const int_type = try typeExpr(gz, scope, params[0]); 9571 const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{ 9572 // zig fmt: off 9573 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), 9574 .operation = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_rmw_op_type } }, params[2]), 9575 .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[3]), 9576 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[4]), 9577 // zig fmt: on 9578 }); 9579 return rvalue(gz, ri, result, node); 9580 }, 9581 .atomic_store => { 9582 const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order); 9583 const int_type = try typeExpr(gz, scope, params[0]); 9584 _ = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{ 9585 // zig fmt: off 9586 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), 9587 .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]), 9588 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[3]), 9589 // zig fmt: on 9590 }); 9591 return rvalue(gz, ri, .void_value, node); 9592 }, 9593 .mul_add => { 9594 const float_type = try typeExpr(gz, scope, params[0]); 9595 const mulend1 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[1]); 9596 const mulend2 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[2]); 9597 const addend = try expr(gz, scope, .{ .rl = .{ .ty = float_type } }, params[3]); 9598 const result = try gz.addPlNode(.mul_add, node, Zir.Inst.MulAdd{ 9599 .mulend1 = mulend1, 9600 .mulend2 = mulend2, 9601 .addend = addend, 9602 }); 9603 return rvalue(gz, ri, result, node); 9604 }, 9605 .call => { 9606 const call_modifier_ty = try gz.addBuiltinValue(node, .call_modifier); 9607 const modifier = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = call_modifier_ty } }, params[0], .call_modifier); 9608 const callee = try expr(gz, scope, .{ .rl = .none }, params[1]); 9609 const args = try expr(gz, scope, .{ .rl = .none }, params[2]); 9610 const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{ 9611 .modifier = modifier, 9612 .callee = callee, 9613 .args = args, 9614 .flags = .{ 9615 .is_nosuspend = gz.nosuspend_node != .none, 9616 .ensure_result_used = false, 9617 }, 9618 }); 9619 return rvalue(gz, ri, result, node); 9620 }, 9621 .field_parent_ptr => { 9622 const parent_ptr_type = try ri.rl.resultTypeForCast(gz, node, builtin_name); 9623 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .field_name); 9624 const result = try gz.addExtendedPayloadSmall(.field_parent_ptr, 0, Zir.Inst.FieldParentPtr{ 9625 .src_node = gz.nodeIndexToRelative(node), 9626 .parent_ptr_type = parent_ptr_type, 9627 .field_name = field_name, 9628 .field_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), 9629 }); 9630 return rvalue(gz, ri, result, node); 9631 }, 9632 .memcpy => { 9633 _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{ 9634 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), 9635 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]), 9636 }); 9637 return rvalue(gz, ri, .void_value, node); 9638 }, 9639 .memset => { 9640 const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]); 9641 const lhs_ty = try gz.addUnNode(.typeof, lhs, params[0]); 9642 const elem_ty = try gz.addUnNode(.indexable_ptr_elem_type, lhs_ty, params[0]); 9643 _ = try gz.addPlNode(.memset, node, Zir.Inst.Bin{ 9644 .lhs = lhs, 9645 .rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = elem_ty } }, params[1]), 9646 }); 9647 return rvalue(gz, ri, .void_value, node); 9648 }, 9649 .memmove => { 9650 _ = try gz.addPlNode(.memmove, node, Zir.Inst.Bin{ 9651 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), 9652 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]), 9653 }); 9654 return rvalue(gz, ri, .void_value, node); 9655 }, 9656 .shuffle => { 9657 const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{ 9658 .elem_type = try typeExpr(gz, scope, params[0]), 9659 .a = try expr(gz, scope, .{ .rl = .none }, params[1]), 9660 .b = try expr(gz, scope, .{ .rl = .none }, params[2]), 9661 .mask = try comptimeExpr(gz, scope, .{ .rl = .none }, params[3], .operand_shuffle_mask), 9662 }); 9663 return rvalue(gz, ri, result, node); 9664 }, 9665 .select => { 9666 const result = try gz.addExtendedPayload(.select, Zir.Inst.Select{ 9667 .node = gz.nodeIndexToRelative(node), 9668 .elem_type = try typeExpr(gz, scope, params[0]), 9669 .pred = try expr(gz, scope, .{ .rl = .none }, params[1]), 9670 .a = try expr(gz, scope, .{ .rl = .none }, params[2]), 9671 .b = try expr(gz, scope, .{ .rl = .none }, params[3]), 9672 }); 9673 return rvalue(gz, ri, result, node); 9674 }, 9675 .Vector => { 9676 const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{ 9677 .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .type), 9678 .rhs = try typeExpr(gz, scope, params[1]), 9679 }); 9680 return rvalue(gz, ri, result, node); 9681 }, 9682 .prefetch => { 9683 const prefetch_options_ty = try gz.addBuiltinValue(node, .prefetch_options); 9684 const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]); 9685 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = prefetch_options_ty } }, params[1], .prefetch_options); 9686 _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{ 9687 .node = gz.nodeIndexToRelative(node), 9688 .lhs = ptr, 9689 .rhs = options, 9690 }); 9691 return rvalue(gz, ri, .void_value, node); 9692 }, 9693 .c_va_arg => { 9694 const result = try gz.addExtendedPayload(.c_va_arg, Zir.Inst.BinNode{ 9695 .node = gz.nodeIndexToRelative(node), 9696 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), 9697 .rhs = try typeExpr(gz, scope, params[1]), 9698 }); 9699 return rvalue(gz, ri, result, node); 9700 }, 9701 .c_va_copy => { 9702 const result = try gz.addExtendedPayload(.c_va_copy, Zir.Inst.UnNode{ 9703 .node = gz.nodeIndexToRelative(node), 9704 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]), 9705 }); 9706 return rvalue(gz, ri, result, node); 9707 }, 9708 .c_va_end => { 9709 const result = try gz.addExtendedPayload(.c_va_end, Zir.Inst.UnNode{ 9710 .node = gz.nodeIndexToRelative(node), 9711 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]), 9712 }); 9713 return rvalue(gz, ri, result, node); 9714 }, 9715 .c_va_start => { 9716 if (!astgen.fn_var_args) { 9717 return astgen.failNode(node, "'@cVaStart' in a non-variadic function", .{}); 9718 } 9719 return rvalue(gz, ri, try gz.addNodeExtended(.c_va_start, node), node); 9720 }, 9721 9722 .work_item_id => { 9723 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .work_group_dim_index); 9724 const result = try gz.addExtendedPayload(.work_item_id, Zir.Inst.UnNode{ 9725 .node = gz.nodeIndexToRelative(node), 9726 .operand = operand, 9727 }); 9728 return rvalue(gz, ri, result, node); 9729 }, 9730 .work_group_size => { 9731 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .work_group_dim_index); 9732 const result = try gz.addExtendedPayload(.work_group_size, Zir.Inst.UnNode{ 9733 .node = gz.nodeIndexToRelative(node), 9734 .operand = operand, 9735 }); 9736 return rvalue(gz, ri, result, node); 9737 }, 9738 .work_group_id => { 9739 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .work_group_dim_index); 9740 const result = try gz.addExtendedPayload(.work_group_id, Zir.Inst.UnNode{ 9741 .node = gz.nodeIndexToRelative(node), 9742 .operand = operand, 9743 }); 9744 return rvalue(gz, ri, result, node); 9745 }, 9746 } 9747 } 9748 fn builtinReify( 9749 gz: *GenZir, 9750 scope: *Scope, 9751 ri: ResultInfo, 9752 node: Ast.Node.Index, 9753 arg_node: Ast.Node.Index, 9754 name_strat: Zir.Inst.NameStrategy, 9755 ) InnerError!Zir.Inst.Ref { 9756 const astgen = gz.astgen; 9757 const gpa = astgen.gpa; 9758 9759 const type_info_ty = try gz.addBuiltinValue(node, .type_info); 9760 const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = type_info_ty } }, arg_node); 9761 9762 try gz.instructions.ensureUnusedCapacity(gpa, 1); 9763 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 9764 9765 const payload_index = try astgen.addExtra(Zir.Inst.Reify{ 9766 .node = node, // Absolute node index -- see the definition of `Reify`. 9767 .operand = operand, 9768 .src_line = astgen.source_line, 9769 }); 9770 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 9771 astgen.instructions.appendAssumeCapacity(.{ 9772 .tag = .extended, 9773 .data = .{ .extended = .{ 9774 .opcode = .reify, 9775 .small = @intFromEnum(name_strat), 9776 .operand = payload_index, 9777 } }, 9778 }); 9779 gz.instructions.appendAssumeCapacity(new_index); 9780 const result = new_index.toRef(); 9781 return rvalue(gz, ri, result, node); 9782 } 9783 9784 fn hasDeclOrField( 9785 gz: *GenZir, 9786 scope: *Scope, 9787 ri: ResultInfo, 9788 node: Ast.Node.Index, 9789 lhs_node: Ast.Node.Index, 9790 rhs_node: Ast.Node.Index, 9791 tag: Zir.Inst.Tag, 9792 ) InnerError!Zir.Inst.Ref { 9793 const container_type = try typeExpr(gz, scope, lhs_node); 9794 const name = try comptimeExpr( 9795 gz, 9796 scope, 9797 .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, 9798 rhs_node, 9799 if (tag == .has_decl) .decl_name else .field_name, 9800 ); 9801 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 9802 .lhs = container_type, 9803 .rhs = name, 9804 }); 9805 return rvalue(gz, ri, result, node); 9806 } 9807 9808 fn typeCast( 9809 gz: *GenZir, 9810 scope: *Scope, 9811 ri: ResultInfo, 9812 node: Ast.Node.Index, 9813 operand_node: Ast.Node.Index, 9814 tag: Zir.Inst.Tag, 9815 builtin_name: []const u8, 9816 ) InnerError!Zir.Inst.Ref { 9817 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 9818 const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name); 9819 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node); 9820 9821 try emitDbgStmt(gz, cursor); 9822 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 9823 .lhs = result_type, 9824 .rhs = operand, 9825 }); 9826 return rvalue(gz, ri, result, node); 9827 } 9828 9829 fn simpleUnOpType( 9830 gz: *GenZir, 9831 scope: *Scope, 9832 ri: ResultInfo, 9833 node: Ast.Node.Index, 9834 operand_node: Ast.Node.Index, 9835 tag: Zir.Inst.Tag, 9836 ) InnerError!Zir.Inst.Ref { 9837 const operand = try typeExpr(gz, scope, operand_node); 9838 const result = try gz.addUnNode(tag, operand, node); 9839 return rvalue(gz, ri, result, node); 9840 } 9841 9842 fn simpleUnOp( 9843 gz: *GenZir, 9844 scope: *Scope, 9845 ri: ResultInfo, 9846 node: Ast.Node.Index, 9847 operand_ri: ResultInfo, 9848 operand_node: Ast.Node.Index, 9849 tag: Zir.Inst.Tag, 9850 ) InnerError!Zir.Inst.Ref { 9851 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 9852 const operand = if (tag == .compile_error) 9853 try comptimeExpr(gz, scope, operand_ri, operand_node, .compile_error_string) 9854 else 9855 try expr(gz, scope, operand_ri, operand_node); 9856 switch (tag) { 9857 .tag_name, .error_name, .int_from_ptr => try emitDbgStmt(gz, cursor), 9858 else => {}, 9859 } 9860 const result = try gz.addUnNode(tag, operand, node); 9861 return rvalue(gz, ri, result, node); 9862 } 9863 9864 fn negation( 9865 gz: *GenZir, 9866 scope: *Scope, 9867 ri: ResultInfo, 9868 node: Ast.Node.Index, 9869 ) InnerError!Zir.Inst.Ref { 9870 const astgen = gz.astgen; 9871 const tree = astgen.tree; 9872 9873 // Check for float literal as the sub-expression because we want to preserve 9874 // its negativity rather than having it go through comptime subtraction. 9875 const operand_node = tree.nodeData(node).node; 9876 if (tree.nodeTag(operand_node) == .number_literal) { 9877 return numberLiteral(gz, ri, operand_node, node, .negative); 9878 } 9879 9880 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node); 9881 const result = try gz.addUnNode(.negate, operand, node); 9882 return rvalue(gz, ri, result, node); 9883 } 9884 9885 fn cmpxchg( 9886 gz: *GenZir, 9887 scope: *Scope, 9888 ri: ResultInfo, 9889 node: Ast.Node.Index, 9890 params: []const Ast.Node.Index, 9891 small: u16, 9892 ) InnerError!Zir.Inst.Ref { 9893 const int_type = try typeExpr(gz, scope, params[0]); 9894 const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order); 9895 const result = try gz.addExtendedPayloadSmall(.cmpxchg, small, Zir.Inst.Cmpxchg{ 9896 // zig fmt: off 9897 .node = gz.nodeIndexToRelative(node), 9898 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), 9899 .expected_value = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]), 9900 .new_value = try expr(gz, scope, .{ .rl = .{ .coerced_ty = int_type } }, params[3]), 9901 .success_order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[4]), 9902 .failure_order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[5]), 9903 // zig fmt: on 9904 }); 9905 return rvalue(gz, ri, result, node); 9906 } 9907 9908 fn bitBuiltin( 9909 gz: *GenZir, 9910 scope: *Scope, 9911 ri: ResultInfo, 9912 node: Ast.Node.Index, 9913 operand_node: Ast.Node.Index, 9914 tag: Zir.Inst.Tag, 9915 ) InnerError!Zir.Inst.Ref { 9916 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node); 9917 const result = try gz.addUnNode(tag, operand, node); 9918 return rvalue(gz, ri, result, node); 9919 } 9920 9921 fn divBuiltin( 9922 gz: *GenZir, 9923 scope: *Scope, 9924 ri: ResultInfo, 9925 node: Ast.Node.Index, 9926 lhs_node: Ast.Node.Index, 9927 rhs_node: Ast.Node.Index, 9928 tag: Zir.Inst.Tag, 9929 ) InnerError!Zir.Inst.Ref { 9930 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 9931 const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node); 9932 const rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node); 9933 9934 try emitDbgStmt(gz, cursor); 9935 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); 9936 return rvalue(gz, ri, result, node); 9937 } 9938 9939 fn simpleCBuiltin( 9940 gz: *GenZir, 9941 scope: *Scope, 9942 ri: ResultInfo, 9943 node: Ast.Node.Index, 9944 operand_node: Ast.Node.Index, 9945 tag: Zir.Inst.Extended, 9946 ) InnerError!Zir.Inst.Ref { 9947 const name: []const u8 = if (tag == .c_undef) "C undef" else "C include"; 9948 if (!gz.c_import) return gz.astgen.failNode(node, "{s} valid only inside C import block", .{name}); 9949 const operand = try comptimeExpr( 9950 gz, 9951 scope, 9952 .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, 9953 operand_node, 9954 if (tag == .c_undef) .operand_cUndef_macro_name else .operand_cInclude_file_name, 9955 ); 9956 _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{ 9957 .node = gz.nodeIndexToRelative(node), 9958 .operand = operand, 9959 }); 9960 return rvalue(gz, ri, .void_value, node); 9961 } 9962 9963 fn offsetOf( 9964 gz: *GenZir, 9965 scope: *Scope, 9966 ri: ResultInfo, 9967 node: Ast.Node.Index, 9968 lhs_node: Ast.Node.Index, 9969 rhs_node: Ast.Node.Index, 9970 tag: Zir.Inst.Tag, 9971 ) InnerError!Zir.Inst.Ref { 9972 const type_inst = try typeExpr(gz, scope, lhs_node); 9973 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, rhs_node, .field_name); 9974 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 9975 .lhs = type_inst, 9976 .rhs = field_name, 9977 }); 9978 return rvalue(gz, ri, result, node); 9979 } 9980 9981 fn shiftOp( 9982 gz: *GenZir, 9983 scope: *Scope, 9984 ri: ResultInfo, 9985 node: Ast.Node.Index, 9986 lhs_node: Ast.Node.Index, 9987 rhs_node: Ast.Node.Index, 9988 tag: Zir.Inst.Tag, 9989 ) InnerError!Zir.Inst.Ref { 9990 const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node); 9991 9992 const cursor = switch (gz.astgen.tree.nodeTag(node)) { 9993 .shl, .shr => maybeAdvanceSourceCursorToMainToken(gz, node), 9994 else => undefined, 9995 }; 9996 9997 const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node); 9998 const rhs = try expr(gz, scope, .{ .rl = .{ .ty = log2_int_type }, .ctx = .shift_op }, rhs_node); 9999 10000 switch (gz.astgen.tree.nodeTag(node)) { 10001 .shl, .shr => try emitDbgStmt(gz, cursor), 10002 else => undefined, 10003 } 10004 10005 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 10006 .lhs = lhs, 10007 .rhs = rhs, 10008 }); 10009 return rvalue(gz, ri, result, node); 10010 } 10011 10012 fn cImport( 10013 gz: *GenZir, 10014 scope: *Scope, 10015 node: Ast.Node.Index, 10016 body_node: Ast.Node.Index, 10017 ) InnerError!Zir.Inst.Ref { 10018 const astgen = gz.astgen; 10019 const gpa = astgen.gpa; 10020 10021 if (gz.c_import) return gz.astgen.failNode(node, "cannot nest @cImport", .{}); 10022 10023 var block_scope = gz.makeSubBlock(scope); 10024 block_scope.is_comptime = true; 10025 block_scope.c_import = true; 10026 defer block_scope.unstack(); 10027 10028 const block_inst = try gz.makeBlockInst(.c_import, node); 10029 const block_result = try fullBodyExpr(&block_scope, &block_scope.base, .{ .rl = .none }, body_node, .normal); 10030 _ = try gz.addUnNode(.ensure_result_used, block_result, node); 10031 if (!gz.refIsNoReturn(block_result)) { 10032 _ = try block_scope.addBreak(.break_inline, block_inst, .void_value); 10033 } 10034 try block_scope.setBlockBody(block_inst); 10035 // block_scope unstacked now, can add new instructions to gz 10036 try gz.instructions.append(gpa, block_inst); 10037 10038 return block_inst.toRef(); 10039 } 10040 10041 fn overflowArithmetic( 10042 gz: *GenZir, 10043 scope: *Scope, 10044 ri: ResultInfo, 10045 node: Ast.Node.Index, 10046 params: []const Ast.Node.Index, 10047 tag: Zir.Inst.Extended, 10048 ) InnerError!Zir.Inst.Ref { 10049 const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]); 10050 const rhs = try expr(gz, scope, .{ .rl = .none }, params[1]); 10051 const result = try gz.addExtendedPayload(tag, Zir.Inst.BinNode{ 10052 .node = gz.nodeIndexToRelative(node), 10053 .lhs = lhs, 10054 .rhs = rhs, 10055 }); 10056 return rvalue(gz, ri, result, node); 10057 } 10058 10059 fn callExpr( 10060 gz: *GenZir, 10061 scope: *Scope, 10062 ri: ResultInfo, 10063 /// If this is not `.none` and this call is a decl literal form (`.foo(...)`), then this 10064 /// type is used as the decl literal result type instead of the result type from `ri.rl`. 10065 override_decl_literal_type: Zir.Inst.Ref, 10066 node: Ast.Node.Index, 10067 call: Ast.full.Call, 10068 ) InnerError!Zir.Inst.Ref { 10069 const astgen = gz.astgen; 10070 10071 const callee = try calleeExpr(gz, scope, ri.rl, override_decl_literal_type, call.ast.fn_expr); 10072 const modifier: std.builtin.CallModifier = blk: { 10073 if (gz.nosuspend_node != .none) { 10074 break :blk .no_suspend; 10075 } 10076 break :blk .auto; 10077 }; 10078 10079 { 10080 astgen.advanceSourceCursor(astgen.tree.tokenStart(call.ast.lparen)); 10081 const line = astgen.source_line - gz.decl_line; 10082 const column = astgen.source_column; 10083 // Sema expects a dbg_stmt immediately before call, 10084 try emitDbgStmtForceCurrentIndex(gz, .{ line, column }); 10085 } 10086 10087 switch (callee) { 10088 .direct => |obj| assert(obj != .none), 10089 .field => |field| assert(field.obj_ptr != .none), 10090 } 10091 10092 const call_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 10093 const call_inst = call_index.toRef(); 10094 try gz.astgen.instructions.append(astgen.gpa, undefined); 10095 try gz.instructions.append(astgen.gpa, call_index); 10096 10097 const scratch_top = astgen.scratch.items.len; 10098 defer astgen.scratch.items.len = scratch_top; 10099 10100 var scratch_index = scratch_top; 10101 try astgen.scratch.resize(astgen.gpa, scratch_top + call.ast.params.len); 10102 10103 for (call.ast.params) |param_node| { 10104 var arg_block = gz.makeSubBlock(scope); 10105 defer arg_block.unstack(); 10106 10107 // `call_inst` is reused to provide the param type. 10108 const arg_ref = try fullBodyExpr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node, .normal); 10109 _ = try arg_block.addBreakWithSrcNode(.break_inline, call_index, arg_ref, param_node); 10110 10111 const body = arg_block.instructionsSlice(); 10112 try astgen.scratch.ensureUnusedCapacity(astgen.gpa, countBodyLenAfterFixups(astgen, body)); 10113 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 10114 10115 astgen.scratch.items[scratch_index] = @intCast(astgen.scratch.items.len - scratch_top); 10116 scratch_index += 1; 10117 } 10118 10119 // If our result location is a try/catch/error-union-if/return, a function argument, 10120 // or an initializer for a `const` variable, the error trace propagates. 10121 // Otherwise, it should always be popped (handled in Sema). 10122 const propagate_error_trace = switch (ri.ctx) { 10123 .error_handling_expr, .@"return", .fn_arg, .const_init => true, 10124 else => false, 10125 }; 10126 10127 switch (callee) { 10128 .direct => |callee_obj| { 10129 const payload_index = try addExtra(astgen, Zir.Inst.Call{ 10130 .callee = callee_obj, 10131 .flags = .{ 10132 .pop_error_return_trace = !propagate_error_trace, 10133 .packed_modifier = @intCast(@intFromEnum(modifier)), 10134 .args_len = @intCast(call.ast.params.len), 10135 }, 10136 }); 10137 if (call.ast.params.len != 0) { 10138 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]); 10139 } 10140 gz.astgen.instructions.set(@intFromEnum(call_index), .{ 10141 .tag = .call, 10142 .data = .{ .pl_node = .{ 10143 .src_node = gz.nodeIndexToRelative(node), 10144 .payload_index = payload_index, 10145 } }, 10146 }); 10147 }, 10148 .field => |callee_field| { 10149 const payload_index = try addExtra(astgen, Zir.Inst.FieldCall{ 10150 .obj_ptr = callee_field.obj_ptr, 10151 .field_name_start = callee_field.field_name_start, 10152 .flags = .{ 10153 .pop_error_return_trace = !propagate_error_trace, 10154 .packed_modifier = @intCast(@intFromEnum(modifier)), 10155 .args_len = @intCast(call.ast.params.len), 10156 }, 10157 }); 10158 if (call.ast.params.len != 0) { 10159 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]); 10160 } 10161 gz.astgen.instructions.set(@intFromEnum(call_index), .{ 10162 .tag = .field_call, 10163 .data = .{ .pl_node = .{ 10164 .src_node = gz.nodeIndexToRelative(node), 10165 .payload_index = payload_index, 10166 } }, 10167 }); 10168 }, 10169 } 10170 return rvalue(gz, ri, call_inst, node); // TODO function call with result location 10171 } 10172 10173 const Callee = union(enum) { 10174 field: struct { 10175 /// A *pointer* to the object the field is fetched on, so that we can 10176 /// promote the lvalue to an address if the first parameter requires it. 10177 obj_ptr: Zir.Inst.Ref, 10178 /// Offset into `string_bytes`. 10179 field_name_start: Zir.NullTerminatedString, 10180 }, 10181 direct: Zir.Inst.Ref, 10182 }; 10183 10184 /// calleeExpr generates the function part of a call expression (f in f(x)), but 10185 /// *not* the callee argument to the @call() builtin. Its purpose is to 10186 /// distinguish between standard calls and method call syntax `a.b()`. Thus, if 10187 /// the lhs is a field access, we return using the `field` union field; 10188 /// otherwise, we use the `direct` union field. 10189 fn calleeExpr( 10190 gz: *GenZir, 10191 scope: *Scope, 10192 call_rl: ResultInfo.Loc, 10193 /// If this is not `.none` and this call is a decl literal form (`.foo(...)`), then this 10194 /// type is used as the decl literal result type instead of the result type from `call_rl`. 10195 override_decl_literal_type: Zir.Inst.Ref, 10196 node: Ast.Node.Index, 10197 ) InnerError!Callee { 10198 const astgen = gz.astgen; 10199 const tree = astgen.tree; 10200 10201 const tag = tree.nodeTag(node); 10202 switch (tag) { 10203 .field_access => { 10204 const object_node, const field_ident = tree.nodeData(node).node_and_token; 10205 const str_index = try astgen.identAsString(field_ident); 10206 // Capture the object by reference so we can promote it to an 10207 // address in Sema if needed. 10208 const lhs = try expr(gz, scope, .{ .rl = .ref }, object_node); 10209 10210 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 10211 try emitDbgStmt(gz, cursor); 10212 10213 return .{ .field = .{ 10214 .obj_ptr = lhs, 10215 .field_name_start = str_index, 10216 } }; 10217 }, 10218 .enum_literal => { 10219 const res_ty = res_ty: { 10220 if (override_decl_literal_type != .none) break :res_ty override_decl_literal_type; 10221 break :res_ty try call_rl.resultType(gz, node) orelse { 10222 // No result type; lower to a literal call of an enum literal. 10223 return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) }; 10224 }; 10225 }; 10226 // Decl literal call syntax, e.g. 10227 // `const foo: T = .init();` 10228 // Look up `init` in `T`, but don't try and coerce it. 10229 const str_index = try astgen.identAsString(tree.nodeMainToken(node)); 10230 const callee = try gz.addPlNode(.decl_literal_no_coerce, node, Zir.Inst.Field{ 10231 .lhs = res_ty, 10232 .field_name_start = str_index, 10233 }); 10234 return .{ .direct = callee }; 10235 }, 10236 else => return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) }, 10237 } 10238 } 10239 10240 const primitive_instrs = std.StaticStringMap(Zir.Inst.Ref).initComptime(.{ 10241 .{ "anyerror", .anyerror_type }, 10242 .{ "anyframe", .anyframe_type }, 10243 .{ "anyopaque", .anyopaque_type }, 10244 .{ "bool", .bool_type }, 10245 .{ "c_int", .c_int_type }, 10246 .{ "c_long", .c_long_type }, 10247 .{ "c_longdouble", .c_longdouble_type }, 10248 .{ "c_longlong", .c_longlong_type }, 10249 .{ "c_char", .c_char_type }, 10250 .{ "c_short", .c_short_type }, 10251 .{ "c_uint", .c_uint_type }, 10252 .{ "c_ulong", .c_ulong_type }, 10253 .{ "c_ulonglong", .c_ulonglong_type }, 10254 .{ "c_ushort", .c_ushort_type }, 10255 .{ "comptime_float", .comptime_float_type }, 10256 .{ "comptime_int", .comptime_int_type }, 10257 .{ "f128", .f128_type }, 10258 .{ "f16", .f16_type }, 10259 .{ "f32", .f32_type }, 10260 .{ "f64", .f64_type }, 10261 .{ "f80", .f80_type }, 10262 .{ "false", .bool_false }, 10263 .{ "i16", .i16_type }, 10264 .{ "i32", .i32_type }, 10265 .{ "i64", .i64_type }, 10266 .{ "i128", .i128_type }, 10267 .{ "i8", .i8_type }, 10268 .{ "isize", .isize_type }, 10269 .{ "noreturn", .noreturn_type }, 10270 .{ "null", .null_value }, 10271 .{ "true", .bool_true }, 10272 .{ "type", .type_type }, 10273 .{ "u16", .u16_type }, 10274 .{ "u29", .u29_type }, 10275 .{ "u32", .u32_type }, 10276 .{ "u64", .u64_type }, 10277 .{ "u128", .u128_type }, 10278 .{ "u1", .u1_type }, 10279 .{ "u8", .u8_type }, 10280 .{ "undefined", .undef }, 10281 .{ "usize", .usize_type }, 10282 .{ "void", .void_type }, 10283 }); 10284 10285 comptime { 10286 // These checks ensure that std.zig.primitives stays in sync with the primitive->Zir map. 10287 const primitives = std.zig.primitives; 10288 for (primitive_instrs.keys(), primitive_instrs.values()) |key, value| { 10289 if (!primitives.isPrimitive(key)) { 10290 @compileError("std.zig.isPrimitive() is not aware of Zir instr '" ++ @tagName(value) ++ "'"); 10291 } 10292 } 10293 for (primitives.names.keys()) |key| { 10294 if (primitive_instrs.get(key) == null) { 10295 @compileError("std.zig.primitives entry '" ++ key ++ "' does not have a corresponding Zir instr"); 10296 } 10297 } 10298 } 10299 10300 fn nodeIsTriviallyZero(tree: *const Ast, node: Ast.Node.Index) bool { 10301 switch (tree.nodeTag(node)) { 10302 .number_literal => { 10303 const ident = tree.nodeMainToken(node); 10304 return switch (std.zig.parseNumberLiteral(tree.tokenSlice(ident))) { 10305 .int => |number| switch (number) { 10306 0 => true, 10307 else => false, 10308 }, 10309 else => false, 10310 }; 10311 }, 10312 else => return false, 10313 } 10314 } 10315 10316 fn nodeMayAppendToErrorTrace(tree: *const Ast, start_node: Ast.Node.Index) bool { 10317 var node = start_node; 10318 while (true) { 10319 switch (tree.nodeTag(node)) { 10320 // These don't have the opportunity to call any runtime functions. 10321 .error_value, 10322 .identifier, 10323 .@"comptime", 10324 => return false, 10325 10326 // Forward the question to the LHS sub-expression. 10327 .@"try", 10328 .@"nosuspend", 10329 => node = tree.nodeData(node).node, 10330 .grouped_expression, 10331 .unwrap_optional, 10332 => node = tree.nodeData(node).node_and_token[0], 10333 10334 // Anything that does not eval to an error is guaranteed to pop any 10335 // additions to the error trace, so it effectively does not append. 10336 else => return nodeMayEvalToError(tree, start_node) != .never, 10337 } 10338 } 10339 } 10340 10341 fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.EvalToError { 10342 var node = start_node; 10343 while (true) { 10344 switch (tree.nodeTag(node)) { 10345 .root, 10346 .test_decl, 10347 .switch_case, 10348 .switch_case_inline, 10349 .switch_case_one, 10350 .switch_case_inline_one, 10351 .container_field_init, 10352 .container_field_align, 10353 .container_field, 10354 .asm_output, 10355 .asm_input, 10356 => unreachable, 10357 10358 .error_value => return .always, 10359 10360 .@"asm", 10361 .asm_simple, 10362 .asm_legacy, 10363 .identifier, 10364 .field_access, 10365 .deref, 10366 .array_access, 10367 .while_simple, 10368 .while_cont, 10369 .for_simple, 10370 .if_simple, 10371 .@"while", 10372 .@"if", 10373 .@"for", 10374 .@"switch", 10375 .switch_comma, 10376 .call_one, 10377 .call_one_comma, 10378 .call, 10379 .call_comma, 10380 => return .maybe, 10381 10382 .@"return", 10383 .@"break", 10384 .@"continue", 10385 .bit_not, 10386 .bool_not, 10387 .global_var_decl, 10388 .local_var_decl, 10389 .simple_var_decl, 10390 .aligned_var_decl, 10391 .@"defer", 10392 .@"errdefer", 10393 .address_of, 10394 .optional_type, 10395 .negation, 10396 .negation_wrap, 10397 .@"resume", 10398 .array_type, 10399 .array_type_sentinel, 10400 .ptr_type_aligned, 10401 .ptr_type_sentinel, 10402 .ptr_type, 10403 .ptr_type_bit_range, 10404 .@"suspend", 10405 .fn_proto_simple, 10406 .fn_proto_multi, 10407 .fn_proto_one, 10408 .fn_proto, 10409 .fn_decl, 10410 .anyframe_type, 10411 .anyframe_literal, 10412 .number_literal, 10413 .enum_literal, 10414 .string_literal, 10415 .multiline_string_literal, 10416 .char_literal, 10417 .unreachable_literal, 10418 .error_set_decl, 10419 .container_decl, 10420 .container_decl_trailing, 10421 .container_decl_two, 10422 .container_decl_two_trailing, 10423 .container_decl_arg, 10424 .container_decl_arg_trailing, 10425 .tagged_union, 10426 .tagged_union_trailing, 10427 .tagged_union_two, 10428 .tagged_union_two_trailing, 10429 .tagged_union_enum_tag, 10430 .tagged_union_enum_tag_trailing, 10431 .add, 10432 .add_wrap, 10433 .add_sat, 10434 .array_cat, 10435 .array_mult, 10436 .assign, 10437 .assign_destructure, 10438 .assign_bit_and, 10439 .assign_bit_or, 10440 .assign_shl, 10441 .assign_shl_sat, 10442 .assign_shr, 10443 .assign_bit_xor, 10444 .assign_div, 10445 .assign_sub, 10446 .assign_sub_wrap, 10447 .assign_sub_sat, 10448 .assign_mod, 10449 .assign_add, 10450 .assign_add_wrap, 10451 .assign_add_sat, 10452 .assign_mul, 10453 .assign_mul_wrap, 10454 .assign_mul_sat, 10455 .bang_equal, 10456 .bit_and, 10457 .bit_or, 10458 .shl, 10459 .shl_sat, 10460 .shr, 10461 .bit_xor, 10462 .bool_and, 10463 .bool_or, 10464 .div, 10465 .equal_equal, 10466 .error_union, 10467 .greater_or_equal, 10468 .greater_than, 10469 .less_or_equal, 10470 .less_than, 10471 .merge_error_sets, 10472 .mod, 10473 .mul, 10474 .mul_wrap, 10475 .mul_sat, 10476 .switch_range, 10477 .for_range, 10478 .sub, 10479 .sub_wrap, 10480 .sub_sat, 10481 .slice, 10482 .slice_open, 10483 .slice_sentinel, 10484 .array_init_one, 10485 .array_init_one_comma, 10486 .array_init_dot_two, 10487 .array_init_dot_two_comma, 10488 .array_init_dot, 10489 .array_init_dot_comma, 10490 .array_init, 10491 .array_init_comma, 10492 .struct_init_one, 10493 .struct_init_one_comma, 10494 .struct_init_dot_two, 10495 .struct_init_dot_two_comma, 10496 .struct_init_dot, 10497 .struct_init_dot_comma, 10498 .struct_init, 10499 .struct_init_comma, 10500 => return .never, 10501 10502 // Forward the question to the LHS sub-expression. 10503 .@"try", 10504 .@"comptime", 10505 .@"nosuspend", 10506 => node = tree.nodeData(node).node, 10507 .grouped_expression, 10508 .unwrap_optional, 10509 => node = tree.nodeData(node).node_and_token[0], 10510 10511 // LHS sub-expression may still be an error under the outer optional or error union 10512 .@"catch", 10513 .@"orelse", 10514 => return .maybe, 10515 10516 .block_two, 10517 .block_two_semicolon, 10518 .block, 10519 .block_semicolon, 10520 => { 10521 const lbrace = tree.nodeMainToken(node); 10522 if (tree.tokenTag(lbrace - 1) == .colon) { 10523 // Labeled blocks may need a memory location to forward 10524 // to their break statements. 10525 return .maybe; 10526 } else { 10527 return .never; 10528 } 10529 }, 10530 10531 .builtin_call, 10532 .builtin_call_comma, 10533 .builtin_call_two, 10534 .builtin_call_two_comma, 10535 => { 10536 const builtin_token = tree.nodeMainToken(node); 10537 const builtin_name = tree.tokenSlice(builtin_token); 10538 // If the builtin is an invalid name, we don't cause an error here; instead 10539 // let it pass, and the error will be "invalid builtin function" later. 10540 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return .maybe; 10541 return builtin_info.eval_to_error; 10542 }, 10543 } 10544 } 10545 } 10546 10547 /// Returns `true` if it is known the type expression has more than one possible value; 10548 /// `false` otherwise. 10549 fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.Index) bool { 10550 var node = start_node; 10551 while (true) { 10552 switch (tree.nodeTag(node)) { 10553 .root, 10554 .test_decl, 10555 .switch_case, 10556 .switch_case_inline, 10557 .switch_case_one, 10558 .switch_case_inline_one, 10559 .container_field_init, 10560 .container_field_align, 10561 .container_field, 10562 .asm_output, 10563 .asm_input, 10564 .global_var_decl, 10565 .local_var_decl, 10566 .simple_var_decl, 10567 .aligned_var_decl, 10568 => unreachable, 10569 10570 .@"return", 10571 .@"break", 10572 .@"continue", 10573 .bit_not, 10574 .bool_not, 10575 .@"defer", 10576 .@"errdefer", 10577 .address_of, 10578 .negation, 10579 .negation_wrap, 10580 .@"resume", 10581 .array_type, 10582 .@"suspend", 10583 .fn_decl, 10584 .anyframe_literal, 10585 .number_literal, 10586 .enum_literal, 10587 .string_literal, 10588 .multiline_string_literal, 10589 .char_literal, 10590 .unreachable_literal, 10591 .error_set_decl, 10592 .container_decl, 10593 .container_decl_trailing, 10594 .container_decl_two, 10595 .container_decl_two_trailing, 10596 .container_decl_arg, 10597 .container_decl_arg_trailing, 10598 .tagged_union, 10599 .tagged_union_trailing, 10600 .tagged_union_two, 10601 .tagged_union_two_trailing, 10602 .tagged_union_enum_tag, 10603 .tagged_union_enum_tag_trailing, 10604 .@"asm", 10605 .asm_simple, 10606 .asm_legacy, 10607 .add, 10608 .add_wrap, 10609 .add_sat, 10610 .array_cat, 10611 .array_mult, 10612 .assign, 10613 .assign_destructure, 10614 .assign_bit_and, 10615 .assign_bit_or, 10616 .assign_shl, 10617 .assign_shl_sat, 10618 .assign_shr, 10619 .assign_bit_xor, 10620 .assign_div, 10621 .assign_sub, 10622 .assign_sub_wrap, 10623 .assign_sub_sat, 10624 .assign_mod, 10625 .assign_add, 10626 .assign_add_wrap, 10627 .assign_add_sat, 10628 .assign_mul, 10629 .assign_mul_wrap, 10630 .assign_mul_sat, 10631 .bang_equal, 10632 .bit_and, 10633 .bit_or, 10634 .shl, 10635 .shl_sat, 10636 .shr, 10637 .bit_xor, 10638 .bool_and, 10639 .bool_or, 10640 .div, 10641 .equal_equal, 10642 .error_union, 10643 .greater_or_equal, 10644 .greater_than, 10645 .less_or_equal, 10646 .less_than, 10647 .merge_error_sets, 10648 .mod, 10649 .mul, 10650 .mul_wrap, 10651 .mul_sat, 10652 .switch_range, 10653 .for_range, 10654 .field_access, 10655 .sub, 10656 .sub_wrap, 10657 .sub_sat, 10658 .slice, 10659 .slice_open, 10660 .slice_sentinel, 10661 .deref, 10662 .array_access, 10663 .error_value, 10664 .while_simple, 10665 .while_cont, 10666 .for_simple, 10667 .if_simple, 10668 .@"catch", 10669 .@"orelse", 10670 .array_init_one, 10671 .array_init_one_comma, 10672 .array_init_dot_two, 10673 .array_init_dot_two_comma, 10674 .array_init_dot, 10675 .array_init_dot_comma, 10676 .array_init, 10677 .array_init_comma, 10678 .struct_init_one, 10679 .struct_init_one_comma, 10680 .struct_init_dot_two, 10681 .struct_init_dot_two_comma, 10682 .struct_init_dot, 10683 .struct_init_dot_comma, 10684 .struct_init, 10685 .struct_init_comma, 10686 .@"while", 10687 .@"if", 10688 .@"for", 10689 .@"switch", 10690 .switch_comma, 10691 .call_one, 10692 .call_one_comma, 10693 .call, 10694 .call_comma, 10695 .block_two, 10696 .block_two_semicolon, 10697 .block, 10698 .block_semicolon, 10699 .builtin_call, 10700 .builtin_call_comma, 10701 .builtin_call_two, 10702 .builtin_call_two_comma, 10703 // these are function bodies, not pointers 10704 .fn_proto_simple, 10705 .fn_proto_multi, 10706 .fn_proto_one, 10707 .fn_proto, 10708 => return false, 10709 10710 // Forward the question to the LHS sub-expression. 10711 .@"try", 10712 .@"comptime", 10713 .@"nosuspend", 10714 => node = tree.nodeData(node).node, 10715 .grouped_expression, 10716 .unwrap_optional, 10717 => node = tree.nodeData(node).node_and_token[0], 10718 10719 .ptr_type_aligned, 10720 .ptr_type_sentinel, 10721 .ptr_type, 10722 .ptr_type_bit_range, 10723 .optional_type, 10724 .anyframe_type, 10725 .array_type_sentinel, 10726 => return true, 10727 10728 .identifier => { 10729 const ident_bytes = tree.tokenSlice(tree.nodeMainToken(node)); 10730 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) { 10731 .anyerror_type, 10732 .anyframe_type, 10733 .anyopaque_type, 10734 .bool_type, 10735 .c_int_type, 10736 .c_long_type, 10737 .c_longdouble_type, 10738 .c_longlong_type, 10739 .c_char_type, 10740 .c_short_type, 10741 .c_uint_type, 10742 .c_ulong_type, 10743 .c_ulonglong_type, 10744 .c_ushort_type, 10745 .comptime_float_type, 10746 .comptime_int_type, 10747 .f16_type, 10748 .f32_type, 10749 .f64_type, 10750 .f80_type, 10751 .f128_type, 10752 .i16_type, 10753 .i32_type, 10754 .i64_type, 10755 .i128_type, 10756 .i8_type, 10757 .isize_type, 10758 .type_type, 10759 .u16_type, 10760 .u29_type, 10761 .u32_type, 10762 .u64_type, 10763 .u128_type, 10764 .u1_type, 10765 .u8_type, 10766 .usize_type, 10767 => return true, 10768 10769 .void_type, 10770 .bool_false, 10771 .bool_true, 10772 .null_value, 10773 .undef, 10774 .noreturn_type, 10775 => return false, 10776 10777 else => unreachable, // that's all the values from `primitives`. 10778 } else { 10779 return false; 10780 } 10781 }, 10782 } 10783 } 10784 } 10785 10786 /// Returns `true` if it is known the expression is a type that cannot be used at runtime; 10787 /// `false` otherwise. 10788 fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { 10789 var node = start_node; 10790 while (true) { 10791 switch (tree.nodeTag(node)) { 10792 .root, 10793 .test_decl, 10794 .switch_case, 10795 .switch_case_inline, 10796 .switch_case_one, 10797 .switch_case_inline_one, 10798 .container_field_init, 10799 .container_field_align, 10800 .container_field, 10801 .asm_output, 10802 .asm_input, 10803 .global_var_decl, 10804 .local_var_decl, 10805 .simple_var_decl, 10806 .aligned_var_decl, 10807 => unreachable, 10808 10809 .@"return", 10810 .@"break", 10811 .@"continue", 10812 .bit_not, 10813 .bool_not, 10814 .@"defer", 10815 .@"errdefer", 10816 .address_of, 10817 .negation, 10818 .negation_wrap, 10819 .@"resume", 10820 .array_type, 10821 .@"suspend", 10822 .fn_decl, 10823 .anyframe_literal, 10824 .number_literal, 10825 .enum_literal, 10826 .string_literal, 10827 .multiline_string_literal, 10828 .char_literal, 10829 .unreachable_literal, 10830 .error_set_decl, 10831 .container_decl, 10832 .container_decl_trailing, 10833 .container_decl_two, 10834 .container_decl_two_trailing, 10835 .container_decl_arg, 10836 .container_decl_arg_trailing, 10837 .tagged_union, 10838 .tagged_union_trailing, 10839 .tagged_union_two, 10840 .tagged_union_two_trailing, 10841 .tagged_union_enum_tag, 10842 .tagged_union_enum_tag_trailing, 10843 .@"asm", 10844 .asm_simple, 10845 .asm_legacy, 10846 .add, 10847 .add_wrap, 10848 .add_sat, 10849 .array_cat, 10850 .array_mult, 10851 .assign, 10852 .assign_destructure, 10853 .assign_bit_and, 10854 .assign_bit_or, 10855 .assign_shl, 10856 .assign_shl_sat, 10857 .assign_shr, 10858 .assign_bit_xor, 10859 .assign_div, 10860 .assign_sub, 10861 .assign_sub_wrap, 10862 .assign_sub_sat, 10863 .assign_mod, 10864 .assign_add, 10865 .assign_add_wrap, 10866 .assign_add_sat, 10867 .assign_mul, 10868 .assign_mul_wrap, 10869 .assign_mul_sat, 10870 .bang_equal, 10871 .bit_and, 10872 .bit_or, 10873 .shl, 10874 .shl_sat, 10875 .shr, 10876 .bit_xor, 10877 .bool_and, 10878 .bool_or, 10879 .div, 10880 .equal_equal, 10881 .error_union, 10882 .greater_or_equal, 10883 .greater_than, 10884 .less_or_equal, 10885 .less_than, 10886 .merge_error_sets, 10887 .mod, 10888 .mul, 10889 .mul_wrap, 10890 .mul_sat, 10891 .switch_range, 10892 .for_range, 10893 .field_access, 10894 .sub, 10895 .sub_wrap, 10896 .sub_sat, 10897 .slice, 10898 .slice_open, 10899 .slice_sentinel, 10900 .deref, 10901 .array_access, 10902 .error_value, 10903 .while_simple, 10904 .while_cont, 10905 .for_simple, 10906 .if_simple, 10907 .@"catch", 10908 .@"orelse", 10909 .array_init_one, 10910 .array_init_one_comma, 10911 .array_init_dot_two, 10912 .array_init_dot_two_comma, 10913 .array_init_dot, 10914 .array_init_dot_comma, 10915 .array_init, 10916 .array_init_comma, 10917 .struct_init_one, 10918 .struct_init_one_comma, 10919 .struct_init_dot_two, 10920 .struct_init_dot_two_comma, 10921 .struct_init_dot, 10922 .struct_init_dot_comma, 10923 .struct_init, 10924 .struct_init_comma, 10925 .@"while", 10926 .@"if", 10927 .@"for", 10928 .@"switch", 10929 .switch_comma, 10930 .call_one, 10931 .call_one_comma, 10932 .call, 10933 .call_comma, 10934 .block_two, 10935 .block_two_semicolon, 10936 .block, 10937 .block_semicolon, 10938 .builtin_call, 10939 .builtin_call_comma, 10940 .builtin_call_two, 10941 .builtin_call_two_comma, 10942 .ptr_type_aligned, 10943 .ptr_type_sentinel, 10944 .ptr_type, 10945 .ptr_type_bit_range, 10946 .optional_type, 10947 .anyframe_type, 10948 .array_type_sentinel, 10949 => return false, 10950 10951 // these are function bodies, not pointers 10952 .fn_proto_simple, 10953 .fn_proto_multi, 10954 .fn_proto_one, 10955 .fn_proto, 10956 => return true, 10957 10958 // Forward the question to the LHS sub-expression. 10959 .@"try", 10960 .@"comptime", 10961 .@"nosuspend", 10962 => node = tree.nodeData(node).node, 10963 .grouped_expression, 10964 .unwrap_optional, 10965 => node = tree.nodeData(node).node_and_token[0], 10966 10967 .identifier => { 10968 const ident_bytes = tree.tokenSlice(tree.nodeMainToken(node)); 10969 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) { 10970 .anyerror_type, 10971 .anyframe_type, 10972 .anyopaque_type, 10973 .bool_type, 10974 .c_int_type, 10975 .c_long_type, 10976 .c_longdouble_type, 10977 .c_longlong_type, 10978 .c_char_type, 10979 .c_short_type, 10980 .c_uint_type, 10981 .c_ulong_type, 10982 .c_ulonglong_type, 10983 .c_ushort_type, 10984 .f16_type, 10985 .f32_type, 10986 .f64_type, 10987 .f80_type, 10988 .f128_type, 10989 .i16_type, 10990 .i32_type, 10991 .i64_type, 10992 .i128_type, 10993 .i8_type, 10994 .isize_type, 10995 .u16_type, 10996 .u29_type, 10997 .u32_type, 10998 .u64_type, 10999 .u128_type, 11000 .u1_type, 11001 .u8_type, 11002 .usize_type, 11003 .void_type, 11004 .bool_false, 11005 .bool_true, 11006 .null_value, 11007 .undef, 11008 .noreturn_type, 11009 => return false, 11010 11011 .comptime_float_type, 11012 .comptime_int_type, 11013 .type_type, 11014 => return true, 11015 11016 else => unreachable, // that's all the values from `primitives`. 11017 } else { 11018 return false; 11019 } 11020 }, 11021 } 11022 } 11023 } 11024 11025 /// Applies `rl` semantics to `result`. Expressions which do not do their own handling of 11026 /// result locations must call this function on their result. 11027 /// As an example, if `ri.rl` is `.ptr`, it will write the result to the pointer. 11028 /// If `ri.rl` is `.ty`, it will coerce the result to the type. 11029 /// Assumes nothing stacked on `gz`. 11030 fn rvalue( 11031 gz: *GenZir, 11032 ri: ResultInfo, 11033 raw_result: Zir.Inst.Ref, 11034 src_node: Ast.Node.Index, 11035 ) InnerError!Zir.Inst.Ref { 11036 return rvalueInner(gz, ri, raw_result, src_node, true); 11037 } 11038 11039 /// Like `rvalue`, but refuses to perform coercions before taking references for 11040 /// the `ref_coerced_ty` result type. This is used for local variables which do 11041 /// not have `alloc`s, because we want variables to have consistent addresses, 11042 /// i.e. we want them to act like lvalues. 11043 fn rvalueNoCoercePreRef( 11044 gz: *GenZir, 11045 ri: ResultInfo, 11046 raw_result: Zir.Inst.Ref, 11047 src_node: Ast.Node.Index, 11048 ) InnerError!Zir.Inst.Ref { 11049 return rvalueInner(gz, ri, raw_result, src_node, false); 11050 } 11051 11052 fn rvalueInner( 11053 gz: *GenZir, 11054 ri: ResultInfo, 11055 raw_result: Zir.Inst.Ref, 11056 src_node: Ast.Node.Index, 11057 allow_coerce_pre_ref: bool, 11058 ) InnerError!Zir.Inst.Ref { 11059 const result = r: { 11060 if (raw_result.toIndex()) |result_index| { 11061 const zir_tags = gz.astgen.instructions.items(.tag); 11062 const data = gz.astgen.instructions.items(.data)[@intFromEnum(result_index)]; 11063 if (zir_tags[@intFromEnum(result_index)].isAlwaysVoid(data)) { 11064 break :r Zir.Inst.Ref.void_value; 11065 } 11066 } 11067 break :r raw_result; 11068 }; 11069 if (gz.endsWithNoReturn()) return result; 11070 switch (ri.rl) { 11071 .none, .coerced_ty => return result, 11072 .discard => { 11073 // Emit a compile error for discarding error values. 11074 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); 11075 return .void_value; 11076 }, 11077 .ref, .ref_coerced_ty => { 11078 const coerced_result = if (allow_coerce_pre_ref and ri.rl == .ref_coerced_ty) res: { 11079 const ptr_ty = ri.rl.ref_coerced_ty; 11080 break :res try gz.addPlNode(.coerce_ptr_elem_ty, src_node, Zir.Inst.Bin{ 11081 .lhs = ptr_ty, 11082 .rhs = result, 11083 }); 11084 } else result; 11085 // We need a pointer but we have a value. 11086 // Unfortunately it's not quite as simple as directly emitting a ref 11087 // instruction here because we need subsequent address-of operator on 11088 // const locals to return the same address. 11089 const astgen = gz.astgen; 11090 const tree = astgen.tree; 11091 const src_token = tree.firstToken(src_node); 11092 const result_index = coerced_result.toIndex() orelse 11093 return gz.addUnTok(.ref, coerced_result, src_token); 11094 const gop = try astgen.ref_table.getOrPut(astgen.gpa, result_index); 11095 if (!gop.found_existing) { 11096 gop.value_ptr.* = try gz.makeUnTok(.ref, coerced_result, src_token); 11097 } 11098 return gop.value_ptr.*.toRef(); 11099 }, 11100 .ty => |ty_inst| { 11101 // Quickly eliminate some common, unnecessary type coercion. 11102 const as_ty = @as(u64, @intFromEnum(Zir.Inst.Ref.type_type)) << 32; 11103 const as_bool = @as(u64, @intFromEnum(Zir.Inst.Ref.bool_type)) << 32; 11104 const as_void = @as(u64, @intFromEnum(Zir.Inst.Ref.void_type)) << 32; 11105 const as_comptime_int = @as(u64, @intFromEnum(Zir.Inst.Ref.comptime_int_type)) << 32; 11106 const as_usize = @as(u64, @intFromEnum(Zir.Inst.Ref.usize_type)) << 32; 11107 const as_u1 = @as(u64, @intFromEnum(Zir.Inst.Ref.u1_type)) << 32; 11108 const as_u8 = @as(u64, @intFromEnum(Zir.Inst.Ref.u8_type)) << 32; 11109 switch ((@as(u64, @intFromEnum(ty_inst)) << 32) | @as(u64, @intFromEnum(result))) { 11110 as_ty | @intFromEnum(Zir.Inst.Ref.u1_type), 11111 as_ty | @intFromEnum(Zir.Inst.Ref.u8_type), 11112 as_ty | @intFromEnum(Zir.Inst.Ref.i8_type), 11113 as_ty | @intFromEnum(Zir.Inst.Ref.u16_type), 11114 as_ty | @intFromEnum(Zir.Inst.Ref.u29_type), 11115 as_ty | @intFromEnum(Zir.Inst.Ref.i16_type), 11116 as_ty | @intFromEnum(Zir.Inst.Ref.u32_type), 11117 as_ty | @intFromEnum(Zir.Inst.Ref.i32_type), 11118 as_ty | @intFromEnum(Zir.Inst.Ref.u64_type), 11119 as_ty | @intFromEnum(Zir.Inst.Ref.i64_type), 11120 as_ty | @intFromEnum(Zir.Inst.Ref.u128_type), 11121 as_ty | @intFromEnum(Zir.Inst.Ref.i128_type), 11122 as_ty | @intFromEnum(Zir.Inst.Ref.usize_type), 11123 as_ty | @intFromEnum(Zir.Inst.Ref.isize_type), 11124 as_ty | @intFromEnum(Zir.Inst.Ref.c_char_type), 11125 as_ty | @intFromEnum(Zir.Inst.Ref.c_short_type), 11126 as_ty | @intFromEnum(Zir.Inst.Ref.c_ushort_type), 11127 as_ty | @intFromEnum(Zir.Inst.Ref.c_int_type), 11128 as_ty | @intFromEnum(Zir.Inst.Ref.c_uint_type), 11129 as_ty | @intFromEnum(Zir.Inst.Ref.c_long_type), 11130 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulong_type), 11131 as_ty | @intFromEnum(Zir.Inst.Ref.c_longlong_type), 11132 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulonglong_type), 11133 as_ty | @intFromEnum(Zir.Inst.Ref.c_longdouble_type), 11134 as_ty | @intFromEnum(Zir.Inst.Ref.f16_type), 11135 as_ty | @intFromEnum(Zir.Inst.Ref.f32_type), 11136 as_ty | @intFromEnum(Zir.Inst.Ref.f64_type), 11137 as_ty | @intFromEnum(Zir.Inst.Ref.f80_type), 11138 as_ty | @intFromEnum(Zir.Inst.Ref.f128_type), 11139 as_ty | @intFromEnum(Zir.Inst.Ref.anyopaque_type), 11140 as_ty | @intFromEnum(Zir.Inst.Ref.bool_type), 11141 as_ty | @intFromEnum(Zir.Inst.Ref.void_type), 11142 as_ty | @intFromEnum(Zir.Inst.Ref.type_type), 11143 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_type), 11144 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_int_type), 11145 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_float_type), 11146 as_ty | @intFromEnum(Zir.Inst.Ref.noreturn_type), 11147 as_ty | @intFromEnum(Zir.Inst.Ref.anyframe_type), 11148 as_ty | @intFromEnum(Zir.Inst.Ref.null_type), 11149 as_ty | @intFromEnum(Zir.Inst.Ref.undefined_type), 11150 as_ty | @intFromEnum(Zir.Inst.Ref.enum_literal_type), 11151 as_ty | @intFromEnum(Zir.Inst.Ref.ptr_usize_type), 11152 as_ty | @intFromEnum(Zir.Inst.Ref.ptr_const_comptime_int_type), 11153 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_u8_type), 11154 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_type), 11155 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_sentinel_0_type), 11156 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_type), 11157 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_sentinel_0_type), 11158 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_void_error_union_type), 11159 as_ty | @intFromEnum(Zir.Inst.Ref.generic_poison_type), 11160 as_ty | @intFromEnum(Zir.Inst.Ref.empty_tuple_type), 11161 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero), 11162 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one), 11163 as_comptime_int | @intFromEnum(Zir.Inst.Ref.negative_one), 11164 as_usize | @intFromEnum(Zir.Inst.Ref.undef_usize), 11165 as_usize | @intFromEnum(Zir.Inst.Ref.zero_usize), 11166 as_usize | @intFromEnum(Zir.Inst.Ref.one_usize), 11167 as_u1 | @intFromEnum(Zir.Inst.Ref.undef_u1), 11168 as_u1 | @intFromEnum(Zir.Inst.Ref.zero_u1), 11169 as_u1 | @intFromEnum(Zir.Inst.Ref.one_u1), 11170 as_u8 | @intFromEnum(Zir.Inst.Ref.zero_u8), 11171 as_u8 | @intFromEnum(Zir.Inst.Ref.one_u8), 11172 as_u8 | @intFromEnum(Zir.Inst.Ref.four_u8), 11173 as_bool | @intFromEnum(Zir.Inst.Ref.undef_bool), 11174 as_bool | @intFromEnum(Zir.Inst.Ref.bool_true), 11175 as_bool | @intFromEnum(Zir.Inst.Ref.bool_false), 11176 as_void | @intFromEnum(Zir.Inst.Ref.void_value), 11177 => return result, // type of result is already correct 11178 11179 as_bool | @intFromEnum(Zir.Inst.Ref.undef) => return .undef_bool, 11180 as_usize | @intFromEnum(Zir.Inst.Ref.undef) => return .undef_usize, 11181 as_usize | @intFromEnum(Zir.Inst.Ref.undef_u1) => return .undef_usize, 11182 as_u1 | @intFromEnum(Zir.Inst.Ref.undef) => return .undef_u1, 11183 11184 as_usize | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_usize, 11185 as_u1 | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_u1, 11186 as_u8 | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_u8, 11187 as_usize | @intFromEnum(Zir.Inst.Ref.one) => return .one_usize, 11188 as_u1 | @intFromEnum(Zir.Inst.Ref.one) => return .one_u1, 11189 as_u8 | @intFromEnum(Zir.Inst.Ref.one) => return .one_u8, 11190 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero, 11191 as_u1 | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero_u1, 11192 as_u8 | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero_u8, 11193 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one, 11194 as_u1 | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one_u1, 11195 as_u8 | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one_u8, 11196 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_u1) => return .zero, 11197 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_u8) => return .zero, 11198 as_usize | @intFromEnum(Zir.Inst.Ref.zero_u1) => return .zero_usize, 11199 as_usize | @intFromEnum(Zir.Inst.Ref.zero_u8) => return .zero_usize, 11200 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_u1) => return .one, 11201 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_u8) => return .one, 11202 as_usize | @intFromEnum(Zir.Inst.Ref.one_u1) => return .one_usize, 11203 as_usize | @intFromEnum(Zir.Inst.Ref.one_u8) => return .one_usize, 11204 11205 // Need an explicit type coercion instruction. 11206 else => return gz.addPlNode(ri.zirTag(), src_node, Zir.Inst.As{ 11207 .dest_type = ty_inst, 11208 .operand = result, 11209 }), 11210 } 11211 }, 11212 .ptr => |ptr_res| { 11213 _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{ 11214 .lhs = ptr_res.inst, 11215 .rhs = result, 11216 }); 11217 return .void_value; 11218 }, 11219 .inferred_ptr => |alloc| { 11220 _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{ 11221 .lhs = alloc, 11222 .rhs = result, 11223 }); 11224 return .void_value; 11225 }, 11226 .destructure => |destructure| { 11227 const components = destructure.components; 11228 _ = try gz.addPlNode(.validate_destructure, src_node, Zir.Inst.ValidateDestructure{ 11229 .operand = result, 11230 .destructure_node = gz.nodeIndexToRelative(destructure.src_node), 11231 .expect_len = @intCast(components.len), 11232 }); 11233 for (components, 0..) |component, i| { 11234 if (component == .discard) continue; 11235 const elem_val = try gz.add(.{ 11236 .tag = .elem_val_imm, 11237 .data = .{ .elem_val_imm = .{ 11238 .operand = result, 11239 .idx = @intCast(i), 11240 } }, 11241 }); 11242 switch (component) { 11243 .typed_ptr => |ptr_res| { 11244 _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{ 11245 .lhs = ptr_res.inst, 11246 .rhs = elem_val, 11247 }); 11248 }, 11249 .inferred_ptr => |ptr_inst| { 11250 _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{ 11251 .lhs = ptr_inst, 11252 .rhs = elem_val, 11253 }); 11254 }, 11255 .discard => unreachable, 11256 } 11257 } 11258 return .void_value; 11259 }, 11260 } 11261 } 11262 11263 /// Given an identifier token, obtain the string for it. 11264 /// If the token uses @"" syntax, parses as a string, reports errors if applicable, 11265 /// and allocates the result within `astgen.arena`. 11266 /// Otherwise, returns a reference to the source code bytes directly. 11267 /// See also `appendIdentStr` and `parseStrLit`. 11268 fn identifierTokenString(astgen: *AstGen, token: Ast.TokenIndex) InnerError![]const u8 { 11269 const tree = astgen.tree; 11270 assert(tree.tokenTag(token) == .identifier); 11271 const ident_name = tree.tokenSlice(token); 11272 if (!mem.startsWith(u8, ident_name, "@")) { 11273 return ident_name; 11274 } 11275 var buf: ArrayListUnmanaged(u8) = .empty; 11276 defer buf.deinit(astgen.gpa); 11277 try astgen.parseStrLit(token, &buf, ident_name, 1); 11278 if (mem.indexOfScalar(u8, buf.items, 0) != null) { 11279 return astgen.failTok(token, "identifier cannot contain null bytes", .{}); 11280 } else if (buf.items.len == 0) { 11281 return astgen.failTok(token, "identifier cannot be empty", .{}); 11282 } 11283 const duped = try astgen.arena.dupe(u8, buf.items); 11284 return duped; 11285 } 11286 11287 /// Given an identifier token, obtain the string for it (possibly parsing as a string 11288 /// literal if it is @"" syntax), and append the string to `buf`. 11289 /// See also `identifierTokenString` and `parseStrLit`. 11290 fn appendIdentStr( 11291 astgen: *AstGen, 11292 token: Ast.TokenIndex, 11293 buf: *ArrayListUnmanaged(u8), 11294 ) InnerError!void { 11295 const tree = astgen.tree; 11296 assert(tree.tokenTag(token) == .identifier); 11297 const ident_name = tree.tokenSlice(token); 11298 if (!mem.startsWith(u8, ident_name, "@")) { 11299 return buf.appendSlice(astgen.gpa, ident_name); 11300 } else { 11301 const start = buf.items.len; 11302 try astgen.parseStrLit(token, buf, ident_name, 1); 11303 const slice = buf.items[start..]; 11304 if (mem.indexOfScalar(u8, slice, 0) != null) { 11305 return astgen.failTok(token, "identifier cannot contain null bytes", .{}); 11306 } else if (slice.len == 0) { 11307 return astgen.failTok(token, "identifier cannot be empty", .{}); 11308 } 11309 } 11310 } 11311 11312 /// Appends the result to `buf`. 11313 fn parseStrLit( 11314 astgen: *AstGen, 11315 token: Ast.TokenIndex, 11316 buf: *ArrayListUnmanaged(u8), 11317 bytes: []const u8, 11318 offset: u32, 11319 ) InnerError!void { 11320 const raw_string = bytes[offset..]; 11321 const result = r: { 11322 var aw: std.io.Writer.Allocating = .fromArrayList(astgen.gpa, buf); 11323 defer buf.* = aw.toArrayList(); 11324 break :r std.zig.string_literal.parseWrite(&aw.writer, raw_string) catch |err| switch (err) { 11325 error.WriteFailed => return error.OutOfMemory, 11326 }; 11327 }; 11328 switch (result) { 11329 .success => return, 11330 .failure => |err| return astgen.failWithStrLitError(err, token, bytes, offset), 11331 } 11332 } 11333 11334 fn failWithStrLitError( 11335 astgen: *AstGen, 11336 err: std.zig.string_literal.Error, 11337 token: Ast.TokenIndex, 11338 bytes: []const u8, 11339 offset: u32, 11340 ) InnerError { 11341 const raw_string = bytes[offset..]; 11342 return failOff(astgen, token, @intCast(offset + err.offset()), "{f}", .{err.fmt(raw_string)}); 11343 } 11344 11345 fn failNode( 11346 astgen: *AstGen, 11347 node: Ast.Node.Index, 11348 comptime format: []const u8, 11349 args: anytype, 11350 ) InnerError { 11351 return astgen.failNodeNotes(node, format, args, &[0]u32{}); 11352 } 11353 11354 fn appendErrorNode( 11355 astgen: *AstGen, 11356 node: Ast.Node.Index, 11357 comptime format: []const u8, 11358 args: anytype, 11359 ) Allocator.Error!void { 11360 try astgen.appendErrorNodeNotes(node, format, args, &[0]u32{}); 11361 } 11362 11363 fn appendErrorNodeNotes( 11364 astgen: *AstGen, 11365 node: Ast.Node.Index, 11366 comptime format: []const u8, 11367 args: anytype, 11368 notes: []const u32, 11369 ) Allocator.Error!void { 11370 @branchHint(.cold); 11371 const gpa = astgen.gpa; 11372 const string_bytes = &astgen.string_bytes; 11373 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len); 11374 try string_bytes.print(gpa, format ++ "\x00", args); 11375 const notes_index: u32 = if (notes.len != 0) blk: { 11376 const notes_start = astgen.extra.items.len; 11377 try astgen.extra.ensureTotalCapacity(gpa, notes_start + 1 + notes.len); 11378 astgen.extra.appendAssumeCapacity(@intCast(notes.len)); 11379 astgen.extra.appendSliceAssumeCapacity(notes); 11380 break :blk @intCast(notes_start); 11381 } else 0; 11382 try astgen.compile_errors.append(gpa, .{ 11383 .msg = msg, 11384 .node = node.toOptional(), 11385 .token = .none, 11386 .byte_offset = 0, 11387 .notes = notes_index, 11388 }); 11389 } 11390 11391 fn failNodeNotes( 11392 astgen: *AstGen, 11393 node: Ast.Node.Index, 11394 comptime format: []const u8, 11395 args: anytype, 11396 notes: []const u32, 11397 ) InnerError { 11398 try appendErrorNodeNotes(astgen, node, format, args, notes); 11399 return error.AnalysisFail; 11400 } 11401 11402 fn failTok( 11403 astgen: *AstGen, 11404 token: Ast.TokenIndex, 11405 comptime format: []const u8, 11406 args: anytype, 11407 ) InnerError { 11408 return astgen.failTokNotes(token, format, args, &[0]u32{}); 11409 } 11410 11411 fn appendErrorTok( 11412 astgen: *AstGen, 11413 token: Ast.TokenIndex, 11414 comptime format: []const u8, 11415 args: anytype, 11416 ) !void { 11417 try astgen.appendErrorTokNotesOff(token, 0, format, args, &[0]u32{}); 11418 } 11419 11420 fn failTokNotes( 11421 astgen: *AstGen, 11422 token: Ast.TokenIndex, 11423 comptime format: []const u8, 11424 args: anytype, 11425 notes: []const u32, 11426 ) InnerError { 11427 try appendErrorTokNotesOff(astgen, token, 0, format, args, notes); 11428 return error.AnalysisFail; 11429 } 11430 11431 fn appendErrorTokNotes( 11432 astgen: *AstGen, 11433 token: Ast.TokenIndex, 11434 comptime format: []const u8, 11435 args: anytype, 11436 notes: []const u32, 11437 ) !void { 11438 return appendErrorTokNotesOff(astgen, token, 0, format, args, notes); 11439 } 11440 11441 /// Same as `fail`, except given a token plus an offset from its starting byte 11442 /// offset. 11443 fn failOff( 11444 astgen: *AstGen, 11445 token: Ast.TokenIndex, 11446 byte_offset: u32, 11447 comptime format: []const u8, 11448 args: anytype, 11449 ) InnerError { 11450 try appendErrorTokNotesOff(astgen, token, byte_offset, format, args, &.{}); 11451 return error.AnalysisFail; 11452 } 11453 11454 fn appendErrorTokNotesOff( 11455 astgen: *AstGen, 11456 token: Ast.TokenIndex, 11457 byte_offset: u32, 11458 comptime format: []const u8, 11459 args: anytype, 11460 notes: []const u32, 11461 ) !void { 11462 @branchHint(.cold); 11463 const gpa = astgen.gpa; 11464 const string_bytes = &astgen.string_bytes; 11465 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len); 11466 try string_bytes.print(gpa, format ++ "\x00", args); 11467 const notes_index: u32 = if (notes.len != 0) blk: { 11468 const notes_start = astgen.extra.items.len; 11469 try astgen.extra.ensureTotalCapacity(gpa, notes_start + 1 + notes.len); 11470 astgen.extra.appendAssumeCapacity(@intCast(notes.len)); 11471 astgen.extra.appendSliceAssumeCapacity(notes); 11472 break :blk @intCast(notes_start); 11473 } else 0; 11474 try astgen.compile_errors.append(gpa, .{ 11475 .msg = msg, 11476 .node = .none, 11477 .token = .fromToken(token), 11478 .byte_offset = byte_offset, 11479 .notes = notes_index, 11480 }); 11481 } 11482 11483 fn errNoteTok( 11484 astgen: *AstGen, 11485 token: Ast.TokenIndex, 11486 comptime format: []const u8, 11487 args: anytype, 11488 ) Allocator.Error!u32 { 11489 return errNoteTokOff(astgen, token, 0, format, args); 11490 } 11491 11492 fn errNoteTokOff( 11493 astgen: *AstGen, 11494 token: Ast.TokenIndex, 11495 byte_offset: u32, 11496 comptime format: []const u8, 11497 args: anytype, 11498 ) Allocator.Error!u32 { 11499 @branchHint(.cold); 11500 const string_bytes = &astgen.string_bytes; 11501 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len); 11502 try string_bytes.print(astgen.gpa, format ++ "\x00", args); 11503 return astgen.addExtra(Zir.Inst.CompileErrors.Item{ 11504 .msg = msg, 11505 .node = .none, 11506 .token = .fromToken(token), 11507 .byte_offset = byte_offset, 11508 .notes = 0, 11509 }); 11510 } 11511 11512 fn errNoteNode( 11513 astgen: *AstGen, 11514 node: Ast.Node.Index, 11515 comptime format: []const u8, 11516 args: anytype, 11517 ) Allocator.Error!u32 { 11518 @branchHint(.cold); 11519 const string_bytes = &astgen.string_bytes; 11520 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len); 11521 try string_bytes.print(astgen.gpa, format ++ "\x00", args); 11522 return astgen.addExtra(Zir.Inst.CompileErrors.Item{ 11523 .msg = msg, 11524 .node = node.toOptional(), 11525 .token = .none, 11526 .byte_offset = 0, 11527 .notes = 0, 11528 }); 11529 } 11530 11531 fn identAsString(astgen: *AstGen, ident_token: Ast.TokenIndex) !Zir.NullTerminatedString { 11532 const gpa = astgen.gpa; 11533 const string_bytes = &astgen.string_bytes; 11534 const str_index: u32 = @intCast(string_bytes.items.len); 11535 try astgen.appendIdentStr(ident_token, string_bytes); 11536 const key: []const u8 = string_bytes.items[str_index..]; 11537 const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{ 11538 .bytes = string_bytes, 11539 }, StringIndexContext{ 11540 .bytes = string_bytes, 11541 }); 11542 if (gop.found_existing) { 11543 string_bytes.shrinkRetainingCapacity(str_index); 11544 return @enumFromInt(gop.key_ptr.*); 11545 } else { 11546 gop.key_ptr.* = str_index; 11547 try string_bytes.append(gpa, 0); 11548 return @enumFromInt(str_index); 11549 } 11550 } 11551 11552 const IndexSlice = struct { index: Zir.NullTerminatedString, len: u32 }; 11553 11554 fn strLitAsString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !IndexSlice { 11555 const gpa = astgen.gpa; 11556 const string_bytes = &astgen.string_bytes; 11557 const str_index: u32 = @intCast(string_bytes.items.len); 11558 const token_bytes = astgen.tree.tokenSlice(str_lit_token); 11559 try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0); 11560 const key: []const u8 = string_bytes.items[str_index..]; 11561 if (std.mem.indexOfScalar(u8, key, 0)) |_| return .{ 11562 .index = @enumFromInt(str_index), 11563 .len = @intCast(key.len), 11564 }; 11565 const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{ 11566 .bytes = string_bytes, 11567 }, StringIndexContext{ 11568 .bytes = string_bytes, 11569 }); 11570 if (gop.found_existing) { 11571 string_bytes.shrinkRetainingCapacity(str_index); 11572 return .{ 11573 .index = @enumFromInt(gop.key_ptr.*), 11574 .len = @intCast(key.len), 11575 }; 11576 } else { 11577 gop.key_ptr.* = str_index; 11578 // Still need a null byte because we are using the same table 11579 // to lookup null terminated strings, so if we get a match, it has to 11580 // be null terminated for that to work. 11581 try string_bytes.append(gpa, 0); 11582 return .{ 11583 .index = @enumFromInt(str_index), 11584 .len = @intCast(key.len), 11585 }; 11586 } 11587 } 11588 11589 fn strLitNodeAsString(astgen: *AstGen, node: Ast.Node.Index) !IndexSlice { 11590 const tree = astgen.tree; 11591 11592 const start, const end = tree.nodeData(node).token_and_token; 11593 11594 const gpa = astgen.gpa; 11595 const string_bytes = &astgen.string_bytes; 11596 const str_index = string_bytes.items.len; 11597 11598 // First line: do not append a newline. 11599 var tok_i = start; 11600 { 11601 const slice = tree.tokenSlice(tok_i); 11602 const line_bytes = slice[2..]; 11603 try string_bytes.appendSlice(gpa, line_bytes); 11604 tok_i += 1; 11605 } 11606 // Following lines: each line prepends a newline. 11607 while (tok_i <= end) : (tok_i += 1) { 11608 const slice = tree.tokenSlice(tok_i); 11609 const line_bytes = slice[2..]; 11610 try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1); 11611 string_bytes.appendAssumeCapacity('\n'); 11612 string_bytes.appendSliceAssumeCapacity(line_bytes); 11613 } 11614 const len = string_bytes.items.len - str_index; 11615 try string_bytes.append(gpa, 0); 11616 return IndexSlice{ 11617 .index = @enumFromInt(str_index), 11618 .len = @intCast(len), 11619 }; 11620 } 11621 11622 const Scope = struct { 11623 tag: Tag, 11624 11625 fn cast(base: *Scope, comptime T: type) ?*T { 11626 if (T == Defer) { 11627 switch (base.tag) { 11628 .defer_normal, .defer_error => return @alignCast(@fieldParentPtr("base", base)), 11629 else => return null, 11630 } 11631 } 11632 if (T == Namespace) { 11633 switch (base.tag) { 11634 .namespace => return @alignCast(@fieldParentPtr("base", base)), 11635 else => return null, 11636 } 11637 } 11638 if (base.tag != T.base_tag) 11639 return null; 11640 11641 return @alignCast(@fieldParentPtr("base", base)); 11642 } 11643 11644 fn parent(base: *Scope) ?*Scope { 11645 return switch (base.tag) { 11646 .gen_zir => base.cast(GenZir).?.parent, 11647 .local_val => base.cast(LocalVal).?.parent, 11648 .local_ptr => base.cast(LocalPtr).?.parent, 11649 .defer_normal, .defer_error => base.cast(Defer).?.parent, 11650 .namespace => base.cast(Namespace).?.parent, 11651 .top => null, 11652 }; 11653 } 11654 11655 const Tag = enum { 11656 gen_zir, 11657 local_val, 11658 local_ptr, 11659 defer_normal, 11660 defer_error, 11661 namespace, 11662 top, 11663 }; 11664 11665 /// The category of identifier. These tag names are user-visible in compile errors. 11666 const IdCat = enum { 11667 @"function parameter", 11668 @"local constant", 11669 @"local variable", 11670 @"switch tag capture", 11671 capture, 11672 }; 11673 11674 /// This is always a `const` local and importantly the `inst` is a value type, not a pointer. 11675 /// This structure lives as long as the AST generation of the Block 11676 /// node that contains the variable. 11677 const LocalVal = struct { 11678 const base_tag: Tag = .local_val; 11679 base: Scope = Scope{ .tag = base_tag }, 11680 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11681 parent: *Scope, 11682 gen_zir: *GenZir, 11683 inst: Zir.Inst.Ref, 11684 /// Source location of the corresponding variable declaration. 11685 token_src: Ast.TokenIndex, 11686 /// Track the first identifier where it is referenced. 11687 /// .none means never referenced. 11688 used: Ast.OptionalTokenIndex = .none, 11689 /// Track the identifier where it is discarded, like this `_ = foo;`. 11690 /// .none means never discarded. 11691 discarded: Ast.OptionalTokenIndex = .none, 11692 is_used_or_discarded: ?*bool = null, 11693 /// String table index. 11694 name: Zir.NullTerminatedString, 11695 id_cat: IdCat, 11696 }; 11697 11698 /// This could be a `const` or `var` local. It has a pointer instead of a value. 11699 /// This structure lives as long as the AST generation of the Block 11700 /// node that contains the variable. 11701 const LocalPtr = struct { 11702 const base_tag: Tag = .local_ptr; 11703 base: Scope = Scope{ .tag = base_tag }, 11704 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11705 parent: *Scope, 11706 gen_zir: *GenZir, 11707 ptr: Zir.Inst.Ref, 11708 /// Source location of the corresponding variable declaration. 11709 token_src: Ast.TokenIndex, 11710 /// Track the first identifier where it is referenced. 11711 /// .none means never referenced. 11712 used: Ast.OptionalTokenIndex = .none, 11713 /// Track the identifier where it is discarded, like this `_ = foo;`. 11714 /// .none means never discarded. 11715 discarded: Ast.OptionalTokenIndex = .none, 11716 /// Whether this value is used as an lvalue after initialization. 11717 /// If not, we know it can be `const`, so will emit a compile error if it is `var`. 11718 used_as_lvalue: bool = false, 11719 /// String table index. 11720 name: Zir.NullTerminatedString, 11721 id_cat: IdCat, 11722 /// true means we find out during Sema whether the value is comptime. 11723 /// false means it is already known at AstGen the value is runtime-known. 11724 maybe_comptime: bool, 11725 }; 11726 11727 const Defer = struct { 11728 base: Scope, 11729 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11730 parent: *Scope, 11731 index: u32, 11732 len: u32, 11733 remapped_err_code: Zir.Inst.OptionalIndex = .none, 11734 }; 11735 11736 /// Represents a global scope that has any number of declarations in it. 11737 /// Each declaration has this as the parent scope. 11738 const Namespace = struct { 11739 const base_tag: Tag = .namespace; 11740 base: Scope = Scope{ .tag = base_tag }, 11741 11742 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11743 parent: *Scope, 11744 /// Maps string table index to the source location of declaration, 11745 /// for the purposes of reporting name shadowing compile errors. 11746 decls: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .empty, 11747 node: Ast.Node.Index, 11748 inst: Zir.Inst.Index, 11749 maybe_generic: bool, 11750 11751 /// The astgen scope containing this namespace. 11752 /// Only valid during astgen. 11753 declaring_gz: ?*GenZir, 11754 11755 /// Set of captures used by this namespace. 11756 captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Capture, Zir.NullTerminatedString) = .empty, 11757 11758 fn deinit(self: *Namespace, gpa: Allocator) void { 11759 self.decls.deinit(gpa); 11760 self.captures.deinit(gpa); 11761 self.* = undefined; 11762 } 11763 }; 11764 11765 const Top = struct { 11766 const base_tag: Scope.Tag = .top; 11767 base: Scope = Scope{ .tag = base_tag }, 11768 }; 11769 }; 11770 11771 /// This is a temporary structure; references to it are valid only 11772 /// while constructing a `Zir`. 11773 const GenZir = struct { 11774 const base_tag: Scope.Tag = .gen_zir; 11775 base: Scope = Scope{ .tag = base_tag }, 11776 /// Whether we're already in a scope known to be comptime. This is set 11777 /// whenever we know Sema will analyze the current block with `is_comptime`, 11778 /// for instance when we're within a `struct_decl` or a `block_comptime`. 11779 is_comptime: bool, 11780 /// Whether we're in an expression within a `@TypeOf` operand. In this case, closure of runtime 11781 /// variables is permitted where it is usually not. 11782 is_typeof: bool = false, 11783 /// This is set to true for a `GenZir` of a `block_inline`, indicating that 11784 /// exits from this block should use `break_inline` rather than `break`. 11785 is_inline: bool = false, 11786 c_import: bool = false, 11787 /// The containing decl AST node. 11788 decl_node_index: Ast.Node.Index, 11789 /// The containing decl line index, absolute. 11790 decl_line: u32, 11791 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11792 parent: *Scope, 11793 /// All `GenZir` scopes for the same ZIR share this. 11794 astgen: *AstGen, 11795 /// Keeps track of the list of instructions in this scope. Possibly shared. 11796 /// Indexes to instructions in `astgen`. 11797 instructions: *ArrayListUnmanaged(Zir.Inst.Index), 11798 /// A sub-block may share its instructions ArrayList with containing GenZir, 11799 /// if use is strictly nested. This saves prior size of list for unstacking. 11800 instructions_top: usize, 11801 label: ?Label = null, 11802 break_block: Zir.Inst.OptionalIndex = .none, 11803 continue_block: Zir.Inst.OptionalIndex = .none, 11804 /// Only valid when setBreakResultInfo is called. 11805 break_result_info: AstGen.ResultInfo = undefined, 11806 continue_result_info: AstGen.ResultInfo = undefined, 11807 11808 suspend_node: Ast.Node.OptionalIndex = .none, 11809 nosuspend_node: Ast.Node.OptionalIndex = .none, 11810 /// Set if this GenZir is a defer. 11811 cur_defer_node: Ast.Node.OptionalIndex = .none, 11812 // Set if this GenZir is a defer or it is inside a defer. 11813 any_defer_node: Ast.Node.OptionalIndex = .none, 11814 11815 const unstacked_top = std.math.maxInt(usize); 11816 /// Call unstack before adding any new instructions to containing GenZir. 11817 fn unstack(self: *GenZir) void { 11818 if (self.instructions_top != unstacked_top) { 11819 self.instructions.items.len = self.instructions_top; 11820 self.instructions_top = unstacked_top; 11821 } 11822 } 11823 11824 fn isEmpty(self: *const GenZir) bool { 11825 return (self.instructions_top == unstacked_top) or 11826 (self.instructions.items.len == self.instructions_top); 11827 } 11828 11829 fn instructionsSlice(self: *const GenZir) []Zir.Inst.Index { 11830 return if (self.instructions_top == unstacked_top) 11831 &[0]Zir.Inst.Index{} 11832 else 11833 self.instructions.items[self.instructions_top..]; 11834 } 11835 11836 fn instructionsSliceUpto(self: *const GenZir, stacked_gz: *GenZir) []Zir.Inst.Index { 11837 return if (self.instructions_top == unstacked_top) 11838 &[0]Zir.Inst.Index{} 11839 else if (self.instructions == stacked_gz.instructions and stacked_gz.instructions_top != unstacked_top) 11840 self.instructions.items[self.instructions_top..stacked_gz.instructions_top] 11841 else 11842 self.instructions.items[self.instructions_top..]; 11843 } 11844 11845 fn instructionsSliceUptoOpt(gz: *const GenZir, maybe_stacked_gz: ?*GenZir) []Zir.Inst.Index { 11846 if (maybe_stacked_gz) |stacked_gz| { 11847 return gz.instructionsSliceUpto(stacked_gz); 11848 } else { 11849 return gz.instructionsSlice(); 11850 } 11851 } 11852 11853 fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir { 11854 return .{ 11855 .is_comptime = gz.is_comptime, 11856 .is_typeof = gz.is_typeof, 11857 .c_import = gz.c_import, 11858 .decl_node_index = gz.decl_node_index, 11859 .decl_line = gz.decl_line, 11860 .parent = scope, 11861 .astgen = gz.astgen, 11862 .suspend_node = gz.suspend_node, 11863 .nosuspend_node = gz.nosuspend_node, 11864 .any_defer_node = gz.any_defer_node, 11865 .instructions = gz.instructions, 11866 .instructions_top = gz.instructions.items.len, 11867 }; 11868 } 11869 11870 const Label = struct { 11871 token: Ast.TokenIndex, 11872 block_inst: Zir.Inst.Index, 11873 used: bool = false, 11874 used_for_continue: bool = false, 11875 }; 11876 11877 /// Assumes nothing stacked on `gz`. 11878 fn endsWithNoReturn(gz: GenZir) bool { 11879 if (gz.isEmpty()) return false; 11880 const tags = gz.astgen.instructions.items(.tag); 11881 const last_inst = gz.instructions.items[gz.instructions.items.len - 1]; 11882 return tags[@intFromEnum(last_inst)].isNoReturn(); 11883 } 11884 11885 /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`. 11886 fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool { 11887 if (inst_ref == .unreachable_value) return true; 11888 if (inst_ref.toIndex()) |inst_index| { 11889 return gz.astgen.instructions.items(.tag)[@intFromEnum(inst_index)].isNoReturn(); 11890 } 11891 return false; 11892 } 11893 11894 fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) Ast.Node.Offset { 11895 return gz.decl_node_index.toOffset(node_index); 11896 } 11897 11898 fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) Ast.TokenOffset { 11899 return .init(gz.srcToken(), token); 11900 } 11901 11902 fn srcToken(gz: GenZir) Ast.TokenIndex { 11903 return gz.astgen.tree.firstToken(gz.decl_node_index); 11904 } 11905 11906 fn setBreakResultInfo(gz: *GenZir, parent_ri: AstGen.ResultInfo) void { 11907 // Depending on whether the result location is a pointer or value, different 11908 // ZIR needs to be generated. In the former case we rely on storing to the 11909 // pointer to communicate the result, and use breakvoid; in the latter case 11910 // the block break instructions will have the result values. 11911 switch (parent_ri.rl) { 11912 .coerced_ty => |ty_inst| { 11913 // Type coercion needs to happen before breaks. 11914 gz.break_result_info = .{ .rl = .{ .ty = ty_inst }, .ctx = parent_ri.ctx }; 11915 }, 11916 .discard => { 11917 // We don't forward the result context here. This prevents 11918 // "unnecessary discard" errors from being caused by expressions 11919 // far from the actual discard, such as a `break` from a 11920 // discarded block. 11921 gz.break_result_info = .{ .rl = .discard }; 11922 }, 11923 else => { 11924 gz.break_result_info = parent_ri; 11925 }, 11926 } 11927 } 11928 11929 /// Assumes nothing stacked on `gz`. Unstacks `gz`. 11930 fn setBoolBrBody(gz: *GenZir, bool_br: Zir.Inst.Index, bool_br_lhs: Zir.Inst.Ref) !void { 11931 const astgen = gz.astgen; 11932 const gpa = astgen.gpa; 11933 const body = gz.instructionsSlice(); 11934 const body_len = astgen.countBodyLenAfterFixups(body); 11935 try astgen.extra.ensureUnusedCapacity( 11936 gpa, 11937 @typeInfo(Zir.Inst.BoolBr).@"struct".fields.len + body_len, 11938 ); 11939 const zir_datas = astgen.instructions.items(.data); 11940 zir_datas[@intFromEnum(bool_br)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.BoolBr{ 11941 .lhs = bool_br_lhs, 11942 .body_len = body_len, 11943 }); 11944 astgen.appendBodyWithFixups(body); 11945 gz.unstack(); 11946 } 11947 11948 /// Assumes nothing stacked on `gz`. Unstacks `gz`. 11949 /// Asserts `inst` is not a `block_comptime`. 11950 fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void { 11951 const astgen = gz.astgen; 11952 const gpa = astgen.gpa; 11953 const body = gz.instructionsSlice(); 11954 const body_len = astgen.countBodyLenAfterFixups(body); 11955 11956 const zir_tags = astgen.instructions.items(.tag); 11957 assert(zir_tags[@intFromEnum(inst)] != .block_comptime); // use `setComptimeBlockBody` instead 11958 11959 try astgen.extra.ensureUnusedCapacity( 11960 gpa, 11961 @typeInfo(Zir.Inst.Block).@"struct".fields.len + body_len, 11962 ); 11963 const zir_datas = astgen.instructions.items(.data); 11964 zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity( 11965 Zir.Inst.Block{ .body_len = body_len }, 11966 ); 11967 astgen.appendBodyWithFixups(body); 11968 gz.unstack(); 11969 } 11970 11971 /// Assumes nothing stacked on `gz`. Unstacks `gz`. 11972 /// Asserts `inst` is a `block_comptime`. 11973 fn setBlockComptimeBody(gz: *GenZir, inst: Zir.Inst.Index, comptime_reason: std.zig.SimpleComptimeReason) !void { 11974 const astgen = gz.astgen; 11975 const gpa = astgen.gpa; 11976 const body = gz.instructionsSlice(); 11977 const body_len = astgen.countBodyLenAfterFixups(body); 11978 11979 const zir_tags = astgen.instructions.items(.tag); 11980 assert(zir_tags[@intFromEnum(inst)] == .block_comptime); // use `setBlockBody` instead 11981 11982 try astgen.extra.ensureUnusedCapacity( 11983 gpa, 11984 @typeInfo(Zir.Inst.BlockComptime).@"struct".fields.len + body_len, 11985 ); 11986 const zir_datas = astgen.instructions.items(.data); 11987 zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity( 11988 Zir.Inst.BlockComptime{ 11989 .reason = comptime_reason, 11990 .body_len = body_len, 11991 }, 11992 ); 11993 astgen.appendBodyWithFixups(body); 11994 gz.unstack(); 11995 } 11996 11997 /// Assumes nothing stacked on `gz`. Unstacks `gz`. 11998 fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void { 11999 const astgen = gz.astgen; 12000 const gpa = astgen.gpa; 12001 const body = gz.instructionsSlice(); 12002 const body_len = astgen.countBodyLenAfterFixups(body); 12003 try astgen.extra.ensureUnusedCapacity( 12004 gpa, 12005 @typeInfo(Zir.Inst.Try).@"struct".fields.len + body_len, 12006 ); 12007 const zir_datas = astgen.instructions.items(.data); 12008 zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity( 12009 Zir.Inst.Try{ 12010 .operand = operand, 12011 .body_len = body_len, 12012 }, 12013 ); 12014 astgen.appendBodyWithFixups(body); 12015 gz.unstack(); 12016 } 12017 12018 /// Must be called with the following stack set up: 12019 /// * gz (bottom) 12020 /// * ret_gz 12021 /// * cc_gz 12022 /// * body_gz (top) 12023 /// Unstacks all of those except for `gz`. 12024 fn addFunc( 12025 gz: *GenZir, 12026 args: struct { 12027 src_node: Ast.Node.Index, 12028 lbrace_line: u32 = 0, 12029 lbrace_column: u32 = 0, 12030 param_block: Zir.Inst.Index, 12031 12032 ret_gz: ?*GenZir, 12033 body_gz: ?*GenZir, 12034 cc_gz: ?*GenZir, 12035 12036 ret_param_refs: []Zir.Inst.Index, 12037 param_insts: []Zir.Inst.Index, // refs to params in `body_gz` should still be in `astgen.ref_table` 12038 ret_ty_is_generic: bool, 12039 12040 cc_ref: Zir.Inst.Ref, 12041 ret_ref: Zir.Inst.Ref, 12042 12043 noalias_bits: u32, 12044 is_var_args: bool, 12045 is_inferred_error: bool, 12046 is_noinline: bool, 12047 12048 /// Ignored if `body_gz == null`. 12049 proto_hash: std.zig.SrcHash, 12050 }, 12051 ) !Zir.Inst.Ref { 12052 assert(args.src_node != .root); 12053 const astgen = gz.astgen; 12054 const gpa = astgen.gpa; 12055 const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref; 12056 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12057 12058 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12059 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12060 12061 const body, const cc_body, const ret_body = bodies: { 12062 var stacked_gz: ?*GenZir = null; 12063 const body: []const Zir.Inst.Index = if (args.body_gz) |body_gz| body: { 12064 const body = body_gz.instructionsSliceUptoOpt(stacked_gz); 12065 stacked_gz = body_gz; 12066 break :body body; 12067 } else &.{}; 12068 const cc_body: []const Zir.Inst.Index = if (args.cc_gz) |cc_gz| body: { 12069 const cc_body = cc_gz.instructionsSliceUptoOpt(stacked_gz); 12070 stacked_gz = cc_gz; 12071 break :body cc_body; 12072 } else &.{}; 12073 const ret_body: []const Zir.Inst.Index = if (args.ret_gz) |ret_gz| body: { 12074 const ret_body = ret_gz.instructionsSliceUptoOpt(stacked_gz); 12075 stacked_gz = ret_gz; 12076 break :body ret_body; 12077 } else &.{}; 12078 break :bodies .{ body, cc_body, ret_body }; 12079 }; 12080 12081 var src_locs_and_hash_buffer: [7]u32 = undefined; 12082 const src_locs_and_hash: []const u32 = if (args.body_gz != null) src_locs_and_hash: { 12083 const tree = astgen.tree; 12084 const fn_decl = args.src_node; 12085 const block = switch (tree.nodeTag(fn_decl)) { 12086 .fn_decl => tree.nodeData(fn_decl).node_and_node[1], 12087 .test_decl => tree.nodeData(fn_decl).opt_token_and_node[1], 12088 else => unreachable, 12089 }; 12090 const rbrace_start = tree.tokenStart(tree.lastToken(block)); 12091 astgen.advanceSourceCursor(rbrace_start); 12092 const rbrace_line: u32 = @intCast(astgen.source_line - gz.decl_line); 12093 const rbrace_column: u32 = @intCast(astgen.source_column); 12094 12095 const columns = args.lbrace_column | (rbrace_column << 16); 12096 12097 const proto_hash_arr: [4]u32 = @bitCast(args.proto_hash); 12098 12099 src_locs_and_hash_buffer = .{ 12100 args.lbrace_line, 12101 rbrace_line, 12102 columns, 12103 proto_hash_arr[0], 12104 proto_hash_arr[1], 12105 proto_hash_arr[2], 12106 proto_hash_arr[3], 12107 }; 12108 break :src_locs_and_hash &src_locs_and_hash_buffer; 12109 } else &.{}; 12110 12111 const body_len = astgen.countBodyLenAfterFixupsExtraRefs(body, args.param_insts); 12112 12113 const tag: Zir.Inst.Tag, const payload_index: u32 = if (args.cc_ref != .none or 12114 args.is_var_args or args.noalias_bits != 0 or args.is_noinline) 12115 inst_info: { 12116 try astgen.extra.ensureUnusedCapacity( 12117 gpa, 12118 @typeInfo(Zir.Inst.FuncFancy).@"struct".fields.len + 12119 fancyFnExprExtraLen(astgen, &.{}, cc_body, args.cc_ref) + 12120 fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) + 12121 body_len + src_locs_and_hash.len + 12122 @intFromBool(args.noalias_bits != 0), 12123 ); 12124 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{ 12125 .param_block = args.param_block, 12126 .body_len = body_len, 12127 .bits = .{ 12128 .is_var_args = args.is_var_args, 12129 .is_inferred_error = args.is_inferred_error, 12130 .is_noinline = args.is_noinline, 12131 .has_any_noalias = args.noalias_bits != 0, 12132 12133 .has_cc_ref = args.cc_ref != .none, 12134 .has_ret_ty_ref = ret_ref != .none, 12135 12136 .has_cc_body = cc_body.len != 0, 12137 .has_ret_ty_body = ret_body.len != 0, 12138 12139 .ret_ty_is_generic = args.ret_ty_is_generic, 12140 }, 12141 }); 12142 12143 const zir_datas = astgen.instructions.items(.data); 12144 if (cc_body.len != 0) { 12145 astgen.extra.appendAssumeCapacity(astgen.countBodyLenAfterFixups(cc_body)); 12146 astgen.appendBodyWithFixups(cc_body); 12147 const break_extra = zir_datas[@intFromEnum(cc_body[cc_body.len - 1])].@"break".payload_index; 12148 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] = 12149 @intFromEnum(new_index); 12150 } else if (args.cc_ref != .none) { 12151 astgen.extra.appendAssumeCapacity(@intFromEnum(args.cc_ref)); 12152 } 12153 if (ret_body.len != 0) { 12154 astgen.extra.appendAssumeCapacity( 12155 astgen.countBodyLenAfterFixups(args.ret_param_refs) + 12156 astgen.countBodyLenAfterFixups(ret_body), 12157 ); 12158 astgen.appendBodyWithFixups(args.ret_param_refs); 12159 astgen.appendBodyWithFixups(ret_body); 12160 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index; 12161 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] = 12162 @intFromEnum(new_index); 12163 } else if (ret_ref != .none) { 12164 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref)); 12165 } 12166 12167 if (args.noalias_bits != 0) { 12168 astgen.extra.appendAssumeCapacity(args.noalias_bits); 12169 } 12170 12171 astgen.appendBodyWithFixupsExtraRefsArrayList(&astgen.extra, body, args.param_insts); 12172 astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash); 12173 12174 break :inst_info .{ .func_fancy, payload_index }; 12175 } else inst_info: { 12176 try astgen.extra.ensureUnusedCapacity( 12177 gpa, 12178 @typeInfo(Zir.Inst.Func).@"struct".fields.len + 1 + 12179 fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) + 12180 body_len + src_locs_and_hash.len, 12181 ); 12182 12183 const ret_body_len = if (ret_body.len != 0) 12184 countBodyLenAfterFixups(astgen, args.ret_param_refs) + countBodyLenAfterFixups(astgen, ret_body) 12185 else 12186 @intFromBool(ret_ref != .none); 12187 12188 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{ 12189 .param_block = args.param_block, 12190 .ret_ty = .{ 12191 .body_len = @intCast(ret_body_len), 12192 .is_generic = args.ret_ty_is_generic, 12193 }, 12194 .body_len = body_len, 12195 }); 12196 const zir_datas = astgen.instructions.items(.data); 12197 if (ret_body.len != 0) { 12198 astgen.appendBodyWithFixups(args.ret_param_refs); 12199 astgen.appendBodyWithFixups(ret_body); 12200 12201 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index; 12202 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] = 12203 @intFromEnum(new_index); 12204 } else if (ret_ref != .none) { 12205 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref)); 12206 } 12207 astgen.appendBodyWithFixupsExtraRefsArrayList(&astgen.extra, body, args.param_insts); 12208 astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash); 12209 12210 break :inst_info .{ 12211 if (args.is_inferred_error) .func_inferred else .func, 12212 payload_index, 12213 }; 12214 }; 12215 12216 // Order is important when unstacking. 12217 if (args.body_gz) |body_gz| body_gz.unstack(); 12218 if (args.cc_gz) |cc_gz| cc_gz.unstack(); 12219 if (args.ret_gz) |ret_gz| ret_gz.unstack(); 12220 12221 astgen.instructions.appendAssumeCapacity(.{ 12222 .tag = tag, 12223 .data = .{ .pl_node = .{ 12224 .src_node = gz.nodeIndexToRelative(args.src_node), 12225 .payload_index = payload_index, 12226 } }, 12227 }); 12228 gz.instructions.appendAssumeCapacity(new_index); 12229 return new_index.toRef(); 12230 } 12231 12232 fn fancyFnExprExtraLen(astgen: *AstGen, param_refs_body: []const Zir.Inst.Index, main_body: []const Zir.Inst.Index, ref: Zir.Inst.Ref) u32 { 12233 return countBodyLenAfterFixups(astgen, param_refs_body) + 12234 countBodyLenAfterFixups(astgen, main_body) + 12235 // If there is a body, we need an element for its length; otherwise, if there is a ref, we need to include that. 12236 @intFromBool(main_body.len > 0 or ref != .none); 12237 } 12238 12239 fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref { 12240 return gz.add(.{ 12241 .tag = .int, 12242 .data = .{ .int = integer }, 12243 }); 12244 } 12245 12246 fn addIntBig(gz: *GenZir, limbs: []const std.math.big.Limb) !Zir.Inst.Ref { 12247 const astgen = gz.astgen; 12248 const gpa = astgen.gpa; 12249 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12250 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12251 try astgen.string_bytes.ensureUnusedCapacity(gpa, @sizeOf(std.math.big.Limb) * limbs.len); 12252 12253 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12254 astgen.instructions.appendAssumeCapacity(.{ 12255 .tag = .int_big, 12256 .data = .{ .str = .{ 12257 .start = @enumFromInt(astgen.string_bytes.items.len), 12258 .len = @intCast(limbs.len), 12259 } }, 12260 }); 12261 gz.instructions.appendAssumeCapacity(new_index); 12262 astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs)); 12263 return new_index.toRef(); 12264 } 12265 12266 fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref { 12267 return gz.add(.{ 12268 .tag = .float, 12269 .data = .{ .float = number }, 12270 }); 12271 } 12272 12273 fn addUnNode( 12274 gz: *GenZir, 12275 tag: Zir.Inst.Tag, 12276 operand: Zir.Inst.Ref, 12277 /// Absolute node index. This function does the conversion to offset from Decl. 12278 src_node: Ast.Node.Index, 12279 ) !Zir.Inst.Ref { 12280 assert(operand != .none); 12281 return gz.add(.{ 12282 .tag = tag, 12283 .data = .{ .un_node = .{ 12284 .operand = operand, 12285 .src_node = gz.nodeIndexToRelative(src_node), 12286 } }, 12287 }); 12288 } 12289 12290 fn makeUnNode( 12291 gz: *GenZir, 12292 tag: Zir.Inst.Tag, 12293 operand: Zir.Inst.Ref, 12294 /// Absolute node index. This function does the conversion to offset from Decl. 12295 src_node: Ast.Node.Index, 12296 ) !Zir.Inst.Index { 12297 assert(operand != .none); 12298 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12299 try gz.astgen.instructions.append(gz.astgen.gpa, .{ 12300 .tag = tag, 12301 .data = .{ .un_node = .{ 12302 .operand = operand, 12303 .src_node = gz.nodeIndexToRelative(src_node), 12304 } }, 12305 }); 12306 return new_index; 12307 } 12308 12309 fn addPlNode( 12310 gz: *GenZir, 12311 tag: Zir.Inst.Tag, 12312 /// Absolute node index. This function does the conversion to offset from Decl. 12313 src_node: Ast.Node.Index, 12314 extra: anytype, 12315 ) !Zir.Inst.Ref { 12316 const gpa = gz.astgen.gpa; 12317 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12318 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 12319 12320 const payload_index = try gz.astgen.addExtra(extra); 12321 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12322 gz.astgen.instructions.appendAssumeCapacity(.{ 12323 .tag = tag, 12324 .data = .{ .pl_node = .{ 12325 .src_node = gz.nodeIndexToRelative(src_node), 12326 .payload_index = payload_index, 12327 } }, 12328 }); 12329 gz.instructions.appendAssumeCapacity(new_index); 12330 return new_index.toRef(); 12331 } 12332 12333 fn addPlNodePayloadIndex( 12334 gz: *GenZir, 12335 tag: Zir.Inst.Tag, 12336 /// Absolute node index. This function does the conversion to offset from Decl. 12337 src_node: Ast.Node.Index, 12338 payload_index: u32, 12339 ) !Zir.Inst.Ref { 12340 return try gz.add(.{ 12341 .tag = tag, 12342 .data = .{ .pl_node = .{ 12343 .src_node = gz.nodeIndexToRelative(src_node), 12344 .payload_index = payload_index, 12345 } }, 12346 }); 12347 } 12348 12349 /// Supports `param_gz` stacked on `gz`. Assumes nothing stacked on `param_gz`. Unstacks `param_gz`. 12350 fn addParam( 12351 gz: *GenZir, 12352 param_gz: *GenZir, 12353 /// Previous parameters, which might be referenced in `param_gz` (the new parameter type). 12354 /// `ref`s of these instructions will be put into this param's type body, and removed from `AstGen.ref_table`. 12355 prev_param_insts: []const Zir.Inst.Index, 12356 ty_is_generic: bool, 12357 tag: Zir.Inst.Tag, 12358 /// Absolute token index. This function does the conversion to Decl offset. 12359 abs_tok_index: Ast.TokenIndex, 12360 name: Zir.NullTerminatedString, 12361 ) !Zir.Inst.Index { 12362 const gpa = gz.astgen.gpa; 12363 const param_body = param_gz.instructionsSlice(); 12364 const body_len = gz.astgen.countBodyLenAfterFixupsExtraRefs(param_body, prev_param_insts); 12365 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 12366 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).@"struct".fields.len + body_len); 12367 12368 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{ 12369 .name = name, 12370 .type = .{ 12371 .body_len = @intCast(body_len), 12372 .is_generic = ty_is_generic, 12373 }, 12374 }); 12375 gz.astgen.appendBodyWithFixupsExtraRefsArrayList(&gz.astgen.extra, param_body, prev_param_insts); 12376 param_gz.unstack(); 12377 12378 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12379 gz.astgen.instructions.appendAssumeCapacity(.{ 12380 .tag = tag, 12381 .data = .{ .pl_tok = .{ 12382 .src_tok = gz.tokenIndexToRelative(abs_tok_index), 12383 .payload_index = payload_index, 12384 } }, 12385 }); 12386 gz.instructions.appendAssumeCapacity(new_index); 12387 return new_index; 12388 } 12389 12390 fn addBuiltinValue(gz: *GenZir, src_node: Ast.Node.Index, val: Zir.Inst.BuiltinValue) !Zir.Inst.Ref { 12391 return addExtendedNodeSmall(gz, .builtin_value, src_node, @intFromEnum(val)); 12392 } 12393 12394 fn addExtendedPayload(gz: *GenZir, opcode: Zir.Inst.Extended, extra: anytype) !Zir.Inst.Ref { 12395 return addExtendedPayloadSmall(gz, opcode, undefined, extra); 12396 } 12397 12398 fn addExtendedPayloadSmall( 12399 gz: *GenZir, 12400 opcode: Zir.Inst.Extended, 12401 small: u16, 12402 extra: anytype, 12403 ) !Zir.Inst.Ref { 12404 const gpa = gz.astgen.gpa; 12405 12406 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12407 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 12408 12409 const payload_index = try gz.astgen.addExtra(extra); 12410 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12411 gz.astgen.instructions.appendAssumeCapacity(.{ 12412 .tag = .extended, 12413 .data = .{ .extended = .{ 12414 .opcode = opcode, 12415 .small = small, 12416 .operand = payload_index, 12417 } }, 12418 }); 12419 gz.instructions.appendAssumeCapacity(new_index); 12420 return new_index.toRef(); 12421 } 12422 12423 fn addExtendedMultiOp( 12424 gz: *GenZir, 12425 opcode: Zir.Inst.Extended, 12426 node: Ast.Node.Index, 12427 operands: []const Zir.Inst.Ref, 12428 ) !Zir.Inst.Ref { 12429 const astgen = gz.astgen; 12430 const gpa = astgen.gpa; 12431 12432 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12433 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12434 try astgen.extra.ensureUnusedCapacity( 12435 gpa, 12436 @typeInfo(Zir.Inst.NodeMultiOp).@"struct".fields.len + operands.len, 12437 ); 12438 12439 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{ 12440 .src_node = gz.nodeIndexToRelative(node), 12441 }); 12442 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12443 astgen.instructions.appendAssumeCapacity(.{ 12444 .tag = .extended, 12445 .data = .{ .extended = .{ 12446 .opcode = opcode, 12447 .small = @intCast(operands.len), 12448 .operand = payload_index, 12449 } }, 12450 }); 12451 gz.instructions.appendAssumeCapacity(new_index); 12452 astgen.appendRefsAssumeCapacity(operands); 12453 return new_index.toRef(); 12454 } 12455 12456 fn addExtendedMultiOpPayloadIndex( 12457 gz: *GenZir, 12458 opcode: Zir.Inst.Extended, 12459 payload_index: u32, 12460 trailing_len: usize, 12461 ) !Zir.Inst.Ref { 12462 const astgen = gz.astgen; 12463 const gpa = astgen.gpa; 12464 12465 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12466 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12467 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12468 astgen.instructions.appendAssumeCapacity(.{ 12469 .tag = .extended, 12470 .data = .{ .extended = .{ 12471 .opcode = opcode, 12472 .small = @intCast(trailing_len), 12473 .operand = payload_index, 12474 } }, 12475 }); 12476 gz.instructions.appendAssumeCapacity(new_index); 12477 return new_index.toRef(); 12478 } 12479 12480 fn addExtendedNodeSmall( 12481 gz: *GenZir, 12482 opcode: Zir.Inst.Extended, 12483 src_node: Ast.Node.Index, 12484 small: u16, 12485 ) !Zir.Inst.Ref { 12486 const astgen = gz.astgen; 12487 const gpa = astgen.gpa; 12488 12489 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12490 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12491 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12492 astgen.instructions.appendAssumeCapacity(.{ 12493 .tag = .extended, 12494 .data = .{ .extended = .{ 12495 .opcode = opcode, 12496 .small = small, 12497 .operand = @bitCast(@intFromEnum(gz.nodeIndexToRelative(src_node))), 12498 } }, 12499 }); 12500 gz.instructions.appendAssumeCapacity(new_index); 12501 return new_index.toRef(); 12502 } 12503 12504 fn addUnTok( 12505 gz: *GenZir, 12506 tag: Zir.Inst.Tag, 12507 operand: Zir.Inst.Ref, 12508 /// Absolute token index. This function does the conversion to Decl offset. 12509 abs_tok_index: Ast.TokenIndex, 12510 ) !Zir.Inst.Ref { 12511 assert(operand != .none); 12512 return gz.add(.{ 12513 .tag = tag, 12514 .data = .{ .un_tok = .{ 12515 .operand = operand, 12516 .src_tok = gz.tokenIndexToRelative(abs_tok_index), 12517 } }, 12518 }); 12519 } 12520 12521 fn makeUnTok( 12522 gz: *GenZir, 12523 tag: Zir.Inst.Tag, 12524 operand: Zir.Inst.Ref, 12525 /// Absolute token index. This function does the conversion to Decl offset. 12526 abs_tok_index: Ast.TokenIndex, 12527 ) !Zir.Inst.Index { 12528 const astgen = gz.astgen; 12529 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12530 assert(operand != .none); 12531 try astgen.instructions.append(astgen.gpa, .{ 12532 .tag = tag, 12533 .data = .{ .un_tok = .{ 12534 .operand = operand, 12535 .src_tok = gz.tokenIndexToRelative(abs_tok_index), 12536 } }, 12537 }); 12538 return new_index; 12539 } 12540 12541 fn addStrTok( 12542 gz: *GenZir, 12543 tag: Zir.Inst.Tag, 12544 str_index: Zir.NullTerminatedString, 12545 /// Absolute token index. This function does the conversion to Decl offset. 12546 abs_tok_index: Ast.TokenIndex, 12547 ) !Zir.Inst.Ref { 12548 return gz.add(.{ 12549 .tag = tag, 12550 .data = .{ .str_tok = .{ 12551 .start = str_index, 12552 .src_tok = gz.tokenIndexToRelative(abs_tok_index), 12553 } }, 12554 }); 12555 } 12556 12557 fn addSaveErrRetIndex( 12558 gz: *GenZir, 12559 cond: union(enum) { 12560 always: void, 12561 if_of_error_type: Zir.Inst.Ref, 12562 }, 12563 ) !Zir.Inst.Index { 12564 return gz.addAsIndex(.{ 12565 .tag = .save_err_ret_index, 12566 .data = .{ .save_err_ret_index = .{ 12567 .operand = switch (cond) { 12568 .if_of_error_type => |x| x, 12569 else => .none, 12570 }, 12571 } }, 12572 }); 12573 } 12574 12575 const BranchTarget = union(enum) { 12576 ret, 12577 block: Zir.Inst.Index, 12578 }; 12579 12580 fn addRestoreErrRetIndex( 12581 gz: *GenZir, 12582 bt: BranchTarget, 12583 cond: union(enum) { 12584 always: void, 12585 if_non_error: Zir.Inst.Ref, 12586 }, 12587 src_node: Ast.Node.Index, 12588 ) !Zir.Inst.Index { 12589 switch (cond) { 12590 .always => return gz.addAsIndex(.{ 12591 .tag = .restore_err_ret_index_unconditional, 12592 .data = .{ .un_node = .{ 12593 .operand = switch (bt) { 12594 .ret => .none, 12595 .block => |b| b.toRef(), 12596 }, 12597 .src_node = gz.nodeIndexToRelative(src_node), 12598 } }, 12599 }), 12600 .if_non_error => |operand| switch (bt) { 12601 .ret => return gz.addAsIndex(.{ 12602 .tag = .restore_err_ret_index_fn_entry, 12603 .data = .{ .un_node = .{ 12604 .operand = operand, 12605 .src_node = gz.nodeIndexToRelative(src_node), 12606 } }, 12607 }), 12608 .block => |block| return (try gz.addExtendedPayload( 12609 .restore_err_ret_index, 12610 Zir.Inst.RestoreErrRetIndex{ 12611 .src_node = gz.nodeIndexToRelative(src_node), 12612 .block = block.toRef(), 12613 .operand = operand, 12614 }, 12615 )).toIndex().?, 12616 }, 12617 } 12618 } 12619 12620 fn addBreak( 12621 gz: *GenZir, 12622 tag: Zir.Inst.Tag, 12623 block_inst: Zir.Inst.Index, 12624 operand: Zir.Inst.Ref, 12625 ) !Zir.Inst.Index { 12626 const gpa = gz.astgen.gpa; 12627 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12628 12629 const new_index = try gz.makeBreak(tag, block_inst, operand); 12630 gz.instructions.appendAssumeCapacity(new_index); 12631 return new_index; 12632 } 12633 12634 fn makeBreak( 12635 gz: *GenZir, 12636 tag: Zir.Inst.Tag, 12637 block_inst: Zir.Inst.Index, 12638 operand: Zir.Inst.Ref, 12639 ) !Zir.Inst.Index { 12640 return gz.makeBreakCommon(tag, block_inst, operand, null); 12641 } 12642 12643 fn addBreakWithSrcNode( 12644 gz: *GenZir, 12645 tag: Zir.Inst.Tag, 12646 block_inst: Zir.Inst.Index, 12647 operand: Zir.Inst.Ref, 12648 operand_src_node: Ast.Node.Index, 12649 ) !Zir.Inst.Index { 12650 const gpa = gz.astgen.gpa; 12651 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12652 12653 const new_index = try gz.makeBreakWithSrcNode(tag, block_inst, operand, operand_src_node); 12654 gz.instructions.appendAssumeCapacity(new_index); 12655 return new_index; 12656 } 12657 12658 fn makeBreakWithSrcNode( 12659 gz: *GenZir, 12660 tag: Zir.Inst.Tag, 12661 block_inst: Zir.Inst.Index, 12662 operand: Zir.Inst.Ref, 12663 operand_src_node: Ast.Node.Index, 12664 ) !Zir.Inst.Index { 12665 return gz.makeBreakCommon(tag, block_inst, operand, operand_src_node); 12666 } 12667 12668 fn makeBreakCommon( 12669 gz: *GenZir, 12670 tag: Zir.Inst.Tag, 12671 block_inst: Zir.Inst.Index, 12672 operand: Zir.Inst.Ref, 12673 operand_src_node: ?Ast.Node.Index, 12674 ) !Zir.Inst.Index { 12675 const gpa = gz.astgen.gpa; 12676 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 12677 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Break).@"struct".fields.len); 12678 12679 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12680 gz.astgen.instructions.appendAssumeCapacity(.{ 12681 .tag = tag, 12682 .data = .{ .@"break" = .{ 12683 .operand = operand, 12684 .payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Break{ 12685 .operand_src_node = if (operand_src_node) |src_node| 12686 gz.nodeIndexToRelative(src_node).toOptional() 12687 else 12688 .none, 12689 .block_inst = block_inst, 12690 }), 12691 } }, 12692 }); 12693 return new_index; 12694 } 12695 12696 fn addBin( 12697 gz: *GenZir, 12698 tag: Zir.Inst.Tag, 12699 lhs: Zir.Inst.Ref, 12700 rhs: Zir.Inst.Ref, 12701 ) !Zir.Inst.Ref { 12702 assert(lhs != .none); 12703 assert(rhs != .none); 12704 return gz.add(.{ 12705 .tag = tag, 12706 .data = .{ .bin = .{ 12707 .lhs = lhs, 12708 .rhs = rhs, 12709 } }, 12710 }); 12711 } 12712 12713 fn addDefer(gz: *GenZir, index: u32, len: u32) !void { 12714 _ = try gz.add(.{ 12715 .tag = .@"defer", 12716 .data = .{ .@"defer" = .{ 12717 .index = index, 12718 .len = len, 12719 } }, 12720 }); 12721 } 12722 12723 fn addDecl( 12724 gz: *GenZir, 12725 tag: Zir.Inst.Tag, 12726 decl_index: u32, 12727 src_node: Ast.Node.Index, 12728 ) !Zir.Inst.Ref { 12729 return gz.add(.{ 12730 .tag = tag, 12731 .data = .{ .pl_node = .{ 12732 .src_node = gz.nodeIndexToRelative(src_node), 12733 .payload_index = decl_index, 12734 } }, 12735 }); 12736 } 12737 12738 fn addNode( 12739 gz: *GenZir, 12740 tag: Zir.Inst.Tag, 12741 /// Absolute node index. This function does the conversion to offset from Decl. 12742 src_node: Ast.Node.Index, 12743 ) !Zir.Inst.Ref { 12744 return gz.add(.{ 12745 .tag = tag, 12746 .data = .{ .node = gz.nodeIndexToRelative(src_node) }, 12747 }); 12748 } 12749 12750 fn addInstNode( 12751 gz: *GenZir, 12752 tag: Zir.Inst.Tag, 12753 inst: Zir.Inst.Index, 12754 /// Absolute node index. This function does the conversion to offset from Decl. 12755 src_node: Ast.Node.Index, 12756 ) !Zir.Inst.Ref { 12757 return gz.add(.{ 12758 .tag = tag, 12759 .data = .{ .inst_node = .{ 12760 .inst = inst, 12761 .src_node = gz.nodeIndexToRelative(src_node), 12762 } }, 12763 }); 12764 } 12765 12766 fn addNodeExtended( 12767 gz: *GenZir, 12768 opcode: Zir.Inst.Extended, 12769 /// Absolute node index. This function does the conversion to offset from Decl. 12770 src_node: Ast.Node.Index, 12771 ) !Zir.Inst.Ref { 12772 return gz.add(.{ 12773 .tag = .extended, 12774 .data = .{ .extended = .{ 12775 .opcode = opcode, 12776 .small = undefined, 12777 .operand = @bitCast(@intFromEnum(gz.nodeIndexToRelative(src_node))), 12778 } }, 12779 }); 12780 } 12781 12782 fn addAllocExtended( 12783 gz: *GenZir, 12784 args: struct { 12785 /// Absolute node index. This function does the conversion to offset from Decl. 12786 node: Ast.Node.Index, 12787 type_inst: Zir.Inst.Ref, 12788 align_inst: Zir.Inst.Ref, 12789 is_const: bool, 12790 is_comptime: bool, 12791 }, 12792 ) !Zir.Inst.Ref { 12793 const astgen = gz.astgen; 12794 const gpa = astgen.gpa; 12795 12796 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12797 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12798 try astgen.extra.ensureUnusedCapacity( 12799 gpa, 12800 @typeInfo(Zir.Inst.AllocExtended).@"struct".fields.len + 12801 @intFromBool(args.type_inst != .none) + 12802 @intFromBool(args.align_inst != .none), 12803 ); 12804 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.AllocExtended{ 12805 .src_node = gz.nodeIndexToRelative(args.node), 12806 }); 12807 if (args.type_inst != .none) { 12808 astgen.extra.appendAssumeCapacity(@intFromEnum(args.type_inst)); 12809 } 12810 if (args.align_inst != .none) { 12811 astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst)); 12812 } 12813 12814 const has_type: u4 = @intFromBool(args.type_inst != .none); 12815 const has_align: u4 = @intFromBool(args.align_inst != .none); 12816 const is_const: u4 = @intFromBool(args.is_const); 12817 const is_comptime: u4 = @intFromBool(args.is_comptime); 12818 const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3); 12819 12820 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12821 astgen.instructions.appendAssumeCapacity(.{ 12822 .tag = .extended, 12823 .data = .{ .extended = .{ 12824 .opcode = .alloc, 12825 .small = small, 12826 .operand = payload_index, 12827 } }, 12828 }); 12829 gz.instructions.appendAssumeCapacity(new_index); 12830 return new_index.toRef(); 12831 } 12832 12833 fn addAsm( 12834 gz: *GenZir, 12835 args: struct { 12836 tag: Zir.Inst.Extended, 12837 /// Absolute node index. This function does the conversion to offset from Decl. 12838 node: Ast.Node.Index, 12839 asm_source: Zir.NullTerminatedString, 12840 output_type_bits: u32, 12841 is_volatile: bool, 12842 outputs: []const Zir.Inst.Asm.Output, 12843 inputs: []const Zir.Inst.Asm.Input, 12844 clobbers: Zir.Inst.Ref, 12845 }, 12846 ) !Zir.Inst.Ref { 12847 const astgen = gz.astgen; 12848 const gpa = astgen.gpa; 12849 12850 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12851 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12852 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Asm).@"struct".fields.len + 12853 args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).@"struct".fields.len + 12854 args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).@"struct".fields.len); 12855 12856 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{ 12857 .src_node = gz.nodeIndexToRelative(args.node), 12858 .asm_source = args.asm_source, 12859 .output_type_bits = args.output_type_bits, 12860 .clobbers = args.clobbers, 12861 }); 12862 for (args.outputs) |output| { 12863 _ = gz.astgen.addExtraAssumeCapacity(output); 12864 } 12865 for (args.inputs) |input| { 12866 _ = gz.astgen.addExtraAssumeCapacity(input); 12867 } 12868 12869 const small: Zir.Inst.Asm.Small = .{ 12870 .outputs_len = @intCast(args.outputs.len), 12871 .inputs_len = @intCast(args.inputs.len), 12872 .is_volatile = args.is_volatile, 12873 }; 12874 12875 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12876 astgen.instructions.appendAssumeCapacity(.{ 12877 .tag = .extended, 12878 .data = .{ .extended = .{ 12879 .opcode = args.tag, 12880 .small = @bitCast(small), 12881 .operand = payload_index, 12882 } }, 12883 }); 12884 gz.instructions.appendAssumeCapacity(new_index); 12885 return new_index.toRef(); 12886 } 12887 12888 /// Note that this returns a `Zir.Inst.Index` not a ref. 12889 /// Does *not* append the block instruction to the scope. 12890 /// Leaves the `payload_index` field undefined. 12891 fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index { 12892 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12893 const gpa = gz.astgen.gpa; 12894 try gz.astgen.instructions.append(gpa, .{ 12895 .tag = tag, 12896 .data = .{ .pl_node = .{ 12897 .src_node = gz.nodeIndexToRelative(node), 12898 .payload_index = undefined, 12899 } }, 12900 }); 12901 return new_index; 12902 } 12903 12904 /// Note that this returns a `Zir.Inst.Index` not a ref. 12905 /// Does *not* append the block instruction to the scope. 12906 /// Leaves the `payload_index` field undefined. Use `setDeclaration` to finalize. 12907 fn makeDeclaration(gz: *GenZir, node: Ast.Node.Index) !Zir.Inst.Index { 12908 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12909 try gz.astgen.instructions.append(gz.astgen.gpa, .{ 12910 .tag = .declaration, 12911 .data = .{ .declaration = .{ 12912 .src_node = node, 12913 .payload_index = undefined, 12914 } }, 12915 }); 12916 return new_index; 12917 } 12918 12919 /// Note that this returns a `Zir.Inst.Index` not a ref. 12920 /// Leaves the `payload_index` field undefined. 12921 fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index { 12922 const gpa = gz.astgen.gpa; 12923 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12924 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12925 try gz.astgen.instructions.append(gpa, .{ 12926 .tag = tag, 12927 .data = .{ .pl_node = .{ 12928 .src_node = gz.nodeIndexToRelative(node), 12929 .payload_index = undefined, 12930 } }, 12931 }); 12932 gz.instructions.appendAssumeCapacity(new_index); 12933 return new_index; 12934 } 12935 12936 fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct { 12937 src_node: Ast.Node.Index, 12938 captures_len: u32, 12939 fields_len: u32, 12940 decls_len: u32, 12941 has_backing_int: bool, 12942 layout: std.builtin.Type.ContainerLayout, 12943 known_non_opv: bool, 12944 known_comptime_only: bool, 12945 any_comptime_fields: bool, 12946 any_default_inits: bool, 12947 any_aligned_fields: bool, 12948 fields_hash: std.zig.SrcHash, 12949 name_strat: Zir.Inst.NameStrategy, 12950 }) !void { 12951 const astgen = gz.astgen; 12952 const gpa = astgen.gpa; 12953 12954 // Node .root is valid for the root `struct_decl` of a file! 12955 assert(args.src_node != .root or gz.parent.tag == .top); 12956 12957 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); 12958 12959 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len + 3); 12960 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.StructDecl{ 12961 .fields_hash_0 = fields_hash_arr[0], 12962 .fields_hash_1 = fields_hash_arr[1], 12963 .fields_hash_2 = fields_hash_arr[2], 12964 .fields_hash_3 = fields_hash_arr[3], 12965 .src_line = astgen.source_line, 12966 .src_node = args.src_node, 12967 }); 12968 12969 if (args.captures_len != 0) { 12970 astgen.extra.appendAssumeCapacity(args.captures_len); 12971 } 12972 if (args.fields_len != 0) { 12973 astgen.extra.appendAssumeCapacity(args.fields_len); 12974 } 12975 if (args.decls_len != 0) { 12976 astgen.extra.appendAssumeCapacity(args.decls_len); 12977 } 12978 astgen.instructions.set(@intFromEnum(inst), .{ 12979 .tag = .extended, 12980 .data = .{ .extended = .{ 12981 .opcode = .struct_decl, 12982 .small = @bitCast(Zir.Inst.StructDecl.Small{ 12983 .has_captures_len = args.captures_len != 0, 12984 .has_fields_len = args.fields_len != 0, 12985 .has_decls_len = args.decls_len != 0, 12986 .has_backing_int = args.has_backing_int, 12987 .known_non_opv = args.known_non_opv, 12988 .known_comptime_only = args.known_comptime_only, 12989 .name_strategy = args.name_strat, 12990 .layout = args.layout, 12991 .any_comptime_fields = args.any_comptime_fields, 12992 .any_default_inits = args.any_default_inits, 12993 .any_aligned_fields = args.any_aligned_fields, 12994 }), 12995 .operand = payload_index, 12996 } }, 12997 }); 12998 } 12999 13000 fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct { 13001 src_node: Ast.Node.Index, 13002 tag_type: Zir.Inst.Ref, 13003 captures_len: u32, 13004 body_len: u32, 13005 fields_len: u32, 13006 decls_len: u32, 13007 layout: std.builtin.Type.ContainerLayout, 13008 auto_enum_tag: bool, 13009 any_aligned_fields: bool, 13010 fields_hash: std.zig.SrcHash, 13011 name_strat: Zir.Inst.NameStrategy, 13012 }) !void { 13013 const astgen = gz.astgen; 13014 const gpa = astgen.gpa; 13015 13016 assert(args.src_node != .root); 13017 13018 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); 13019 13020 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).@"struct".fields.len + 5); 13021 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.UnionDecl{ 13022 .fields_hash_0 = fields_hash_arr[0], 13023 .fields_hash_1 = fields_hash_arr[1], 13024 .fields_hash_2 = fields_hash_arr[2], 13025 .fields_hash_3 = fields_hash_arr[3], 13026 .src_line = astgen.source_line, 13027 .src_node = args.src_node, 13028 }); 13029 13030 if (args.tag_type != .none) { 13031 astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type)); 13032 } 13033 if (args.captures_len != 0) { 13034 astgen.extra.appendAssumeCapacity(args.captures_len); 13035 } 13036 if (args.body_len != 0) { 13037 astgen.extra.appendAssumeCapacity(args.body_len); 13038 } 13039 if (args.fields_len != 0) { 13040 astgen.extra.appendAssumeCapacity(args.fields_len); 13041 } 13042 if (args.decls_len != 0) { 13043 astgen.extra.appendAssumeCapacity(args.decls_len); 13044 } 13045 astgen.instructions.set(@intFromEnum(inst), .{ 13046 .tag = .extended, 13047 .data = .{ .extended = .{ 13048 .opcode = .union_decl, 13049 .small = @bitCast(Zir.Inst.UnionDecl.Small{ 13050 .has_tag_type = args.tag_type != .none, 13051 .has_captures_len = args.captures_len != 0, 13052 .has_body_len = args.body_len != 0, 13053 .has_fields_len = args.fields_len != 0, 13054 .has_decls_len = args.decls_len != 0, 13055 .name_strategy = args.name_strat, 13056 .layout = args.layout, 13057 .auto_enum_tag = args.auto_enum_tag, 13058 .any_aligned_fields = args.any_aligned_fields, 13059 }), 13060 .operand = payload_index, 13061 } }, 13062 }); 13063 } 13064 13065 fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct { 13066 src_node: Ast.Node.Index, 13067 tag_type: Zir.Inst.Ref, 13068 captures_len: u32, 13069 body_len: u32, 13070 fields_len: u32, 13071 decls_len: u32, 13072 nonexhaustive: bool, 13073 fields_hash: std.zig.SrcHash, 13074 name_strat: Zir.Inst.NameStrategy, 13075 }) !void { 13076 const astgen = gz.astgen; 13077 const gpa = astgen.gpa; 13078 13079 assert(args.src_node != .root); 13080 13081 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); 13082 13083 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.EnumDecl).@"struct".fields.len + 5); 13084 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.EnumDecl{ 13085 .fields_hash_0 = fields_hash_arr[0], 13086 .fields_hash_1 = fields_hash_arr[1], 13087 .fields_hash_2 = fields_hash_arr[2], 13088 .fields_hash_3 = fields_hash_arr[3], 13089 .src_line = astgen.source_line, 13090 .src_node = args.src_node, 13091 }); 13092 13093 if (args.tag_type != .none) { 13094 astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type)); 13095 } 13096 if (args.captures_len != 0) { 13097 astgen.extra.appendAssumeCapacity(args.captures_len); 13098 } 13099 if (args.body_len != 0) { 13100 astgen.extra.appendAssumeCapacity(args.body_len); 13101 } 13102 if (args.fields_len != 0) { 13103 astgen.extra.appendAssumeCapacity(args.fields_len); 13104 } 13105 if (args.decls_len != 0) { 13106 astgen.extra.appendAssumeCapacity(args.decls_len); 13107 } 13108 astgen.instructions.set(@intFromEnum(inst), .{ 13109 .tag = .extended, 13110 .data = .{ .extended = .{ 13111 .opcode = .enum_decl, 13112 .small = @bitCast(Zir.Inst.EnumDecl.Small{ 13113 .has_tag_type = args.tag_type != .none, 13114 .has_captures_len = args.captures_len != 0, 13115 .has_body_len = args.body_len != 0, 13116 .has_fields_len = args.fields_len != 0, 13117 .has_decls_len = args.decls_len != 0, 13118 .name_strategy = args.name_strat, 13119 .nonexhaustive = args.nonexhaustive, 13120 }), 13121 .operand = payload_index, 13122 } }, 13123 }); 13124 } 13125 13126 fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct { 13127 src_node: Ast.Node.Index, 13128 captures_len: u32, 13129 decls_len: u32, 13130 name_strat: Zir.Inst.NameStrategy, 13131 }) !void { 13132 const astgen = gz.astgen; 13133 const gpa = astgen.gpa; 13134 13135 assert(args.src_node != .root); 13136 13137 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).@"struct".fields.len + 2); 13138 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.OpaqueDecl{ 13139 .src_line = astgen.source_line, 13140 .src_node = args.src_node, 13141 }); 13142 13143 if (args.captures_len != 0) { 13144 astgen.extra.appendAssumeCapacity(args.captures_len); 13145 } 13146 if (args.decls_len != 0) { 13147 astgen.extra.appendAssumeCapacity(args.decls_len); 13148 } 13149 astgen.instructions.set(@intFromEnum(inst), .{ 13150 .tag = .extended, 13151 .data = .{ .extended = .{ 13152 .opcode = .opaque_decl, 13153 .small = @bitCast(Zir.Inst.OpaqueDecl.Small{ 13154 .has_captures_len = args.captures_len != 0, 13155 .has_decls_len = args.decls_len != 0, 13156 .name_strategy = args.name_strat, 13157 }), 13158 .operand = payload_index, 13159 } }, 13160 }); 13161 } 13162 13163 fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref { 13164 return (try gz.addAsIndex(inst)).toRef(); 13165 } 13166 13167 fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index { 13168 const gpa = gz.astgen.gpa; 13169 try gz.instructions.ensureUnusedCapacity(gpa, 1); 13170 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 13171 13172 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 13173 gz.astgen.instructions.appendAssumeCapacity(inst); 13174 gz.instructions.appendAssumeCapacity(new_index); 13175 return new_index; 13176 } 13177 13178 fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index { 13179 const gpa = gz.astgen.gpa; 13180 try gz.instructions.ensureUnusedCapacity(gpa, 1); 13181 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 13182 13183 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 13184 gz.astgen.instructions.len += 1; 13185 gz.instructions.appendAssumeCapacity(new_index); 13186 return new_index; 13187 } 13188 13189 fn addRet(gz: *GenZir, ri: ResultInfo, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void { 13190 switch (ri.rl) { 13191 .ptr => |ptr_res| _ = try gz.addUnNode(.ret_load, ptr_res.inst, node), 13192 .coerced_ty => _ = try gz.addUnNode(.ret_node, operand, node), 13193 else => unreachable, 13194 } 13195 } 13196 13197 fn addDbgVar(gz: *GenZir, tag: Zir.Inst.Tag, name: Zir.NullTerminatedString, inst: Zir.Inst.Ref) !void { 13198 if (gz.is_comptime) return; 13199 13200 _ = try gz.add(.{ .tag = tag, .data = .{ 13201 .str_op = .{ 13202 .str = name, 13203 .operand = inst, 13204 }, 13205 } }); 13206 } 13207 }; 13208 13209 /// This can only be for short-lived references; the memory becomes invalidated 13210 /// when another string is added. 13211 fn nullTerminatedString(astgen: AstGen, index: Zir.NullTerminatedString) [*:0]const u8 { 13212 return @ptrCast(astgen.string_bytes.items[@intFromEnum(index)..]); 13213 } 13214 13215 /// Local variables shadowing detection, including function parameters. 13216 fn detectLocalShadowing( 13217 astgen: *AstGen, 13218 scope: *Scope, 13219 ident_name: Zir.NullTerminatedString, 13220 name_token: Ast.TokenIndex, 13221 token_bytes: []const u8, 13222 id_cat: Scope.IdCat, 13223 ) !void { 13224 const gpa = astgen.gpa; 13225 if (token_bytes[0] != '@' and isPrimitive(token_bytes)) { 13226 return astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{ 13227 token_bytes, 13228 }, &[_]u32{ 13229 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{ 13230 token_bytes, 13231 }), 13232 }); 13233 } 13234 13235 var s = scope; 13236 var outer_scope = false; 13237 while (true) switch (s.tag) { 13238 .local_val => { 13239 const local_val = s.cast(Scope.LocalVal).?; 13240 if (local_val.name == ident_name) { 13241 const name_slice = mem.span(astgen.nullTerminatedString(ident_name)); 13242 const name = try gpa.dupe(u8, name_slice); 13243 defer gpa.free(name); 13244 if (outer_scope) { 13245 return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{ 13246 @tagName(id_cat), name, @tagName(local_val.id_cat), 13247 }, &[_]u32{ 13248 try astgen.errNoteTok( 13249 local_val.token_src, 13250 "previous declaration here", 13251 .{}, 13252 ), 13253 }); 13254 } 13255 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{ 13256 @tagName(local_val.id_cat), name, 13257 }, &[_]u32{ 13258 try astgen.errNoteTok( 13259 local_val.token_src, 13260 "previous declaration here", 13261 .{}, 13262 ), 13263 }); 13264 } 13265 s = local_val.parent; 13266 }, 13267 .local_ptr => { 13268 const local_ptr = s.cast(Scope.LocalPtr).?; 13269 if (local_ptr.name == ident_name) { 13270 const name_slice = mem.span(astgen.nullTerminatedString(ident_name)); 13271 const name = try gpa.dupe(u8, name_slice); 13272 defer gpa.free(name); 13273 if (outer_scope) { 13274 return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{ 13275 @tagName(id_cat), name, @tagName(local_ptr.id_cat), 13276 }, &[_]u32{ 13277 try astgen.errNoteTok( 13278 local_ptr.token_src, 13279 "previous declaration here", 13280 .{}, 13281 ), 13282 }); 13283 } 13284 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{ 13285 @tagName(local_ptr.id_cat), name, 13286 }, &[_]u32{ 13287 try astgen.errNoteTok( 13288 local_ptr.token_src, 13289 "previous declaration here", 13290 .{}, 13291 ), 13292 }); 13293 } 13294 s = local_ptr.parent; 13295 }, 13296 .namespace => { 13297 outer_scope = true; 13298 const ns = s.cast(Scope.Namespace).?; 13299 const decl_node = ns.decls.get(ident_name) orelse { 13300 s = ns.parent; 13301 continue; 13302 }; 13303 const name_slice = mem.span(astgen.nullTerminatedString(ident_name)); 13304 const name = try gpa.dupe(u8, name_slice); 13305 defer gpa.free(name); 13306 return astgen.failTokNotes(name_token, "{s} shadows declaration of '{s}'", .{ 13307 @tagName(id_cat), name, 13308 }, &[_]u32{ 13309 try astgen.errNoteNode(decl_node, "declared here", .{}), 13310 }); 13311 }, 13312 .gen_zir => { 13313 s = s.cast(GenZir).?.parent; 13314 outer_scope = true; 13315 }, 13316 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, 13317 .top => break, 13318 }; 13319 } 13320 13321 const LineColumn = struct { u32, u32 }; 13322 13323 /// Advances the source cursor to the main token of `node` if not in comptime scope. 13324 /// Usually paired with `emitDbgStmt`. 13325 fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) LineColumn { 13326 if (gz.is_comptime) return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column }; 13327 13328 const tree = gz.astgen.tree; 13329 const node_start = tree.tokenStart(tree.nodeMainToken(node)); 13330 gz.astgen.advanceSourceCursor(node_start); 13331 13332 return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column }; 13333 } 13334 13335 /// Advances the source cursor to the beginning of `node`. 13336 fn advanceSourceCursorToNode(astgen: *AstGen, node: Ast.Node.Index) void { 13337 const tree = astgen.tree; 13338 const node_start = tree.tokenStart(tree.firstToken(node)); 13339 astgen.advanceSourceCursor(node_start); 13340 } 13341 13342 /// Advances the source cursor to an absolute byte offset `end` in the file. 13343 fn advanceSourceCursor(astgen: *AstGen, end: usize) void { 13344 const source = astgen.tree.source; 13345 var i = astgen.source_offset; 13346 var line = astgen.source_line; 13347 var column = astgen.source_column; 13348 assert(i <= end); 13349 while (i < end) : (i += 1) { 13350 if (source[i] == '\n') { 13351 line += 1; 13352 column = 0; 13353 } else { 13354 column += 1; 13355 } 13356 } 13357 astgen.source_offset = i; 13358 astgen.source_line = line; 13359 astgen.source_column = column; 13360 } 13361 13362 const SourceCursor = struct { 13363 offset: u32, 13364 line: u32, 13365 column: u32, 13366 }; 13367 13368 /// Get the current source cursor, to be restored later with `restoreSourceCursor`. 13369 /// This is useful when analyzing source code out-of-order. 13370 fn saveSourceCursor(astgen: *const AstGen) SourceCursor { 13371 return .{ 13372 .offset = astgen.source_offset, 13373 .line = astgen.source_line, 13374 .column = astgen.source_column, 13375 }; 13376 } 13377 fn restoreSourceCursor(astgen: *AstGen, cursor: SourceCursor) void { 13378 astgen.source_offset = cursor.offset; 13379 astgen.source_line = cursor.line; 13380 astgen.source_column = cursor.column; 13381 } 13382 13383 /// Detects name conflicts for decls and fields, and populates `namespace.decls` with all named declarations. 13384 /// Returns the number of declarations in the namespace, including unnamed declarations (e.g. `comptime` decls). 13385 fn scanContainer( 13386 astgen: *AstGen, 13387 namespace: *Scope.Namespace, 13388 members: []const Ast.Node.Index, 13389 container_kind: enum { @"struct", @"union", @"enum", @"opaque" }, 13390 ) !u32 { 13391 const gpa = astgen.gpa; 13392 const tree = astgen.tree; 13393 13394 var any_invalid_declarations = false; 13395 13396 // This type forms a linked list of source tokens declaring the same name. 13397 const NameEntry = struct { 13398 tok: Ast.TokenIndex, 13399 /// Using a linked list here simplifies memory management, and is acceptable since 13400 ///ewntries are only allocated in error situations. The entries are allocated into the 13401 /// AstGen arena. 13402 next: ?*@This(), 13403 }; 13404 13405 // The maps below are allocated into this SFBA to avoid using the GPA for small namespaces. 13406 var sfba_state = std.heap.stackFallback(512, astgen.gpa); 13407 const sfba = sfba_state.get(); 13408 13409 var names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty; 13410 var test_names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty; 13411 var decltest_names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty; 13412 defer { 13413 names.deinit(sfba); 13414 test_names.deinit(sfba); 13415 decltest_names.deinit(sfba); 13416 } 13417 13418 var any_duplicates = false; 13419 var decl_count: u32 = 0; 13420 for (members) |member_node| { 13421 const Kind = enum { decl, field }; 13422 const kind: Kind, const name_token = switch (tree.nodeTag(member_node)) { 13423 .container_field_init, 13424 .container_field_align, 13425 .container_field, 13426 => blk: { 13427 var full = tree.fullContainerField(member_node).?; 13428 switch (container_kind) { 13429 .@"struct", .@"opaque" => {}, 13430 .@"union", .@"enum" => full.convertToNonTupleLike(astgen.tree), 13431 } 13432 if (full.ast.tuple_like) continue; 13433 break :blk .{ .field, full.ast.main_token }; 13434 }, 13435 13436 .global_var_decl, 13437 .local_var_decl, 13438 .simple_var_decl, 13439 .aligned_var_decl, 13440 => blk: { 13441 decl_count += 1; 13442 break :blk .{ .decl, tree.nodeMainToken(member_node) + 1 }; 13443 }, 13444 13445 .fn_proto_simple, 13446 .fn_proto_multi, 13447 .fn_proto_one, 13448 .fn_proto, 13449 .fn_decl, 13450 => blk: { 13451 decl_count += 1; 13452 const ident = tree.nodeMainToken(member_node) + 1; 13453 if (tree.tokenTag(ident) != .identifier) { 13454 try astgen.appendErrorNode(member_node, "missing function name", .{}); 13455 any_invalid_declarations = true; 13456 continue; 13457 } 13458 break :blk .{ .decl, ident }; 13459 }, 13460 13461 .@"comptime" => { 13462 decl_count += 1; 13463 continue; 13464 }, 13465 13466 .test_decl => { 13467 decl_count += 1; 13468 // We don't want shadowing detection here, and test names work a bit differently, so 13469 // we must do the redeclaration detection ourselves. 13470 const test_name_token = tree.nodeMainToken(member_node) + 1; 13471 const new_ent: NameEntry = .{ 13472 .tok = test_name_token, 13473 .next = null, 13474 }; 13475 switch (tree.tokenTag(test_name_token)) { 13476 else => {}, // unnamed test 13477 .string_literal => { 13478 const name = try astgen.strLitAsString(test_name_token); 13479 const gop = try test_names.getOrPut(sfba, name.index); 13480 if (gop.found_existing) { 13481 var e = gop.value_ptr; 13482 while (e.next) |n| e = n; 13483 e.next = try astgen.arena.create(NameEntry); 13484 e.next.?.* = new_ent; 13485 any_duplicates = true; 13486 } else { 13487 gop.value_ptr.* = new_ent; 13488 } 13489 }, 13490 .identifier => { 13491 const name = try astgen.identAsString(test_name_token); 13492 const gop = try decltest_names.getOrPut(sfba, name); 13493 if (gop.found_existing) { 13494 var e = gop.value_ptr; 13495 while (e.next) |n| e = n; 13496 e.next = try astgen.arena.create(NameEntry); 13497 e.next.?.* = new_ent; 13498 any_duplicates = true; 13499 } else { 13500 gop.value_ptr.* = new_ent; 13501 } 13502 }, 13503 } 13504 continue; 13505 }, 13506 13507 else => unreachable, 13508 }; 13509 13510 const name_str_index = try astgen.identAsString(name_token); 13511 13512 if (kind == .decl) { 13513 // Put the name straight into `decls`, even if there are compile errors. 13514 // This avoids incorrect "undeclared identifier" errors later on. 13515 try namespace.decls.put(gpa, name_str_index, member_node); 13516 } 13517 13518 { 13519 const gop = try names.getOrPut(sfba, name_str_index); 13520 const new_ent: NameEntry = .{ 13521 .tok = name_token, 13522 .next = null, 13523 }; 13524 if (gop.found_existing) { 13525 var e = gop.value_ptr; 13526 while (e.next) |n| e = n; 13527 e.next = try astgen.arena.create(NameEntry); 13528 e.next.?.* = new_ent; 13529 any_duplicates = true; 13530 continue; 13531 } else { 13532 gop.value_ptr.* = new_ent; 13533 } 13534 } 13535 13536 // For fields, we only needed the duplicate check! Decls have some more checks to do, though. 13537 switch (kind) { 13538 .decl => {}, 13539 .field => continue, 13540 } 13541 13542 const token_bytes = astgen.tree.tokenSlice(name_token); 13543 if (token_bytes[0] != '@' and isPrimitive(token_bytes)) { 13544 try astgen.appendErrorTokNotes(name_token, "name shadows primitive '{s}'", .{ 13545 token_bytes, 13546 }, &.{ 13547 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{ 13548 token_bytes, 13549 }), 13550 }); 13551 any_invalid_declarations = true; 13552 continue; 13553 } 13554 13555 var s = namespace.parent; 13556 while (true) switch (s.tag) { 13557 .local_val => { 13558 const local_val = s.cast(Scope.LocalVal).?; 13559 if (local_val.name == name_str_index) { 13560 try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{ 13561 token_bytes, @tagName(local_val.id_cat), 13562 }, &.{ 13563 try astgen.errNoteTok( 13564 local_val.token_src, 13565 "previous declaration here", 13566 .{}, 13567 ), 13568 }); 13569 any_invalid_declarations = true; 13570 break; 13571 } 13572 s = local_val.parent; 13573 }, 13574 .local_ptr => { 13575 const local_ptr = s.cast(Scope.LocalPtr).?; 13576 if (local_ptr.name == name_str_index) { 13577 try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{ 13578 token_bytes, @tagName(local_ptr.id_cat), 13579 }, &.{ 13580 try astgen.errNoteTok( 13581 local_ptr.token_src, 13582 "previous declaration here", 13583 .{}, 13584 ), 13585 }); 13586 any_invalid_declarations = true; 13587 break; 13588 } 13589 s = local_ptr.parent; 13590 }, 13591 .namespace => s = s.cast(Scope.Namespace).?.parent, 13592 .gen_zir => s = s.cast(GenZir).?.parent, 13593 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, 13594 .top => break, 13595 }; 13596 } 13597 13598 if (!any_duplicates) { 13599 if (any_invalid_declarations) return error.AnalysisFail; 13600 return decl_count; 13601 } 13602 13603 for (names.keys(), names.values()) |name, first| { 13604 if (first.next == null) continue; 13605 var notes: std.ArrayListUnmanaged(u32) = .empty; 13606 var prev: NameEntry = first; 13607 while (prev.next) |cur| : (prev = cur.*) { 13608 try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate name here", .{})); 13609 } 13610 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)})); 13611 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name))); 13612 try astgen.appendErrorTokNotes(first.tok, "duplicate {s} member name '{s}'", .{ @tagName(container_kind), name_duped }, notes.items); 13613 any_invalid_declarations = true; 13614 } 13615 13616 for (test_names.keys(), test_names.values()) |name, first| { 13617 if (first.next == null) continue; 13618 var notes: std.ArrayListUnmanaged(u32) = .empty; 13619 var prev: NameEntry = first; 13620 while (prev.next) |cur| : (prev = cur.*) { 13621 try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate test here", .{})); 13622 } 13623 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)})); 13624 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name))); 13625 try astgen.appendErrorTokNotes(first.tok, "duplicate test name '{s}'", .{name_duped}, notes.items); 13626 any_invalid_declarations = true; 13627 } 13628 13629 for (decltest_names.keys(), decltest_names.values()) |name, first| { 13630 if (first.next == null) continue; 13631 var notes: std.ArrayListUnmanaged(u32) = .empty; 13632 var prev: NameEntry = first; 13633 while (prev.next) |cur| : (prev = cur.*) { 13634 try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate decltest here", .{})); 13635 } 13636 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)})); 13637 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name))); 13638 try astgen.appendErrorTokNotes(first.tok, "duplicate decltest '{s}'", .{name_duped}, notes.items); 13639 any_invalid_declarations = true; 13640 } 13641 13642 assert(any_invalid_declarations); 13643 return error.AnalysisFail; 13644 } 13645 13646 /// Assumes capacity for body has already been added. Needed capacity taking into 13647 /// account fixups can be found with `countBodyLenAfterFixups`. 13648 fn appendBodyWithFixups(astgen: *AstGen, body: []const Zir.Inst.Index) void { 13649 return appendBodyWithFixupsArrayList(astgen, &astgen.extra, body); 13650 } 13651 13652 fn appendBodyWithFixupsArrayList( 13653 astgen: *AstGen, 13654 list: *std.ArrayListUnmanaged(u32), 13655 body: []const Zir.Inst.Index, 13656 ) void { 13657 astgen.appendBodyWithFixupsExtraRefsArrayList(list, body, &.{}); 13658 } 13659 13660 fn appendBodyWithFixupsExtraRefsArrayList( 13661 astgen: *AstGen, 13662 list: *std.ArrayListUnmanaged(u32), 13663 body: []const Zir.Inst.Index, 13664 extra_refs: []const Zir.Inst.Index, 13665 ) void { 13666 for (extra_refs) |extra_inst| { 13667 if (astgen.ref_table.fetchRemove(extra_inst)) |kv| { 13668 appendPossiblyRefdBodyInst(astgen, list, kv.value); 13669 } 13670 } 13671 for (body) |body_inst| { 13672 appendPossiblyRefdBodyInst(astgen, list, body_inst); 13673 } 13674 } 13675 13676 fn appendPossiblyRefdBodyInst( 13677 astgen: *AstGen, 13678 list: *std.ArrayListUnmanaged(u32), 13679 body_inst: Zir.Inst.Index, 13680 ) void { 13681 list.appendAssumeCapacity(@intFromEnum(body_inst)); 13682 const kv = astgen.ref_table.fetchRemove(body_inst) orelse return; 13683 const ref_inst = kv.value; 13684 return appendPossiblyRefdBodyInst(astgen, list, ref_inst); 13685 } 13686 13687 fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 { 13688 return astgen.countBodyLenAfterFixupsExtraRefs(body, &.{}); 13689 } 13690 13691 /// Return the number of instructions in `body` after prepending the `ref` instructions in `ref_table`. 13692 /// As well as all instructions in `body`, we also prepend `ref`s of any instruction in `extra_refs`. 13693 /// For instance, if an index has been reserved with a special meaning to a child block, it must be 13694 /// passed to `extra_refs` to ensure `ref`s of that index are added correctly. 13695 fn countBodyLenAfterFixupsExtraRefs(astgen: *AstGen, body: []const Zir.Inst.Index, extra_refs: []const Zir.Inst.Index) u32 { 13696 var count = body.len; 13697 for (body) |body_inst| { 13698 var check_inst = body_inst; 13699 while (astgen.ref_table.get(check_inst)) |ref_inst| { 13700 count += 1; 13701 check_inst = ref_inst; 13702 } 13703 } 13704 for (extra_refs) |extra_inst| { 13705 var check_inst = extra_inst; 13706 while (astgen.ref_table.get(check_inst)) |ref_inst| { 13707 count += 1; 13708 check_inst = ref_inst; 13709 } 13710 } 13711 return @intCast(count); 13712 } 13713 13714 fn emitDbgStmt(gz: *GenZir, lc: LineColumn) !void { 13715 if (gz.is_comptime) return; 13716 if (gz.instructions.items.len > gz.instructions_top) { 13717 const astgen = gz.astgen; 13718 const last = gz.instructions.items[gz.instructions.items.len - 1]; 13719 if (astgen.instructions.items(.tag)[@intFromEnum(last)] == .dbg_stmt) { 13720 astgen.instructions.items(.data)[@intFromEnum(last)].dbg_stmt = .{ 13721 .line = lc[0], 13722 .column = lc[1], 13723 }; 13724 return; 13725 } 13726 } 13727 13728 _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{ 13729 .dbg_stmt = .{ 13730 .line = lc[0], 13731 .column = lc[1], 13732 }, 13733 } }); 13734 } 13735 13736 /// In some cases, Sema expects us to generate a `dbg_stmt` at the instruction 13737 /// *index* directly preceding the next instruction (e.g. if a call is %10, it 13738 /// expects a dbg_stmt at %9). TODO: this logic may allow redundant dbg_stmt 13739 /// instructions; fix up Sema so we don't need it! 13740 fn emitDbgStmtForceCurrentIndex(gz: *GenZir, lc: LineColumn) !void { 13741 const astgen = gz.astgen; 13742 if (gz.instructions.items.len > gz.instructions_top and 13743 @intFromEnum(gz.instructions.items[gz.instructions.items.len - 1]) == astgen.instructions.len - 1) 13744 { 13745 const last = astgen.instructions.len - 1; 13746 if (astgen.instructions.items(.tag)[last] == .dbg_stmt) { 13747 astgen.instructions.items(.data)[last].dbg_stmt = .{ 13748 .line = lc[0], 13749 .column = lc[1], 13750 }; 13751 return; 13752 } 13753 } 13754 13755 _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{ 13756 .dbg_stmt = .{ 13757 .line = lc[0], 13758 .column = lc[1], 13759 }, 13760 } }); 13761 } 13762 13763 fn lowerAstErrors(astgen: *AstGen) error{OutOfMemory}!void { 13764 const gpa = astgen.gpa; 13765 const tree = astgen.tree; 13766 assert(tree.errors.len > 0); 13767 13768 var msg: std.io.Writer.Allocating = .init(gpa); 13769 defer msg.deinit(); 13770 const msg_w = &msg.writer; 13771 13772 var notes: std.ArrayListUnmanaged(u32) = .empty; 13773 defer notes.deinit(gpa); 13774 13775 const token_starts = tree.tokens.items(.start); 13776 const token_tags = tree.tokens.items(.tag); 13777 const parse_err = tree.errors[0]; 13778 const tok = parse_err.token + @intFromBool(parse_err.token_is_prev); 13779 const tok_start = token_starts[tok]; 13780 const start_char = tree.source[tok_start]; 13781 13782 if (token_tags[tok] == .invalid and 13783 (start_char == '\"' or start_char == '\'' or start_char == '/' or mem.startsWith(u8, tree.source[tok_start..], "\\\\"))) 13784 { 13785 const tok_len: u32 = @intCast(tree.tokenSlice(tok).len); 13786 const tok_end = tok_start + tok_len; 13787 const bad_off = blk: { 13788 var idx = tok_start; 13789 while (idx < tok_end) : (idx += 1) { 13790 switch (tree.source[idx]) { 13791 0x00...0x09, 0x0b...0x1f, 0x7f => break, 13792 else => {}, 13793 } 13794 } 13795 break :blk idx - tok_start; 13796 }; 13797 13798 const ast_err: Ast.Error = .{ 13799 .tag = Ast.Error.Tag.invalid_byte, 13800 .token = tok, 13801 .extra = .{ .offset = bad_off }, 13802 }; 13803 msg.clearRetainingCapacity(); 13804 tree.renderError(ast_err, msg_w) catch return error.OutOfMemory; 13805 return try astgen.appendErrorTokNotesOff(tok, bad_off, "{s}", .{msg.getWritten()}, notes.items); 13806 } 13807 13808 var cur_err = tree.errors[0]; 13809 for (tree.errors[1..]) |err| { 13810 if (err.is_note) { 13811 tree.renderError(err, msg_w) catch return error.OutOfMemory; 13812 try notes.append(gpa, try astgen.errNoteTok(err.token, "{s}", .{msg.getWritten()})); 13813 } else { 13814 // Flush error 13815 const extra_offset = tree.errorOffset(cur_err); 13816 tree.renderError(cur_err, msg_w) catch return error.OutOfMemory; 13817 try astgen.appendErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.getWritten()}, notes.items); 13818 notes.clearRetainingCapacity(); 13819 cur_err = err; 13820 13821 // TODO: `Parse` currently does not have good error recovery mechanisms, so the remaining errors could be bogus. 13822 // As such, we'll ignore all remaining errors for now. We should improve `Parse` so that we can report all the errors. 13823 return; 13824 } 13825 msg.clearRetainingCapacity(); 13826 } 13827 13828 // Flush error 13829 const extra_offset = tree.errorOffset(cur_err); 13830 tree.renderError(cur_err, msg_w) catch return error.OutOfMemory; 13831 try astgen.appendErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.getWritten()}, notes.items); 13832 } 13833 13834 const DeclarationName = union(enum) { 13835 named: Ast.TokenIndex, 13836 named_test: Ast.TokenIndex, 13837 decltest: Ast.TokenIndex, 13838 unnamed_test, 13839 @"comptime", 13840 }; 13841 13842 fn addFailedDeclaration( 13843 wip_members: *WipMembers, 13844 gz: *GenZir, 13845 kind: Zir.Inst.Declaration.Unwrapped.Kind, 13846 name: Zir.NullTerminatedString, 13847 src_node: Ast.Node.Index, 13848 is_pub: bool, 13849 ) !void { 13850 const decl_inst = try gz.makeDeclaration(src_node); 13851 wip_members.nextDecl(decl_inst); 13852 13853 var dummy_gz = gz.makeSubBlock(&gz.base); 13854 13855 var value_gz = gz.makeSubBlock(&gz.base); // scope doesn't matter here 13856 _ = try value_gz.add(.{ 13857 .tag = .extended, 13858 .data = .{ .extended = .{ 13859 .opcode = .astgen_error, 13860 .small = undefined, 13861 .operand = undefined, 13862 } }, 13863 }); 13864 13865 try setDeclaration(decl_inst, .{ 13866 .src_hash = @splat(0), // use a fixed hash to represent an AstGen failure; we don't care about source changes if AstGen still failed! 13867 .src_line = gz.astgen.source_line, 13868 .src_column = gz.astgen.source_column, 13869 .kind = kind, 13870 .name = name, 13871 .is_pub = is_pub, 13872 .is_threadlocal = false, 13873 .linkage = .normal, 13874 .type_gz = &dummy_gz, 13875 .align_gz = &dummy_gz, 13876 .linksection_gz = &dummy_gz, 13877 .addrspace_gz = &dummy_gz, 13878 .value_gz = &value_gz, 13879 }); 13880 } 13881 13882 /// Sets all extra data for a `declaration` instruction. 13883 /// Unstacks `type_gz`, `align_gz`, `linksection_gz`, `addrspace_gz`, and `value_gz`. 13884 fn setDeclaration( 13885 decl_inst: Zir.Inst.Index, 13886 args: struct { 13887 src_hash: std.zig.SrcHash, 13888 src_line: u32, 13889 src_column: u32, 13890 13891 kind: Zir.Inst.Declaration.Unwrapped.Kind, 13892 name: Zir.NullTerminatedString, 13893 is_pub: bool, 13894 is_threadlocal: bool, 13895 linkage: Zir.Inst.Declaration.Unwrapped.Linkage, 13896 lib_name: Zir.NullTerminatedString = .empty, 13897 13898 type_gz: *GenZir, 13899 /// Must be stacked on `type_gz`. 13900 align_gz: *GenZir, 13901 /// Must be stacked on `align_gz`. 13902 linksection_gz: *GenZir, 13903 /// Must be stacked on `linksection_gz`. 13904 addrspace_gz: *GenZir, 13905 /// Must be stacked on `addrspace_gz` and have nothing stacked on top of it. 13906 value_gz: *GenZir, 13907 }, 13908 ) !void { 13909 const astgen = args.value_gz.astgen; 13910 const gpa = astgen.gpa; 13911 13912 const type_body = args.type_gz.instructionsSliceUpto(args.align_gz); 13913 const align_body = args.align_gz.instructionsSliceUpto(args.linksection_gz); 13914 const linksection_body = args.linksection_gz.instructionsSliceUpto(args.addrspace_gz); 13915 const addrspace_body = args.addrspace_gz.instructionsSliceUpto(args.value_gz); 13916 const value_body = args.value_gz.instructionsSlice(); 13917 13918 const has_name = args.name != .empty; 13919 const has_lib_name = args.lib_name != .empty; 13920 const has_type_body = type_body.len != 0; 13921 const has_special_body = align_body.len != 0 or linksection_body.len != 0 or addrspace_body.len != 0; 13922 const has_value_body = value_body.len != 0; 13923 13924 const id: Zir.Inst.Declaration.Flags.Id = switch (args.kind) { 13925 .unnamed_test => .unnamed_test, 13926 .@"test" => .@"test", 13927 .decltest => .decltest, 13928 .@"comptime" => .@"comptime", 13929 .@"const" => switch (args.linkage) { 13930 .normal => if (args.is_pub) id: { 13931 if (has_special_body) break :id .pub_const; 13932 if (has_type_body) break :id .pub_const_typed; 13933 break :id .pub_const_simple; 13934 } else id: { 13935 if (has_special_body) break :id .@"const"; 13936 if (has_type_body) break :id .const_typed; 13937 break :id .const_simple; 13938 }, 13939 .@"extern" => if (args.is_pub) id: { 13940 if (has_lib_name) break :id .pub_extern_const; 13941 if (has_special_body) break :id .pub_extern_const; 13942 break :id .pub_extern_const_simple; 13943 } else id: { 13944 if (has_lib_name) break :id .extern_const; 13945 if (has_special_body) break :id .extern_const; 13946 break :id .extern_const_simple; 13947 }, 13948 .@"export" => if (args.is_pub) .pub_export_const else .export_const, 13949 }, 13950 .@"var" => switch (args.linkage) { 13951 .normal => if (args.is_pub) id: { 13952 if (args.is_threadlocal) break :id .pub_var_threadlocal; 13953 if (has_special_body) break :id .pub_var; 13954 if (has_type_body) break :id .pub_var; 13955 break :id .pub_var_simple; 13956 } else id: { 13957 if (args.is_threadlocal) break :id .var_threadlocal; 13958 if (has_special_body) break :id .@"var"; 13959 if (has_type_body) break :id .@"var"; 13960 break :id .var_simple; 13961 }, 13962 .@"extern" => if (args.is_pub) id: { 13963 if (args.is_threadlocal) break :id .pub_extern_var_threadlocal; 13964 break :id .pub_extern_var; 13965 } else id: { 13966 if (args.is_threadlocal) break :id .extern_var_threadlocal; 13967 break :id .extern_var; 13968 }, 13969 .@"export" => if (args.is_pub) id: { 13970 if (args.is_threadlocal) break :id .pub_export_var_threadlocal; 13971 break :id .pub_export_var; 13972 } else id: { 13973 if (args.is_threadlocal) break :id .export_var_threadlocal; 13974 break :id .export_var; 13975 }, 13976 }, 13977 }; 13978 13979 assert(id.hasTypeBody() or !has_type_body); 13980 assert(id.hasSpecialBodies() or !has_special_body); 13981 assert(id.hasValueBody() == has_value_body); 13982 assert(id.linkage() == args.linkage); 13983 assert(id.hasName() == has_name); 13984 assert(id.hasLibName() or !has_lib_name); 13985 assert(id.isPub() == args.is_pub); 13986 assert(id.isThreadlocal() == args.is_threadlocal); 13987 13988 const type_len = astgen.countBodyLenAfterFixups(type_body); 13989 const align_len = astgen.countBodyLenAfterFixups(align_body); 13990 const linksection_len = astgen.countBodyLenAfterFixups(linksection_body); 13991 const addrspace_len = astgen.countBodyLenAfterFixups(addrspace_body); 13992 const value_len = astgen.countBodyLenAfterFixups(value_body); 13993 13994 const src_hash_arr: [4]u32 = @bitCast(args.src_hash); 13995 const flags: Zir.Inst.Declaration.Flags = .{ 13996 .src_line = @intCast(args.src_line), 13997 .src_column = @intCast(args.src_column), 13998 .id = id, 13999 }; 14000 const flags_arr: [2]u32 = @bitCast(flags); 14001 14002 const need_extra: usize = 14003 @typeInfo(Zir.Inst.Declaration).@"struct".fields.len + 14004 @as(usize, @intFromBool(id.hasName())) + 14005 @as(usize, @intFromBool(id.hasLibName())) + 14006 @as(usize, @intFromBool(id.hasTypeBody())) + 14007 3 * @as(usize, @intFromBool(id.hasSpecialBodies())) + 14008 @as(usize, @intFromBool(id.hasValueBody())) + 14009 type_len + align_len + linksection_len + addrspace_len + value_len; 14010 14011 try astgen.extra.ensureUnusedCapacity(gpa, need_extra); 14012 14013 const extra: Zir.Inst.Declaration = .{ 14014 .src_hash_0 = src_hash_arr[0], 14015 .src_hash_1 = src_hash_arr[1], 14016 .src_hash_2 = src_hash_arr[2], 14017 .src_hash_3 = src_hash_arr[3], 14018 .flags_0 = flags_arr[0], 14019 .flags_1 = flags_arr[1], 14020 }; 14021 astgen.instructions.items(.data)[@intFromEnum(decl_inst)].declaration.payload_index = 14022 astgen.addExtraAssumeCapacity(extra); 14023 14024 if (id.hasName()) { 14025 astgen.extra.appendAssumeCapacity(@intFromEnum(args.name)); 14026 } 14027 if (id.hasLibName()) { 14028 astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name)); 14029 } 14030 if (id.hasTypeBody()) { 14031 astgen.extra.appendAssumeCapacity(type_len); 14032 } 14033 if (id.hasSpecialBodies()) { 14034 astgen.extra.appendSliceAssumeCapacity(&.{ 14035 align_len, 14036 linksection_len, 14037 addrspace_len, 14038 }); 14039 } 14040 if (id.hasValueBody()) { 14041 astgen.extra.appendAssumeCapacity(value_len); 14042 } 14043 14044 astgen.appendBodyWithFixups(type_body); 14045 astgen.appendBodyWithFixups(align_body); 14046 astgen.appendBodyWithFixups(linksection_body); 14047 astgen.appendBodyWithFixups(addrspace_body); 14048 astgen.appendBodyWithFixups(value_body); 14049 14050 args.value_gz.unstack(); 14051 args.addrspace_gz.unstack(); 14052 args.linksection_gz.unstack(); 14053 args.align_gz.unstack(); 14054 args.type_gz.unstack(); 14055 } 14056 14057 /// Given a list of instructions, returns a list of all instructions which are a `ref` of one of the originals, 14058 /// from `astgen.ref_table`, non-recursively. The entries are removed from `astgen.ref_table`, and the returned 14059 /// slice can then be treated as its own body, to append `ref` instructions to a body other than the one they 14060 /// would normally exist in. 14061 /// 14062 /// This is used when lowering functions. Very rarely, the callconv expression, align expression, etc may reference 14063 /// function parameters via `¶m`; in this case, we need to lower to a `ref` instruction in the callconv/align/etc 14064 /// body, rather than in the declaration body. However, we don't append these bodies to `extra` until we've evaluated 14065 /// *all* of the bodies into a big `GenZir` stack. Therefore, we use this function to pull out these per-body `ref` 14066 /// instructions which must be emitted. 14067 fn fetchRemoveRefEntries(astgen: *AstGen, param_insts: []const Zir.Inst.Index) ![]Zir.Inst.Index { 14068 var refs: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty; 14069 for (param_insts) |param_inst| { 14070 if (astgen.ref_table.fetchRemove(param_inst)) |kv| { 14071 try refs.append(astgen.arena, kv.value); 14072 } 14073 } 14074 return refs.items; 14075 } 14076 14077 test { 14078 _ = &generate; 14079 }