blob fcec69ed (566247B) - 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 Zir.NullTerminatedString, 101 => @intFromEnum(@field(extra, field.name)), 102 103 i32, 104 Zir.Inst.Call.Flags, 105 Zir.Inst.BuiltinCall.Flags, 106 Zir.Inst.SwitchBlock.Bits, 107 Zir.Inst.SwitchBlockErrUnion.Bits, 108 Zir.Inst.FuncFancy.Bits, 109 Zir.Inst.Declaration.Flags, 110 => @bitCast(@field(extra, field.name)), 111 112 else => @compileError("bad field type"), 113 }; 114 i += 1; 115 } 116 } 117 118 fn reserveExtra(astgen: *AstGen, size: usize) Allocator.Error!u32 { 119 const extra_index: u32 = @intCast(astgen.extra.items.len); 120 try astgen.extra.resize(astgen.gpa, extra_index + size); 121 return extra_index; 122 } 123 124 fn appendRefs(astgen: *AstGen, refs: []const Zir.Inst.Ref) !void { 125 return astgen.extra.appendSlice(astgen.gpa, @ptrCast(refs)); 126 } 127 128 fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void { 129 astgen.extra.appendSliceAssumeCapacity(@ptrCast(refs)); 130 } 131 132 pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { 133 assert(tree.mode == .zig); 134 135 var arena = std.heap.ArenaAllocator.init(gpa); 136 defer arena.deinit(); 137 138 var nodes_need_rl = try AstRlAnnotate.annotate(gpa, arena.allocator(), tree); 139 defer nodes_need_rl.deinit(gpa); 140 141 var astgen: AstGen = .{ 142 .gpa = gpa, 143 .arena = arena.allocator(), 144 .tree = &tree, 145 .nodes_need_rl = &nodes_need_rl, 146 .src_hasher = undefined, // `structDeclInner` for the root struct will set this 147 }; 148 defer astgen.deinit(gpa); 149 150 // String table index 0 is reserved for `NullTerminatedString.empty`. 151 try astgen.string_bytes.append(gpa, 0); 152 153 // We expect at least as many ZIR instructions and extra data items 154 // as AST nodes. 155 try astgen.instructions.ensureTotalCapacity(gpa, tree.nodes.len); 156 157 // First few indexes of extra are reserved and set at the end. 158 const reserved_count = @typeInfo(Zir.ExtraIndex).@"enum".fields.len; 159 try astgen.extra.ensureTotalCapacity(gpa, tree.nodes.len + reserved_count); 160 astgen.extra.items.len += reserved_count; 161 162 var top_scope: Scope.Top = .{}; 163 164 var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty; 165 var gen_scope: GenZir = .{ 166 .is_comptime = true, 167 .parent = &top_scope.base, 168 .anon_name_strategy = .parent, 169 .decl_node_index = 0, 170 .decl_line = 0, 171 .astgen = &astgen, 172 .instructions = &gz_instructions, 173 .instructions_top = 0, 174 }; 175 defer gz_instructions.deinit(gpa); 176 177 // The AST -> ZIR lowering process assumes an AST that does not have any parse errors. 178 // Parse errors, or AstGen errors in the root struct, are considered "fatal", so we emit no ZIR. 179 const fatal = if (tree.errors.len == 0) fatal: { 180 if (AstGen.structDeclInner( 181 &gen_scope, 182 &gen_scope.base, 183 0, 184 tree.containerDeclRoot(), 185 .auto, 186 0, 187 )) |struct_decl_ref| { 188 assert(struct_decl_ref.toIndex().? == .main_struct_inst); 189 break :fatal false; 190 } else |err| switch (err) { 191 error.OutOfMemory => return error.OutOfMemory, 192 error.AnalysisFail => break :fatal true, // Handled via compile_errors below. 193 } 194 } else fatal: { 195 try lowerAstErrors(&astgen); 196 break :fatal true; 197 }; 198 199 const err_index = @intFromEnum(Zir.ExtraIndex.compile_errors); 200 if (astgen.compile_errors.items.len == 0) { 201 astgen.extra.items[err_index] = 0; 202 } else { 203 try astgen.extra.ensureUnusedCapacity(gpa, 1 + astgen.compile_errors.items.len * 204 @typeInfo(Zir.Inst.CompileErrors.Item).@"struct".fields.len); 205 206 astgen.extra.items[err_index] = astgen.addExtraAssumeCapacity(Zir.Inst.CompileErrors{ 207 .items_len = @intCast(astgen.compile_errors.items.len), 208 }); 209 210 for (astgen.compile_errors.items) |item| { 211 _ = astgen.addExtraAssumeCapacity(item); 212 } 213 } 214 215 const imports_index = @intFromEnum(Zir.ExtraIndex.imports); 216 if (astgen.imports.count() == 0) { 217 astgen.extra.items[imports_index] = 0; 218 } else { 219 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Imports).@"struct".fields.len + 220 astgen.imports.count() * @typeInfo(Zir.Inst.Imports.Item).@"struct".fields.len); 221 222 astgen.extra.items[imports_index] = astgen.addExtraAssumeCapacity(Zir.Inst.Imports{ 223 .imports_len = @intCast(astgen.imports.count()), 224 }); 225 226 var it = astgen.imports.iterator(); 227 while (it.next()) |entry| { 228 _ = astgen.addExtraAssumeCapacity(Zir.Inst.Imports.Item{ 229 .name = entry.key_ptr.*, 230 .token = entry.value_ptr.*, 231 }); 232 } 233 } 234 235 return .{ 236 .instructions = if (fatal) .empty else astgen.instructions.toOwnedSlice(), 237 .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa), 238 .extra = try astgen.extra.toOwnedSlice(gpa), 239 }; 240 } 241 242 fn deinit(astgen: *AstGen, gpa: Allocator) void { 243 astgen.instructions.deinit(gpa); 244 astgen.extra.deinit(gpa); 245 astgen.string_table.deinit(gpa); 246 astgen.string_bytes.deinit(gpa); 247 astgen.compile_errors.deinit(gpa); 248 astgen.imports.deinit(gpa); 249 astgen.scratch.deinit(gpa); 250 astgen.ref_table.deinit(gpa); 251 } 252 253 const ResultInfo = struct { 254 /// The semantics requested for the result location 255 rl: Loc, 256 257 /// The "operator" consuming the result location 258 ctx: Context = .none, 259 260 /// Turns a `coerced_ty` back into a `ty`. Should be called at branch points 261 /// such as if and switch expressions. 262 fn br(ri: ResultInfo) ResultInfo { 263 return switch (ri.rl) { 264 .coerced_ty => |ty| .{ 265 .rl = .{ .ty = ty }, 266 .ctx = ri.ctx, 267 }, 268 else => ri, 269 }; 270 } 271 272 fn zirTag(ri: ResultInfo) Zir.Inst.Tag { 273 switch (ri.rl) { 274 .ty => return switch (ri.ctx) { 275 .shift_op => .as_shift_operand, 276 else => .as_node, 277 }, 278 else => unreachable, 279 } 280 } 281 282 const Loc = union(enum) { 283 /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the 284 /// expression should be generated. The result instruction from the expression must 285 /// be ignored. 286 discard, 287 /// The expression has an inferred type, and it will be evaluated as an rvalue. 288 none, 289 /// The expression will be coerced into this type, but it will be evaluated as an rvalue. 290 ty: Zir.Inst.Ref, 291 /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion, 292 /// so no `as` instruction needs to be emitted. 293 coerced_ty: Zir.Inst.Ref, 294 /// The expression must generate a pointer rather than a value. For example, the left hand side 295 /// of an assignment uses this kind of result location. 296 ref, 297 /// The expression must generate a pointer rather than a value, and the pointer will be coerced 298 /// by other code to this type, which is guaranteed by earlier instructions to be a pointer type. 299 ref_coerced_ty: Zir.Inst.Ref, 300 /// The expression must store its result into this typed pointer. The result instruction 301 /// from the expression must be ignored. 302 ptr: PtrResultLoc, 303 /// The expression must store its result into this allocation, which has an inferred type. 304 /// The result instruction from the expression must be ignored. 305 /// Always an instruction with tag `alloc_inferred`. 306 inferred_ptr: Zir.Inst.Ref, 307 /// The expression has a sequence of pointers to store its results into due to a destructure 308 /// operation. Each of these pointers may or may not have an inferred type. 309 destructure: struct { 310 /// The AST node of the destructure operation itself. 311 src_node: Ast.Node.Index, 312 /// The pointers to store results into. 313 components: []const DestructureComponent, 314 }, 315 316 const DestructureComponent = union(enum) { 317 typed_ptr: PtrResultLoc, 318 inferred_ptr: Zir.Inst.Ref, 319 discard, 320 }; 321 322 const PtrResultLoc = struct { 323 inst: Zir.Inst.Ref, 324 src_node: ?Ast.Node.Index = null, 325 }; 326 327 /// Find the result type for a cast builtin given the result location. 328 /// If the location does not have a known result type, returns `null`. 329 fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index) !?Zir.Inst.Ref { 330 return switch (rl) { 331 .discard, .none, .ref, .inferred_ptr, .destructure => null, 332 .ty, .coerced_ty => |ty_ref| ty_ref, 333 .ref_coerced_ty => |ptr_ty| try gz.addUnNode(.elem_type, ptr_ty, node), 334 .ptr => |ptr| { 335 const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node); 336 return try gz.addUnNode(.elem_type, ptr_ty, node); 337 }, 338 }; 339 } 340 341 /// Find the result type for a cast builtin given the result location. 342 /// If the location does not have a known result type, emits an error on 343 /// the given node. 344 fn resultTypeForCast(rl: Loc, gz: *GenZir, node: Ast.Node.Index, builtin_name: []const u8) !Zir.Inst.Ref { 345 const astgen = gz.astgen; 346 if (try rl.resultType(gz, node)) |ty| return ty; 347 switch (rl) { 348 .destructure => |destructure| return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ 349 try astgen.errNoteNode(destructure.src_node, "destructure expressions do not provide a single result type", .{}), 350 try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}), 351 }), 352 else => return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ 353 try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}), 354 }), 355 } 356 } 357 }; 358 359 const Context = enum { 360 /// The expression is the operand to a return expression. 361 @"return", 362 /// The expression is the input to an error-handling operator (if-else, try, or catch). 363 error_handling_expr, 364 /// The expression is the right-hand side of a shift operation. 365 shift_op, 366 /// The expression is an argument in a function call. 367 fn_arg, 368 /// The expression is the right-hand side of an initializer for a `const` variable 369 const_init, 370 /// The expression is the right-hand side of an assignment expression. 371 assignment, 372 /// No specific operator in particular. 373 none, 374 }; 375 }; 376 377 const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } }; 378 const coerced_linksection_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }; 379 const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } }; 380 const coerced_bool_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .bool_type } }; 381 382 fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 383 return comptimeExpr(gz, scope, coerced_type_ri, type_node); 384 } 385 386 fn reachableTypeExpr( 387 gz: *GenZir, 388 scope: *Scope, 389 type_node: Ast.Node.Index, 390 reachable_node: Ast.Node.Index, 391 ) InnerError!Zir.Inst.Ref { 392 return reachableExprComptime(gz, scope, coerced_type_ri, type_node, reachable_node, true); 393 } 394 395 /// Same as `expr` but fails with a compile error if the result type is `noreturn`. 396 fn reachableExpr( 397 gz: *GenZir, 398 scope: *Scope, 399 ri: ResultInfo, 400 node: Ast.Node.Index, 401 reachable_node: Ast.Node.Index, 402 ) InnerError!Zir.Inst.Ref { 403 return reachableExprComptime(gz, scope, ri, node, reachable_node, false); 404 } 405 406 fn reachableExprComptime( 407 gz: *GenZir, 408 scope: *Scope, 409 ri: ResultInfo, 410 node: Ast.Node.Index, 411 reachable_node: Ast.Node.Index, 412 force_comptime: bool, 413 ) InnerError!Zir.Inst.Ref { 414 const result_inst = if (force_comptime) 415 try comptimeExpr(gz, scope, ri, node) 416 else 417 try expr(gz, scope, ri, node); 418 419 if (gz.refIsNoReturn(result_inst)) { 420 try gz.astgen.appendErrorNodeNotes(reachable_node, "unreachable code", .{}, &[_]u32{ 421 try gz.astgen.errNoteNode(node, "control flow is diverted here", .{}), 422 }); 423 } 424 return result_inst; 425 } 426 427 fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 428 const astgen = gz.astgen; 429 const tree = astgen.tree; 430 const node_tags = tree.nodes.items(.tag); 431 const main_tokens = tree.nodes.items(.main_token); 432 switch (node_tags[node]) { 433 .root => unreachable, 434 .@"usingnamespace" => unreachable, 435 .test_decl => unreachable, 436 .global_var_decl => unreachable, 437 .local_var_decl => unreachable, 438 .simple_var_decl => unreachable, 439 .aligned_var_decl => unreachable, 440 .switch_case => unreachable, 441 .switch_case_inline => unreachable, 442 .switch_case_one => unreachable, 443 .switch_case_inline_one => unreachable, 444 .container_field_init => unreachable, 445 .container_field_align => unreachable, 446 .container_field => unreachable, 447 .asm_output => unreachable, 448 .asm_input => unreachable, 449 450 .assign, 451 .assign_destructure, 452 .assign_bit_and, 453 .assign_bit_or, 454 .assign_shl, 455 .assign_shl_sat, 456 .assign_shr, 457 .assign_bit_xor, 458 .assign_div, 459 .assign_sub, 460 .assign_sub_wrap, 461 .assign_sub_sat, 462 .assign_mod, 463 .assign_add, 464 .assign_add_wrap, 465 .assign_add_sat, 466 .assign_mul, 467 .assign_mul_wrap, 468 .assign_mul_sat, 469 .add, 470 .add_wrap, 471 .add_sat, 472 .sub, 473 .sub_wrap, 474 .sub_sat, 475 .mul, 476 .mul_wrap, 477 .mul_sat, 478 .div, 479 .mod, 480 .bit_and, 481 .bit_or, 482 .shl, 483 .shl_sat, 484 .shr, 485 .bit_xor, 486 .bang_equal, 487 .equal_equal, 488 .greater_than, 489 .greater_or_equal, 490 .less_than, 491 .less_or_equal, 492 .array_cat, 493 .array_mult, 494 .bool_and, 495 .bool_or, 496 .@"asm", 497 .asm_simple, 498 .string_literal, 499 .number_literal, 500 .call, 501 .call_comma, 502 .async_call, 503 .async_call_comma, 504 .call_one, 505 .call_one_comma, 506 .async_call_one, 507 .async_call_one_comma, 508 .unreachable_literal, 509 .@"return", 510 .@"if", 511 .if_simple, 512 .@"while", 513 .while_simple, 514 .while_cont, 515 .bool_not, 516 .address_of, 517 .optional_type, 518 .block, 519 .block_semicolon, 520 .block_two, 521 .block_two_semicolon, 522 .@"break", 523 .ptr_type_aligned, 524 .ptr_type_sentinel, 525 .ptr_type, 526 .ptr_type_bit_range, 527 .array_type, 528 .array_type_sentinel, 529 .enum_literal, 530 .multiline_string_literal, 531 .char_literal, 532 .@"defer", 533 .@"errdefer", 534 .@"catch", 535 .error_union, 536 .merge_error_sets, 537 .switch_range, 538 .for_range, 539 .@"await", 540 .bit_not, 541 .negation, 542 .negation_wrap, 543 .@"resume", 544 .@"try", 545 .slice, 546 .slice_open, 547 .slice_sentinel, 548 .array_init_one, 549 .array_init_one_comma, 550 .array_init_dot_two, 551 .array_init_dot_two_comma, 552 .array_init_dot, 553 .array_init_dot_comma, 554 .array_init, 555 .array_init_comma, 556 .struct_init_one, 557 .struct_init_one_comma, 558 .struct_init_dot_two, 559 .struct_init_dot_two_comma, 560 .struct_init_dot, 561 .struct_init_dot_comma, 562 .struct_init, 563 .struct_init_comma, 564 .@"switch", 565 .switch_comma, 566 .@"for", 567 .for_simple, 568 .@"suspend", 569 .@"continue", 570 .fn_proto_simple, 571 .fn_proto_multi, 572 .fn_proto_one, 573 .fn_proto, 574 .fn_decl, 575 .anyframe_type, 576 .anyframe_literal, 577 .error_set_decl, 578 .container_decl, 579 .container_decl_trailing, 580 .container_decl_two, 581 .container_decl_two_trailing, 582 .container_decl_arg, 583 .container_decl_arg_trailing, 584 .tagged_union, 585 .tagged_union_trailing, 586 .tagged_union_two, 587 .tagged_union_two_trailing, 588 .tagged_union_enum_tag, 589 .tagged_union_enum_tag_trailing, 590 .@"comptime", 591 .@"nosuspend", 592 .error_value, 593 => return astgen.failNode(node, "invalid left-hand side to assignment", .{}), 594 595 .builtin_call, 596 .builtin_call_comma, 597 .builtin_call_two, 598 .builtin_call_two_comma, 599 => { 600 const builtin_token = main_tokens[node]; 601 const builtin_name = tree.tokenSlice(builtin_token); 602 // If the builtin is an invalid name, we don't cause an error here; instead 603 // let it pass, and the error will be "invalid builtin function" later. 604 if (BuiltinFn.list.get(builtin_name)) |info| { 605 if (!info.allows_lvalue) { 606 return astgen.failNode(node, "invalid left-hand side to assignment", .{}); 607 } 608 } 609 }, 610 611 // These can be assigned to. 612 .unwrap_optional, 613 .deref, 614 .field_access, 615 .array_access, 616 .identifier, 617 .grouped_expression, 618 .@"orelse", 619 => {}, 620 } 621 return expr(gz, scope, .{ .rl = .ref }, node); 622 } 623 624 /// Turn Zig AST into untyped ZIR instructions. 625 /// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the 626 /// result instruction can be used to inspect whether it is isNoReturn() but that is it, 627 /// it must otherwise not be used. 628 fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 629 const astgen = gz.astgen; 630 const tree = astgen.tree; 631 const main_tokens = tree.nodes.items(.main_token); 632 const token_tags = tree.tokens.items(.tag); 633 const node_datas = tree.nodes.items(.data); 634 const node_tags = tree.nodes.items(.tag); 635 636 const prev_anon_name_strategy = gz.anon_name_strategy; 637 defer gz.anon_name_strategy = prev_anon_name_strategy; 638 if (!nodeUsesAnonNameStrategy(tree, node)) { 639 gz.anon_name_strategy = .anon; 640 } 641 642 switch (node_tags[node]) { 643 .root => unreachable, // Top-level declaration. 644 .@"usingnamespace" => unreachable, // Top-level declaration. 645 .test_decl => unreachable, // Top-level declaration. 646 .container_field_init => unreachable, // Top-level declaration. 647 .container_field_align => unreachable, // Top-level declaration. 648 .container_field => unreachable, // Top-level declaration. 649 .fn_decl => unreachable, // Top-level declaration. 650 651 .global_var_decl => unreachable, // Handled in `blockExpr`. 652 .local_var_decl => unreachable, // Handled in `blockExpr`. 653 .simple_var_decl => unreachable, // Handled in `blockExpr`. 654 .aligned_var_decl => unreachable, // Handled in `blockExpr`. 655 .@"defer" => unreachable, // Handled in `blockExpr`. 656 .@"errdefer" => unreachable, // Handled in `blockExpr`. 657 658 .switch_case => unreachable, // Handled in `switchExpr`. 659 .switch_case_inline => unreachable, // Handled in `switchExpr`. 660 .switch_case_one => unreachable, // Handled in `switchExpr`. 661 .switch_case_inline_one => unreachable, // Handled in `switchExpr`. 662 .switch_range => unreachable, // Handled in `switchExpr`. 663 664 .asm_output => unreachable, // Handled in `asmExpr`. 665 .asm_input => unreachable, // Handled in `asmExpr`. 666 667 .for_range => unreachable, // Handled in `forExpr`. 668 669 .assign => { 670 try assign(gz, scope, node); 671 return rvalue(gz, ri, .void_value, node); 672 }, 673 674 .assign_destructure => { 675 // Note that this variant does not declare any new var/const: that 676 // variant is handled by `blockExprStmts`. 677 try assignDestructure(gz, scope, node); 678 return rvalue(gz, ri, .void_value, node); 679 }, 680 681 .assign_shl => { 682 try assignShift(gz, scope, node, .shl); 683 return rvalue(gz, ri, .void_value, node); 684 }, 685 .assign_shl_sat => { 686 try assignShiftSat(gz, scope, node); 687 return rvalue(gz, ri, .void_value, node); 688 }, 689 .assign_shr => { 690 try assignShift(gz, scope, node, .shr); 691 return rvalue(gz, ri, .void_value, node); 692 }, 693 694 .assign_bit_and => { 695 try assignOp(gz, scope, node, .bit_and); 696 return rvalue(gz, ri, .void_value, node); 697 }, 698 .assign_bit_or => { 699 try assignOp(gz, scope, node, .bit_or); 700 return rvalue(gz, ri, .void_value, node); 701 }, 702 .assign_bit_xor => { 703 try assignOp(gz, scope, node, .xor); 704 return rvalue(gz, ri, .void_value, node); 705 }, 706 .assign_div => { 707 try assignOp(gz, scope, node, .div); 708 return rvalue(gz, ri, .void_value, node); 709 }, 710 .assign_sub => { 711 try assignOp(gz, scope, node, .sub); 712 return rvalue(gz, ri, .void_value, node); 713 }, 714 .assign_sub_wrap => { 715 try assignOp(gz, scope, node, .subwrap); 716 return rvalue(gz, ri, .void_value, node); 717 }, 718 .assign_sub_sat => { 719 try assignOp(gz, scope, node, .sub_sat); 720 return rvalue(gz, ri, .void_value, node); 721 }, 722 .assign_mod => { 723 try assignOp(gz, scope, node, .mod_rem); 724 return rvalue(gz, ri, .void_value, node); 725 }, 726 .assign_add => { 727 try assignOp(gz, scope, node, .add); 728 return rvalue(gz, ri, .void_value, node); 729 }, 730 .assign_add_wrap => { 731 try assignOp(gz, scope, node, .addwrap); 732 return rvalue(gz, ri, .void_value, node); 733 }, 734 .assign_add_sat => { 735 try assignOp(gz, scope, node, .add_sat); 736 return rvalue(gz, ri, .void_value, node); 737 }, 738 .assign_mul => { 739 try assignOp(gz, scope, node, .mul); 740 return rvalue(gz, ri, .void_value, node); 741 }, 742 .assign_mul_wrap => { 743 try assignOp(gz, scope, node, .mulwrap); 744 return rvalue(gz, ri, .void_value, node); 745 }, 746 .assign_mul_sat => { 747 try assignOp(gz, scope, node, .mul_sat); 748 return rvalue(gz, ri, .void_value, node); 749 }, 750 751 // zig fmt: off 752 .shl => return shiftOp(gz, scope, ri, node, node_datas[node].lhs, node_datas[node].rhs, .shl), 753 .shr => return shiftOp(gz, scope, ri, node, node_datas[node].lhs, node_datas[node].rhs, .shr), 754 755 .add => return simpleBinOp(gz, scope, ri, node, .add), 756 .add_wrap => return simpleBinOp(gz, scope, ri, node, .addwrap), 757 .add_sat => return simpleBinOp(gz, scope, ri, node, .add_sat), 758 .sub => return simpleBinOp(gz, scope, ri, node, .sub), 759 .sub_wrap => return simpleBinOp(gz, scope, ri, node, .subwrap), 760 .sub_sat => return simpleBinOp(gz, scope, ri, node, .sub_sat), 761 .mul => return simpleBinOp(gz, scope, ri, node, .mul), 762 .mul_wrap => return simpleBinOp(gz, scope, ri, node, .mulwrap), 763 .mul_sat => return simpleBinOp(gz, scope, ri, node, .mul_sat), 764 .div => return simpleBinOp(gz, scope, ri, node, .div), 765 .mod => return simpleBinOp(gz, scope, ri, node, .mod_rem), 766 .shl_sat => return simpleBinOp(gz, scope, ri, node, .shl_sat), 767 768 .bit_and => return simpleBinOp(gz, scope, ri, node, .bit_and), 769 .bit_or => return simpleBinOp(gz, scope, ri, node, .bit_or), 770 .bit_xor => return simpleBinOp(gz, scope, ri, node, .xor), 771 .bang_equal => return simpleBinOp(gz, scope, ri, node, .cmp_neq), 772 .equal_equal => return simpleBinOp(gz, scope, ri, node, .cmp_eq), 773 .greater_than => return simpleBinOp(gz, scope, ri, node, .cmp_gt), 774 .greater_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_gte), 775 .less_than => return simpleBinOp(gz, scope, ri, node, .cmp_lt), 776 .less_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_lte), 777 .array_cat => return simpleBinOp(gz, scope, ri, node, .array_cat), 778 779 .array_mult => { 780 // This syntax form does not currently use the result type in the language specification. 781 // However, the result type can be used to emit more optimal code for large multiplications by 782 // having Sema perform a coercion before the multiplication operation. 783 const result = try gz.addPlNode(.array_mul, node, Zir.Inst.ArrayMul{ 784 .res_ty = if (try ri.rl.resultType(gz, node)) |t| t else .none, 785 .lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs), 786 .rhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs), 787 }); 788 return rvalue(gz, ri, result, node); 789 }, 790 791 .error_union => return simpleBinOp(gz, scope, ri, node, .error_union_type), 792 .merge_error_sets => return simpleBinOp(gz, scope, ri, node, .merge_error_sets), 793 794 .bool_and => return boolBinOp(gz, scope, ri, node, .bool_br_and), 795 .bool_or => return boolBinOp(gz, scope, ri, node, .bool_br_or), 796 797 .bool_not => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, node_datas[node].lhs, .bool_not), 798 .bit_not => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .bit_not), 799 800 .negation => return negation(gz, scope, ri, node), 801 .negation_wrap => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .negate_wrap), 802 803 .identifier => return identifier(gz, scope, ri, node), 804 805 .asm_simple, 806 .@"asm", 807 => return asmExpr(gz, scope, ri, node, tree.fullAsm(node).?), 808 809 .string_literal => return stringLiteral(gz, ri, node), 810 .multiline_string_literal => return multilineStringLiteral(gz, ri, node), 811 812 .number_literal => return numberLiteral(gz, ri, node, node, .positive), 813 // zig fmt: on 814 815 .builtin_call_two, .builtin_call_two_comma => { 816 if (node_datas[node].lhs == 0) { 817 const params = [_]Ast.Node.Index{}; 818 return builtinCall(gz, scope, ri, node, ¶ms, false); 819 } else if (node_datas[node].rhs == 0) { 820 const params = [_]Ast.Node.Index{node_datas[node].lhs}; 821 return builtinCall(gz, scope, ri, node, ¶ms, false); 822 } else { 823 const params = [_]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; 824 return builtinCall(gz, scope, ri, node, ¶ms, false); 825 } 826 }, 827 .builtin_call, .builtin_call_comma => { 828 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; 829 return builtinCall(gz, scope, ri, node, params, false); 830 }, 831 832 .call_one, 833 .call_one_comma, 834 .async_call_one, 835 .async_call_one_comma, 836 .call, 837 .call_comma, 838 .async_call, 839 .async_call_comma, 840 => { 841 var buf: [1]Ast.Node.Index = undefined; 842 return callExpr(gz, scope, ri, node, tree.fullCall(&buf, node).?); 843 }, 844 845 .unreachable_literal => { 846 try emitDbgNode(gz, node); 847 _ = try gz.addAsIndex(.{ 848 .tag = .@"unreachable", 849 .data = .{ .@"unreachable" = .{ 850 .src_node = gz.nodeIndexToRelative(node), 851 } }, 852 }); 853 return Zir.Inst.Ref.unreachable_value; 854 }, 855 .@"return" => return ret(gz, scope, node), 856 .field_access => return fieldAccess(gz, scope, ri, node), 857 858 .if_simple, 859 .@"if", 860 => { 861 const if_full = tree.fullIf(node).?; 862 no_switch_on_err: { 863 const error_token = if_full.error_token orelse break :no_switch_on_err; 864 const full_switch = tree.fullSwitch(if_full.ast.else_expr) orelse break :no_switch_on_err; 865 if (full_switch.label_token != null) break :no_switch_on_err; 866 if (node_tags[full_switch.ast.condition] != .identifier) break :no_switch_on_err; 867 if (!mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(main_tokens[full_switch.ast.condition]))) break :no_switch_on_err; 868 return switchExprErrUnion(gz, scope, ri.br(), node, .@"if"); 869 } 870 return ifExpr(gz, scope, ri.br(), node, if_full); 871 }, 872 873 .while_simple, 874 .while_cont, 875 .@"while", 876 => return whileExpr(gz, scope, ri.br(), node, tree.fullWhile(node).?, false), 877 878 .for_simple, .@"for" => return forExpr(gz, scope, ri.br(), node, tree.fullFor(node).?, false), 879 880 .slice_open => { 881 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); 882 883 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 884 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs); 885 try emitDbgStmt(gz, cursor); 886 const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{ 887 .lhs = lhs, 888 .start = start, 889 }); 890 return rvalue(gz, ri, result, node); 891 }, 892 .slice => { 893 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice); 894 const lhs_node = node_datas[node].lhs; 895 const lhs_tag = node_tags[lhs_node]; 896 const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel; 897 const lhs_is_open_slice = lhs_tag == .slice_open or 898 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0); 899 if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) { 900 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs); 901 902 const start = if (lhs_is_slice_sentinel) start: { 903 const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel); 904 break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start); 905 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs); 906 907 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 908 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; 909 try emitDbgStmt(gz, cursor); 910 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{ 911 .lhs = lhs, 912 .start = start, 913 .len = len, 914 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node), 915 .sentinel = .none, 916 }); 917 return rvalue(gz, ri, result, node); 918 } 919 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); 920 921 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 922 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); 923 const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end); 924 try emitDbgStmt(gz, cursor); 925 const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{ 926 .lhs = lhs, 927 .start = start, 928 .end = end, 929 }); 930 return rvalue(gz, ri, result, node); 931 }, 932 .slice_sentinel => { 933 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel); 934 const lhs_node = node_datas[node].lhs; 935 const lhs_tag = node_tags[lhs_node]; 936 const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel; 937 const lhs_is_open_slice = lhs_tag == .slice_open or 938 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0); 939 if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) { 940 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs); 941 942 const start = if (lhs_is_slice_sentinel) start: { 943 const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel); 944 break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start); 945 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs); 946 947 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 948 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; 949 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel); 950 try emitDbgStmt(gz, cursor); 951 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{ 952 .lhs = lhs, 953 .start = start, 954 .len = len, 955 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node), 956 .sentinel = sentinel, 957 }); 958 return rvalue(gz, ri, result, node); 959 } 960 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); 961 962 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 963 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); 964 const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; 965 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel); 966 try emitDbgStmt(gz, cursor); 967 const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{ 968 .lhs = lhs, 969 .start = start, 970 .end = end, 971 .sentinel = sentinel, 972 }); 973 return rvalue(gz, ri, result, node); 974 }, 975 976 .deref => { 977 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs); 978 _ = try gz.addUnNode(.validate_deref, lhs, node); 979 switch (ri.rl) { 980 .ref, .ref_coerced_ty => return lhs, 981 else => { 982 const result = try gz.addUnNode(.load, lhs, node); 983 return rvalue(gz, ri, result, node); 984 }, 985 } 986 }, 987 .address_of => { 988 const operand_rl: ResultInfo.Loc = if (try ri.rl.resultType(gz, node)) |res_ty_inst| rl: { 989 _ = try gz.addUnTok(.validate_ref_ty, res_ty_inst, tree.firstToken(node)); 990 break :rl .{ .ref_coerced_ty = res_ty_inst }; 991 } else .ref; 992 const result = try expr(gz, scope, .{ .rl = operand_rl }, node_datas[node].lhs); 993 return rvalue(gz, ri, result, node); 994 }, 995 .optional_type => { 996 const operand = try typeExpr(gz, scope, node_datas[node].lhs); 997 const result = try gz.addUnNode(.optional_type, operand, node); 998 return rvalue(gz, ri, result, node); 999 }, 1000 .unwrap_optional => switch (ri.rl) { 1001 .ref, .ref_coerced_ty => { 1002 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); 1003 1004 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 1005 try emitDbgStmt(gz, cursor); 1006 1007 return gz.addUnNode(.optional_payload_safe_ptr, lhs, node); 1008 }, 1009 else => { 1010 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs); 1011 1012 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 1013 try emitDbgStmt(gz, cursor); 1014 1015 return rvalue(gz, ri, try gz.addUnNode(.optional_payload_safe, lhs, node), node); 1016 }, 1017 }, 1018 .block_two, .block_two_semicolon => { 1019 const statements = [2]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; 1020 if (node_datas[node].lhs == 0) { 1021 return blockExpr(gz, scope, ri, node, statements[0..0], .normal); 1022 } else if (node_datas[node].rhs == 0) { 1023 return blockExpr(gz, scope, ri, node, statements[0..1], .normal); 1024 } else { 1025 return blockExpr(gz, scope, ri, node, statements[0..2], .normal); 1026 } 1027 }, 1028 .block, .block_semicolon => { 1029 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; 1030 return blockExpr(gz, scope, ri, node, statements, .normal); 1031 }, 1032 .enum_literal => if (try ri.rl.resultType(gz, node)) |res_ty| { 1033 const str_index = try astgen.identAsString(main_tokens[node]); 1034 const res = try gz.addPlNode(.decl_literal, node, Zir.Inst.Field{ 1035 .lhs = res_ty, 1036 .field_name_start = str_index, 1037 }); 1038 switch (ri.rl) { 1039 .discard, .none, .ref => unreachable, // no result type 1040 .ty, .coerced_ty => return res, // `decl_literal` does the coercion for us 1041 .ref_coerced_ty, .ptr, .inferred_ptr, .destructure => return rvalue(gz, ri, res, node), 1042 } 1043 } else return simpleStrTok(gz, ri, main_tokens[node], node, .enum_literal), 1044 .error_value => return simpleStrTok(gz, ri, node_datas[node].rhs, node, .error_value), 1045 // TODO restore this when implementing https://github.com/ziglang/zig/issues/6025 1046 // .anyframe_literal => return rvalue(gz, ri, .anyframe_type, node), 1047 .anyframe_literal => { 1048 const result = try gz.addUnNode(.anyframe_type, .void_type, node); 1049 return rvalue(gz, ri, result, node); 1050 }, 1051 .anyframe_type => { 1052 const return_type = try typeExpr(gz, scope, node_datas[node].rhs); 1053 const result = try gz.addUnNode(.anyframe_type, return_type, node); 1054 return rvalue(gz, ri, result, node); 1055 }, 1056 .@"catch" => { 1057 const catch_token = main_tokens[node]; 1058 const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe) 1059 catch_token + 2 1060 else 1061 null; 1062 no_switch_on_err: { 1063 const capture_token = payload_token orelse break :no_switch_on_err; 1064 const full_switch = tree.fullSwitch(node_datas[node].rhs) orelse break :no_switch_on_err; 1065 if (full_switch.label_token != null) break :no_switch_on_err; 1066 if (node_tags[full_switch.ast.condition] != .identifier) break :no_switch_on_err; 1067 if (!mem.eql(u8, tree.tokenSlice(capture_token), tree.tokenSlice(main_tokens[full_switch.ast.condition]))) break :no_switch_on_err; 1068 return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch"); 1069 } 1070 switch (ri.rl) { 1071 .ref, .ref_coerced_ty => return orelseCatchExpr( 1072 gz, 1073 scope, 1074 ri, 1075 node, 1076 node_datas[node].lhs, 1077 .is_non_err_ptr, 1078 .err_union_payload_unsafe_ptr, 1079 .err_union_code_ptr, 1080 node_datas[node].rhs, 1081 payload_token, 1082 ), 1083 else => return orelseCatchExpr( 1084 gz, 1085 scope, 1086 ri, 1087 node, 1088 node_datas[node].lhs, 1089 .is_non_err, 1090 .err_union_payload_unsafe, 1091 .err_union_code, 1092 node_datas[node].rhs, 1093 payload_token, 1094 ), 1095 } 1096 }, 1097 .@"orelse" => switch (ri.rl) { 1098 .ref, .ref_coerced_ty => return orelseCatchExpr( 1099 gz, 1100 scope, 1101 ri, 1102 node, 1103 node_datas[node].lhs, 1104 .is_non_null_ptr, 1105 .optional_payload_unsafe_ptr, 1106 undefined, 1107 node_datas[node].rhs, 1108 null, 1109 ), 1110 else => return orelseCatchExpr( 1111 gz, 1112 scope, 1113 ri, 1114 node, 1115 node_datas[node].lhs, 1116 .is_non_null, 1117 .optional_payload_unsafe, 1118 undefined, 1119 node_datas[node].rhs, 1120 null, 1121 ), 1122 }, 1123 1124 .ptr_type_aligned, 1125 .ptr_type_sentinel, 1126 .ptr_type, 1127 .ptr_type_bit_range, 1128 => return ptrType(gz, scope, ri, node, tree.fullPtrType(node).?), 1129 1130 .container_decl, 1131 .container_decl_trailing, 1132 .container_decl_arg, 1133 .container_decl_arg_trailing, 1134 .container_decl_two, 1135 .container_decl_two_trailing, 1136 .tagged_union, 1137 .tagged_union_trailing, 1138 .tagged_union_enum_tag, 1139 .tagged_union_enum_tag_trailing, 1140 .tagged_union_two, 1141 .tagged_union_two_trailing, 1142 => { 1143 var buf: [2]Ast.Node.Index = undefined; 1144 return containerDecl(gz, scope, ri, node, tree.fullContainerDecl(&buf, node).?); 1145 }, 1146 1147 .@"break" => return breakExpr(gz, scope, node), 1148 .@"continue" => return continueExpr(gz, scope, node), 1149 .grouped_expression => return expr(gz, scope, ri, node_datas[node].lhs), 1150 .array_type => return arrayType(gz, scope, ri, node), 1151 .array_type_sentinel => return arrayTypeSentinel(gz, scope, ri, node), 1152 .char_literal => return charLiteral(gz, ri, node), 1153 .error_set_decl => return errorSetDecl(gz, ri, node), 1154 .array_access => return arrayAccess(gz, scope, ri, node), 1155 .@"comptime" => return comptimeExprAst(gz, scope, ri, node), 1156 .@"switch", .switch_comma => return switchExpr(gz, scope, ri.br(), node, tree.fullSwitch(node).?), 1157 1158 .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node), 1159 .@"suspend" => return suspendExpr(gz, scope, node), 1160 .@"await" => return awaitExpr(gz, scope, ri, node), 1161 .@"resume" => return resumeExpr(gz, scope, ri, node), 1162 1163 .@"try" => return tryExpr(gz, scope, ri, node, node_datas[node].lhs), 1164 1165 .array_init_one, 1166 .array_init_one_comma, 1167 .array_init_dot_two, 1168 .array_init_dot_two_comma, 1169 .array_init_dot, 1170 .array_init_dot_comma, 1171 .array_init, 1172 .array_init_comma, 1173 => { 1174 var buf: [2]Ast.Node.Index = undefined; 1175 return arrayInitExpr(gz, scope, ri, node, tree.fullArrayInit(&buf, node).?); 1176 }, 1177 1178 .struct_init_one, 1179 .struct_init_one_comma, 1180 .struct_init_dot_two, 1181 .struct_init_dot_two_comma, 1182 .struct_init_dot, 1183 .struct_init_dot_comma, 1184 .struct_init, 1185 .struct_init_comma, 1186 => { 1187 var buf: [2]Ast.Node.Index = undefined; 1188 return structInitExpr(gz, scope, ri, node, tree.fullStructInit(&buf, node).?); 1189 }, 1190 1191 .fn_proto_simple, 1192 .fn_proto_multi, 1193 .fn_proto_one, 1194 .fn_proto, 1195 => { 1196 var buf: [1]Ast.Node.Index = undefined; 1197 return fnProtoExpr(gz, scope, ri, node, tree.fullFnProto(&buf, node).?); 1198 }, 1199 } 1200 } 1201 1202 fn nosuspendExpr( 1203 gz: *GenZir, 1204 scope: *Scope, 1205 ri: ResultInfo, 1206 node: Ast.Node.Index, 1207 ) InnerError!Zir.Inst.Ref { 1208 const astgen = gz.astgen; 1209 const tree = astgen.tree; 1210 const node_datas = tree.nodes.items(.data); 1211 const body_node = node_datas[node].lhs; 1212 assert(body_node != 0); 1213 if (gz.nosuspend_node != 0) { 1214 try astgen.appendErrorNodeNotes(node, "redundant nosuspend block", .{}, &[_]u32{ 1215 try astgen.errNoteNode(gz.nosuspend_node, "other nosuspend block here", .{}), 1216 }); 1217 } 1218 gz.nosuspend_node = node; 1219 defer gz.nosuspend_node = 0; 1220 return expr(gz, scope, ri, body_node); 1221 } 1222 1223 fn suspendExpr( 1224 gz: *GenZir, 1225 scope: *Scope, 1226 node: Ast.Node.Index, 1227 ) InnerError!Zir.Inst.Ref { 1228 const astgen = gz.astgen; 1229 const gpa = astgen.gpa; 1230 const tree = astgen.tree; 1231 const node_datas = tree.nodes.items(.data); 1232 const body_node = node_datas[node].lhs; 1233 1234 if (gz.nosuspend_node != 0) { 1235 return astgen.failNodeNotes(node, "suspend inside nosuspend block", .{}, &[_]u32{ 1236 try astgen.errNoteNode(gz.nosuspend_node, "nosuspend block here", .{}), 1237 }); 1238 } 1239 if (gz.suspend_node != 0) { 1240 return astgen.failNodeNotes(node, "cannot suspend inside suspend block", .{}, &[_]u32{ 1241 try astgen.errNoteNode(gz.suspend_node, "other suspend block here", .{}), 1242 }); 1243 } 1244 assert(body_node != 0); 1245 1246 const suspend_inst = try gz.makeBlockInst(.suspend_block, node); 1247 try gz.instructions.append(gpa, suspend_inst); 1248 1249 var suspend_scope = gz.makeSubBlock(scope); 1250 suspend_scope.suspend_node = node; 1251 defer suspend_scope.unstack(); 1252 1253 const body_result = try fullBodyExpr(&suspend_scope, &suspend_scope.base, .{ .rl = .none }, body_node, .normal); 1254 if (!gz.refIsNoReturn(body_result)) { 1255 _ = try suspend_scope.addBreak(.break_inline, suspend_inst, .void_value); 1256 } 1257 try suspend_scope.setBlockBody(suspend_inst); 1258 1259 return suspend_inst.toRef(); 1260 } 1261 1262 fn awaitExpr( 1263 gz: *GenZir, 1264 scope: *Scope, 1265 ri: ResultInfo, 1266 node: Ast.Node.Index, 1267 ) InnerError!Zir.Inst.Ref { 1268 const astgen = gz.astgen; 1269 const tree = astgen.tree; 1270 const node_datas = tree.nodes.items(.data); 1271 const rhs_node = node_datas[node].lhs; 1272 1273 if (gz.suspend_node != 0) { 1274 return astgen.failNodeNotes(node, "cannot await inside suspend block", .{}, &[_]u32{ 1275 try astgen.errNoteNode(gz.suspend_node, "suspend block here", .{}), 1276 }); 1277 } 1278 const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node); 1279 const result = if (gz.nosuspend_node != 0) 1280 try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{ 1281 .node = gz.nodeIndexToRelative(node), 1282 .operand = operand, 1283 }) 1284 else 1285 try gz.addUnNode(.@"await", operand, node); 1286 1287 return rvalue(gz, ri, result, node); 1288 } 1289 1290 fn resumeExpr( 1291 gz: *GenZir, 1292 scope: *Scope, 1293 ri: ResultInfo, 1294 node: Ast.Node.Index, 1295 ) InnerError!Zir.Inst.Ref { 1296 const astgen = gz.astgen; 1297 const tree = astgen.tree; 1298 const node_datas = tree.nodes.items(.data); 1299 const rhs_node = node_datas[node].lhs; 1300 const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node); 1301 const result = try gz.addUnNode(.@"resume", operand, node); 1302 return rvalue(gz, ri, result, node); 1303 } 1304 1305 fn fnProtoExpr( 1306 gz: *GenZir, 1307 scope: *Scope, 1308 ri: ResultInfo, 1309 node: Ast.Node.Index, 1310 fn_proto: Ast.full.FnProto, 1311 ) InnerError!Zir.Inst.Ref { 1312 const astgen = gz.astgen; 1313 const tree = astgen.tree; 1314 const token_tags = tree.tokens.items(.tag); 1315 1316 if (fn_proto.name_token) |some| { 1317 return astgen.failTok(some, "function type cannot have a name", .{}); 1318 } 1319 1320 const is_extern = blk: { 1321 const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false; 1322 break :blk token_tags[maybe_extern_token] == .keyword_extern; 1323 }; 1324 assert(!is_extern); 1325 1326 var block_scope = gz.makeSubBlock(scope); 1327 defer block_scope.unstack(); 1328 1329 const block_inst = try gz.makeBlockInst(.block_inline, node); 1330 1331 var noalias_bits: u32 = 0; 1332 const is_var_args = is_var_args: { 1333 var param_type_i: usize = 0; 1334 var it = fn_proto.iterate(tree); 1335 while (it.next()) |param| : (param_type_i += 1) { 1336 const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) { 1337 .keyword_noalias => is_comptime: { 1338 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse 1339 return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{})); 1340 break :is_comptime false; 1341 }, 1342 .keyword_comptime => true, 1343 else => false, 1344 } else false; 1345 1346 const is_anytype = if (param.anytype_ellipsis3) |token| blk: { 1347 switch (token_tags[token]) { 1348 .keyword_anytype => break :blk true, 1349 .ellipsis3 => break :is_var_args true, 1350 else => unreachable, 1351 } 1352 } else false; 1353 1354 const param_name = if (param.name_token) |name_token| blk: { 1355 if (mem.eql(u8, "_", tree.tokenSlice(name_token))) 1356 break :blk .empty; 1357 1358 break :blk try astgen.identAsString(name_token); 1359 } else .empty; 1360 1361 if (is_anytype) { 1362 const name_token = param.name_token orelse param.anytype_ellipsis3.?; 1363 1364 const tag: Zir.Inst.Tag = if (is_comptime) 1365 .param_anytype_comptime 1366 else 1367 .param_anytype; 1368 _ = try block_scope.addStrTok(tag, param_name, name_token); 1369 } else { 1370 const param_type_node = param.type_expr; 1371 assert(param_type_node != 0); 1372 var param_gz = block_scope.makeSubBlock(scope); 1373 defer param_gz.unstack(); 1374 const param_type = try fullBodyExpr(¶m_gz, scope, coerced_type_ri, param_type_node, .normal); 1375 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1); 1376 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node); 1377 const main_tokens = tree.nodes.items(.main_token); 1378 const name_token = param.name_token orelse main_tokens[param_type_node]; 1379 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param; 1380 // We pass `prev_param_insts` as `&.{}` here because a function prototype can't refer to previous 1381 // arguments (we haven't set up scopes here). 1382 const param_inst = try block_scope.addParam(¶m_gz, &.{}, tag, name_token, param_name); 1383 assert(param_inst_expected == param_inst); 1384 } 1385 } 1386 break :is_var_args false; 1387 }; 1388 1389 if (fn_proto.ast.align_expr != 0) { 1390 return astgen.failNode(fn_proto.ast.align_expr, "function type cannot have an alignment", .{}); 1391 } 1392 1393 if (fn_proto.ast.addrspace_expr != 0) { 1394 return astgen.failNode(fn_proto.ast.addrspace_expr, "function type cannot have an addrspace", .{}); 1395 } 1396 1397 if (fn_proto.ast.section_expr != 0) { 1398 return astgen.failNode(fn_proto.ast.section_expr, "function type cannot have a linksection", .{}); 1399 } 1400 1401 const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0) 1402 try expr( 1403 &block_scope, 1404 scope, 1405 .{ .rl = .{ .coerced_ty = try block_scope.addBuiltinValue(fn_proto.ast.callconv_expr, .calling_convention) } }, 1406 fn_proto.ast.callconv_expr, 1407 ) 1408 else 1409 Zir.Inst.Ref.none; 1410 1411 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; 1412 const is_inferred_error = token_tags[maybe_bang] == .bang; 1413 if (is_inferred_error) { 1414 return astgen.failTok(maybe_bang, "function type cannot have an inferred error set", .{}); 1415 } 1416 const ret_ty = try expr(&block_scope, scope, coerced_type_ri, fn_proto.ast.return_type); 1417 1418 const result = try block_scope.addFunc(.{ 1419 .src_node = fn_proto.ast.proto_node, 1420 1421 .cc_ref = cc, 1422 .cc_gz = null, 1423 .ret_ref = ret_ty, 1424 .ret_gz = null, 1425 1426 .ret_param_refs = &.{}, 1427 .param_insts = &.{}, 1428 1429 .param_block = block_inst, 1430 .body_gz = null, 1431 .lib_name = .empty, 1432 .is_var_args = is_var_args, 1433 .is_inferred_error = false, 1434 .is_test = false, 1435 .is_extern = false, 1436 .is_noinline = false, 1437 .noalias_bits = noalias_bits, 1438 1439 .proto_hash = undefined, // ignored for `body_gz == null` 1440 }); 1441 1442 _ = try block_scope.addBreak(.break_inline, block_inst, result); 1443 try block_scope.setBlockBody(block_inst); 1444 try gz.instructions.append(astgen.gpa, block_inst); 1445 1446 return rvalue(gz, ri, block_inst.toRef(), fn_proto.ast.proto_node); 1447 } 1448 1449 fn arrayInitExpr( 1450 gz: *GenZir, 1451 scope: *Scope, 1452 ri: ResultInfo, 1453 node: Ast.Node.Index, 1454 array_init: Ast.full.ArrayInit, 1455 ) InnerError!Zir.Inst.Ref { 1456 const astgen = gz.astgen; 1457 const tree = astgen.tree; 1458 const node_tags = tree.nodes.items(.tag); 1459 const main_tokens = tree.nodes.items(.main_token); 1460 1461 assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init. 1462 1463 const array_ty: Zir.Inst.Ref, const elem_ty: Zir.Inst.Ref = inst: { 1464 if (array_init.ast.type_expr == 0) break :inst .{ .none, .none }; 1465 1466 infer: { 1467 const array_type: Ast.full.ArrayType = tree.fullArrayType(array_init.ast.type_expr) orelse break :infer; 1468 // This intentionally does not support `@"_"` syntax. 1469 if (node_tags[array_type.ast.elem_count] == .identifier and 1470 mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_")) 1471 { 1472 const len_inst = try gz.addInt(array_init.ast.elements.len); 1473 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type); 1474 if (array_type.ast.sentinel == 0) { 1475 const array_type_inst = try gz.addPlNode(.array_type, array_init.ast.type_expr, Zir.Inst.Bin{ 1476 .lhs = len_inst, 1477 .rhs = elem_type, 1478 }); 1479 break :inst .{ array_type_inst, elem_type }; 1480 } else { 1481 const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel); 1482 const array_type_inst = try gz.addPlNode( 1483 .array_type_sentinel, 1484 array_init.ast.type_expr, 1485 Zir.Inst.ArrayTypeSentinel{ 1486 .len = len_inst, 1487 .elem_type = elem_type, 1488 .sentinel = sentinel, 1489 }, 1490 ); 1491 break :inst .{ array_type_inst, elem_type }; 1492 } 1493 } 1494 } 1495 const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr); 1496 _ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{ 1497 .ty = array_type_inst, 1498 .init_count = @intCast(array_init.ast.elements.len), 1499 }); 1500 break :inst .{ array_type_inst, .none }; 1501 }; 1502 1503 if (array_ty != .none) { 1504 // Typed inits do not use RLS for language simplicity. 1505 switch (ri.rl) { 1506 .discard => { 1507 if (elem_ty != .none) { 1508 const elem_ri: ResultInfo = .{ .rl = .{ .ty = elem_ty } }; 1509 for (array_init.ast.elements) |elem_init| { 1510 _ = try expr(gz, scope, elem_ri, elem_init); 1511 } 1512 } else { 1513 for (array_init.ast.elements, 0..) |elem_init, i| { 1514 const this_elem_ty = try gz.add(.{ 1515 .tag = .array_init_elem_type, 1516 .data = .{ .bin = .{ 1517 .lhs = array_ty, 1518 .rhs = @enumFromInt(i), 1519 } }, 1520 }); 1521 _ = try expr(gz, scope, .{ .rl = .{ .ty = this_elem_ty } }, elem_init); 1522 } 1523 } 1524 return .void_value; 1525 }, 1526 .ref => return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, true), 1527 else => { 1528 const array_inst = try arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, false); 1529 return rvalue(gz, ri, array_inst, node); 1530 }, 1531 } 1532 } 1533 1534 switch (ri.rl) { 1535 .none => return arrayInitExprAnon(gz, scope, node, array_init.ast.elements), 1536 .discard => { 1537 for (array_init.ast.elements) |elem_init| { 1538 _ = try expr(gz, scope, .{ .rl = .discard }, elem_init); 1539 } 1540 return Zir.Inst.Ref.void_value; 1541 }, 1542 .ref => { 1543 const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements); 1544 return gz.addUnTok(.ref, result, tree.firstToken(node)); 1545 }, 1546 .ref_coerced_ty => |ptr_ty_inst| { 1547 const dest_arr_ty_inst = try gz.addPlNode(.validate_array_init_ref_ty, node, Zir.Inst.ArrayInitRefTy{ 1548 .ptr_ty = ptr_ty_inst, 1549 .elem_count = @intCast(array_init.ast.elements.len), 1550 }); 1551 return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, dest_arr_ty_inst, .none, true); 1552 }, 1553 .ty, .coerced_ty => |result_ty_inst| { 1554 _ = try gz.addPlNode(.validate_array_init_result_ty, node, Zir.Inst.ArrayInit{ 1555 .ty = result_ty_inst, 1556 .init_count = @intCast(array_init.ast.elements.len), 1557 }); 1558 return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, result_ty_inst, .none, false); 1559 }, 1560 .ptr => |ptr| { 1561 try arrayInitExprPtr(gz, scope, node, array_init.ast.elements, ptr.inst); 1562 return .void_value; 1563 }, 1564 .inferred_ptr => { 1565 // We can't get elem pointers of an untyped inferred alloc, so must perform a 1566 // standard anonymous initialization followed by an rvalue store. 1567 // See corresponding logic in structInitExpr. 1568 const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements); 1569 return rvalue(gz, ri, result, node); 1570 }, 1571 .destructure => |destructure| { 1572 // Untyped init - destructure directly into result pointers 1573 if (array_init.ast.elements.len != destructure.components.len) { 1574 return astgen.failNodeNotes(node, "expected {} elements for destructure, found {}", .{ 1575 destructure.components.len, 1576 array_init.ast.elements.len, 1577 }, &.{ 1578 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}), 1579 }); 1580 } 1581 for (array_init.ast.elements, destructure.components) |elem_init, ds_comp| { 1582 const elem_ri: ResultInfo = .{ .rl = switch (ds_comp) { 1583 .typed_ptr => |ptr_rl| .{ .ptr = ptr_rl }, 1584 .inferred_ptr => |ptr_inst| .{ .inferred_ptr = ptr_inst }, 1585 .discard => .discard, 1586 } }; 1587 _ = try expr(gz, scope, elem_ri, elem_init); 1588 } 1589 return .void_value; 1590 }, 1591 } 1592 } 1593 1594 /// An array initialization expression using an `array_init_anon` instruction. 1595 fn arrayInitExprAnon( 1596 gz: *GenZir, 1597 scope: *Scope, 1598 node: Ast.Node.Index, 1599 elements: []const Ast.Node.Index, 1600 ) InnerError!Zir.Inst.Ref { 1601 const astgen = gz.astgen; 1602 1603 const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{ 1604 .operands_len = @intCast(elements.len), 1605 }); 1606 var extra_index = try reserveExtra(astgen, elements.len); 1607 1608 for (elements) |elem_init| { 1609 const elem_ref = try expr(gz, scope, .{ .rl = .none }, elem_init); 1610 astgen.extra.items[extra_index] = @intFromEnum(elem_ref); 1611 extra_index += 1; 1612 } 1613 return try gz.addPlNodePayloadIndex(.array_init_anon, node, payload_index); 1614 } 1615 1616 /// An array initialization expression using an `array_init` or `array_init_ref` instruction. 1617 fn arrayInitExprTyped( 1618 gz: *GenZir, 1619 scope: *Scope, 1620 node: Ast.Node.Index, 1621 elements: []const Ast.Node.Index, 1622 ty_inst: Zir.Inst.Ref, 1623 maybe_elem_ty_inst: Zir.Inst.Ref, 1624 is_ref: bool, 1625 ) InnerError!Zir.Inst.Ref { 1626 const astgen = gz.astgen; 1627 1628 const len = elements.len + 1; // +1 for type 1629 const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{ 1630 .operands_len = @intCast(len), 1631 }); 1632 var extra_index = try reserveExtra(astgen, len); 1633 astgen.extra.items[extra_index] = @intFromEnum(ty_inst); 1634 extra_index += 1; 1635 1636 if (maybe_elem_ty_inst != .none) { 1637 const elem_ri: ResultInfo = .{ .rl = .{ .coerced_ty = maybe_elem_ty_inst } }; 1638 for (elements) |elem_init| { 1639 const elem_inst = try expr(gz, scope, elem_ri, elem_init); 1640 astgen.extra.items[extra_index] = @intFromEnum(elem_inst); 1641 extra_index += 1; 1642 } 1643 } else { 1644 for (elements, 0..) |elem_init, i| { 1645 const ri: ResultInfo = .{ .rl = .{ .coerced_ty = try gz.add(.{ 1646 .tag = .array_init_elem_type, 1647 .data = .{ .bin = .{ 1648 .lhs = ty_inst, 1649 .rhs = @enumFromInt(i), 1650 } }, 1651 }) } }; 1652 1653 const elem_inst = try expr(gz, scope, ri, elem_init); 1654 astgen.extra.items[extra_index] = @intFromEnum(elem_inst); 1655 extra_index += 1; 1656 } 1657 } 1658 1659 const tag: Zir.Inst.Tag = if (is_ref) .array_init_ref else .array_init; 1660 return try gz.addPlNodePayloadIndex(tag, node, payload_index); 1661 } 1662 1663 /// An array initialization expression using element pointers. 1664 fn arrayInitExprPtr( 1665 gz: *GenZir, 1666 scope: *Scope, 1667 node: Ast.Node.Index, 1668 elements: []const Ast.Node.Index, 1669 ptr_inst: Zir.Inst.Ref, 1670 ) InnerError!void { 1671 const astgen = gz.astgen; 1672 1673 const array_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node); 1674 1675 const payload_index = try addExtra(astgen, Zir.Inst.Block{ 1676 .body_len = @intCast(elements.len), 1677 }); 1678 var extra_index = try reserveExtra(astgen, elements.len); 1679 1680 for (elements, 0..) |elem_init, i| { 1681 const elem_ptr_inst = try gz.addPlNode(.array_init_elem_ptr, elem_init, Zir.Inst.ElemPtrImm{ 1682 .ptr = array_ptr_inst, 1683 .index = @intCast(i), 1684 }); 1685 astgen.extra.items[extra_index] = @intFromEnum(elem_ptr_inst.toIndex().?); 1686 extra_index += 1; 1687 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = elem_ptr_inst } } }, elem_init); 1688 } 1689 1690 _ = try gz.addPlNodePayloadIndex(.validate_ptr_array_init, node, payload_index); 1691 } 1692 1693 fn structInitExpr( 1694 gz: *GenZir, 1695 scope: *Scope, 1696 ri: ResultInfo, 1697 node: Ast.Node.Index, 1698 struct_init: Ast.full.StructInit, 1699 ) InnerError!Zir.Inst.Ref { 1700 const astgen = gz.astgen; 1701 const tree = astgen.tree; 1702 1703 if (struct_init.ast.type_expr == 0) { 1704 if (struct_init.ast.fields.len == 0) { 1705 // Anonymous init with no fields. 1706 switch (ri.rl) { 1707 .discard => return .void_value, 1708 .ref_coerced_ty => |ptr_ty_inst| return gz.addUnNode(.struct_init_empty_ref_result, ptr_ty_inst, node), 1709 .ty, .coerced_ty => |ty_inst| return gz.addUnNode(.struct_init_empty_result, ty_inst, node), 1710 .ptr => { 1711 // TODO: should we modify this to use RLS for the field stores here? 1712 const ty_inst = (try ri.rl.resultType(gz, node)).?; 1713 const val = try gz.addUnNode(.struct_init_empty_result, ty_inst, node); 1714 return rvalue(gz, ri, val, node); 1715 }, 1716 .none, .ref, .inferred_ptr => { 1717 return rvalue(gz, ri, .empty_tuple, node); 1718 }, 1719 .destructure => |destructure| { 1720 return astgen.failNodeNotes(node, "empty initializer cannot be destructured", .{}, &.{ 1721 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}), 1722 }); 1723 }, 1724 } 1725 } 1726 } else array: { 1727 const node_tags = tree.nodes.items(.tag); 1728 const main_tokens = tree.nodes.items(.main_token); 1729 const array_type: Ast.full.ArrayType = tree.fullArrayType(struct_init.ast.type_expr) orelse { 1730 if (struct_init.ast.fields.len == 0) { 1731 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); 1732 const result = try gz.addUnNode(.struct_init_empty, ty_inst, node); 1733 return rvalue(gz, ri, result, node); 1734 } 1735 break :array; 1736 }; 1737 const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and 1738 // This intentionally does not support `@"_"` syntax. 1739 mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_"); 1740 if (struct_init.ast.fields.len == 0) { 1741 if (is_inferred_array_len) { 1742 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type); 1743 const array_type_inst = if (array_type.ast.sentinel == 0) blk: { 1744 break :blk try gz.addPlNode(.array_type, struct_init.ast.type_expr, Zir.Inst.Bin{ 1745 .lhs = .zero_usize, 1746 .rhs = elem_type, 1747 }); 1748 } else blk: { 1749 const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel); 1750 break :blk try gz.addPlNode( 1751 .array_type_sentinel, 1752 struct_init.ast.type_expr, 1753 Zir.Inst.ArrayTypeSentinel{ 1754 .len = .zero_usize, 1755 .elem_type = elem_type, 1756 .sentinel = sentinel, 1757 }, 1758 ); 1759 }; 1760 const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node); 1761 return rvalue(gz, ri, result, node); 1762 } 1763 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); 1764 const result = try gz.addUnNode(.struct_init_empty, ty_inst, node); 1765 return rvalue(gz, ri, result, node); 1766 } else { 1767 return astgen.failNode( 1768 struct_init.ast.type_expr, 1769 "initializing array with struct syntax", 1770 .{}, 1771 ); 1772 } 1773 } 1774 1775 { 1776 var sfba = std.heap.stackFallback(256, astgen.arena); 1777 const sfba_allocator = sfba.get(); 1778 1779 var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator); 1780 try duplicate_names.ensureTotalCapacity(@intCast(struct_init.ast.fields.len)); 1781 1782 // When there aren't errors, use this to avoid a second iteration. 1783 var any_duplicate = false; 1784 1785 for (struct_init.ast.fields) |field| { 1786 const name_token = tree.firstToken(field) - 2; 1787 const name_index = try astgen.identAsString(name_token); 1788 1789 const gop = try duplicate_names.getOrPut(name_index); 1790 1791 if (gop.found_existing) { 1792 try gop.value_ptr.append(sfba_allocator, name_token); 1793 any_duplicate = true; 1794 } else { 1795 gop.value_ptr.* = .{}; 1796 try gop.value_ptr.append(sfba_allocator, name_token); 1797 } 1798 } 1799 1800 if (any_duplicate) { 1801 var it = duplicate_names.iterator(); 1802 1803 while (it.next()) |entry| { 1804 const record = entry.value_ptr.*; 1805 if (record.items.len > 1) { 1806 var error_notes = std.ArrayList(u32).init(astgen.arena); 1807 1808 for (record.items[1..]) |duplicate| { 1809 try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate name here", .{})); 1810 } 1811 1812 try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{})); 1813 1814 try astgen.appendErrorTokNotes( 1815 record.items[0], 1816 "duplicate struct field name", 1817 .{}, 1818 error_notes.items, 1819 ); 1820 } 1821 } 1822 1823 return error.AnalysisFail; 1824 } 1825 } 1826 1827 if (struct_init.ast.type_expr != 0) { 1828 // Typed inits do not use RLS for language simplicity. 1829 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); 1830 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); 1831 switch (ri.rl) { 1832 .ref => return structInitExprTyped(gz, scope, node, struct_init, ty_inst, true), 1833 else => { 1834 const struct_inst = try structInitExprTyped(gz, scope, node, struct_init, ty_inst, false); 1835 return rvalue(gz, ri, struct_inst, node); 1836 }, 1837 } 1838 } 1839 1840 switch (ri.rl) { 1841 .none => return structInitExprAnon(gz, scope, node, struct_init), 1842 .discard => { 1843 // Even if discarding we must perform side-effects. 1844 for (struct_init.ast.fields) |field_init| { 1845 _ = try expr(gz, scope, .{ .rl = .discard }, field_init); 1846 } 1847 return .void_value; 1848 }, 1849 .ref => { 1850 const result = try structInitExprAnon(gz, scope, node, struct_init); 1851 return gz.addUnTok(.ref, result, tree.firstToken(node)); 1852 }, 1853 .ref_coerced_ty => |ptr_ty_inst| { 1854 const result_ty_inst = try gz.addUnNode(.elem_type, ptr_ty_inst, node); 1855 _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node); 1856 return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, true); 1857 }, 1858 .ty, .coerced_ty => |result_ty_inst| { 1859 _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node); 1860 return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, false); 1861 }, 1862 .ptr => |ptr| { 1863 try structInitExprPtr(gz, scope, node, struct_init, ptr.inst); 1864 return .void_value; 1865 }, 1866 .inferred_ptr => { 1867 // We can't get field pointers of an untyped inferred alloc, so must perform a 1868 // standard anonymous initialization followed by an rvalue store. 1869 // See corresponding logic in arrayInitExpr. 1870 const struct_inst = try structInitExprAnon(gz, scope, node, struct_init); 1871 return rvalue(gz, ri, struct_inst, node); 1872 }, 1873 .destructure => |destructure| { 1874 // This is an untyped init, so is an actual struct, which does 1875 // not support destructuring. 1876 return astgen.failNodeNotes(node, "struct value cannot be destructured", .{}, &.{ 1877 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}), 1878 }); 1879 }, 1880 } 1881 } 1882 1883 /// A struct initialization expression using a `struct_init_anon` instruction. 1884 fn structInitExprAnon( 1885 gz: *GenZir, 1886 scope: *Scope, 1887 node: Ast.Node.Index, 1888 struct_init: Ast.full.StructInit, 1889 ) InnerError!Zir.Inst.Ref { 1890 const astgen = gz.astgen; 1891 const tree = astgen.tree; 1892 1893 const payload_index = try addExtra(astgen, Zir.Inst.StructInitAnon{ 1894 .abs_node = node, 1895 .abs_line = astgen.source_line, 1896 .fields_len = @intCast(struct_init.ast.fields.len), 1897 }); 1898 const field_size = @typeInfo(Zir.Inst.StructInitAnon.Item).@"struct".fields.len; 1899 var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size); 1900 1901 for (struct_init.ast.fields) |field_init| { 1902 const name_token = tree.firstToken(field_init) - 2; 1903 const str_index = try astgen.identAsString(name_token); 1904 setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{ 1905 .field_name = str_index, 1906 .init = try expr(gz, scope, .{ .rl = .none }, field_init), 1907 }); 1908 extra_index += field_size; 1909 } 1910 1911 return gz.addPlNodePayloadIndex(.struct_init_anon, node, payload_index); 1912 } 1913 1914 /// A struct initialization expression using a `struct_init` or `struct_init_ref` instruction. 1915 fn structInitExprTyped( 1916 gz: *GenZir, 1917 scope: *Scope, 1918 node: Ast.Node.Index, 1919 struct_init: Ast.full.StructInit, 1920 ty_inst: Zir.Inst.Ref, 1921 is_ref: bool, 1922 ) InnerError!Zir.Inst.Ref { 1923 const astgen = gz.astgen; 1924 const tree = astgen.tree; 1925 1926 const payload_index = try addExtra(astgen, Zir.Inst.StructInit{ 1927 .abs_node = node, 1928 .abs_line = astgen.source_line, 1929 .fields_len = @intCast(struct_init.ast.fields.len), 1930 }); 1931 const field_size = @typeInfo(Zir.Inst.StructInit.Item).@"struct".fields.len; 1932 var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size); 1933 1934 for (struct_init.ast.fields) |field_init| { 1935 const name_token = tree.firstToken(field_init) - 2; 1936 const str_index = try astgen.identAsString(name_token); 1937 const field_ty_inst = try gz.addPlNode(.struct_init_field_type, field_init, Zir.Inst.FieldType{ 1938 .container_type = ty_inst, 1939 .name_start = str_index, 1940 }); 1941 setExtra(astgen, extra_index, Zir.Inst.StructInit.Item{ 1942 .field_type = field_ty_inst.toIndex().?, 1943 .init = try expr(gz, scope, .{ .rl = .{ .coerced_ty = field_ty_inst } }, field_init), 1944 }); 1945 extra_index += field_size; 1946 } 1947 1948 const tag: Zir.Inst.Tag = if (is_ref) .struct_init_ref else .struct_init; 1949 return gz.addPlNodePayloadIndex(tag, node, payload_index); 1950 } 1951 1952 /// A struct initialization expression using field pointers. 1953 fn structInitExprPtr( 1954 gz: *GenZir, 1955 scope: *Scope, 1956 node: Ast.Node.Index, 1957 struct_init: Ast.full.StructInit, 1958 ptr_inst: Zir.Inst.Ref, 1959 ) InnerError!void { 1960 const astgen = gz.astgen; 1961 const tree = astgen.tree; 1962 1963 const struct_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node); 1964 1965 const payload_index = try addExtra(astgen, Zir.Inst.Block{ 1966 .body_len = @intCast(struct_init.ast.fields.len), 1967 }); 1968 var extra_index = try reserveExtra(astgen, struct_init.ast.fields.len); 1969 1970 for (struct_init.ast.fields) |field_init| { 1971 const name_token = tree.firstToken(field_init) - 2; 1972 const str_index = try astgen.identAsString(name_token); 1973 const field_ptr = try gz.addPlNode(.struct_init_field_ptr, field_init, Zir.Inst.Field{ 1974 .lhs = struct_ptr_inst, 1975 .field_name_start = str_index, 1976 }); 1977 astgen.extra.items[extra_index] = @intFromEnum(field_ptr.toIndex().?); 1978 extra_index += 1; 1979 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = field_ptr } } }, field_init); 1980 } 1981 1982 _ = try gz.addPlNodePayloadIndex(.validate_ptr_struct_init, node, payload_index); 1983 } 1984 1985 /// This explicitly calls expr in a comptime scope by wrapping it in a `block_comptime` if 1986 /// necessary. It should be used whenever we need to force compile-time evaluation of something, 1987 /// such as a type. 1988 /// The function corresponding to `comptime` expression syntax is `comptimeExprAst`. 1989 fn comptimeExpr( 1990 gz: *GenZir, 1991 scope: *Scope, 1992 ri: ResultInfo, 1993 node: Ast.Node.Index, 1994 ) InnerError!Zir.Inst.Ref { 1995 if (gz.is_comptime) { 1996 // No need to change anything! 1997 return expr(gz, scope, ri, node); 1998 } 1999 2000 // There's an optimization here: if the body will be evaluated at comptime regardless, there's 2001 // no need to wrap it in a block. This is hard to determine in general, but we can identify a 2002 // common subset of trivially comptime expressions to take down the size of the ZIR a bit. 2003 const tree = gz.astgen.tree; 2004 const main_tokens = tree.nodes.items(.main_token); 2005 const node_tags = tree.nodes.items(.tag); 2006 switch (node_tags[node]) { 2007 // Any identifier in `primitive_instrs` is trivially comptime. In particular, this includes 2008 // some common types, so we can elide `block_comptime` for a few common type annotations. 2009 .identifier => { 2010 const ident_token = main_tokens[node]; 2011 const ident_name_raw = tree.tokenSlice(ident_token); 2012 if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| { 2013 // No need to worry about result location here, we're not creating a comptime block! 2014 return rvalue(gz, ri, zir_const_ref, node); 2015 } 2016 }, 2017 2018 // We can also avoid the block for a few trivial AST tags which are always comptime-known. 2019 .number_literal, .string_literal, .multiline_string_literal, .enum_literal, .error_value => { 2020 // No need to worry about result location here, we're not creating a comptime block! 2021 return expr(gz, scope, ri, node); 2022 }, 2023 2024 // Lastly, for labelled blocks, avoid emitting a labelled block directly inside this 2025 // comptime block, because that would be silly! Note that we don't bother doing this for 2026 // unlabelled blocks, since they don't generate blocks at comptime anyway (see `blockExpr`). 2027 .block_two, .block_two_semicolon, .block, .block_semicolon => { 2028 const token_tags = tree.tokens.items(.tag); 2029 const lbrace = main_tokens[node]; 2030 // Careful! We can't pass in the real result location here, since it may 2031 // refer to runtime memory. A runtime-to-comptime boundary has to remove 2032 // result location information, compute the result, and copy it to the true 2033 // result location at runtime. We do this below as well. 2034 const ty_only_ri: ResultInfo = .{ 2035 .ctx = ri.ctx, 2036 .rl = if (try ri.rl.resultType(gz, node)) |res_ty| 2037 .{ .coerced_ty = res_ty } 2038 else 2039 .none, 2040 }; 2041 if (token_tags[lbrace - 1] == .colon and 2042 token_tags[lbrace - 2] == .identifier) 2043 { 2044 const node_datas = tree.nodes.items(.data); 2045 switch (node_tags[node]) { 2046 .block_two, .block_two_semicolon => { 2047 const stmts: [2]Ast.Node.Index = .{ node_datas[node].lhs, node_datas[node].rhs }; 2048 const stmt_slice = if (stmts[0] == 0) 2049 stmts[0..0] 2050 else if (stmts[1] == 0) 2051 stmts[0..1] 2052 else 2053 stmts[0..2]; 2054 2055 const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmt_slice, true, .normal); 2056 return rvalue(gz, ri, block_ref, node); 2057 }, 2058 .block, .block_semicolon => { 2059 const stmts = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; 2060 // Replace result location and copy back later - see above. 2061 const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmts, true, .normal); 2062 return rvalue(gz, ri, block_ref, node); 2063 }, 2064 else => unreachable, 2065 } 2066 } 2067 }, 2068 2069 // In other cases, we don't optimize anything - we need a wrapper comptime block. 2070 else => {}, 2071 } 2072 2073 var block_scope = gz.makeSubBlock(scope); 2074 block_scope.is_comptime = true; 2075 defer block_scope.unstack(); 2076 2077 const block_inst = try gz.makeBlockInst(.block_comptime, node); 2078 // Replace result location and copy back later - see above. 2079 const ty_only_ri: ResultInfo = .{ 2080 .ctx = ri.ctx, 2081 .rl = if (try ri.rl.resultType(gz, node)) |res_ty| 2082 .{ .coerced_ty = res_ty } 2083 else 2084 .none, 2085 }; 2086 const block_result = try fullBodyExpr(&block_scope, scope, ty_only_ri, node, .normal); 2087 if (!gz.refIsNoReturn(block_result)) { 2088 _ = try block_scope.addBreak(.@"break", block_inst, block_result); 2089 } 2090 try block_scope.setBlockBody(block_inst); 2091 try gz.instructions.append(gz.astgen.gpa, block_inst); 2092 2093 return rvalue(gz, ri, block_inst.toRef(), node); 2094 } 2095 2096 /// This one is for an actual `comptime` syntax, and will emit a compile error if 2097 /// the scope is already known to be comptime-evaluated. 2098 /// See `comptimeExpr` for the helper function for calling expr in a comptime scope. 2099 fn comptimeExprAst( 2100 gz: *GenZir, 2101 scope: *Scope, 2102 ri: ResultInfo, 2103 node: Ast.Node.Index, 2104 ) InnerError!Zir.Inst.Ref { 2105 const astgen = gz.astgen; 2106 if (gz.is_comptime) { 2107 try astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{}); 2108 } 2109 const tree = astgen.tree; 2110 const node_datas = tree.nodes.items(.data); 2111 const body_node = node_datas[node].lhs; 2112 return comptimeExpr(gz, scope, ri, body_node); 2113 } 2114 2115 /// Restore the error return trace index. Performs the restore only if the result is a non-error or 2116 /// if the result location is a non-error-handling expression. 2117 fn restoreErrRetIndex( 2118 gz: *GenZir, 2119 bt: GenZir.BranchTarget, 2120 ri: ResultInfo, 2121 node: Ast.Node.Index, 2122 result: Zir.Inst.Ref, 2123 ) !void { 2124 const op = switch (nodeMayEvalToError(gz.astgen.tree, node)) { 2125 .always => return, // never restore/pop 2126 .never => .none, // always restore/pop 2127 .maybe => switch (ri.ctx) { 2128 .error_handling_expr, .@"return", .fn_arg, .const_init => switch (ri.rl) { 2129 .ptr => |ptr_res| try gz.addUnNode(.load, ptr_res.inst, node), 2130 .inferred_ptr => blk: { 2131 // This is a terrible workaround for Sema's inability to load from a .alloc_inferred ptr 2132 // before its type has been resolved. There is no valid operand to use here, so error 2133 // traces will be popped prematurely. 2134 // TODO: Update this to do a proper load from the rl_ptr, once Sema can support it. 2135 break :blk .none; 2136 }, 2137 .destructure => return, // value must be a tuple or array, so never restore/pop 2138 else => result, 2139 }, 2140 else => .none, // always restore/pop 2141 }, 2142 }; 2143 _ = try gz.addRestoreErrRetIndex(bt, .{ .if_non_error = op }, node); 2144 } 2145 2146 fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 2147 const astgen = parent_gz.astgen; 2148 const tree = astgen.tree; 2149 const node_datas = tree.nodes.items(.data); 2150 const break_label = node_datas[node].lhs; 2151 const rhs = node_datas[node].rhs; 2152 2153 // Look for the label in the scope. 2154 var scope = parent_scope; 2155 while (true) { 2156 switch (scope.tag) { 2157 .gen_zir => { 2158 const block_gz = scope.cast(GenZir).?; 2159 2160 if (block_gz.cur_defer_node != 0) { 2161 // We are breaking out of a `defer` block. 2162 return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{ 2163 try astgen.errNoteNode( 2164 block_gz.cur_defer_node, 2165 "defer expression here", 2166 .{}, 2167 ), 2168 }); 2169 } 2170 2171 const block_inst = blk: { 2172 if (break_label != 0) { 2173 if (block_gz.label) |*label| { 2174 if (try astgen.tokenIdentEql(label.token, break_label)) { 2175 label.used = true; 2176 break :blk label.block_inst; 2177 } 2178 } 2179 } else if (block_gz.break_block.unwrap()) |i| { 2180 break :blk i; 2181 } 2182 // If not the target, start over with the parent 2183 scope = block_gz.parent; 2184 continue; 2185 }; 2186 // If we made it here, this block is the target of the break expr 2187 2188 const break_tag: Zir.Inst.Tag = if (block_gz.is_inline) 2189 .break_inline 2190 else 2191 .@"break"; 2192 2193 if (rhs == 0) { 2194 _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node); 2195 2196 try genDefers(parent_gz, scope, parent_scope, .normal_only); 2197 2198 // As our last action before the break, "pop" the error trace if needed 2199 if (!block_gz.is_comptime) 2200 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, node); 2201 2202 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); 2203 return Zir.Inst.Ref.unreachable_value; 2204 } 2205 2206 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node); 2207 2208 try genDefers(parent_gz, scope, parent_scope, .normal_only); 2209 2210 // As our last action before the break, "pop" the error trace if needed 2211 if (!block_gz.is_comptime) 2212 try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand); 2213 2214 switch (block_gz.break_result_info.rl) { 2215 .ptr => { 2216 // In this case we don't have any mechanism to intercept it; 2217 // we assume the result location is written, and we break with void. 2218 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); 2219 }, 2220 .discard => { 2221 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); 2222 }, 2223 else => { 2224 _ = try parent_gz.addBreakWithSrcNode(break_tag, block_inst, operand, rhs); 2225 }, 2226 } 2227 return Zir.Inst.Ref.unreachable_value; 2228 }, 2229 .local_val => scope = scope.cast(Scope.LocalVal).?.parent, 2230 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, 2231 .namespace => break, 2232 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, 2233 .top => unreachable, 2234 } 2235 } 2236 if (break_label != 0) { 2237 const label_name = try astgen.identifierTokenString(break_label); 2238 return astgen.failTok(break_label, "label not found: '{s}'", .{label_name}); 2239 } else { 2240 return astgen.failNode(node, "break expression outside loop", .{}); 2241 } 2242 } 2243 2244 fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 2245 const astgen = parent_gz.astgen; 2246 const tree = astgen.tree; 2247 const node_datas = tree.nodes.items(.data); 2248 const break_label = node_datas[node].lhs; 2249 const rhs = node_datas[node].rhs; 2250 2251 if (break_label == 0 and rhs != 0) { 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 != 0) { 2263 return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{ 2264 try astgen.errNoteNode( 2265 gen_zir.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 (break_label != 0) 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 (rhs != 0) 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 (rhs != 0) { 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 (break_label != 0) { 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 const node_tags = tree.nodes.items(.tag); 2367 const node_datas = tree.nodes.items(.data); 2368 const main_tokens = tree.nodes.items(.main_token); 2369 const token_tags = tree.tokens.items(.tag); 2370 var stmt_buf: [2]Ast.Node.Index = undefined; 2371 const statements: []const Ast.Node.Index = switch (node_tags[node]) { 2372 else => return expr(gz, scope, ri, node), 2373 .block_two, .block_two_semicolon => if (node_datas[node].lhs == 0) s: { 2374 break :s &.{}; 2375 } else if (node_datas[node].rhs == 0) s: { 2376 stmt_buf[0] = node_datas[node].lhs; 2377 break :s stmt_buf[0..1]; 2378 } else s: { 2379 stmt_buf[0] = node_datas[node].lhs; 2380 stmt_buf[1] = node_datas[node].rhs; 2381 break :s stmt_buf[0..2]; 2382 }, 2383 .block, .block_semicolon => tree.extra_data[node_datas[node].lhs..node_datas[node].rhs], 2384 }; 2385 2386 const lbrace = main_tokens[node]; 2387 if (token_tags[lbrace - 1] == .colon and 2388 token_tags[lbrace - 2] == .identifier) 2389 { 2390 // Labeled blocks are tricky - forwarding result location information properly is non-trivial, 2391 // plus if this block is exited with a `break_inline` we aren't allowed multiple breaks. This 2392 // case is rare, so just treat it as a normal expression and create a nested block. 2393 return blockExpr(gz, scope, ri, node, statements, block_kind); 2394 } 2395 2396 var sub_gz = gz.makeSubBlock(scope); 2397 try blockExprStmts(&sub_gz, &sub_gz.base, statements, block_kind); 2398 2399 return rvalue(gz, ri, .void_value, node); 2400 } 2401 2402 const BlockKind = enum { normal, allow_branch_hint }; 2403 2404 fn blockExpr( 2405 gz: *GenZir, 2406 scope: *Scope, 2407 ri: ResultInfo, 2408 block_node: Ast.Node.Index, 2409 statements: []const Ast.Node.Index, 2410 kind: BlockKind, 2411 ) InnerError!Zir.Inst.Ref { 2412 const astgen = gz.astgen; 2413 const tree = astgen.tree; 2414 const main_tokens = tree.nodes.items(.main_token); 2415 const token_tags = tree.tokens.items(.tag); 2416 2417 const lbrace = main_tokens[block_node]; 2418 if (token_tags[lbrace - 1] == .colon and 2419 token_tags[lbrace - 2] == .identifier) 2420 { 2421 return labeledBlockExpr(gz, scope, ri, block_node, statements, false, kind); 2422 } 2423 2424 if (!gz.is_comptime) { 2425 // Since this block is unlabeled, its control flow is effectively linear and we 2426 // can *almost* get away with inlining the block here. However, we actually need 2427 // to preserve the .block for Sema, to properly pop the error return trace. 2428 2429 const block_tag: Zir.Inst.Tag = .block; 2430 const block_inst = try gz.makeBlockInst(block_tag, block_node); 2431 try gz.instructions.append(astgen.gpa, block_inst); 2432 2433 var block_scope = gz.makeSubBlock(scope); 2434 defer block_scope.unstack(); 2435 2436 try blockExprStmts(&block_scope, &block_scope.base, statements, kind); 2437 2438 if (!block_scope.endsWithNoReturn()) { 2439 // As our last action before the break, "pop" the error trace if needed 2440 _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node); 2441 // No `rvalue` call here, as the block result is always `void`, so we do that below. 2442 _ = try block_scope.addBreak(.@"break", block_inst, .void_value); 2443 } 2444 2445 try block_scope.setBlockBody(block_inst); 2446 } else { 2447 var sub_gz = gz.makeSubBlock(scope); 2448 try blockExprStmts(&sub_gz, &sub_gz.base, statements, kind); 2449 } 2450 2451 return rvalue(gz, ri, .void_value, block_node); 2452 } 2453 2454 fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.TokenIndex) !void { 2455 // Look for the label in the scope. 2456 var scope = parent_scope; 2457 while (true) { 2458 switch (scope.tag) { 2459 .gen_zir => { 2460 const gen_zir = scope.cast(GenZir).?; 2461 if (gen_zir.label) |prev_label| { 2462 if (try astgen.tokenIdentEql(label, prev_label.token)) { 2463 const label_name = try astgen.identifierTokenString(label); 2464 return astgen.failTokNotes(label, "redefinition of label '{s}'", .{ 2465 label_name, 2466 }, &[_]u32{ 2467 try astgen.errNoteTok( 2468 prev_label.token, 2469 "previous definition here", 2470 .{}, 2471 ), 2472 }); 2473 } 2474 } 2475 scope = gen_zir.parent; 2476 }, 2477 .local_val => scope = scope.cast(Scope.LocalVal).?.parent, 2478 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, 2479 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, 2480 .namespace => break, 2481 .top => unreachable, 2482 } 2483 } 2484 } 2485 2486 fn labeledBlockExpr( 2487 gz: *GenZir, 2488 parent_scope: *Scope, 2489 ri: ResultInfo, 2490 block_node: Ast.Node.Index, 2491 statements: []const Ast.Node.Index, 2492 force_comptime: bool, 2493 block_kind: BlockKind, 2494 ) InnerError!Zir.Inst.Ref { 2495 const astgen = gz.astgen; 2496 const tree = astgen.tree; 2497 const main_tokens = tree.nodes.items(.main_token); 2498 const token_tags = tree.tokens.items(.tag); 2499 2500 const lbrace = main_tokens[block_node]; 2501 const label_token = lbrace - 2; 2502 assert(token_tags[label_token] == .identifier); 2503 2504 try astgen.checkLabelRedefinition(parent_scope, label_token); 2505 2506 const need_rl = astgen.nodes_need_rl.contains(block_node); 2507 const block_ri: ResultInfo = if (need_rl) ri else .{ 2508 .rl = switch (ri.rl) { 2509 .ptr => .{ .ty = (try ri.rl.resultType(gz, block_node)).? }, 2510 .inferred_ptr => .none, 2511 else => ri.rl, 2512 }, 2513 .ctx = ri.ctx, 2514 }; 2515 // We need to call `rvalue` to write through to the pointer only if we had a 2516 // result pointer and aren't forwarding it. 2517 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 2518 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 2519 2520 // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct 2521 // so that break statements can reference it. 2522 const block_tag: Zir.Inst.Tag = if (force_comptime) .block_comptime else .block; 2523 const block_inst = try gz.makeBlockInst(block_tag, block_node); 2524 try gz.instructions.append(astgen.gpa, block_inst); 2525 var block_scope = gz.makeSubBlock(parent_scope); 2526 block_scope.label = GenZir.Label{ 2527 .token = label_token, 2528 .block_inst = block_inst, 2529 }; 2530 block_scope.setBreakResultInfo(block_ri); 2531 if (force_comptime) block_scope.is_comptime = true; 2532 defer block_scope.unstack(); 2533 2534 try blockExprStmts(&block_scope, &block_scope.base, statements, block_kind); 2535 if (!block_scope.endsWithNoReturn()) { 2536 // As our last action before the return, "pop" the error trace if needed 2537 _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node); 2538 const result = try rvalue(gz, block_scope.break_result_info, .void_value, block_node); 2539 _ = try block_scope.addBreak(.@"break", block_inst, result); 2540 } 2541 2542 if (!block_scope.label.?.used) { 2543 try astgen.appendErrorTok(label_token, "unused block label", .{}); 2544 } 2545 2546 try block_scope.setBlockBody(block_inst); 2547 if (need_result_rvalue) { 2548 return rvalue(gz, ri, block_inst.toRef(), block_node); 2549 } else { 2550 return block_inst.toRef(); 2551 } 2552 } 2553 2554 fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Node.Index, block_kind: BlockKind) !void { 2555 const astgen = gz.astgen; 2556 const tree = astgen.tree; 2557 const node_tags = tree.nodes.items(.tag); 2558 const node_data = tree.nodes.items(.data); 2559 2560 if (statements.len == 0) return; 2561 2562 var block_arena = std.heap.ArenaAllocator.init(gz.astgen.gpa); 2563 defer block_arena.deinit(); 2564 const block_arena_allocator = block_arena.allocator(); 2565 2566 var noreturn_src_node: Ast.Node.Index = 0; 2567 var scope = parent_scope; 2568 for (statements, 0..) |statement, stmt_idx| { 2569 if (noreturn_src_node != 0) { 2570 try astgen.appendErrorNodeNotes( 2571 statement, 2572 "unreachable code", 2573 .{}, 2574 &[_]u32{ 2575 try astgen.errNoteNode( 2576 noreturn_src_node, 2577 "control flow is diverted here", 2578 .{}, 2579 ), 2580 }, 2581 ); 2582 } 2583 const allow_branch_hint = switch (block_kind) { 2584 .normal => false, 2585 .allow_branch_hint => stmt_idx == 0, 2586 }; 2587 var inner_node = statement; 2588 while (true) { 2589 switch (node_tags[inner_node]) { 2590 // zig fmt: off 2591 .global_var_decl, 2592 .local_var_decl, 2593 .simple_var_decl, 2594 .aligned_var_decl, => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.fullVarDecl(statement).?), 2595 2596 .assign_destructure => scope = try assignDestructureMaybeDecls(gz, scope, statement, block_arena_allocator), 2597 2598 .@"defer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal), 2599 .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error), 2600 2601 .assign => try assign(gz, scope, statement), 2602 2603 .assign_shl => try assignShift(gz, scope, statement, .shl), 2604 .assign_shr => try assignShift(gz, scope, statement, .shr), 2605 2606 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and), 2607 .assign_bit_or => try assignOp(gz, scope, statement, .bit_or), 2608 .assign_bit_xor => try assignOp(gz, scope, statement, .xor), 2609 .assign_div => try assignOp(gz, scope, statement, .div), 2610 .assign_sub => try assignOp(gz, scope, statement, .sub), 2611 .assign_sub_wrap => try assignOp(gz, scope, statement, .subwrap), 2612 .assign_mod => try assignOp(gz, scope, statement, .mod_rem), 2613 .assign_add => try assignOp(gz, scope, statement, .add), 2614 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap), 2615 .assign_mul => try assignOp(gz, scope, statement, .mul), 2616 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap), 2617 2618 .grouped_expression => { 2619 inner_node = node_data[statement].lhs; 2620 continue; 2621 }, 2622 2623 .while_simple, 2624 .while_cont, 2625 .@"while", => _ = try whileExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullWhile(inner_node).?, true), 2626 2627 .for_simple, 2628 .@"for", => _ = try forExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullFor(inner_node).?, true), 2629 2630 // These cases are here to allow branch hints. 2631 .builtin_call_two, .builtin_call_two_comma => { 2632 try emitDbgNode(gz, inner_node); 2633 const ri: ResultInfo = .{ .rl = .none }; 2634 const result = if (node_data[inner_node].lhs == 0) r: { 2635 break :r try builtinCall(gz, scope, ri, inner_node, &.{}, allow_branch_hint); 2636 } else if (node_data[inner_node].rhs == 0) r: { 2637 break :r try builtinCall(gz, scope, ri, inner_node, &.{node_data[inner_node].lhs}, allow_branch_hint); 2638 } else r: { 2639 break :r try builtinCall(gz, scope, ri, inner_node, &.{ 2640 node_data[inner_node].lhs, 2641 node_data[inner_node].rhs, 2642 }, allow_branch_hint); 2643 }; 2644 noreturn_src_node = try addEnsureResult(gz, result, inner_node); 2645 }, 2646 .builtin_call, .builtin_call_comma => { 2647 try emitDbgNode(gz, inner_node); 2648 const ri: ResultInfo = .{ .rl = .none }; 2649 const params = tree.extra_data[node_data[inner_node].lhs..node_data[inner_node].rhs]; 2650 const result = try builtinCall(gz, scope, ri, inner_node, params, allow_branch_hint); 2651 noreturn_src_node = try addEnsureResult(gz, result, inner_node); 2652 }, 2653 2654 else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node), 2655 // zig fmt: on 2656 } 2657 break; 2658 } 2659 } 2660 2661 if (noreturn_src_node == 0) { 2662 try genDefers(gz, parent_scope, scope, .normal_only); 2663 } 2664 try checkUsed(gz, parent_scope, scope); 2665 } 2666 2667 /// Returns AST source node of the thing that is noreturn if the statement is 2668 /// definitely `noreturn`. Otherwise returns 0. 2669 fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.Index { 2670 try emitDbgNode(gz, statement); 2671 // We need to emit an error if the result is not `noreturn` or `void`, but 2672 // we want to avoid adding the ZIR instruction if possible for performance. 2673 const maybe_unused_result = try expr(gz, scope, .{ .rl = .none }, statement); 2674 return addEnsureResult(gz, maybe_unused_result, statement); 2675 } 2676 2677 fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: Ast.Node.Index) InnerError!Ast.Node.Index { 2678 var noreturn_src_node: Ast.Node.Index = 0; 2679 const elide_check = if (maybe_unused_result.toIndex()) |inst| b: { 2680 // Note that this array becomes invalid after appending more items to it 2681 // in the above while loop. 2682 const zir_tags = gz.astgen.instructions.items(.tag); 2683 switch (zir_tags[@intFromEnum(inst)]) { 2684 // For some instructions, modify the zir data 2685 // so we can avoid a separate ensure_result_used instruction. 2686 .call, .field_call => { 2687 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index; 2688 comptime assert(std.meta.fieldIndex(Zir.Inst.Call, "flags") == 2689 std.meta.fieldIndex(Zir.Inst.FieldCall, "flags")); 2690 const flags: *Zir.Inst.Call.Flags = @ptrCast(&gz.astgen.extra.items[ 2691 break_extra + std.meta.fieldIndex(Zir.Inst.Call, "flags").? 2692 ]); 2693 flags.ensure_result_used = true; 2694 break :b true; 2695 }, 2696 .builtin_call => { 2697 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index; 2698 const flags: *Zir.Inst.BuiltinCall.Flags = @ptrCast(&gz.astgen.extra.items[ 2699 break_extra + std.meta.fieldIndex(Zir.Inst.BuiltinCall, "flags").? 2700 ]); 2701 flags.ensure_result_used = true; 2702 break :b true; 2703 }, 2704 2705 // ZIR instructions that might be a type other than `noreturn` or `void`. 2706 .add, 2707 .addwrap, 2708 .add_sat, 2709 .add_unsafe, 2710 .param, 2711 .param_comptime, 2712 .param_anytype, 2713 .param_anytype_comptime, 2714 .alloc, 2715 .alloc_mut, 2716 .alloc_comptime_mut, 2717 .alloc_inferred, 2718 .alloc_inferred_mut, 2719 .alloc_inferred_comptime, 2720 .alloc_inferred_comptime_mut, 2721 .make_ptr_const, 2722 .array_cat, 2723 .array_mul, 2724 .array_type, 2725 .array_type_sentinel, 2726 .elem_type, 2727 .indexable_ptr_elem_type, 2728 .vec_arr_elem_type, 2729 .vector_type, 2730 .indexable_ptr_len, 2731 .anyframe_type, 2732 .as_node, 2733 .as_shift_operand, 2734 .bit_and, 2735 .bitcast, 2736 .bit_or, 2737 .block, 2738 .block_comptime, 2739 .block_inline, 2740 .declaration, 2741 .suspend_block, 2742 .loop, 2743 .bool_br_and, 2744 .bool_br_or, 2745 .bool_not, 2746 .cmp_lt, 2747 .cmp_lte, 2748 .cmp_eq, 2749 .cmp_gte, 2750 .cmp_gt, 2751 .cmp_neq, 2752 .decl_ref, 2753 .decl_val, 2754 .load, 2755 .div, 2756 .elem_ptr, 2757 .elem_val, 2758 .elem_ptr_node, 2759 .elem_val_node, 2760 .elem_val_imm, 2761 .field_ptr, 2762 .field_val, 2763 .field_ptr_named, 2764 .field_val_named, 2765 .func, 2766 .func_inferred, 2767 .func_fancy, 2768 .int, 2769 .int_big, 2770 .float, 2771 .float128, 2772 .int_type, 2773 .is_non_null, 2774 .is_non_null_ptr, 2775 .is_non_err, 2776 .is_non_err_ptr, 2777 .ret_is_non_err, 2778 .mod_rem, 2779 .mul, 2780 .mulwrap, 2781 .mul_sat, 2782 .ref, 2783 .shl, 2784 .shl_sat, 2785 .shr, 2786 .str, 2787 .sub, 2788 .subwrap, 2789 .sub_sat, 2790 .negate, 2791 .negate_wrap, 2792 .typeof, 2793 .typeof_builtin, 2794 .xor, 2795 .optional_type, 2796 .optional_payload_safe, 2797 .optional_payload_unsafe, 2798 .optional_payload_safe_ptr, 2799 .optional_payload_unsafe_ptr, 2800 .err_union_payload_unsafe, 2801 .err_union_payload_unsafe_ptr, 2802 .err_union_code, 2803 .err_union_code_ptr, 2804 .ptr_type, 2805 .enum_literal, 2806 .decl_literal, 2807 .decl_literal_no_coerce, 2808 .merge_error_sets, 2809 .error_union_type, 2810 .bit_not, 2811 .error_value, 2812 .slice_start, 2813 .slice_end, 2814 .slice_sentinel, 2815 .slice_length, 2816 .import, 2817 .switch_block, 2818 .switch_block_ref, 2819 .switch_block_err_union, 2820 .union_init, 2821 .field_type_ref, 2822 .error_set_decl, 2823 .enum_from_int, 2824 .int_from_enum, 2825 .type_info, 2826 .size_of, 2827 .bit_size_of, 2828 .typeof_log2_int_type, 2829 .int_from_ptr, 2830 .align_of, 2831 .int_from_bool, 2832 .embed_file, 2833 .error_name, 2834 .sqrt, 2835 .sin, 2836 .cos, 2837 .tan, 2838 .exp, 2839 .exp2, 2840 .log, 2841 .log2, 2842 .log10, 2843 .abs, 2844 .floor, 2845 .ceil, 2846 .trunc, 2847 .round, 2848 .tag_name, 2849 .type_name, 2850 .frame_type, 2851 .frame_size, 2852 .int_from_float, 2853 .float_from_int, 2854 .ptr_from_int, 2855 .float_cast, 2856 .int_cast, 2857 .ptr_cast, 2858 .truncate, 2859 .has_decl, 2860 .has_field, 2861 .clz, 2862 .ctz, 2863 .pop_count, 2864 .byte_swap, 2865 .bit_reverse, 2866 .div_exact, 2867 .div_floor, 2868 .div_trunc, 2869 .mod, 2870 .rem, 2871 .shl_exact, 2872 .shr_exact, 2873 .bit_offset_of, 2874 .offset_of, 2875 .splat, 2876 .reduce, 2877 .shuffle, 2878 .atomic_load, 2879 .atomic_rmw, 2880 .mul_add, 2881 .max, 2882 .min, 2883 .c_import, 2884 .@"resume", 2885 .@"await", 2886 .ret_err_value_code, 2887 .ret_ptr, 2888 .ret_type, 2889 .for_len, 2890 .@"try", 2891 .try_ptr, 2892 .opt_eu_base_ptr_init, 2893 .coerce_ptr_elem_ty, 2894 .struct_init_empty, 2895 .struct_init_empty_result, 2896 .struct_init_empty_ref_result, 2897 .struct_init_anon, 2898 .struct_init, 2899 .struct_init_ref, 2900 .struct_init_field_type, 2901 .struct_init_field_ptr, 2902 .array_init_anon, 2903 .array_init, 2904 .array_init_ref, 2905 .validate_array_init_ref_ty, 2906 .array_init_elem_type, 2907 .array_init_elem_ptr, 2908 => break :b false, 2909 2910 .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) { 2911 .breakpoint, 2912 .disable_instrumentation, 2913 .set_float_mode, 2914 .branch_hint, 2915 => break :b true, 2916 else => break :b false, 2917 }, 2918 2919 // ZIR instructions that are always `noreturn`. 2920 .@"break", 2921 .break_inline, 2922 .condbr, 2923 .condbr_inline, 2924 .compile_error, 2925 .ret_node, 2926 .ret_load, 2927 .ret_implicit, 2928 .ret_err_value, 2929 .@"unreachable", 2930 .repeat, 2931 .repeat_inline, 2932 .panic, 2933 .trap, 2934 .check_comptime_control_flow, 2935 .switch_continue, 2936 => { 2937 noreturn_src_node = statement; 2938 break :b true; 2939 }, 2940 2941 // ZIR instructions that are always `void`. 2942 .dbg_stmt, 2943 .dbg_var_ptr, 2944 .dbg_var_val, 2945 .ensure_result_used, 2946 .ensure_result_non_error, 2947 .ensure_err_union_payload_void, 2948 .@"export", 2949 .set_eval_branch_quota, 2950 .atomic_store, 2951 .store_node, 2952 .store_to_inferred_ptr, 2953 .resolve_inferred_alloc, 2954 .set_runtime_safety, 2955 .memcpy, 2956 .memset, 2957 .validate_deref, 2958 .validate_destructure, 2959 .save_err_ret_index, 2960 .restore_err_ret_index_unconditional, 2961 .restore_err_ret_index_fn_entry, 2962 .validate_struct_init_ty, 2963 .validate_struct_init_result_ty, 2964 .validate_ptr_struct_init, 2965 .validate_array_init_ty, 2966 .validate_array_init_result_ty, 2967 .validate_ptr_array_init, 2968 .validate_ref_ty, 2969 .try_operand_ty, 2970 .try_ref_operand_ty, 2971 => break :b true, 2972 2973 .@"defer" => unreachable, 2974 .defer_err_code => unreachable, 2975 } 2976 } else switch (maybe_unused_result) { 2977 .none => unreachable, 2978 2979 .unreachable_value => b: { 2980 noreturn_src_node = statement; 2981 break :b true; 2982 }, 2983 2984 .void_value => true, 2985 2986 else => false, 2987 }; 2988 if (!elide_check) { 2989 _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement); 2990 } 2991 return noreturn_src_node; 2992 } 2993 2994 fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct { 2995 have_any: bool, 2996 have_normal: bool, 2997 have_err: bool, 2998 need_err_code: bool, 2999 } { 3000 var have_normal = false; 3001 var have_err = false; 3002 var need_err_code = false; 3003 var scope = inner_scope; 3004 while (scope != outer_scope) { 3005 switch (scope.tag) { 3006 .gen_zir => scope = scope.cast(GenZir).?.parent, 3007 .local_val => scope = scope.cast(Scope.LocalVal).?.parent, 3008 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, 3009 .defer_normal => { 3010 const defer_scope = scope.cast(Scope.Defer).?; 3011 scope = defer_scope.parent; 3012 3013 have_normal = true; 3014 }, 3015 .defer_error => { 3016 const defer_scope = scope.cast(Scope.Defer).?; 3017 scope = defer_scope.parent; 3018 3019 have_err = true; 3020 3021 const have_err_payload = defer_scope.remapped_err_code != .none; 3022 need_err_code = need_err_code or have_err_payload; 3023 }, 3024 .namespace => unreachable, 3025 .top => unreachable, 3026 } 3027 } 3028 return .{ 3029 .have_any = have_normal or have_err, 3030 .have_normal = have_normal, 3031 .have_err = have_err, 3032 .need_err_code = need_err_code, 3033 }; 3034 } 3035 3036 const DefersToEmit = union(enum) { 3037 both: Zir.Inst.Ref, // err code 3038 both_sans_err, 3039 normal_only, 3040 }; 3041 3042 fn genDefers( 3043 gz: *GenZir, 3044 outer_scope: *Scope, 3045 inner_scope: *Scope, 3046 which_ones: DefersToEmit, 3047 ) InnerError!void { 3048 const gpa = gz.astgen.gpa; 3049 3050 var scope = inner_scope; 3051 while (scope != outer_scope) { 3052 switch (scope.tag) { 3053 .gen_zir => scope = scope.cast(GenZir).?.parent, 3054 .local_val => scope = scope.cast(Scope.LocalVal).?.parent, 3055 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, 3056 .defer_normal => { 3057 const defer_scope = scope.cast(Scope.Defer).?; 3058 scope = defer_scope.parent; 3059 try gz.addDefer(defer_scope.index, defer_scope.len); 3060 }, 3061 .defer_error => { 3062 const defer_scope = scope.cast(Scope.Defer).?; 3063 scope = defer_scope.parent; 3064 switch (which_ones) { 3065 .both_sans_err => { 3066 try gz.addDefer(defer_scope.index, defer_scope.len); 3067 }, 3068 .both => |err_code| { 3069 if (defer_scope.remapped_err_code.unwrap()) |remapped_err_code| { 3070 try gz.instructions.ensureUnusedCapacity(gpa, 1); 3071 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 3072 3073 const payload_index = try gz.astgen.addExtra(Zir.Inst.DeferErrCode{ 3074 .remapped_err_code = remapped_err_code, 3075 .index = defer_scope.index, 3076 .len = defer_scope.len, 3077 }); 3078 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 3079 gz.astgen.instructions.appendAssumeCapacity(.{ 3080 .tag = .defer_err_code, 3081 .data = .{ .defer_err_code = .{ 3082 .err_code = err_code, 3083 .payload_index = payload_index, 3084 } }, 3085 }); 3086 gz.instructions.appendAssumeCapacity(new_index); 3087 } else { 3088 try gz.addDefer(defer_scope.index, defer_scope.len); 3089 } 3090 }, 3091 .normal_only => continue, 3092 } 3093 }, 3094 .namespace => unreachable, 3095 .top => unreachable, 3096 } 3097 } 3098 } 3099 3100 fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!void { 3101 const astgen = gz.astgen; 3102 3103 var scope = inner_scope; 3104 while (scope != outer_scope) { 3105 switch (scope.tag) { 3106 .gen_zir => scope = scope.cast(GenZir).?.parent, 3107 .local_val => { 3108 const s = scope.cast(Scope.LocalVal).?; 3109 if (s.used == 0 and s.discarded == 0) { 3110 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)}); 3111 } else if (s.used != 0 and s.discarded != 0) { 3112 try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{ 3113 try gz.astgen.errNoteTok(s.used, "used here", .{}), 3114 }); 3115 } 3116 scope = s.parent; 3117 }, 3118 .local_ptr => { 3119 const s = scope.cast(Scope.LocalPtr).?; 3120 if (s.used == 0 and s.discarded == 0) { 3121 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)}); 3122 } else { 3123 if (s.used != 0 and s.discarded != 0) { 3124 try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{ 3125 try astgen.errNoteTok(s.used, "used here", .{}), 3126 }); 3127 } 3128 if (s.id_cat == .@"local variable" and !s.used_as_lvalue) { 3129 try astgen.appendErrorTokNotes(s.token_src, "local variable is never mutated", .{}, &.{ 3130 try astgen.errNoteTok(s.token_src, "consider using 'const'", .{}), 3131 }); 3132 } 3133 } 3134 3135 scope = s.parent; 3136 }, 3137 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, 3138 .namespace => unreachable, 3139 .top => unreachable, 3140 } 3141 } 3142 } 3143 3144 fn deferStmt( 3145 gz: *GenZir, 3146 scope: *Scope, 3147 node: Ast.Node.Index, 3148 block_arena: Allocator, 3149 scope_tag: Scope.Tag, 3150 ) InnerError!*Scope { 3151 var defer_gen = gz.makeSubBlock(scope); 3152 defer_gen.cur_defer_node = node; 3153 defer_gen.any_defer_node = node; 3154 defer defer_gen.unstack(); 3155 3156 const tree = gz.astgen.tree; 3157 const node_datas = tree.nodes.items(.data); 3158 const expr_node = node_datas[node].rhs; 3159 3160 const payload_token = node_datas[node].lhs; 3161 var local_val_scope: Scope.LocalVal = undefined; 3162 var opt_remapped_err_code: Zir.Inst.OptionalIndex = .none; 3163 const have_err_code = scope_tag == .defer_error and payload_token != 0; 3164 const sub_scope = if (!have_err_code) &defer_gen.base else blk: { 3165 const ident_name = try gz.astgen.identAsString(payload_token); 3166 if (std.mem.eql(u8, tree.tokenSlice(payload_token), "_")) { 3167 try gz.astgen.appendErrorTok(payload_token, "discard of error capture; omit it instead", .{}); 3168 break :blk &defer_gen.base; 3169 } 3170 const remapped_err_code: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 3171 opt_remapped_err_code = remapped_err_code.toOptional(); 3172 try gz.astgen.instructions.append(gz.astgen.gpa, .{ 3173 .tag = .extended, 3174 .data = .{ .extended = .{ 3175 .opcode = .value_placeholder, 3176 .small = undefined, 3177 .operand = undefined, 3178 } }, 3179 }); 3180 const remapped_err_code_ref = remapped_err_code.toRef(); 3181 local_val_scope = .{ 3182 .parent = &defer_gen.base, 3183 .gen_zir = gz, 3184 .name = ident_name, 3185 .inst = remapped_err_code_ref, 3186 .token_src = payload_token, 3187 .id_cat = .capture, 3188 }; 3189 try gz.addDbgVar(.dbg_var_val, ident_name, remapped_err_code_ref); 3190 break :blk &local_val_scope.base; 3191 }; 3192 _ = try unusedResultExpr(&defer_gen, sub_scope, expr_node); 3193 try checkUsed(gz, scope, sub_scope); 3194 _ = try defer_gen.addBreak(.break_inline, @enumFromInt(0), .void_value); 3195 3196 const body = defer_gen.instructionsSlice(); 3197 const extra_insts: []const Zir.Inst.Index = if (opt_remapped_err_code.unwrap()) |ec| &.{ec} else &.{}; 3198 const body_len = gz.astgen.countBodyLenAfterFixupsExtraRefs(body, extra_insts); 3199 3200 const index: u32 = @intCast(gz.astgen.extra.items.len); 3201 try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len); 3202 gz.astgen.appendBodyWithFixupsExtraRefsArrayList(&gz.astgen.extra, body, extra_insts); 3203 3204 const defer_scope = try block_arena.create(Scope.Defer); 3205 3206 defer_scope.* = .{ 3207 .base = .{ .tag = scope_tag }, 3208 .parent = scope, 3209 .index = index, 3210 .len = body_len, 3211 .remapped_err_code = opt_remapped_err_code, 3212 }; 3213 return &defer_scope.base; 3214 } 3215 3216 fn varDecl( 3217 gz: *GenZir, 3218 scope: *Scope, 3219 node: Ast.Node.Index, 3220 block_arena: Allocator, 3221 var_decl: Ast.full.VarDecl, 3222 ) InnerError!*Scope { 3223 try emitDbgNode(gz, node); 3224 const astgen = gz.astgen; 3225 const tree = astgen.tree; 3226 const token_tags = tree.tokens.items(.tag); 3227 const main_tokens = tree.nodes.items(.main_token); 3228 3229 const name_token = var_decl.ast.mut_token + 1; 3230 const ident_name_raw = tree.tokenSlice(name_token); 3231 if (mem.eql(u8, ident_name_raw, "_")) { 3232 return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{}); 3233 } 3234 const ident_name = try astgen.identAsString(name_token); 3235 3236 try astgen.detectLocalShadowing( 3237 scope, 3238 ident_name, 3239 name_token, 3240 ident_name_raw, 3241 if (token_tags[var_decl.ast.mut_token] == .keyword_const) .@"local constant" else .@"local variable", 3242 ); 3243 3244 if (var_decl.ast.init_node == 0) { 3245 return astgen.failNode(node, "variables must be initialized", .{}); 3246 } 3247 3248 if (var_decl.ast.addrspace_node != 0) { 3249 return astgen.failTok(main_tokens[var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw}); 3250 } 3251 3252 if (var_decl.ast.section_node != 0) { 3253 return astgen.failTok(main_tokens[var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw}); 3254 } 3255 3256 const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0) 3257 try expr(gz, scope, coerced_align_ri, var_decl.ast.align_node) 3258 else 3259 .none; 3260 3261 switch (token_tags[var_decl.ast.mut_token]) { 3262 .keyword_const => { 3263 if (var_decl.comptime_token) |comptime_token| { 3264 try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{}); 3265 } 3266 3267 // `comptime const` is a non-fatal error; treat it like the init was marked `comptime`. 3268 const force_comptime = var_decl.comptime_token != null; 3269 3270 // Depending on the type of AST the initialization expression is, we may need an lvalue 3271 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as 3272 // the variable, no memory location needed. 3273 const type_node = var_decl.ast.type_node; 3274 if (align_inst == .none and 3275 !astgen.nodes_need_rl.contains(node)) 3276 { 3277 const result_info: ResultInfo = if (type_node != 0) .{ 3278 .rl = .{ .ty = try typeExpr(gz, scope, type_node) }, 3279 .ctx = .const_init, 3280 } else .{ .rl = .none, .ctx = .const_init }; 3281 const prev_anon_name_strategy = gz.anon_name_strategy; 3282 gz.anon_name_strategy = .dbg_var; 3283 const init_inst = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, force_comptime); 3284 gz.anon_name_strategy = prev_anon_name_strategy; 3285 3286 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst); 3287 3288 // The const init expression may have modified the error return trace, so signal 3289 // to Sema that it should save the new index for restoring later. 3290 if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node)) 3291 _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst }); 3292 3293 const sub_scope = try block_arena.create(Scope.LocalVal); 3294 sub_scope.* = .{ 3295 .parent = scope, 3296 .gen_zir = gz, 3297 .name = ident_name, 3298 .inst = init_inst, 3299 .token_src = name_token, 3300 .id_cat = .@"local constant", 3301 }; 3302 return &sub_scope.base; 3303 } 3304 3305 const is_comptime = gz.is_comptime or 3306 tree.nodes.items(.tag)[var_decl.ast.init_node] == .@"comptime"; 3307 3308 const init_rl: ResultInfo.Loc = if (type_node != 0) init_rl: { 3309 const type_inst = try typeExpr(gz, scope, type_node); 3310 if (align_inst == .none) { 3311 break :init_rl .{ .ptr = .{ .inst = try gz.addUnNode(.alloc, type_inst, node) } }; 3312 } else { 3313 break :init_rl .{ .ptr = .{ .inst = try gz.addAllocExtended(.{ 3314 .node = node, 3315 .type_inst = type_inst, 3316 .align_inst = align_inst, 3317 .is_const = true, 3318 .is_comptime = is_comptime, 3319 }) } }; 3320 } 3321 } else init_rl: { 3322 const alloc_inst = if (align_inst == .none) ptr: { 3323 const tag: Zir.Inst.Tag = if (is_comptime) 3324 .alloc_inferred_comptime 3325 else 3326 .alloc_inferred; 3327 break :ptr try gz.addNode(tag, node); 3328 } else ptr: { 3329 break :ptr try gz.addAllocExtended(.{ 3330 .node = node, 3331 .type_inst = .none, 3332 .align_inst = align_inst, 3333 .is_const = true, 3334 .is_comptime = is_comptime, 3335 }); 3336 }; 3337 break :init_rl .{ .inferred_ptr = alloc_inst }; 3338 }; 3339 const var_ptr: Zir.Inst.Ref, const resolve_inferred: bool = switch (init_rl) { 3340 .ptr => |ptr| .{ ptr.inst, false }, 3341 .inferred_ptr => |inst| .{ inst, true }, 3342 else => unreachable, 3343 }; 3344 const init_result_info: ResultInfo = .{ .rl = init_rl, .ctx = .const_init }; 3345 3346 const prev_anon_name_strategy = gz.anon_name_strategy; 3347 gz.anon_name_strategy = .dbg_var; 3348 defer gz.anon_name_strategy = prev_anon_name_strategy; 3349 const init_inst = try reachableExprComptime(gz, scope, init_result_info, var_decl.ast.init_node, node, force_comptime); 3350 3351 // The const init expression may have modified the error return trace, so signal 3352 // to Sema that it should save the new index for restoring later. 3353 if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node)) 3354 _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst }); 3355 3356 const const_ptr = if (resolve_inferred) 3357 try gz.addUnNode(.resolve_inferred_alloc, var_ptr, node) 3358 else 3359 try gz.addUnNode(.make_ptr_const, var_ptr, node); 3360 3361 try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr); 3362 3363 const sub_scope = try block_arena.create(Scope.LocalPtr); 3364 sub_scope.* = .{ 3365 .parent = scope, 3366 .gen_zir = gz, 3367 .name = ident_name, 3368 .ptr = const_ptr, 3369 .token_src = name_token, 3370 .maybe_comptime = true, 3371 .id_cat = .@"local constant", 3372 }; 3373 return &sub_scope.base; 3374 }, 3375 .keyword_var => { 3376 if (var_decl.comptime_token != null and gz.is_comptime) 3377 return astgen.failTok(var_decl.comptime_token.?, "'comptime var' is redundant in comptime scope", .{}); 3378 const is_comptime = var_decl.comptime_token != null or gz.is_comptime; 3379 const alloc: Zir.Inst.Ref, const resolve_inferred: bool, const result_info: ResultInfo = if (var_decl.ast.type_node != 0) a: { 3380 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node); 3381 const alloc = alloc: { 3382 if (align_inst == .none) { 3383 const tag: Zir.Inst.Tag = if (is_comptime) 3384 .alloc_comptime_mut 3385 else 3386 .alloc_mut; 3387 break :alloc try gz.addUnNode(tag, type_inst, node); 3388 } else { 3389 break :alloc try gz.addAllocExtended(.{ 3390 .node = node, 3391 .type_inst = type_inst, 3392 .align_inst = align_inst, 3393 .is_const = false, 3394 .is_comptime = is_comptime, 3395 }); 3396 } 3397 }; 3398 break :a .{ alloc, false, .{ .rl = .{ .ptr = .{ .inst = alloc } } } }; 3399 } else a: { 3400 const alloc = alloc: { 3401 if (align_inst == .none) { 3402 const tag: Zir.Inst.Tag = if (is_comptime) 3403 .alloc_inferred_comptime_mut 3404 else 3405 .alloc_inferred_mut; 3406 break :alloc try gz.addNode(tag, node); 3407 } else { 3408 break :alloc try gz.addAllocExtended(.{ 3409 .node = node, 3410 .type_inst = .none, 3411 .align_inst = align_inst, 3412 .is_const = false, 3413 .is_comptime = is_comptime, 3414 }); 3415 } 3416 }; 3417 break :a .{ alloc, true, .{ .rl = .{ .inferred_ptr = alloc } } }; 3418 }; 3419 const prev_anon_name_strategy = gz.anon_name_strategy; 3420 gz.anon_name_strategy = .dbg_var; 3421 _ = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, is_comptime); 3422 gz.anon_name_strategy = prev_anon_name_strategy; 3423 const final_ptr: Zir.Inst.Ref = if (resolve_inferred) ptr: { 3424 break :ptr try gz.addUnNode(.resolve_inferred_alloc, alloc, node); 3425 } else alloc; 3426 3427 try gz.addDbgVar(.dbg_var_ptr, ident_name, final_ptr); 3428 3429 const sub_scope = try block_arena.create(Scope.LocalPtr); 3430 sub_scope.* = .{ 3431 .parent = scope, 3432 .gen_zir = gz, 3433 .name = ident_name, 3434 .ptr = final_ptr, 3435 .token_src = name_token, 3436 .maybe_comptime = is_comptime, 3437 .id_cat = .@"local variable", 3438 }; 3439 return &sub_scope.base; 3440 }, 3441 else => unreachable, 3442 } 3443 } 3444 3445 fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void { 3446 // The instruction emitted here is for debugging runtime code. 3447 // If the current block will be evaluated only during semantic analysis 3448 // then no dbg_stmt ZIR instruction is needed. 3449 if (gz.is_comptime) return; 3450 const astgen = gz.astgen; 3451 astgen.advanceSourceCursorToNode(node); 3452 const line = astgen.source_line - gz.decl_line; 3453 const column = astgen.source_column; 3454 try emitDbgStmt(gz, .{ line, column }); 3455 } 3456 3457 fn assign(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void { 3458 try emitDbgNode(gz, infix_node); 3459 const astgen = gz.astgen; 3460 const tree = astgen.tree; 3461 const node_datas = tree.nodes.items(.data); 3462 const main_tokens = tree.nodes.items(.main_token); 3463 const node_tags = tree.nodes.items(.tag); 3464 3465 const lhs = node_datas[infix_node].lhs; 3466 const rhs = node_datas[infix_node].rhs; 3467 if (node_tags[lhs] == .identifier) { 3468 // This intentionally does not support `@"_"` syntax. 3469 const ident_name = tree.tokenSlice(main_tokens[lhs]); 3470 if (mem.eql(u8, ident_name, "_")) { 3471 _ = try expr(gz, scope, .{ .rl = .discard, .ctx = .assignment }, rhs); 3472 return; 3473 } 3474 } 3475 const lvalue = try lvalExpr(gz, scope, lhs); 3476 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ 3477 .inst = lvalue, 3478 .src_node = infix_node, 3479 } } }, rhs); 3480 } 3481 3482 /// Handles destructure assignments where no LHS is a `const` or `var` decl. 3483 fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!void { 3484 try emitDbgNode(gz, node); 3485 const astgen = gz.astgen; 3486 const tree = astgen.tree; 3487 const main_tokens = tree.nodes.items(.main_token); 3488 const node_tags = tree.nodes.items(.tag); 3489 3490 const full = tree.assignDestructure(node); 3491 if (full.comptime_token != null and gz.is_comptime) { 3492 return astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{}); 3493 } 3494 3495 // If this expression is marked comptime, we must wrap the whole thing in a comptime block. 3496 var gz_buf: GenZir = undefined; 3497 const inner_gz = if (full.comptime_token) |_| bs: { 3498 gz_buf = gz.makeSubBlock(scope); 3499 gz_buf.is_comptime = true; 3500 break :bs &gz_buf; 3501 } else gz; 3502 defer if (full.comptime_token) |_| inner_gz.unstack(); 3503 3504 const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len); 3505 for (rl_components, full.ast.variables) |*variable_rl, variable_node| { 3506 if (node_tags[variable_node] == .identifier) { 3507 // This intentionally does not support `@"_"` syntax. 3508 const ident_name = tree.tokenSlice(main_tokens[variable_node]); 3509 if (mem.eql(u8, ident_name, "_")) { 3510 variable_rl.* = .discard; 3511 continue; 3512 } 3513 } 3514 variable_rl.* = .{ .typed_ptr = .{ 3515 .inst = try lvalExpr(inner_gz, scope, variable_node), 3516 .src_node = variable_node, 3517 } }; 3518 } 3519 3520 const ri: ResultInfo = .{ .rl = .{ .destructure = .{ 3521 .src_node = node, 3522 .components = rl_components, 3523 } } }; 3524 3525 _ = try expr(inner_gz, scope, ri, full.ast.value_expr); 3526 3527 if (full.comptime_token) |_| { 3528 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node); 3529 _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value); 3530 try inner_gz.setBlockBody(comptime_block_inst); 3531 try gz.instructions.append(gz.astgen.gpa, comptime_block_inst); 3532 } 3533 } 3534 3535 /// Handles destructure assignments where the LHS may contain `const` or `var` decls. 3536 fn assignDestructureMaybeDecls( 3537 gz: *GenZir, 3538 scope: *Scope, 3539 node: Ast.Node.Index, 3540 block_arena: Allocator, 3541 ) InnerError!*Scope { 3542 try emitDbgNode(gz, node); 3543 const astgen = gz.astgen; 3544 const tree = astgen.tree; 3545 const token_tags = tree.tokens.items(.tag); 3546 const main_tokens = tree.nodes.items(.main_token); 3547 const node_tags = tree.nodes.items(.tag); 3548 3549 const full = tree.assignDestructure(node); 3550 if (full.comptime_token != null and gz.is_comptime) { 3551 try astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{}); 3552 } 3553 3554 const is_comptime = full.comptime_token != null or gz.is_comptime; 3555 const value_is_comptime = node_tags[full.ast.value_expr] == .@"comptime"; 3556 3557 // When declaring consts via a destructure, we always use a result pointer. 3558 // This avoids the need to create tuple types, and is also likely easier to 3559 // optimize, since it's a bit tricky for the optimizer to "split up" the 3560 // value into individual pointer writes down the line. 3561 3562 // We know this rl information won't live past the evaluation of this 3563 // expression, so it may as well go in the block arena. 3564 const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len); 3565 var any_non_const_variables = false; 3566 var any_lvalue_expr = false; 3567 for (rl_components, full.ast.variables) |*variable_rl, variable_node| { 3568 switch (node_tags[variable_node]) { 3569 .identifier => { 3570 // This intentionally does not support `@"_"` syntax. 3571 const ident_name = tree.tokenSlice(main_tokens[variable_node]); 3572 if (mem.eql(u8, ident_name, "_")) { 3573 any_non_const_variables = true; 3574 variable_rl.* = .discard; 3575 continue; 3576 } 3577 }, 3578 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => { 3579 const full_var_decl = tree.fullVarDecl(variable_node).?; 3580 3581 const name_token = full_var_decl.ast.mut_token + 1; 3582 const ident_name_raw = tree.tokenSlice(name_token); 3583 if (mem.eql(u8, ident_name_raw, "_")) { 3584 return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{}); 3585 } 3586 3587 // We detect shadowing in the second pass over these, while we're creating scopes. 3588 3589 if (full_var_decl.ast.addrspace_node != 0) { 3590 return astgen.failTok(main_tokens[full_var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw}); 3591 } 3592 if (full_var_decl.ast.section_node != 0) { 3593 return astgen.failTok(main_tokens[full_var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw}); 3594 } 3595 3596 const is_const = switch (token_tags[full_var_decl.ast.mut_token]) { 3597 .keyword_var => false, 3598 .keyword_const => true, 3599 else => unreachable, 3600 }; 3601 if (!is_const) any_non_const_variables = true; 3602 3603 // We also mark `const`s as comptime if the RHS is definitely comptime-known. 3604 const this_variable_comptime = is_comptime or (is_const and value_is_comptime); 3605 3606 const align_inst: Zir.Inst.Ref = if (full_var_decl.ast.align_node != 0) 3607 try expr(gz, scope, coerced_align_ri, full_var_decl.ast.align_node) 3608 else 3609 .none; 3610 3611 if (full_var_decl.ast.type_node != 0) { 3612 // Typed alloc 3613 const type_inst = try typeExpr(gz, scope, full_var_decl.ast.type_node); 3614 const ptr = if (align_inst == .none) ptr: { 3615 const tag: Zir.Inst.Tag = if (is_const) 3616 .alloc 3617 else if (this_variable_comptime) 3618 .alloc_comptime_mut 3619 else 3620 .alloc_mut; 3621 break :ptr try gz.addUnNode(tag, type_inst, node); 3622 } else try gz.addAllocExtended(.{ 3623 .node = node, 3624 .type_inst = type_inst, 3625 .align_inst = align_inst, 3626 .is_const = is_const, 3627 .is_comptime = this_variable_comptime, 3628 }); 3629 variable_rl.* = .{ .typed_ptr = .{ .inst = ptr } }; 3630 } else { 3631 // Inferred alloc 3632 const ptr = if (align_inst == .none) ptr: { 3633 const tag: Zir.Inst.Tag = if (is_const) tag: { 3634 break :tag if (this_variable_comptime) .alloc_inferred_comptime else .alloc_inferred; 3635 } else tag: { 3636 break :tag if (this_variable_comptime) .alloc_inferred_comptime_mut else .alloc_inferred_mut; 3637 }; 3638 break :ptr try gz.addNode(tag, node); 3639 } else try gz.addAllocExtended(.{ 3640 .node = node, 3641 .type_inst = .none, 3642 .align_inst = align_inst, 3643 .is_const = is_const, 3644 .is_comptime = this_variable_comptime, 3645 }); 3646 variable_rl.* = .{ .inferred_ptr = ptr }; 3647 } 3648 3649 continue; 3650 }, 3651 else => {}, 3652 } 3653 // This variable is just an lvalue expression. 3654 // We will fill in its result pointer later, inside a comptime block. 3655 any_non_const_variables = true; 3656 any_lvalue_expr = true; 3657 variable_rl.* = .{ .typed_ptr = .{ 3658 .inst = undefined, 3659 .src_node = variable_node, 3660 } }; 3661 } 3662 3663 if (full.comptime_token != null and !any_non_const_variables) { 3664 try astgen.appendErrorTok(full.comptime_token.?, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{}); 3665 // Note that this is non-fatal; we will still evaluate at comptime. 3666 } 3667 3668 // If this expression is marked comptime, we must wrap it in a comptime block. 3669 var gz_buf: GenZir = undefined; 3670 const inner_gz = if (full.comptime_token) |_| bs: { 3671 gz_buf = gz.makeSubBlock(scope); 3672 gz_buf.is_comptime = true; 3673 break :bs &gz_buf; 3674 } else gz; 3675 defer if (full.comptime_token) |_| inner_gz.unstack(); 3676 3677 if (any_lvalue_expr) { 3678 // At least one variable was an lvalue expr. Iterate again in order to 3679 // evaluate the lvalues from within the possible block_comptime. 3680 for (rl_components, full.ast.variables) |*variable_rl, variable_node| { 3681 if (variable_rl.* != .typed_ptr) continue; 3682 switch (node_tags[variable_node]) { 3683 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue, 3684 else => {}, 3685 } 3686 variable_rl.typed_ptr.inst = try lvalExpr(inner_gz, scope, variable_node); 3687 } 3688 } 3689 3690 // We can't give a reasonable anon name strategy for destructured inits, so 3691 // leave it at its default of `.anon`. 3692 _ = try reachableExpr(inner_gz, scope, .{ .rl = .{ .destructure = .{ 3693 .src_node = node, 3694 .components = rl_components, 3695 } } }, full.ast.value_expr, node); 3696 3697 if (full.comptime_token) |_| { 3698 // Finish the block_comptime. Inferred alloc resolution etc will occur 3699 // in the parent block. 3700 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node); 3701 _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value); 3702 try inner_gz.setBlockBody(comptime_block_inst); 3703 try gz.instructions.append(gz.astgen.gpa, comptime_block_inst); 3704 } 3705 3706 // Now, iterate over the variable exprs to construct any new scopes. 3707 // If there were any inferred allocations, resolve them. 3708 // If there were any `const` decls, make the pointer constant. 3709 var cur_scope = scope; 3710 for (rl_components, full.ast.variables) |variable_rl, variable_node| { 3711 switch (node_tags[variable_node]) { 3712 .local_var_decl, .simple_var_decl, .aligned_var_decl => {}, 3713 else => continue, // We were mutating an existing lvalue - nothing to do 3714 } 3715 const full_var_decl = tree.fullVarDecl(variable_node).?; 3716 const raw_ptr, const resolve_inferred = switch (variable_rl) { 3717 .discard => unreachable, 3718 .typed_ptr => |typed_ptr| .{ typed_ptr.inst, false }, 3719 .inferred_ptr => |ptr_inst| .{ ptr_inst, true }, 3720 }; 3721 const is_const = switch (token_tags[full_var_decl.ast.mut_token]) { 3722 .keyword_var => false, 3723 .keyword_const => true, 3724 else => unreachable, 3725 }; 3726 3727 // If the alloc was inferred, resolve it. If the alloc was const, make it const. 3728 const final_ptr = if (resolve_inferred) 3729 try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, variable_node) 3730 else if (is_const) 3731 try gz.addUnNode(.make_ptr_const, raw_ptr, node) 3732 else 3733 raw_ptr; 3734 3735 const name_token = full_var_decl.ast.mut_token + 1; 3736 const ident_name_raw = tree.tokenSlice(name_token); 3737 const ident_name = try astgen.identAsString(name_token); 3738 try astgen.detectLocalShadowing( 3739 cur_scope, 3740 ident_name, 3741 name_token, 3742 ident_name_raw, 3743 if (is_const) .@"local constant" else .@"local variable", 3744 ); 3745 try gz.addDbgVar(.dbg_var_ptr, ident_name, final_ptr); 3746 // Finally, create the scope. 3747 const sub_scope = try block_arena.create(Scope.LocalPtr); 3748 sub_scope.* = .{ 3749 .parent = cur_scope, 3750 .gen_zir = gz, 3751 .name = ident_name, 3752 .ptr = final_ptr, 3753 .token_src = name_token, 3754 .maybe_comptime = is_const or is_comptime, 3755 .id_cat = if (is_const) .@"local constant" else .@"local variable", 3756 }; 3757 cur_scope = &sub_scope.base; 3758 } 3759 3760 return cur_scope; 3761 } 3762 3763 fn assignOp( 3764 gz: *GenZir, 3765 scope: *Scope, 3766 infix_node: Ast.Node.Index, 3767 op_inst_tag: Zir.Inst.Tag, 3768 ) InnerError!void { 3769 try emitDbgNode(gz, infix_node); 3770 const astgen = gz.astgen; 3771 const tree = astgen.tree; 3772 const node_datas = tree.nodes.items(.data); 3773 3774 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs); 3775 3776 const cursor = switch (op_inst_tag) { 3777 .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, infix_node), 3778 else => undefined, 3779 }; 3780 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); 3781 3782 const rhs_res_ty = switch (op_inst_tag) { 3783 .add, 3784 .sub, 3785 => try gz.add(.{ 3786 .tag = .extended, 3787 .data = .{ .extended = .{ 3788 .opcode = .inplace_arith_result_ty, 3789 .small = @intFromEnum(@as(Zir.Inst.InplaceOp, switch (op_inst_tag) { 3790 .add => .add_eq, 3791 .sub => .sub_eq, 3792 else => unreachable, 3793 })), 3794 .operand = @intFromEnum(lhs), 3795 } }, 3796 }), 3797 else => try gz.addUnNode(.typeof, lhs, infix_node), // same as LHS type 3798 }; 3799 // Not `coerced_ty` since `add`/etc won't coerce to this type. 3800 const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_res_ty } }, node_datas[infix_node].rhs); 3801 3802 switch (op_inst_tag) { 3803 .add, .sub, .mul, .div, .mod_rem => { 3804 try emitDbgStmt(gz, cursor); 3805 }, 3806 else => {}, 3807 } 3808 const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{ 3809 .lhs = lhs, 3810 .rhs = rhs, 3811 }); 3812 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{ 3813 .lhs = lhs_ptr, 3814 .rhs = result, 3815 }); 3816 } 3817 3818 fn assignShift( 3819 gz: *GenZir, 3820 scope: *Scope, 3821 infix_node: Ast.Node.Index, 3822 op_inst_tag: Zir.Inst.Tag, 3823 ) InnerError!void { 3824 try emitDbgNode(gz, infix_node); 3825 const astgen = gz.astgen; 3826 const tree = astgen.tree; 3827 const node_datas = tree.nodes.items(.data); 3828 3829 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs); 3830 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); 3831 const rhs_type = try gz.addUnNode(.typeof_log2_int_type, lhs, infix_node); 3832 const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_type } }, node_datas[infix_node].rhs); 3833 3834 const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{ 3835 .lhs = lhs, 3836 .rhs = rhs, 3837 }); 3838 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{ 3839 .lhs = lhs_ptr, 3840 .rhs = result, 3841 }); 3842 } 3843 3844 fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void { 3845 try emitDbgNode(gz, infix_node); 3846 const astgen = gz.astgen; 3847 const tree = astgen.tree; 3848 const node_datas = tree.nodes.items(.data); 3849 3850 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs); 3851 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); 3852 // Saturating shift-left allows any integer type for both the LHS and RHS. 3853 const rhs = try expr(gz, scope, .{ .rl = .none }, node_datas[infix_node].rhs); 3854 3855 const result = try gz.addPlNode(.shl_sat, infix_node, Zir.Inst.Bin{ 3856 .lhs = lhs, 3857 .rhs = rhs, 3858 }); 3859 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{ 3860 .lhs = lhs_ptr, 3861 .rhs = result, 3862 }); 3863 } 3864 3865 fn ptrType( 3866 gz: *GenZir, 3867 scope: *Scope, 3868 ri: ResultInfo, 3869 node: Ast.Node.Index, 3870 ptr_info: Ast.full.PtrType, 3871 ) InnerError!Zir.Inst.Ref { 3872 if (ptr_info.size == .C and ptr_info.allowzero_token != null) { 3873 return gz.astgen.failTok(ptr_info.allowzero_token.?, "C pointers always allow address zero", .{}); 3874 } 3875 3876 const source_offset = gz.astgen.source_offset; 3877 const source_line = gz.astgen.source_line; 3878 const source_column = gz.astgen.source_column; 3879 const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type); 3880 3881 var sentinel_ref: Zir.Inst.Ref = .none; 3882 var align_ref: Zir.Inst.Ref = .none; 3883 var addrspace_ref: Zir.Inst.Ref = .none; 3884 var bit_start_ref: Zir.Inst.Ref = .none; 3885 var bit_end_ref: Zir.Inst.Ref = .none; 3886 var trailing_count: u32 = 0; 3887 3888 if (ptr_info.ast.sentinel != 0) { 3889 // These attributes can appear in any order and they all come before the 3890 // element type so we need to reset the source cursor before generating them. 3891 gz.astgen.source_offset = source_offset; 3892 gz.astgen.source_line = source_line; 3893 gz.astgen.source_column = source_column; 3894 3895 sentinel_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, ptr_info.ast.sentinel); 3896 trailing_count += 1; 3897 } 3898 if (ptr_info.ast.addrspace_node != 0) { 3899 gz.astgen.source_offset = source_offset; 3900 gz.astgen.source_line = source_line; 3901 gz.astgen.source_column = source_column; 3902 3903 const addrspace_ty = try gz.addBuiltinValue(ptr_info.ast.addrspace_node, .address_space); 3904 addrspace_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = addrspace_ty } }, ptr_info.ast.addrspace_node); 3905 trailing_count += 1; 3906 } 3907 if (ptr_info.ast.align_node != 0) { 3908 gz.astgen.source_offset = source_offset; 3909 gz.astgen.source_line = source_line; 3910 gz.astgen.source_column = source_column; 3911 3912 align_ref = try expr(gz, scope, coerced_align_ri, ptr_info.ast.align_node); 3913 trailing_count += 1; 3914 } 3915 if (ptr_info.ast.bit_range_start != 0) { 3916 assert(ptr_info.ast.bit_range_end != 0); 3917 bit_start_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, ptr_info.ast.bit_range_start); 3918 bit_end_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, ptr_info.ast.bit_range_end); 3919 trailing_count += 2; 3920 } 3921 3922 const gpa = gz.astgen.gpa; 3923 try gz.instructions.ensureUnusedCapacity(gpa, 1); 3924 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 3925 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.PtrType).@"struct".fields.len + 3926 trailing_count); 3927 3928 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{ 3929 .elem_type = elem_type, 3930 .src_node = gz.nodeIndexToRelative(node), 3931 }); 3932 if (sentinel_ref != .none) { 3933 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(sentinel_ref)); 3934 } 3935 if (align_ref != .none) { 3936 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(align_ref)); 3937 } 3938 if (addrspace_ref != .none) { 3939 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(addrspace_ref)); 3940 } 3941 if (bit_start_ref != .none) { 3942 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_start_ref)); 3943 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_end_ref)); 3944 } 3945 3946 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 3947 const result = new_index.toRef(); 3948 gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{ 3949 .ptr_type = .{ 3950 .flags = .{ 3951 .is_allowzero = ptr_info.allowzero_token != null, 3952 .is_mutable = ptr_info.const_token == null, 3953 .is_volatile = ptr_info.volatile_token != null, 3954 .has_sentinel = sentinel_ref != .none, 3955 .has_align = align_ref != .none, 3956 .has_addrspace = addrspace_ref != .none, 3957 .has_bit_range = bit_start_ref != .none, 3958 }, 3959 .size = ptr_info.size, 3960 .payload_index = payload_index, 3961 }, 3962 } }); 3963 gz.instructions.appendAssumeCapacity(new_index); 3964 3965 return rvalue(gz, ri, result, node); 3966 } 3967 3968 fn arrayType(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref { 3969 const astgen = gz.astgen; 3970 const tree = astgen.tree; 3971 const node_datas = tree.nodes.items(.data); 3972 const node_tags = tree.nodes.items(.tag); 3973 const main_tokens = tree.nodes.items(.main_token); 3974 3975 const len_node = node_datas[node].lhs; 3976 if (node_tags[len_node] == .identifier and 3977 mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_")) 3978 { 3979 return astgen.failNode(len_node, "unable to infer array size", .{}); 3980 } 3981 const len = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node); 3982 const elem_type = try typeExpr(gz, scope, node_datas[node].rhs); 3983 3984 const result = try gz.addPlNode(.array_type, node, Zir.Inst.Bin{ 3985 .lhs = len, 3986 .rhs = elem_type, 3987 }); 3988 return rvalue(gz, ri, result, node); 3989 } 3990 3991 fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref { 3992 const astgen = gz.astgen; 3993 const tree = astgen.tree; 3994 const node_datas = tree.nodes.items(.data); 3995 const node_tags = tree.nodes.items(.tag); 3996 const main_tokens = tree.nodes.items(.main_token); 3997 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.ArrayTypeSentinel); 3998 3999 const len_node = node_datas[node].lhs; 4000 if (node_tags[len_node] == .identifier and 4001 mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_")) 4002 { 4003 return astgen.failNode(len_node, "unable to infer array size", .{}); 4004 } 4005 const len = try reachableExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node, node); 4006 const elem_type = try typeExpr(gz, scope, extra.elem_type); 4007 const sentinel = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = elem_type } }, extra.sentinel, node, true); 4008 4009 const result = try gz.addPlNode(.array_type_sentinel, node, Zir.Inst.ArrayTypeSentinel{ 4010 .len = len, 4011 .elem_type = elem_type, 4012 .sentinel = sentinel, 4013 }); 4014 return rvalue(gz, ri, result, node); 4015 } 4016 4017 const WipMembers = struct { 4018 payload: *ArrayListUnmanaged(u32), 4019 payload_top: usize, 4020 field_bits_start: u32, 4021 fields_start: u32, 4022 fields_end: u32, 4023 decl_index: u32 = 0, 4024 field_index: u32 = 0, 4025 4026 const Self = @This(); 4027 4028 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 { 4029 const payload_top: u32 = @intCast(payload.items.len); 4030 const field_bits_start = payload_top + decl_count; 4031 const fields_start = field_bits_start + if (bits_per_field > 0) blk: { 4032 const fields_per_u32 = 32 / bits_per_field; 4033 break :blk (field_count + fields_per_u32 - 1) / fields_per_u32; 4034 } else 0; 4035 const payload_end = fields_start + field_count * max_field_size; 4036 try payload.resize(gpa, payload_end); 4037 return .{ 4038 .payload = payload, 4039 .payload_top = payload_top, 4040 .field_bits_start = field_bits_start, 4041 .fields_start = fields_start, 4042 .fields_end = fields_start, 4043 }; 4044 } 4045 4046 fn nextDecl(self: *Self, decl_inst: Zir.Inst.Index) void { 4047 self.payload.items[self.payload_top + self.decl_index] = @intFromEnum(decl_inst); 4048 self.decl_index += 1; 4049 } 4050 4051 fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void { 4052 const fields_per_u32 = 32 / bits_per_field; 4053 const index = self.field_bits_start + self.field_index / fields_per_u32; 4054 assert(index < self.fields_start); 4055 var bit_bag: u32 = if (self.field_index % fields_per_u32 == 0) 0 else self.payload.items[index]; 4056 bit_bag >>= bits_per_field; 4057 comptime var i = 0; 4058 inline while (i < bits_per_field) : (i += 1) { 4059 bit_bag |= @as(u32, @intFromBool(bits[i])) << (32 - bits_per_field + i); 4060 } 4061 self.payload.items[index] = bit_bag; 4062 self.field_index += 1; 4063 } 4064 4065 fn appendToField(self: *Self, data: u32) void { 4066 assert(self.fields_end < self.payload.items.len); 4067 self.payload.items[self.fields_end] = data; 4068 self.fields_end += 1; 4069 } 4070 4071 fn finishBits(self: *Self, comptime bits_per_field: u32) void { 4072 if (bits_per_field > 0) { 4073 const fields_per_u32 = 32 / bits_per_field; 4074 const empty_field_slots = fields_per_u32 - (self.field_index % fields_per_u32); 4075 if (self.field_index > 0 and empty_field_slots < fields_per_u32) { 4076 const index = self.field_bits_start + self.field_index / fields_per_u32; 4077 self.payload.items[index] >>= @intCast(empty_field_slots * bits_per_field); 4078 } 4079 } 4080 } 4081 4082 fn declsSlice(self: *Self) []u32 { 4083 return self.payload.items[self.payload_top..][0..self.decl_index]; 4084 } 4085 4086 fn fieldsSlice(self: *Self) []u32 { 4087 return self.payload.items[self.field_bits_start..self.fields_end]; 4088 } 4089 4090 fn deinit(self: *Self) void { 4091 self.payload.items.len = self.payload_top; 4092 } 4093 }; 4094 4095 fn fnDecl( 4096 astgen: *AstGen, 4097 gz: *GenZir, 4098 scope: *Scope, 4099 wip_members: *WipMembers, 4100 decl_node: Ast.Node.Index, 4101 body_node: Ast.Node.Index, 4102 fn_proto: Ast.full.FnProto, 4103 ) InnerError!void { 4104 const tree = astgen.tree; 4105 const token_tags = tree.tokens.items(.tag); 4106 4107 const old_hasher = astgen.src_hasher; 4108 defer astgen.src_hasher = old_hasher; 4109 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 4110 // We don't add the full source yet, because we also need the prototype hash! 4111 // The source slice is added towards the *end* of this function. 4112 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); 4113 4114 // missing function name already checked in scanContainer() 4115 const fn_name_token = fn_proto.name_token.?; 4116 4117 // We insert this at the beginning so that its instruction index marks the 4118 // start of the top level declaration. 4119 const decl_inst = try gz.makeDeclaration(fn_proto.ast.proto_node); 4120 astgen.advanceSourceCursorToNode(decl_node); 4121 4122 const saved_cursor = astgen.saveSourceCursor(); 4123 4124 var decl_gz: GenZir = .{ 4125 .is_comptime = true, 4126 .decl_node_index = fn_proto.ast.proto_node, 4127 .decl_line = astgen.source_line, 4128 .parent = scope, 4129 .astgen = astgen, 4130 .instructions = gz.instructions, 4131 .instructions_top = gz.instructions.items.len, 4132 }; 4133 defer decl_gz.unstack(); 4134 4135 const decl_column = astgen.source_column; 4136 4137 // Set this now, since parameter types, return type, etc may be generic. 4138 const prev_within_fn = astgen.within_fn; 4139 defer astgen.within_fn = prev_within_fn; 4140 astgen.within_fn = true; 4141 4142 const is_pub = fn_proto.visib_token != null; 4143 const is_export = blk: { 4144 const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false; 4145 break :blk token_tags[maybe_export_token] == .keyword_export; 4146 }; 4147 const is_extern = blk: { 4148 const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false; 4149 break :blk token_tags[maybe_extern_token] == .keyword_extern; 4150 }; 4151 const has_inline_keyword = blk: { 4152 const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false; 4153 break :blk token_tags[maybe_inline_token] == .keyword_inline; 4154 }; 4155 const is_noinline = blk: { 4156 const maybe_noinline_token = fn_proto.extern_export_inline_token orelse break :blk false; 4157 break :blk token_tags[maybe_noinline_token] == .keyword_noinline; 4158 }; 4159 4160 wip_members.nextDecl(decl_inst); 4161 4162 // Note that the capacity here may not be sufficient, as this does not include `anytype` parameters. 4163 var param_insts: std.ArrayListUnmanaged(Zir.Inst.Index) = try .initCapacity(astgen.arena, fn_proto.ast.params.len); 4164 4165 var noalias_bits: u32 = 0; 4166 var params_scope = scope; 4167 const is_var_args = is_var_args: { 4168 var param_type_i: usize = 0; 4169 var it = fn_proto.iterate(tree); 4170 while (it.next()) |param| : (param_type_i += 1) { 4171 const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) { 4172 .keyword_noalias => is_comptime: { 4173 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse 4174 return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{})); 4175 break :is_comptime false; 4176 }, 4177 .keyword_comptime => true, 4178 else => false, 4179 } else false; 4180 4181 const is_anytype = if (param.anytype_ellipsis3) |token| blk: { 4182 switch (token_tags[token]) { 4183 .keyword_anytype => break :blk true, 4184 .ellipsis3 => break :is_var_args true, 4185 else => unreachable, 4186 } 4187 } else false; 4188 4189 const param_name: Zir.NullTerminatedString = if (param.name_token) |name_token| blk: { 4190 const name_bytes = tree.tokenSlice(name_token); 4191 if (mem.eql(u8, "_", name_bytes)) 4192 break :blk .empty; 4193 4194 const param_name = try astgen.identAsString(name_token); 4195 if (!is_extern) { 4196 try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes, .@"function parameter"); 4197 } 4198 break :blk param_name; 4199 } else if (!is_extern) { 4200 if (param.anytype_ellipsis3) |tok| { 4201 return astgen.failTok(tok, "missing parameter name", .{}); 4202 } else { 4203 ambiguous: { 4204 if (tree.nodes.items(.tag)[param.type_expr] != .identifier) break :ambiguous; 4205 const main_token = tree.nodes.items(.main_token)[param.type_expr]; 4206 const identifier_str = tree.tokenSlice(main_token); 4207 if (isPrimitive(identifier_str)) break :ambiguous; 4208 return astgen.failNodeNotes( 4209 param.type_expr, 4210 "missing parameter name or type", 4211 .{}, 4212 &[_]u32{ 4213 try astgen.errNoteNode( 4214 param.type_expr, 4215 "if this is a name, annotate its type '{s}: T'", 4216 .{identifier_str}, 4217 ), 4218 try astgen.errNoteNode( 4219 param.type_expr, 4220 "if this is a type, give it a name '<name>: {s}'", 4221 .{identifier_str}, 4222 ), 4223 }, 4224 ); 4225 } 4226 return astgen.failNode(param.type_expr, "missing parameter name", .{}); 4227 } 4228 } else .empty; 4229 4230 const param_inst = if (is_anytype) param: { 4231 const name_token = param.name_token orelse param.anytype_ellipsis3.?; 4232 const tag: Zir.Inst.Tag = if (is_comptime) 4233 .param_anytype_comptime 4234 else 4235 .param_anytype; 4236 break :param try decl_gz.addStrTok(tag, param_name, name_token); 4237 } else param: { 4238 const param_type_node = param.type_expr; 4239 assert(param_type_node != 0); 4240 var param_gz = decl_gz.makeSubBlock(scope); 4241 defer param_gz.unstack(); 4242 const param_type = try fullBodyExpr(¶m_gz, params_scope, coerced_type_ri, param_type_node, .normal); 4243 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1); 4244 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node); 4245 4246 const main_tokens = tree.nodes.items(.main_token); 4247 const name_token = param.name_token orelse main_tokens[param_type_node]; 4248 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param; 4249 const param_inst = try decl_gz.addParam(¶m_gz, param_insts.items, tag, name_token, param_name); 4250 assert(param_inst_expected == param_inst); 4251 break :param param_inst.toRef(); 4252 }; 4253 4254 if (param_name == .empty or is_extern) continue; 4255 4256 const sub_scope = try astgen.arena.create(Scope.LocalVal); 4257 sub_scope.* = .{ 4258 .parent = params_scope, 4259 .gen_zir = &decl_gz, 4260 .name = param_name, 4261 .inst = param_inst, 4262 .token_src = param.name_token.?, 4263 .id_cat = .@"function parameter", 4264 }; 4265 params_scope = &sub_scope.base; 4266 try param_insts.append(astgen.arena, param_inst.toIndex().?); 4267 } 4268 break :is_var_args false; 4269 }; 4270 4271 const lib_name = if (fn_proto.lib_name) |lib_name_token| blk: { 4272 const lib_name_str = try astgen.strLitAsString(lib_name_token); 4273 const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len]; 4274 if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) { 4275 return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{}); 4276 } else if (lib_name_str.len == 0) { 4277 return astgen.failTok(lib_name_token, "library name cannot be empty", .{}); 4278 } 4279 break :blk lib_name_str.index; 4280 } else .empty; 4281 4282 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; 4283 const is_inferred_error = token_tags[maybe_bang] == .bang; 4284 4285 // After creating the function ZIR instruction, it will need to update the break 4286 // instructions inside the expression blocks for align, addrspace, cc, and ret_ty 4287 // to use the function instruction as the "block" to break from. 4288 4289 var ret_gz = decl_gz.makeSubBlock(params_scope); 4290 defer ret_gz.unstack(); 4291 const ret_ref: Zir.Inst.Ref = inst: { 4292 // Parameters are in scope for the return type, so we use `params_scope` here. 4293 // The calling convention will not have parameters in scope, so we'll just use `scope`. 4294 // See #22263 for a proposal to solve the inconsistency here. 4295 const inst = try fullBodyExpr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type, .normal); 4296 if (ret_gz.instructionsSlice().len == 0) { 4297 // In this case we will send a len=0 body which can be encoded more efficiently. 4298 break :inst inst; 4299 } 4300 _ = try ret_gz.addBreak(.break_inline, @enumFromInt(0), inst); 4301 break :inst inst; 4302 }; 4303 const ret_body_param_refs = try astgen.fetchRemoveRefEntries(param_insts.items); 4304 4305 // We're jumping back in source, so restore the cursor. 4306 astgen.restoreSourceCursor(saved_cursor); 4307 4308 var cc_gz = decl_gz.makeSubBlock(scope); 4309 defer cc_gz.unstack(); 4310 const cc_ref: Zir.Inst.Ref = blk: { 4311 if (fn_proto.ast.callconv_expr != 0) { 4312 if (has_inline_keyword) { 4313 return astgen.failNode( 4314 fn_proto.ast.callconv_expr, 4315 "explicit callconv incompatible with inline keyword", 4316 .{}, 4317 ); 4318 } 4319 const inst = try expr( 4320 &cc_gz, 4321 scope, 4322 .{ .rl = .{ .coerced_ty = try cc_gz.addBuiltinValue(fn_proto.ast.callconv_expr, .calling_convention) } }, 4323 fn_proto.ast.callconv_expr, 4324 ); 4325 if (cc_gz.instructionsSlice().len == 0) { 4326 // In this case we will send a len=0 body which can be encoded more efficiently. 4327 break :blk inst; 4328 } 4329 _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst); 4330 break :blk inst; 4331 } else if (is_extern) { 4332 const inst = try cc_gz.addBuiltinValue(decl_node, .calling_convention_c); 4333 _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst); 4334 break :blk inst; 4335 } else if (has_inline_keyword) { 4336 const inst = try cc_gz.addBuiltinValue(decl_node, .calling_convention_inline); 4337 _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst); 4338 break :blk inst; 4339 } else { 4340 break :blk .none; 4341 } 4342 }; 4343 4344 const func_inst: Zir.Inst.Ref = if (body_node == 0) func: { 4345 if (!is_extern) { 4346 return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{}); 4347 } 4348 if (is_inferred_error) { 4349 return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{}); 4350 } 4351 break :func try decl_gz.addFunc(.{ 4352 .src_node = decl_node, 4353 .cc_ref = cc_ref, 4354 .cc_gz = &cc_gz, 4355 .ret_ref = ret_ref, 4356 .ret_gz = &ret_gz, 4357 .ret_param_refs = ret_body_param_refs, 4358 .param_block = decl_inst, 4359 .param_insts = param_insts.items, 4360 .body_gz = null, 4361 .lib_name = lib_name, 4362 .is_var_args = is_var_args, 4363 .is_inferred_error = false, 4364 .is_test = false, 4365 .is_extern = true, 4366 .is_noinline = is_noinline, 4367 .noalias_bits = noalias_bits, 4368 .proto_hash = undefined, // ignored for `body_gz == null` 4369 }); 4370 } else func: { 4371 var body_gz: GenZir = .{ 4372 .is_comptime = false, 4373 .decl_node_index = fn_proto.ast.proto_node, 4374 .decl_line = decl_gz.decl_line, 4375 .parent = params_scope, 4376 .astgen = astgen, 4377 .instructions = gz.instructions, 4378 .instructions_top = gz.instructions.items.len, 4379 }; 4380 defer body_gz.unstack(); 4381 4382 // We want `params_scope` to be stacked like this: 4383 // body_gz (top) 4384 // param2 4385 // param1 4386 // param0 4387 // decl_gz (bottom) 4388 4389 // Construct the prototype hash. 4390 // Leave `astgen.src_hasher` unmodified; this will be used for hashing 4391 // the *whole* function declaration, including its body. 4392 var proto_hasher = astgen.src_hasher; 4393 const proto_node = tree.nodes.items(.data)[decl_node].lhs; 4394 proto_hasher.update(tree.getNodeSource(proto_node)); 4395 var proto_hash: std.zig.SrcHash = undefined; 4396 proto_hasher.final(&proto_hash); 4397 4398 const prev_fn_block = astgen.fn_block; 4399 const prev_fn_ret_ty = astgen.fn_ret_ty; 4400 defer { 4401 astgen.fn_block = prev_fn_block; 4402 astgen.fn_ret_ty = prev_fn_ret_ty; 4403 } 4404 astgen.fn_block = &body_gz; 4405 astgen.fn_ret_ty = if (is_inferred_error or ret_ref.toIndex() != null) r: { 4406 // We're essentially guaranteed to need the return type at some point, 4407 // since the return type is likely not `void` or `noreturn` so there 4408 // will probably be an explicit return requiring RLS. Fetch this 4409 // return type now so the rest of the function can use it. 4410 break :r try body_gz.addNode(.ret_type, decl_node); 4411 } else ret_ref; 4412 4413 const prev_var_args = astgen.fn_var_args; 4414 astgen.fn_var_args = is_var_args; 4415 defer astgen.fn_var_args = prev_var_args; 4416 4417 astgen.advanceSourceCursorToNode(body_node); 4418 const lbrace_line = astgen.source_line - decl_gz.decl_line; 4419 const lbrace_column = astgen.source_column; 4420 4421 _ = try fullBodyExpr(&body_gz, &body_gz.base, .{ .rl = .none }, body_node, .allow_branch_hint); 4422 try checkUsed(gz, scope, params_scope); 4423 4424 if (!body_gz.endsWithNoReturn()) { 4425 // As our last action before the return, "pop" the error trace if needed 4426 _ = try body_gz.addRestoreErrRetIndex(.ret, .always, decl_node); 4427 4428 // Add implicit return at end of function. 4429 _ = try body_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node)); 4430 } 4431 4432 break :func try decl_gz.addFunc(.{ 4433 .src_node = decl_node, 4434 .cc_ref = cc_ref, 4435 .cc_gz = &cc_gz, 4436 .ret_ref = ret_ref, 4437 .ret_gz = &ret_gz, 4438 .ret_param_refs = ret_body_param_refs, 4439 .lbrace_line = lbrace_line, 4440 .lbrace_column = lbrace_column, 4441 .param_block = decl_inst, 4442 .param_insts = param_insts.items, 4443 .body_gz = &body_gz, 4444 .lib_name = lib_name, 4445 .is_var_args = is_var_args, 4446 .is_inferred_error = is_inferred_error, 4447 .is_test = false, 4448 .is_extern = false, 4449 .is_noinline = is_noinline, 4450 .noalias_bits = noalias_bits, 4451 .proto_hash = proto_hash, 4452 }); 4453 }; 4454 4455 // Before we stack more stuff onto `decl_gz`, add its final instruction. 4456 _ = try decl_gz.addBreak(.break_inline, decl_inst, func_inst); 4457 4458 // Now that `cc_gz,` `ret_gz`, and `body_gz` are unstacked, we evaluate align, addrspace, and linksection. 4459 4460 // We're jumping back in source, so restore the cursor. 4461 astgen.restoreSourceCursor(saved_cursor); 4462 4463 var align_gz = decl_gz.makeSubBlock(scope); 4464 defer align_gz.unstack(); 4465 if (fn_proto.ast.align_expr != 0) { 4466 const inst = try expr(&decl_gz, &decl_gz.base, coerced_align_ri, fn_proto.ast.align_expr); 4467 _ = try align_gz.addBreak(.break_inline, decl_inst, inst); 4468 } 4469 4470 var section_gz = align_gz.makeSubBlock(scope); 4471 defer section_gz.unstack(); 4472 if (fn_proto.ast.section_expr != 0) { 4473 const inst = try expr(&decl_gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, fn_proto.ast.section_expr); 4474 _ = try section_gz.addBreak(.break_inline, decl_inst, inst); 4475 } 4476 4477 var addrspace_gz = section_gz.makeSubBlock(scope); 4478 defer addrspace_gz.unstack(); 4479 if (fn_proto.ast.addrspace_expr != 0) { 4480 const addrspace_ty = try decl_gz.addBuiltinValue(fn_proto.ast.addrspace_expr, .address_space); 4481 const inst = try expr(&decl_gz, scope, .{ .rl = .{ .coerced_ty = addrspace_ty } }, fn_proto.ast.addrspace_expr); 4482 _ = try addrspace_gz.addBreak(.break_inline, decl_inst, inst); 4483 } 4484 4485 // *Now* we can incorporate the full source code into the hasher. 4486 astgen.src_hasher.update(tree.getNodeSource(decl_node)); 4487 4488 var hash: std.zig.SrcHash = undefined; 4489 astgen.src_hasher.final(&hash); 4490 try setDeclaration( 4491 decl_inst, 4492 hash, 4493 .{ .named = fn_name_token }, 4494 decl_gz.decl_line, 4495 decl_column, 4496 is_pub, 4497 is_export, 4498 &decl_gz, 4499 .{ 4500 .align_gz = &align_gz, 4501 .linksection_gz = §ion_gz, 4502 .addrspace_gz = &addrspace_gz, 4503 }, 4504 ); 4505 } 4506 4507 fn globalVarDecl( 4508 astgen: *AstGen, 4509 gz: *GenZir, 4510 scope: *Scope, 4511 wip_members: *WipMembers, 4512 node: Ast.Node.Index, 4513 var_decl: Ast.full.VarDecl, 4514 ) InnerError!void { 4515 const tree = astgen.tree; 4516 const token_tags = tree.tokens.items(.tag); 4517 4518 const old_hasher = astgen.src_hasher; 4519 defer astgen.src_hasher = old_hasher; 4520 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 4521 astgen.src_hasher.update(tree.getNodeSource(node)); 4522 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); 4523 4524 const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var; 4525 // We do this at the beginning so that the instruction index marks the range start 4526 // of the top level declaration. 4527 const decl_inst = try gz.makeDeclaration(node); 4528 4529 const name_token = var_decl.ast.mut_token + 1; 4530 astgen.advanceSourceCursorToNode(node); 4531 4532 var block_scope: GenZir = .{ 4533 .parent = scope, 4534 .decl_node_index = node, 4535 .decl_line = astgen.source_line, 4536 .astgen = astgen, 4537 .is_comptime = true, 4538 .instructions = gz.instructions, 4539 .instructions_top = gz.instructions.items.len, 4540 }; 4541 defer block_scope.unstack(); 4542 4543 const decl_column = astgen.source_column; 4544 4545 const is_pub = var_decl.visib_token != null; 4546 const is_export = blk: { 4547 const maybe_export_token = var_decl.extern_export_token orelse break :blk false; 4548 break :blk token_tags[maybe_export_token] == .keyword_export; 4549 }; 4550 const is_extern = blk: { 4551 const maybe_extern_token = var_decl.extern_export_token orelse break :blk false; 4552 break :blk token_tags[maybe_extern_token] == .keyword_extern; 4553 }; 4554 wip_members.nextDecl(decl_inst); 4555 4556 const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: { 4557 if (!is_mutable) { 4558 return astgen.failTok(tok, "threadlocal variable cannot be constant", .{}); 4559 } 4560 break :blk true; 4561 } else false; 4562 4563 const lib_name = if (var_decl.lib_name) |lib_name_token| blk: { 4564 const lib_name_str = try astgen.strLitAsString(lib_name_token); 4565 const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len]; 4566 if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) { 4567 return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{}); 4568 } else if (lib_name_str.len == 0) { 4569 return astgen.failTok(lib_name_token, "library name cannot be empty", .{}); 4570 } 4571 break :blk lib_name_str.index; 4572 } else .empty; 4573 4574 assert(var_decl.comptime_token == null); // handled by parser 4575 4576 const var_inst: Zir.Inst.Ref = if (var_decl.ast.init_node != 0) vi: { 4577 if (is_extern) { 4578 return astgen.failNode( 4579 var_decl.ast.init_node, 4580 "extern variables have no initializers", 4581 .{}, 4582 ); 4583 } 4584 4585 const type_inst: Zir.Inst.Ref = if (var_decl.ast.type_node != 0) 4586 try expr( 4587 &block_scope, 4588 &block_scope.base, 4589 coerced_type_ri, 4590 var_decl.ast.type_node, 4591 ) 4592 else 4593 .none; 4594 4595 block_scope.anon_name_strategy = .parent; 4596 4597 const init_inst = try expr( 4598 &block_scope, 4599 &block_scope.base, 4600 if (type_inst != .none) .{ .rl = .{ .ty = type_inst } } else .{ .rl = .none }, 4601 var_decl.ast.init_node, 4602 ); 4603 4604 if (is_mutable) { 4605 const var_inst = try block_scope.addVar(.{ 4606 .var_type = type_inst, 4607 .lib_name = .empty, 4608 .align_inst = .none, // passed via the decls data 4609 .init = init_inst, 4610 .is_extern = false, 4611 .is_const = !is_mutable, 4612 .is_threadlocal = is_threadlocal, 4613 }); 4614 break :vi var_inst; 4615 } else { 4616 break :vi init_inst; 4617 } 4618 } else if (!is_extern) { 4619 return astgen.failNode(node, "variables must be initialized", .{}); 4620 } else if (var_decl.ast.type_node != 0) vi: { 4621 // Extern variable which has an explicit type. 4622 const type_inst = try typeExpr(&block_scope, &block_scope.base, var_decl.ast.type_node); 4623 4624 block_scope.anon_name_strategy = .parent; 4625 4626 const var_inst = try block_scope.addVar(.{ 4627 .var_type = type_inst, 4628 .lib_name = lib_name, 4629 .align_inst = .none, // passed via the decls data 4630 .init = .none, 4631 .is_extern = true, 4632 .is_const = !is_mutable, 4633 .is_threadlocal = is_threadlocal, 4634 }); 4635 break :vi var_inst; 4636 } else { 4637 return astgen.failNode(node, "unable to infer variable type", .{}); 4638 }; 4639 4640 // We do this at the end so that the instruction index marks the end 4641 // range of a top level declaration. 4642 _ = try block_scope.addBreakWithSrcNode(.break_inline, decl_inst, var_inst, node); 4643 4644 var align_gz = block_scope.makeSubBlock(scope); 4645 if (var_decl.ast.align_node != 0) { 4646 const align_inst = try fullBodyExpr(&align_gz, &align_gz.base, coerced_align_ri, var_decl.ast.align_node, .normal); 4647 _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node); 4648 } 4649 4650 var linksection_gz = align_gz.makeSubBlock(scope); 4651 if (var_decl.ast.section_node != 0) { 4652 const linksection_inst = try fullBodyExpr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, var_decl.ast.section_node, .normal); 4653 _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node); 4654 } 4655 4656 var addrspace_gz = linksection_gz.makeSubBlock(scope); 4657 if (var_decl.ast.addrspace_node != 0) { 4658 const addrspace_ty = try addrspace_gz.addBuiltinValue(var_decl.ast.addrspace_node, .address_space); 4659 const addrspace_inst = try fullBodyExpr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, var_decl.ast.addrspace_node, .normal); 4660 _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node); 4661 } 4662 4663 var hash: std.zig.SrcHash = undefined; 4664 astgen.src_hasher.final(&hash); 4665 try setDeclaration( 4666 decl_inst, 4667 hash, 4668 .{ .named = name_token }, 4669 block_scope.decl_line, 4670 decl_column, 4671 is_pub, 4672 is_export, 4673 &block_scope, 4674 .{ 4675 .align_gz = &align_gz, 4676 .linksection_gz = &linksection_gz, 4677 .addrspace_gz = &addrspace_gz, 4678 }, 4679 ); 4680 } 4681 4682 fn comptimeDecl( 4683 astgen: *AstGen, 4684 gz: *GenZir, 4685 scope: *Scope, 4686 wip_members: *WipMembers, 4687 node: Ast.Node.Index, 4688 ) InnerError!void { 4689 const tree = astgen.tree; 4690 const node_datas = tree.nodes.items(.data); 4691 const body_node = node_datas[node].lhs; 4692 4693 const old_hasher = astgen.src_hasher; 4694 defer astgen.src_hasher = old_hasher; 4695 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 4696 astgen.src_hasher.update(tree.getNodeSource(node)); 4697 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); 4698 4699 // Up top so the ZIR instruction index marks the start range of this 4700 // top-level declaration. 4701 const decl_inst = try gz.makeDeclaration(node); 4702 wip_members.nextDecl(decl_inst); 4703 astgen.advanceSourceCursorToNode(node); 4704 4705 var decl_block: GenZir = .{ 4706 .is_comptime = true, 4707 .decl_node_index = node, 4708 .decl_line = astgen.source_line, 4709 .parent = scope, 4710 .astgen = astgen, 4711 .instructions = gz.instructions, 4712 .instructions_top = gz.instructions.items.len, 4713 }; 4714 defer decl_block.unstack(); 4715 4716 const decl_column = astgen.source_column; 4717 4718 const block_result = try fullBodyExpr(&decl_block, &decl_block.base, .{ .rl = .none }, body_node, .normal); 4719 if (decl_block.isEmpty() or !decl_block.refIsNoReturn(block_result)) { 4720 _ = try decl_block.addBreak(.break_inline, decl_inst, .void_value); 4721 } 4722 4723 var hash: std.zig.SrcHash = undefined; 4724 astgen.src_hasher.final(&hash); 4725 try setDeclaration( 4726 decl_inst, 4727 hash, 4728 .@"comptime", 4729 decl_block.decl_line, 4730 decl_column, 4731 false, 4732 false, 4733 &decl_block, 4734 null, 4735 ); 4736 } 4737 4738 fn usingnamespaceDecl( 4739 astgen: *AstGen, 4740 gz: *GenZir, 4741 scope: *Scope, 4742 wip_members: *WipMembers, 4743 node: Ast.Node.Index, 4744 ) InnerError!void { 4745 const tree = astgen.tree; 4746 const node_datas = tree.nodes.items(.data); 4747 4748 const old_hasher = astgen.src_hasher; 4749 defer astgen.src_hasher = old_hasher; 4750 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 4751 astgen.src_hasher.update(tree.getNodeSource(node)); 4752 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); 4753 4754 const type_expr = node_datas[node].lhs; 4755 const is_pub = blk: { 4756 const main_tokens = tree.nodes.items(.main_token); 4757 const token_tags = tree.tokens.items(.tag); 4758 const main_token = main_tokens[node]; 4759 break :blk (main_token > 0 and token_tags[main_token - 1] == .keyword_pub); 4760 }; 4761 // Up top so the ZIR instruction index marks the start range of this 4762 // top-level declaration. 4763 const decl_inst = try gz.makeDeclaration(node); 4764 wip_members.nextDecl(decl_inst); 4765 astgen.advanceSourceCursorToNode(node); 4766 4767 var decl_block: GenZir = .{ 4768 .is_comptime = true, 4769 .decl_node_index = node, 4770 .decl_line = astgen.source_line, 4771 .parent = scope, 4772 .astgen = astgen, 4773 .instructions = gz.instructions, 4774 .instructions_top = gz.instructions.items.len, 4775 }; 4776 defer decl_block.unstack(); 4777 4778 const decl_column = astgen.source_column; 4779 4780 const namespace_inst = try typeExpr(&decl_block, &decl_block.base, type_expr); 4781 _ = try decl_block.addBreak(.break_inline, decl_inst, namespace_inst); 4782 4783 var hash: std.zig.SrcHash = undefined; 4784 astgen.src_hasher.final(&hash); 4785 try setDeclaration( 4786 decl_inst, 4787 hash, 4788 .@"usingnamespace", 4789 decl_block.decl_line, 4790 decl_column, 4791 is_pub, 4792 false, 4793 &decl_block, 4794 null, 4795 ); 4796 } 4797 4798 fn testDecl( 4799 astgen: *AstGen, 4800 gz: *GenZir, 4801 scope: *Scope, 4802 wip_members: *WipMembers, 4803 node: Ast.Node.Index, 4804 ) InnerError!void { 4805 const tree = astgen.tree; 4806 const node_datas = tree.nodes.items(.data); 4807 const body_node = node_datas[node].rhs; 4808 4809 const old_hasher = astgen.src_hasher; 4810 defer astgen.src_hasher = old_hasher; 4811 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 4812 astgen.src_hasher.update(tree.getNodeSource(node)); 4813 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); 4814 4815 // Up top so the ZIR instruction index marks the start range of this 4816 // top-level declaration. 4817 const decl_inst = try gz.makeDeclaration(node); 4818 4819 wip_members.nextDecl(decl_inst); 4820 astgen.advanceSourceCursorToNode(node); 4821 4822 var decl_block: GenZir = .{ 4823 .is_comptime = true, 4824 .decl_node_index = node, 4825 .decl_line = astgen.source_line, 4826 .parent = scope, 4827 .astgen = astgen, 4828 .instructions = gz.instructions, 4829 .instructions_top = gz.instructions.items.len, 4830 }; 4831 defer decl_block.unstack(); 4832 4833 const decl_column = astgen.source_column; 4834 4835 const main_tokens = tree.nodes.items(.main_token); 4836 const token_tags = tree.tokens.items(.tag); 4837 const test_token = main_tokens[node]; 4838 const test_name_token = test_token + 1; 4839 const test_name: DeclarationName = switch (token_tags[test_name_token]) { 4840 else => .unnamed_test, 4841 .string_literal => .{ .named_test = test_name_token }, 4842 .identifier => blk: { 4843 const ident_name_raw = tree.tokenSlice(test_name_token); 4844 4845 if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{}); 4846 4847 // if not @"" syntax, just use raw token slice 4848 if (ident_name_raw[0] != '@') { 4849 if (isPrimitive(ident_name_raw)) return astgen.failTok(test_name_token, "cannot test a primitive", .{}); 4850 } 4851 4852 // Local variables, including function parameters. 4853 const name_str_index = try astgen.identAsString(test_name_token); 4854 var s = scope; 4855 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already 4856 var num_namespaces_out: u32 = 0; 4857 var capturing_namespace: ?*Scope.Namespace = null; 4858 while (true) switch (s.tag) { 4859 .local_val => { 4860 const local_val = s.cast(Scope.LocalVal).?; 4861 if (local_val.name == name_str_index) { 4862 local_val.used = test_name_token; 4863 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{ 4864 @tagName(local_val.id_cat), 4865 }, &[_]u32{ 4866 try astgen.errNoteTok(local_val.token_src, "{s} declared here", .{ 4867 @tagName(local_val.id_cat), 4868 }), 4869 }); 4870 } 4871 s = local_val.parent; 4872 }, 4873 .local_ptr => { 4874 const local_ptr = s.cast(Scope.LocalPtr).?; 4875 if (local_ptr.name == name_str_index) { 4876 local_ptr.used = test_name_token; 4877 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{ 4878 @tagName(local_ptr.id_cat), 4879 }, &[_]u32{ 4880 try astgen.errNoteTok(local_ptr.token_src, "{s} declared here", .{ 4881 @tagName(local_ptr.id_cat), 4882 }), 4883 }); 4884 } 4885 s = local_ptr.parent; 4886 }, 4887 .gen_zir => s = s.cast(GenZir).?.parent, 4888 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, 4889 .namespace => { 4890 const ns = s.cast(Scope.Namespace).?; 4891 if (ns.decls.get(name_str_index)) |i| { 4892 if (found_already) |f| { 4893 return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{ 4894 try astgen.errNoteNode(f, "declared here", .{}), 4895 try astgen.errNoteNode(i, "also declared here", .{}), 4896 }); 4897 } 4898 // We found a match but must continue looking for ambiguous references to decls. 4899 found_already = i; 4900 } 4901 num_namespaces_out += 1; 4902 capturing_namespace = ns; 4903 s = ns.parent; 4904 }, 4905 .top => break, 4906 }; 4907 if (found_already == null) { 4908 const ident_name = try astgen.identifierTokenString(test_name_token); 4909 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name}); 4910 } 4911 4912 break :blk .{ .decltest = test_name_token }; 4913 }, 4914 }; 4915 4916 var fn_block: GenZir = .{ 4917 .is_comptime = false, 4918 .decl_node_index = node, 4919 .decl_line = decl_block.decl_line, 4920 .parent = &decl_block.base, 4921 .astgen = astgen, 4922 .instructions = decl_block.instructions, 4923 .instructions_top = decl_block.instructions.items.len, 4924 }; 4925 defer fn_block.unstack(); 4926 4927 const prev_within_fn = astgen.within_fn; 4928 const prev_fn_block = astgen.fn_block; 4929 const prev_fn_ret_ty = astgen.fn_ret_ty; 4930 astgen.within_fn = true; 4931 astgen.fn_block = &fn_block; 4932 astgen.fn_ret_ty = .anyerror_void_error_union_type; 4933 defer { 4934 astgen.within_fn = prev_within_fn; 4935 astgen.fn_block = prev_fn_block; 4936 astgen.fn_ret_ty = prev_fn_ret_ty; 4937 } 4938 4939 astgen.advanceSourceCursorToNode(body_node); 4940 const lbrace_line = astgen.source_line - decl_block.decl_line; 4941 const lbrace_column = astgen.source_column; 4942 4943 const block_result = try fullBodyExpr(&fn_block, &fn_block.base, .{ .rl = .none }, body_node, .normal); 4944 if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) { 4945 4946 // As our last action before the return, "pop" the error trace if needed 4947 _ = try fn_block.addRestoreErrRetIndex(.ret, .always, node); 4948 4949 // Add implicit return at end of function. 4950 _ = try fn_block.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node)); 4951 } 4952 4953 const func_inst = try decl_block.addFunc(.{ 4954 .src_node = node, 4955 4956 .cc_ref = .none, 4957 .cc_gz = null, 4958 .ret_ref = .anyerror_void_error_union_type, 4959 .ret_gz = null, 4960 4961 .ret_param_refs = &.{}, 4962 .param_insts = &.{}, 4963 4964 .lbrace_line = lbrace_line, 4965 .lbrace_column = lbrace_column, 4966 .param_block = decl_inst, 4967 .body_gz = &fn_block, 4968 .lib_name = .empty, 4969 .is_var_args = false, 4970 .is_inferred_error = false, 4971 .is_test = true, 4972 .is_extern = false, 4973 .is_noinline = false, 4974 .noalias_bits = 0, 4975 4976 // Tests don't have a prototype that needs hashing 4977 .proto_hash = .{0} ** 16, 4978 }); 4979 4980 _ = try decl_block.addBreak(.break_inline, decl_inst, func_inst); 4981 4982 var hash: std.zig.SrcHash = undefined; 4983 astgen.src_hasher.final(&hash); 4984 try setDeclaration( 4985 decl_inst, 4986 hash, 4987 test_name, 4988 decl_block.decl_line, 4989 decl_column, 4990 false, 4991 false, 4992 &decl_block, 4993 null, 4994 ); 4995 } 4996 4997 fn structDeclInner( 4998 gz: *GenZir, 4999 scope: *Scope, 5000 node: Ast.Node.Index, 5001 container_decl: Ast.full.ContainerDecl, 5002 layout: std.builtin.Type.ContainerLayout, 5003 backing_int_node: Ast.Node.Index, 5004 ) InnerError!Zir.Inst.Ref { 5005 const astgen = gz.astgen; 5006 const gpa = astgen.gpa; 5007 const tree = astgen.tree; 5008 5009 { 5010 const is_tuple = for (container_decl.ast.members) |member_node| { 5011 const container_field = tree.fullContainerField(member_node) orelse continue; 5012 if (container_field.ast.tuple_like) break true; 5013 } else false; 5014 5015 if (is_tuple) { 5016 if (node == 0) { 5017 return astgen.failTok(0, "file cannot be a tuple", .{}); 5018 } else { 5019 return tupleDecl(gz, scope, node, container_decl, layout, backing_int_node); 5020 } 5021 } 5022 } 5023 5024 const decl_inst = try gz.reserveInstructionIndex(); 5025 5026 if (container_decl.ast.members.len == 0 and backing_int_node == 0) { 5027 try gz.setStruct(decl_inst, .{ 5028 .src_node = node, 5029 .layout = layout, 5030 .captures_len = 0, 5031 .fields_len = 0, 5032 .decls_len = 0, 5033 .has_backing_int = false, 5034 .known_non_opv = false, 5035 .known_comptime_only = false, 5036 .any_comptime_fields = false, 5037 .any_default_inits = false, 5038 .any_aligned_fields = false, 5039 .fields_hash = std.zig.hashSrc(@tagName(layout)), 5040 }); 5041 return decl_inst.toRef(); 5042 } 5043 5044 var namespace: Scope.Namespace = .{ 5045 .parent = scope, 5046 .node = node, 5047 .inst = decl_inst, 5048 .declaring_gz = gz, 5049 .maybe_generic = astgen.within_fn, 5050 }; 5051 defer namespace.deinit(gpa); 5052 5053 // The struct_decl instruction introduces a scope in which the decls of the struct 5054 // are in scope, so that field types, alignments, and default value expressions 5055 // can refer to decls within the struct itself. 5056 astgen.advanceSourceCursorToNode(node); 5057 var block_scope: GenZir = .{ 5058 .parent = &namespace.base, 5059 .decl_node_index = node, 5060 .decl_line = gz.decl_line, 5061 .astgen = astgen, 5062 .is_comptime = true, 5063 .instructions = gz.instructions, 5064 .instructions_top = gz.instructions.items.len, 5065 }; 5066 defer block_scope.unstack(); 5067 5068 const scratch_top = astgen.scratch.items.len; 5069 defer astgen.scratch.items.len = scratch_top; 5070 5071 var backing_int_body_len: usize = 0; 5072 const backing_int_ref: Zir.Inst.Ref = blk: { 5073 if (backing_int_node != 0) { 5074 if (layout != .@"packed") { 5075 return astgen.failNode(backing_int_node, "non-packed struct does not support backing integer type", .{}); 5076 } else { 5077 const backing_int_ref = try typeExpr(&block_scope, &namespace.base, backing_int_node); 5078 if (!block_scope.isEmpty()) { 5079 if (!block_scope.endsWithNoReturn()) { 5080 _ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref); 5081 } 5082 5083 const body = block_scope.instructionsSlice(); 5084 const old_scratch_len = astgen.scratch.items.len; 5085 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); 5086 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 5087 backing_int_body_len = astgen.scratch.items.len - old_scratch_len; 5088 block_scope.instructions.items.len = block_scope.instructions_top; 5089 } 5090 break :blk backing_int_ref; 5091 } 5092 } else { 5093 break :blk .none; 5094 } 5095 }; 5096 5097 const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"struct"); 5098 const field_count: u32 = @intCast(container_decl.ast.members.len - decl_count); 5099 5100 const bits_per_field = 4; 5101 const max_field_size = 5; 5102 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size); 5103 defer wip_members.deinit(); 5104 5105 // We will use the scratch buffer, starting here, for the bodies: 5106 // bodies: { // for every fields_len 5107 // field_type_body_inst: Inst, // for each field_type_body_len 5108 // align_body_inst: Inst, // for each align_body_len 5109 // init_body_inst: Inst, // for each init_body_len 5110 // } 5111 // Note that the scratch buffer is simultaneously being used by WipMembers, however 5112 // it will not access any elements beyond this point in the ArrayList. It also 5113 // accesses via the ArrayList items field so it can handle the scratch buffer being 5114 // reallocated. 5115 // No defer needed here because it is handled by `wip_members.deinit()` above. 5116 const bodies_start = astgen.scratch.items.len; 5117 5118 const old_hasher = astgen.src_hasher; 5119 defer astgen.src_hasher = old_hasher; 5120 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 5121 astgen.src_hasher.update(@tagName(layout)); 5122 if (backing_int_node != 0) { 5123 astgen.src_hasher.update(tree.getNodeSource(backing_int_node)); 5124 } 5125 5126 var known_non_opv = false; 5127 var known_comptime_only = false; 5128 var any_comptime_fields = false; 5129 var any_aligned_fields = false; 5130 var any_default_inits = false; 5131 for (container_decl.ast.members) |member_node| { 5132 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { 5133 .decl => continue, 5134 .field => |field| field, 5135 }; 5136 5137 astgen.src_hasher.update(tree.getNodeSource(member_node)); 5138 5139 const field_name = try astgen.identAsString(member.ast.main_token); 5140 member.convertToNonTupleLike(astgen.tree.nodes); 5141 assert(!member.ast.tuple_like); 5142 wip_members.appendToField(@intFromEnum(field_name)); 5143 5144 if (member.ast.type_expr == 0) { 5145 return astgen.failTok(member.ast.main_token, "struct field missing type", .{}); 5146 } 5147 5148 const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr); 5149 const have_type_body = !block_scope.isEmpty(); 5150 const have_align = member.ast.align_expr != 0; 5151 const have_value = member.ast.value_expr != 0; 5152 const is_comptime = member.comptime_token != null; 5153 5154 if (is_comptime) { 5155 switch (layout) { 5156 .@"packed", .@"extern" => return astgen.failTok(member.comptime_token.?, "{s} struct fields cannot be marked comptime", .{@tagName(layout)}), 5157 .auto => any_comptime_fields = true, 5158 } 5159 } else { 5160 known_non_opv = known_non_opv or 5161 nodeImpliesMoreThanOnePossibleValue(tree, member.ast.type_expr); 5162 known_comptime_only = known_comptime_only or 5163 nodeImpliesComptimeOnly(tree, member.ast.type_expr); 5164 } 5165 wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, have_type_body }); 5166 5167 if (have_type_body) { 5168 if (!block_scope.endsWithNoReturn()) { 5169 _ = try block_scope.addBreak(.break_inline, decl_inst, field_type); 5170 } 5171 const body = block_scope.instructionsSlice(); 5172 const old_scratch_len = astgen.scratch.items.len; 5173 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); 5174 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 5175 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len)); 5176 block_scope.instructions.items.len = block_scope.instructions_top; 5177 } else { 5178 wip_members.appendToField(@intFromEnum(field_type)); 5179 } 5180 5181 if (have_align) { 5182 if (layout == .@"packed") { 5183 return astgen.failNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{}); 5184 } 5185 any_aligned_fields = true; 5186 const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, member.ast.align_expr); 5187 if (!block_scope.endsWithNoReturn()) { 5188 _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref); 5189 } 5190 const body = block_scope.instructionsSlice(); 5191 const old_scratch_len = astgen.scratch.items.len; 5192 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); 5193 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 5194 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len)); 5195 block_scope.instructions.items.len = block_scope.instructions_top; 5196 } 5197 5198 if (have_value) { 5199 any_default_inits = true; 5200 5201 // The decl_inst is used as here so that we can easily reconstruct a mapping 5202 // between it and the field type when the fields inits are analyzed. 5203 const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = decl_inst.toRef() } }; 5204 5205 const default_inst = try expr(&block_scope, &namespace.base, ri, member.ast.value_expr); 5206 if (!block_scope.endsWithNoReturn()) { 5207 _ = try block_scope.addBreak(.break_inline, decl_inst, default_inst); 5208 } 5209 const body = block_scope.instructionsSlice(); 5210 const old_scratch_len = astgen.scratch.items.len; 5211 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); 5212 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 5213 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len)); 5214 block_scope.instructions.items.len = block_scope.instructions_top; 5215 } else if (member.comptime_token) |comptime_token| { 5216 return astgen.failTok(comptime_token, "comptime field without default initialization value", .{}); 5217 } 5218 } 5219 5220 var fields_hash: std.zig.SrcHash = undefined; 5221 astgen.src_hasher.final(&fields_hash); 5222 5223 try gz.setStruct(decl_inst, .{ 5224 .src_node = node, 5225 .layout = layout, 5226 .captures_len = @intCast(namespace.captures.count()), 5227 .fields_len = field_count, 5228 .decls_len = decl_count, 5229 .has_backing_int = backing_int_ref != .none, 5230 .known_non_opv = known_non_opv, 5231 .known_comptime_only = known_comptime_only, 5232 .any_comptime_fields = any_comptime_fields, 5233 .any_default_inits = any_default_inits, 5234 .any_aligned_fields = any_aligned_fields, 5235 .fields_hash = fields_hash, 5236 }); 5237 5238 wip_members.finishBits(bits_per_field); 5239 const decls_slice = wip_members.declsSlice(); 5240 const fields_slice = wip_members.fieldsSlice(); 5241 const bodies_slice = astgen.scratch.items[bodies_start..]; 5242 try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len + 2 + 5243 decls_slice.len + namespace.captures.count() + fields_slice.len + bodies_slice.len); 5244 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); 5245 if (backing_int_ref != .none) { 5246 astgen.extra.appendAssumeCapacity(@intCast(backing_int_body_len)); 5247 if (backing_int_body_len == 0) { 5248 astgen.extra.appendAssumeCapacity(@intFromEnum(backing_int_ref)); 5249 } else { 5250 astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]); 5251 } 5252 } 5253 astgen.extra.appendSliceAssumeCapacity(decls_slice); 5254 astgen.extra.appendSliceAssumeCapacity(fields_slice); 5255 astgen.extra.appendSliceAssumeCapacity(bodies_slice); 5256 5257 block_scope.unstack(); 5258 return decl_inst.toRef(); 5259 } 5260 5261 fn tupleDecl( 5262 gz: *GenZir, 5263 scope: *Scope, 5264 node: Ast.Node.Index, 5265 container_decl: Ast.full.ContainerDecl, 5266 layout: std.builtin.Type.ContainerLayout, 5267 backing_int_node: Ast.Node.Index, 5268 ) InnerError!Zir.Inst.Ref { 5269 const astgen = gz.astgen; 5270 const gpa = astgen.gpa; 5271 const tree = astgen.tree; 5272 5273 const node_tags = tree.nodes.items(.tag); 5274 5275 switch (layout) { 5276 .auto => {}, 5277 .@"extern", .@"packed" => return astgen.failNode(node, "{s} tuples are not supported", .{@tagName(layout)}), 5278 } 5279 5280 if (backing_int_node != 0) { 5281 return astgen.failNode(backing_int_node, "tuple does not support backing integer type", .{}); 5282 } 5283 5284 // We will use the scratch buffer, starting here, for the field data: 5285 // 1. fields: { // for every `fields_len` (stored in `extended.small`) 5286 // type: Inst.Ref, 5287 // init: Inst.Ref, // `.none` for non-`comptime` fields 5288 // } 5289 const fields_start = astgen.scratch.items.len; 5290 defer astgen.scratch.items.len = fields_start; 5291 5292 try astgen.scratch.ensureUnusedCapacity(gpa, container_decl.ast.members.len * 2); 5293 5294 for (container_decl.ast.members) |member_node| { 5295 const field = tree.fullContainerField(member_node) orelse { 5296 const tuple_member = for (container_decl.ast.members) |maybe_tuple| switch (node_tags[maybe_tuple]) { 5297 .container_field_init, 5298 .container_field_align, 5299 .container_field, 5300 => break maybe_tuple, 5301 else => {}, 5302 } else unreachable; 5303 return astgen.failNodeNotes( 5304 member_node, 5305 "tuple declarations cannot contain declarations", 5306 .{}, 5307 &.{try astgen.errNoteNode(tuple_member, "tuple field here", .{})}, 5308 ); 5309 }; 5310 5311 if (!field.ast.tuple_like) { 5312 return astgen.failTok(field.ast.main_token, "tuple field has a name", .{}); 5313 } 5314 5315 if (field.ast.align_expr != 0) { 5316 return astgen.failTok(field.ast.main_token, "tuple field has alignment", .{}); 5317 } 5318 5319 if (field.ast.value_expr != 0 and field.comptime_token == null) { 5320 return astgen.failTok(field.ast.main_token, "non-comptime tuple field has default initialization value", .{}); 5321 } 5322 5323 if (field.ast.value_expr == 0 and field.comptime_token != null) { 5324 return astgen.failTok(field.comptime_token.?, "comptime field without default initialization value", .{}); 5325 } 5326 5327 const field_type_ref = try typeExpr(gz, scope, field.ast.type_expr); 5328 astgen.scratch.appendAssumeCapacity(@intFromEnum(field_type_ref)); 5329 5330 if (field.ast.value_expr != 0) { 5331 const field_init_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_type_ref } }, field.ast.value_expr); 5332 astgen.scratch.appendAssumeCapacity(@intFromEnum(field_init_ref)); 5333 } else { 5334 astgen.scratch.appendAssumeCapacity(@intFromEnum(Zir.Inst.Ref.none)); 5335 } 5336 } 5337 5338 const fields_len = std.math.cast(u16, container_decl.ast.members.len) orelse { 5339 return astgen.failNode(node, "this compiler implementation only supports 65535 tuple fields", .{}); 5340 }; 5341 5342 const extra_trail = astgen.scratch.items[fields_start..]; 5343 assert(extra_trail.len == fields_len * 2); 5344 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.TupleDecl).@"struct".fields.len + extra_trail.len); 5345 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.TupleDecl{ 5346 .src_node = gz.nodeIndexToRelative(node), 5347 }); 5348 astgen.extra.appendSliceAssumeCapacity(extra_trail); 5349 5350 return gz.add(.{ 5351 .tag = .extended, 5352 .data = .{ .extended = .{ 5353 .opcode = .tuple_decl, 5354 .small = fields_len, 5355 .operand = payload_index, 5356 } }, 5357 }); 5358 } 5359 5360 fn unionDeclInner( 5361 gz: *GenZir, 5362 scope: *Scope, 5363 node: Ast.Node.Index, 5364 members: []const Ast.Node.Index, 5365 layout: std.builtin.Type.ContainerLayout, 5366 arg_node: Ast.Node.Index, 5367 auto_enum_tok: ?Ast.TokenIndex, 5368 ) InnerError!Zir.Inst.Ref { 5369 const decl_inst = try gz.reserveInstructionIndex(); 5370 5371 const astgen = gz.astgen; 5372 const gpa = astgen.gpa; 5373 5374 var namespace: Scope.Namespace = .{ 5375 .parent = scope, 5376 .node = node, 5377 .inst = decl_inst, 5378 .declaring_gz = gz, 5379 .maybe_generic = astgen.within_fn, 5380 }; 5381 defer namespace.deinit(gpa); 5382 5383 // The union_decl instruction introduces a scope in which the decls of the union 5384 // are in scope, so that field types, alignments, and default value expressions 5385 // can refer to decls within the union itself. 5386 astgen.advanceSourceCursorToNode(node); 5387 var block_scope: GenZir = .{ 5388 .parent = &namespace.base, 5389 .decl_node_index = node, 5390 .decl_line = gz.decl_line, 5391 .astgen = astgen, 5392 .is_comptime = true, 5393 .instructions = gz.instructions, 5394 .instructions_top = gz.instructions.items.len, 5395 }; 5396 defer block_scope.unstack(); 5397 5398 const decl_count = try astgen.scanContainer(&namespace, members, .@"union"); 5399 const field_count: u32 = @intCast(members.len - decl_count); 5400 5401 if (layout != .auto and (auto_enum_tok != null or arg_node != 0)) { 5402 if (arg_node != 0) { 5403 return astgen.failNode(arg_node, "{s} union does not support enum tag type", .{@tagName(layout)}); 5404 } else { 5405 return astgen.failTok(auto_enum_tok.?, "{s} union does not support enum tag type", .{@tagName(layout)}); 5406 } 5407 } 5408 5409 const arg_inst: Zir.Inst.Ref = if (arg_node != 0) 5410 try typeExpr(&block_scope, &namespace.base, arg_node) 5411 else 5412 .none; 5413 5414 const bits_per_field = 4; 5415 const max_field_size = 4; 5416 var any_aligned_fields = false; 5417 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size); 5418 defer wip_members.deinit(); 5419 5420 const old_hasher = astgen.src_hasher; 5421 defer astgen.src_hasher = old_hasher; 5422 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 5423 astgen.src_hasher.update(@tagName(layout)); 5424 astgen.src_hasher.update(&.{@intFromBool(auto_enum_tok != null)}); 5425 if (arg_node != 0) { 5426 astgen.src_hasher.update(astgen.tree.getNodeSource(arg_node)); 5427 } 5428 5429 for (members) |member_node| { 5430 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { 5431 .decl => continue, 5432 .field => |field| field, 5433 }; 5434 astgen.src_hasher.update(astgen.tree.getNodeSource(member_node)); 5435 member.convertToNonTupleLike(astgen.tree.nodes); 5436 if (member.ast.tuple_like) { 5437 return astgen.failTok(member.ast.main_token, "union field missing name", .{}); 5438 } 5439 if (member.comptime_token) |comptime_token| { 5440 return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{}); 5441 } 5442 5443 const field_name = try astgen.identAsString(member.ast.main_token); 5444 wip_members.appendToField(@intFromEnum(field_name)); 5445 5446 const have_type = member.ast.type_expr != 0; 5447 const have_align = member.ast.align_expr != 0; 5448 const have_value = member.ast.value_expr != 0; 5449 const unused = false; 5450 wip_members.nextField(bits_per_field, .{ have_type, have_align, have_value, unused }); 5451 5452 if (have_type) { 5453 const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr); 5454 wip_members.appendToField(@intFromEnum(field_type)); 5455 } else if (arg_inst == .none and auto_enum_tok == null) { 5456 return astgen.failNode(member_node, "union field missing type", .{}); 5457 } 5458 if (have_align) { 5459 const align_inst = try expr(&block_scope, &block_scope.base, coerced_align_ri, member.ast.align_expr); 5460 wip_members.appendToField(@intFromEnum(align_inst)); 5461 any_aligned_fields = true; 5462 } 5463 if (have_value) { 5464 if (arg_inst == .none) { 5465 return astgen.failNodeNotes( 5466 node, 5467 "explicitly valued tagged union missing integer tag type", 5468 .{}, 5469 &[_]u32{ 5470 try astgen.errNoteNode( 5471 member.ast.value_expr, 5472 "tag value specified here", 5473 .{}, 5474 ), 5475 }, 5476 ); 5477 } 5478 if (auto_enum_tok == null) { 5479 return astgen.failNodeNotes( 5480 node, 5481 "explicitly valued tagged union requires inferred enum tag type", 5482 .{}, 5483 &[_]u32{ 5484 try astgen.errNoteNode( 5485 member.ast.value_expr, 5486 "tag value specified here", 5487 .{}, 5488 ), 5489 }, 5490 ); 5491 } 5492 const tag_value = try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr); 5493 wip_members.appendToField(@intFromEnum(tag_value)); 5494 } 5495 } 5496 5497 var fields_hash: std.zig.SrcHash = undefined; 5498 astgen.src_hasher.final(&fields_hash); 5499 5500 if (!block_scope.isEmpty()) { 5501 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); 5502 } 5503 5504 const body = block_scope.instructionsSlice(); 5505 const body_len = astgen.countBodyLenAfterFixups(body); 5506 5507 try gz.setUnion(decl_inst, .{ 5508 .src_node = node, 5509 .layout = layout, 5510 .tag_type = arg_inst, 5511 .captures_len = @intCast(namespace.captures.count()), 5512 .body_len = body_len, 5513 .fields_len = field_count, 5514 .decls_len = decl_count, 5515 .auto_enum_tag = auto_enum_tok != null, 5516 .any_aligned_fields = any_aligned_fields, 5517 .fields_hash = fields_hash, 5518 }); 5519 5520 wip_members.finishBits(bits_per_field); 5521 const decls_slice = wip_members.declsSlice(); 5522 const fields_slice = wip_members.fieldsSlice(); 5523 try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len + body_len + fields_slice.len); 5524 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); 5525 astgen.extra.appendSliceAssumeCapacity(decls_slice); 5526 astgen.appendBodyWithFixups(body); 5527 astgen.extra.appendSliceAssumeCapacity(fields_slice); 5528 5529 block_scope.unstack(); 5530 return decl_inst.toRef(); 5531 } 5532 5533 fn containerDecl( 5534 gz: *GenZir, 5535 scope: *Scope, 5536 ri: ResultInfo, 5537 node: Ast.Node.Index, 5538 container_decl: Ast.full.ContainerDecl, 5539 ) InnerError!Zir.Inst.Ref { 5540 const astgen = gz.astgen; 5541 const gpa = astgen.gpa; 5542 const tree = astgen.tree; 5543 const token_tags = tree.tokens.items(.tag); 5544 5545 const prev_fn_block = astgen.fn_block; 5546 astgen.fn_block = null; 5547 defer astgen.fn_block = prev_fn_block; 5548 5549 // We must not create any types until Sema. Here the goal is only to generate 5550 // ZIR for all the field types, alignments, and default value expressions. 5551 5552 switch (token_tags[container_decl.ast.main_token]) { 5553 .keyword_struct => { 5554 const layout: std.builtin.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (token_tags[t]) { 5555 .keyword_packed => .@"packed", 5556 .keyword_extern => .@"extern", 5557 else => unreachable, 5558 } else .auto; 5559 5560 const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg); 5561 return rvalue(gz, ri, result, node); 5562 }, 5563 .keyword_union => { 5564 const layout: std.builtin.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (token_tags[t]) { 5565 .keyword_packed => .@"packed", 5566 .keyword_extern => .@"extern", 5567 else => unreachable, 5568 } else .auto; 5569 5570 const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, container_decl.ast.enum_token); 5571 return rvalue(gz, ri, result, node); 5572 }, 5573 .keyword_enum => { 5574 if (container_decl.layout_token) |t| { 5575 return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{}); 5576 } 5577 // Count total fields as well as how many have explicitly provided tag values. 5578 const counts = blk: { 5579 var values: usize = 0; 5580 var total_fields: usize = 0; 5581 var decls: usize = 0; 5582 var nonexhaustive_node: Ast.Node.Index = 0; 5583 var nonfinal_nonexhaustive = false; 5584 for (container_decl.ast.members) |member_node| { 5585 var member = tree.fullContainerField(member_node) orelse { 5586 decls += 1; 5587 continue; 5588 }; 5589 member.convertToNonTupleLike(astgen.tree.nodes); 5590 if (member.ast.tuple_like) { 5591 return astgen.failTok(member.ast.main_token, "enum field missing name", .{}); 5592 } 5593 if (member.comptime_token) |comptime_token| { 5594 return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{}); 5595 } 5596 if (member.ast.type_expr != 0) { 5597 return astgen.failNodeNotes( 5598 member.ast.type_expr, 5599 "enum fields do not have types", 5600 .{}, 5601 &[_]u32{ 5602 try astgen.errNoteNode( 5603 node, 5604 "consider 'union(enum)' here to make it a tagged union", 5605 .{}, 5606 ), 5607 }, 5608 ); 5609 } 5610 if (member.ast.align_expr != 0) { 5611 return astgen.failNode(member.ast.align_expr, "enum fields cannot be aligned", .{}); 5612 } 5613 5614 const name_token = member.ast.main_token; 5615 if (mem.eql(u8, tree.tokenSlice(name_token), "_")) { 5616 if (nonexhaustive_node != 0) { 5617 return astgen.failNodeNotes( 5618 member_node, 5619 "redundant non-exhaustive enum mark", 5620 .{}, 5621 &[_]u32{ 5622 try astgen.errNoteNode( 5623 nonexhaustive_node, 5624 "other mark here", 5625 .{}, 5626 ), 5627 }, 5628 ); 5629 } 5630 nonexhaustive_node = member_node; 5631 if (member.ast.value_expr != 0) { 5632 return astgen.failNode(member.ast.value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{}); 5633 } 5634 continue; 5635 } else if (nonexhaustive_node != 0) { 5636 nonfinal_nonexhaustive = true; 5637 } 5638 total_fields += 1; 5639 if (member.ast.value_expr != 0) { 5640 if (container_decl.ast.arg == 0) { 5641 return astgen.failNode(member.ast.value_expr, "value assigned to enum tag with inferred tag type", .{}); 5642 } 5643 values += 1; 5644 } 5645 } 5646 if (nonfinal_nonexhaustive) { 5647 return astgen.failNode(nonexhaustive_node, "'_' field of non-exhaustive enum must be last", .{}); 5648 } 5649 break :blk .{ 5650 .total_fields = total_fields, 5651 .values = values, 5652 .decls = decls, 5653 .nonexhaustive_node = nonexhaustive_node, 5654 }; 5655 }; 5656 if (counts.nonexhaustive_node != 0 and container_decl.ast.arg == 0) { 5657 return astgen.failNodeNotes( 5658 node, 5659 "non-exhaustive enum missing integer tag type", 5660 .{}, 5661 &[_]u32{ 5662 try astgen.errNoteNode( 5663 counts.nonexhaustive_node, 5664 "marked non-exhaustive here", 5665 .{}, 5666 ), 5667 }, 5668 ); 5669 } 5670 // In this case we must generate ZIR code for the tag values, similar to 5671 // how structs are handled above. 5672 const nonexhaustive = counts.nonexhaustive_node != 0; 5673 5674 const decl_inst = try gz.reserveInstructionIndex(); 5675 5676 var namespace: Scope.Namespace = .{ 5677 .parent = scope, 5678 .node = node, 5679 .inst = decl_inst, 5680 .declaring_gz = gz, 5681 .maybe_generic = astgen.within_fn, 5682 }; 5683 defer namespace.deinit(gpa); 5684 5685 // The enum_decl instruction introduces a scope in which the decls of the enum 5686 // are in scope, so that tag values can refer to decls within the enum itself. 5687 astgen.advanceSourceCursorToNode(node); 5688 var block_scope: GenZir = .{ 5689 .parent = &namespace.base, 5690 .decl_node_index = node, 5691 .decl_line = gz.decl_line, 5692 .astgen = astgen, 5693 .is_comptime = true, 5694 .instructions = gz.instructions, 5695 .instructions_top = gz.instructions.items.len, 5696 }; 5697 defer block_scope.unstack(); 5698 5699 _ = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"enum"); 5700 namespace.base.tag = .namespace; 5701 5702 const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0) 5703 try comptimeExpr(&block_scope, &namespace.base, coerced_type_ri, container_decl.ast.arg) 5704 else 5705 .none; 5706 5707 const bits_per_field = 1; 5708 const max_field_size = 2; 5709 var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(counts.decls), @intCast(counts.total_fields), bits_per_field, max_field_size); 5710 defer wip_members.deinit(); 5711 5712 const old_hasher = astgen.src_hasher; 5713 defer astgen.src_hasher = old_hasher; 5714 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 5715 if (container_decl.ast.arg != 0) { 5716 astgen.src_hasher.update(tree.getNodeSource(container_decl.ast.arg)); 5717 } 5718 astgen.src_hasher.update(&.{@intFromBool(nonexhaustive)}); 5719 5720 for (container_decl.ast.members) |member_node| { 5721 if (member_node == counts.nonexhaustive_node) 5722 continue; 5723 astgen.src_hasher.update(tree.getNodeSource(member_node)); 5724 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { 5725 .decl => continue, 5726 .field => |field| field, 5727 }; 5728 member.convertToNonTupleLike(astgen.tree.nodes); 5729 assert(member.comptime_token == null); 5730 assert(member.ast.type_expr == 0); 5731 assert(member.ast.align_expr == 0); 5732 5733 const field_name = try astgen.identAsString(member.ast.main_token); 5734 wip_members.appendToField(@intFromEnum(field_name)); 5735 5736 const have_value = member.ast.value_expr != 0; 5737 wip_members.nextField(bits_per_field, .{have_value}); 5738 5739 if (have_value) { 5740 if (arg_inst == .none) { 5741 return astgen.failNodeNotes( 5742 node, 5743 "explicitly valued enum missing integer tag type", 5744 .{}, 5745 &[_]u32{ 5746 try astgen.errNoteNode( 5747 member.ast.value_expr, 5748 "tag value specified here", 5749 .{}, 5750 ), 5751 }, 5752 ); 5753 } 5754 const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr); 5755 wip_members.appendToField(@intFromEnum(tag_value_inst)); 5756 } 5757 } 5758 5759 if (!block_scope.isEmpty()) { 5760 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); 5761 } 5762 5763 var fields_hash: std.zig.SrcHash = undefined; 5764 astgen.src_hasher.final(&fields_hash); 5765 5766 const body = block_scope.instructionsSlice(); 5767 const body_len = astgen.countBodyLenAfterFixups(body); 5768 5769 try gz.setEnum(decl_inst, .{ 5770 .src_node = node, 5771 .nonexhaustive = nonexhaustive, 5772 .tag_type = arg_inst, 5773 .captures_len = @intCast(namespace.captures.count()), 5774 .body_len = body_len, 5775 .fields_len = @intCast(counts.total_fields), 5776 .decls_len = @intCast(counts.decls), 5777 .fields_hash = fields_hash, 5778 }); 5779 5780 wip_members.finishBits(bits_per_field); 5781 const decls_slice = wip_members.declsSlice(); 5782 const fields_slice = wip_members.fieldsSlice(); 5783 try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len + body_len + fields_slice.len); 5784 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); 5785 astgen.extra.appendSliceAssumeCapacity(decls_slice); 5786 astgen.appendBodyWithFixups(body); 5787 astgen.extra.appendSliceAssumeCapacity(fields_slice); 5788 5789 block_scope.unstack(); 5790 return rvalue(gz, ri, decl_inst.toRef(), node); 5791 }, 5792 .keyword_opaque => { 5793 assert(container_decl.ast.arg == 0); 5794 5795 const decl_inst = try gz.reserveInstructionIndex(); 5796 5797 var namespace: Scope.Namespace = .{ 5798 .parent = scope, 5799 .node = node, 5800 .inst = decl_inst, 5801 .declaring_gz = gz, 5802 .maybe_generic = astgen.within_fn, 5803 }; 5804 defer namespace.deinit(gpa); 5805 5806 astgen.advanceSourceCursorToNode(node); 5807 var block_scope: GenZir = .{ 5808 .parent = &namespace.base, 5809 .decl_node_index = node, 5810 .decl_line = gz.decl_line, 5811 .astgen = astgen, 5812 .is_comptime = true, 5813 .instructions = gz.instructions, 5814 .instructions_top = gz.instructions.items.len, 5815 }; 5816 defer block_scope.unstack(); 5817 5818 const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"opaque"); 5819 5820 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0); 5821 defer wip_members.deinit(); 5822 5823 for (container_decl.ast.members) |member_node| { 5824 const res = try containerMember(&block_scope, &namespace.base, &wip_members, member_node); 5825 if (res == .field) { 5826 return astgen.failNode(member_node, "opaque types cannot have fields", .{}); 5827 } 5828 } 5829 5830 try gz.setOpaque(decl_inst, .{ 5831 .src_node = node, 5832 .captures_len = @intCast(namespace.captures.count()), 5833 .decls_len = decl_count, 5834 }); 5835 5836 wip_members.finishBits(0); 5837 const decls_slice = wip_members.declsSlice(); 5838 try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len); 5839 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); 5840 astgen.extra.appendSliceAssumeCapacity(decls_slice); 5841 5842 block_scope.unstack(); 5843 return rvalue(gz, ri, decl_inst.toRef(), node); 5844 }, 5845 else => unreachable, 5846 } 5847 } 5848 5849 const ContainerMemberResult = union(enum) { decl, field: Ast.full.ContainerField }; 5850 5851 fn containerMember( 5852 gz: *GenZir, 5853 scope: *Scope, 5854 wip_members: *WipMembers, 5855 member_node: Ast.Node.Index, 5856 ) InnerError!ContainerMemberResult { 5857 const astgen = gz.astgen; 5858 const tree = astgen.tree; 5859 const node_tags = tree.nodes.items(.tag); 5860 const node_datas = tree.nodes.items(.data); 5861 switch (node_tags[member_node]) { 5862 .container_field_init, 5863 .container_field_align, 5864 .container_field, 5865 => return ContainerMemberResult{ .field = tree.fullContainerField(member_node).? }, 5866 5867 .fn_proto, 5868 .fn_proto_multi, 5869 .fn_proto_one, 5870 .fn_proto_simple, 5871 .fn_decl, 5872 => { 5873 var buf: [1]Ast.Node.Index = undefined; 5874 const full = tree.fullFnProto(&buf, member_node).?; 5875 const body = if (node_tags[member_node] == .fn_decl) node_datas[member_node].rhs else 0; 5876 5877 const prev_decl_index = wip_members.decl_index; 5878 astgen.fnDecl(gz, scope, wip_members, member_node, body, full) catch |err| switch (err) { 5879 error.OutOfMemory => return error.OutOfMemory, 5880 error.AnalysisFail => { 5881 wip_members.decl_index = prev_decl_index; 5882 try addFailedDeclaration( 5883 wip_members, 5884 gz, 5885 .{ .named = full.name_token.? }, 5886 full.ast.proto_node, 5887 full.visib_token != null, 5888 ); 5889 }, 5890 }; 5891 }, 5892 5893 .global_var_decl, 5894 .local_var_decl, 5895 .simple_var_decl, 5896 .aligned_var_decl, 5897 => { 5898 const full = tree.fullVarDecl(member_node).?; 5899 const prev_decl_index = wip_members.decl_index; 5900 astgen.globalVarDecl(gz, scope, wip_members, member_node, full) catch |err| switch (err) { 5901 error.OutOfMemory => return error.OutOfMemory, 5902 error.AnalysisFail => { 5903 wip_members.decl_index = prev_decl_index; 5904 try addFailedDeclaration( 5905 wip_members, 5906 gz, 5907 .{ .named = full.ast.mut_token + 1 }, 5908 member_node, 5909 full.visib_token != null, 5910 ); 5911 }, 5912 }; 5913 }, 5914 5915 .@"comptime" => { 5916 const prev_decl_index = wip_members.decl_index; 5917 astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) { 5918 error.OutOfMemory => return error.OutOfMemory, 5919 error.AnalysisFail => { 5920 wip_members.decl_index = prev_decl_index; 5921 try addFailedDeclaration( 5922 wip_members, 5923 gz, 5924 .@"comptime", 5925 member_node, 5926 false, 5927 ); 5928 }, 5929 }; 5930 }, 5931 .@"usingnamespace" => { 5932 const prev_decl_index = wip_members.decl_index; 5933 astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) { 5934 error.OutOfMemory => return error.OutOfMemory, 5935 error.AnalysisFail => { 5936 wip_members.decl_index = prev_decl_index; 5937 try addFailedDeclaration( 5938 wip_members, 5939 gz, 5940 .@"usingnamespace", 5941 member_node, 5942 is_pub: { 5943 const main_tokens = tree.nodes.items(.main_token); 5944 const token_tags = tree.tokens.items(.tag); 5945 const main_token = main_tokens[member_node]; 5946 break :is_pub main_token > 0 and token_tags[main_token - 1] == .keyword_pub; 5947 }, 5948 ); 5949 }, 5950 }; 5951 }, 5952 .test_decl => { 5953 const prev_decl_index = wip_members.decl_index; 5954 // We need to have *some* decl here so that the decl count matches what's expected. 5955 // Since it doesn't strictly matter *what* this is, let's save ourselves the trouble 5956 // of duplicating the test name logic, and just assume this is an unnamed test. 5957 astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) { 5958 error.OutOfMemory => return error.OutOfMemory, 5959 error.AnalysisFail => { 5960 wip_members.decl_index = prev_decl_index; 5961 try addFailedDeclaration( 5962 wip_members, 5963 gz, 5964 .unnamed_test, 5965 member_node, 5966 false, 5967 ); 5968 }, 5969 }; 5970 }, 5971 else => unreachable, 5972 } 5973 return .decl; 5974 } 5975 5976 fn errorSetDecl(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 5977 const astgen = gz.astgen; 5978 const gpa = astgen.gpa; 5979 const tree = astgen.tree; 5980 const main_tokens = tree.nodes.items(.main_token); 5981 const token_tags = tree.tokens.items(.tag); 5982 5983 const payload_index = try reserveExtra(astgen, @typeInfo(Zir.Inst.ErrorSetDecl).@"struct".fields.len); 5984 var fields_len: usize = 0; 5985 { 5986 var idents: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.TokenIndex) = .empty; 5987 defer idents.deinit(gpa); 5988 5989 const error_token = main_tokens[node]; 5990 var tok_i = error_token + 2; 5991 while (true) : (tok_i += 1) { 5992 switch (token_tags[tok_i]) { 5993 .doc_comment, .comma => {}, 5994 .identifier => { 5995 const str_index = try astgen.identAsString(tok_i); 5996 const gop = try idents.getOrPut(gpa, str_index); 5997 if (gop.found_existing) { 5998 const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(str_index))); 5999 defer gpa.free(name); 6000 return astgen.failTokNotes( 6001 tok_i, 6002 "duplicate error set field '{s}'", 6003 .{name}, 6004 &[_]u32{ 6005 try astgen.errNoteTok( 6006 gop.value_ptr.*, 6007 "previous declaration here", 6008 .{}, 6009 ), 6010 }, 6011 ); 6012 } 6013 gop.value_ptr.* = tok_i; 6014 6015 try astgen.extra.append(gpa, @intFromEnum(str_index)); 6016 fields_len += 1; 6017 }, 6018 .r_brace => break, 6019 else => unreachable, 6020 } 6021 } 6022 } 6023 6024 setExtra(astgen, payload_index, Zir.Inst.ErrorSetDecl{ 6025 .fields_len = @intCast(fields_len), 6026 }); 6027 const result = try gz.addPlNodePayloadIndex(.error_set_decl, node, payload_index); 6028 return rvalue(gz, ri, result, node); 6029 } 6030 6031 fn tryExpr( 6032 parent_gz: *GenZir, 6033 scope: *Scope, 6034 ri: ResultInfo, 6035 node: Ast.Node.Index, 6036 operand_node: Ast.Node.Index, 6037 ) InnerError!Zir.Inst.Ref { 6038 const astgen = parent_gz.astgen; 6039 6040 const fn_block = astgen.fn_block orelse { 6041 return astgen.failNode(node, "'try' outside function scope", .{}); 6042 }; 6043 6044 if (parent_gz.any_defer_node != 0) { 6045 return astgen.failNodeNotes(node, "'try' not allowed inside defer expression", .{}, &.{ 6046 try astgen.errNoteNode( 6047 parent_gz.any_defer_node, 6048 "defer expression here", 6049 .{}, 6050 ), 6051 }); 6052 } 6053 6054 // Ensure debug line/column information is emitted for this try expression. 6055 // Then we will save the line/column so that we can emit another one that goes 6056 // "backwards" because we want to evaluate the operand, but then put the debug 6057 // info back at the try keyword for error return tracing. 6058 if (!parent_gz.is_comptime) { 6059 try emitDbgNode(parent_gz, node); 6060 } 6061 const try_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; 6062 6063 const operand_rl: ResultInfo.Loc, const block_tag: Zir.Inst.Tag = switch (ri.rl) { 6064 .ref => .{ .ref, .try_ptr }, 6065 .ref_coerced_ty => |payload_ptr_ty| .{ 6066 .{ .ref_coerced_ty = try parent_gz.addUnNode(.try_ref_operand_ty, payload_ptr_ty, node) }, 6067 .try_ptr, 6068 }, 6069 else => if (try ri.rl.resultType(parent_gz, node)) |payload_ty| .{ 6070 // `coerced_ty` is OK due to the `rvalue` call below 6071 .{ .coerced_ty = try parent_gz.addUnNode(.try_operand_ty, payload_ty, node) }, 6072 .@"try", 6073 } else .{ .none, .@"try" }, 6074 }; 6075 const operand_ri: ResultInfo = .{ .rl = operand_rl, .ctx = .error_handling_expr }; 6076 // This could be a pointer or value depending on the `ri` parameter. 6077 const operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, node); 6078 const try_inst = try parent_gz.makeBlockInst(block_tag, node); 6079 try parent_gz.instructions.append(astgen.gpa, try_inst); 6080 6081 var else_scope = parent_gz.makeSubBlock(scope); 6082 defer else_scope.unstack(); 6083 6084 const err_tag = switch (ri.rl) { 6085 .ref, .ref_coerced_ty => Zir.Inst.Tag.err_union_code_ptr, 6086 else => Zir.Inst.Tag.err_union_code, 6087 }; 6088 const err_code = try else_scope.addUnNode(err_tag, operand, node); 6089 try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code }); 6090 try emitDbgStmt(&else_scope, try_lc); 6091 _ = try else_scope.addUnNode(.ret_node, err_code, node); 6092 6093 try else_scope.setTryBody(try_inst, operand); 6094 const result = try_inst.toRef(); 6095 switch (ri.rl) { 6096 .ref, .ref_coerced_ty => return result, 6097 else => return rvalue(parent_gz, ri, result, node), 6098 } 6099 } 6100 6101 fn orelseCatchExpr( 6102 parent_gz: *GenZir, 6103 scope: *Scope, 6104 ri: ResultInfo, 6105 node: Ast.Node.Index, 6106 lhs: Ast.Node.Index, 6107 cond_op: Zir.Inst.Tag, 6108 unwrap_op: Zir.Inst.Tag, 6109 unwrap_code_op: Zir.Inst.Tag, 6110 rhs: Ast.Node.Index, 6111 payload_token: ?Ast.TokenIndex, 6112 ) InnerError!Zir.Inst.Ref { 6113 const astgen = parent_gz.astgen; 6114 const tree = astgen.tree; 6115 6116 const need_rl = astgen.nodes_need_rl.contains(node); 6117 const block_ri: ResultInfo = if (need_rl) ri else .{ 6118 .rl = switch (ri.rl) { 6119 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 6120 .inferred_ptr => .none, 6121 else => ri.rl, 6122 }, 6123 .ctx = ri.ctx, 6124 }; 6125 // We need to call `rvalue` to write through to the pointer only if we had a 6126 // result pointer and aren't forwarding it. 6127 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 6128 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 6129 6130 const do_err_trace = astgen.fn_block != null and (cond_op == .is_non_err or cond_op == .is_non_err_ptr); 6131 6132 var block_scope = parent_gz.makeSubBlock(scope); 6133 block_scope.setBreakResultInfo(block_ri); 6134 defer block_scope.unstack(); 6135 6136 const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) { 6137 .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none }, 6138 else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none }, 6139 }; 6140 // This could be a pointer or value depending on the `operand_ri` parameter. 6141 // We cannot use `block_scope.break_result_info` because that has the bare 6142 // type, whereas this expression has the optional type. Later we make 6143 // up for this fact by calling rvalue on the else branch. 6144 const operand = try reachableExpr(&block_scope, &block_scope.base, operand_ri, lhs, rhs); 6145 const cond = try block_scope.addUnNode(cond_op, operand, node); 6146 const condbr = try block_scope.addCondBr(.condbr, node); 6147 6148 const block = try parent_gz.makeBlockInst(.block, node); 6149 try block_scope.setBlockBody(block); 6150 // block_scope unstacked now, can add new instructions to parent_gz 6151 try parent_gz.instructions.append(astgen.gpa, block); 6152 6153 var then_scope = block_scope.makeSubBlock(scope); 6154 defer then_scope.unstack(); 6155 6156 // This could be a pointer or value depending on `unwrap_op`. 6157 const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node); 6158 const then_result = switch (ri.rl) { 6159 .ref, .ref_coerced_ty => unwrapped_payload, 6160 else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node), 6161 }; 6162 _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node); 6163 6164 var else_scope = block_scope.makeSubBlock(scope); 6165 defer else_scope.unstack(); 6166 6167 // We know that the operand (almost certainly) modified the error return trace, 6168 // so signal to Sema that it should save the new index for restoring later. 6169 if (do_err_trace and nodeMayAppendToErrorTrace(tree, lhs)) 6170 _ = try else_scope.addSaveErrRetIndex(.always); 6171 6172 var err_val_scope: Scope.LocalVal = undefined; 6173 const else_sub_scope = blk: { 6174 const payload = payload_token orelse break :blk &else_scope.base; 6175 const err_str = tree.tokenSlice(payload); 6176 if (mem.eql(u8, err_str, "_")) { 6177 try astgen.appendErrorTok(payload, "discard of error capture; omit it instead", .{}); 6178 break :blk &else_scope.base; 6179 } 6180 const err_name = try astgen.identAsString(payload); 6181 6182 try astgen.detectLocalShadowing(scope, err_name, payload, err_str, .capture); 6183 6184 err_val_scope = .{ 6185 .parent = &else_scope.base, 6186 .gen_zir = &else_scope, 6187 .name = err_name, 6188 .inst = try else_scope.addUnNode(unwrap_code_op, operand, node), 6189 .token_src = payload, 6190 .id_cat = .capture, 6191 }; 6192 break :blk &err_val_scope.base; 6193 }; 6194 6195 const else_result = try fullBodyExpr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs, .allow_branch_hint); 6196 if (!else_scope.endsWithNoReturn()) { 6197 // As our last action before the break, "pop" the error trace if needed 6198 if (do_err_trace) 6199 try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, rhs, else_result); 6200 6201 _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, rhs); 6202 } 6203 try checkUsed(parent_gz, &else_scope.base, else_sub_scope); 6204 6205 try setCondBrPayload(condbr, cond, &then_scope, &else_scope); 6206 6207 if (need_result_rvalue) { 6208 return rvalue(parent_gz, ri, block.toRef(), node); 6209 } else { 6210 return block.toRef(); 6211 } 6212 } 6213 6214 /// Return whether the identifier names of two tokens are equal. Resolves @"" 6215 /// tokens without allocating. 6216 /// OK in theory it could do it without allocating. This implementation 6217 /// allocates when the @"" form is used. 6218 fn tokenIdentEql(astgen: *AstGen, token1: Ast.TokenIndex, token2: Ast.TokenIndex) !bool { 6219 const ident_name_1 = try astgen.identifierTokenString(token1); 6220 const ident_name_2 = try astgen.identifierTokenString(token2); 6221 return mem.eql(u8, ident_name_1, ident_name_2); 6222 } 6223 6224 fn fieldAccess( 6225 gz: *GenZir, 6226 scope: *Scope, 6227 ri: ResultInfo, 6228 node: Ast.Node.Index, 6229 ) InnerError!Zir.Inst.Ref { 6230 switch (ri.rl) { 6231 .ref, .ref_coerced_ty => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node), 6232 else => { 6233 const access = try addFieldAccess(.field_val, gz, scope, .{ .rl = .none }, node); 6234 return rvalue(gz, ri, access, node); 6235 }, 6236 } 6237 } 6238 6239 fn addFieldAccess( 6240 tag: Zir.Inst.Tag, 6241 gz: *GenZir, 6242 scope: *Scope, 6243 lhs_ri: ResultInfo, 6244 node: Ast.Node.Index, 6245 ) InnerError!Zir.Inst.Ref { 6246 const astgen = gz.astgen; 6247 const tree = astgen.tree; 6248 const main_tokens = tree.nodes.items(.main_token); 6249 const node_datas = tree.nodes.items(.data); 6250 6251 const object_node = node_datas[node].lhs; 6252 const dot_token = main_tokens[node]; 6253 const field_ident = dot_token + 1; 6254 const str_index = try astgen.identAsString(field_ident); 6255 const lhs = try expr(gz, scope, lhs_ri, object_node); 6256 6257 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 6258 try emitDbgStmt(gz, cursor); 6259 6260 return gz.addPlNode(tag, node, Zir.Inst.Field{ 6261 .lhs = lhs, 6262 .field_name_start = str_index, 6263 }); 6264 } 6265 6266 fn arrayAccess( 6267 gz: *GenZir, 6268 scope: *Scope, 6269 ri: ResultInfo, 6270 node: Ast.Node.Index, 6271 ) InnerError!Zir.Inst.Ref { 6272 const tree = gz.astgen.tree; 6273 const node_datas = tree.nodes.items(.data); 6274 switch (ri.rl) { 6275 .ref, .ref_coerced_ty => { 6276 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); 6277 6278 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 6279 6280 const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs); 6281 try emitDbgStmt(gz, cursor); 6282 6283 return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); 6284 }, 6285 else => { 6286 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs); 6287 6288 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 6289 6290 const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs); 6291 try emitDbgStmt(gz, cursor); 6292 6293 return rvalue(gz, ri, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }), node); 6294 }, 6295 } 6296 } 6297 6298 fn simpleBinOp( 6299 gz: *GenZir, 6300 scope: *Scope, 6301 ri: ResultInfo, 6302 node: Ast.Node.Index, 6303 op_inst_tag: Zir.Inst.Tag, 6304 ) InnerError!Zir.Inst.Ref { 6305 const astgen = gz.astgen; 6306 const tree = astgen.tree; 6307 const node_datas = tree.nodes.items(.data); 6308 6309 if (op_inst_tag == .cmp_neq or op_inst_tag == .cmp_eq) { 6310 const node_tags = tree.nodes.items(.tag); 6311 const str = if (op_inst_tag == .cmp_eq) "==" else "!="; 6312 if (node_tags[node_datas[node].lhs] == .string_literal or 6313 node_tags[node_datas[node].rhs] == .string_literal) 6314 return astgen.failNode(node, "cannot compare strings with {s}", .{str}); 6315 } 6316 6317 const lhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].lhs, node); 6318 const cursor = switch (op_inst_tag) { 6319 .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, node), 6320 else => undefined, 6321 }; 6322 const rhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].rhs, node); 6323 6324 switch (op_inst_tag) { 6325 .add, .sub, .mul, .div, .mod_rem => { 6326 try emitDbgStmt(gz, cursor); 6327 }, 6328 else => {}, 6329 } 6330 const result = try gz.addPlNode(op_inst_tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); 6331 return rvalue(gz, ri, result, node); 6332 } 6333 6334 fn simpleStrTok( 6335 gz: *GenZir, 6336 ri: ResultInfo, 6337 ident_token: Ast.TokenIndex, 6338 node: Ast.Node.Index, 6339 op_inst_tag: Zir.Inst.Tag, 6340 ) InnerError!Zir.Inst.Ref { 6341 const astgen = gz.astgen; 6342 const str_index = try astgen.identAsString(ident_token); 6343 const result = try gz.addStrTok(op_inst_tag, str_index, ident_token); 6344 return rvalue(gz, ri, result, node); 6345 } 6346 6347 fn boolBinOp( 6348 gz: *GenZir, 6349 scope: *Scope, 6350 ri: ResultInfo, 6351 node: Ast.Node.Index, 6352 zir_tag: Zir.Inst.Tag, 6353 ) InnerError!Zir.Inst.Ref { 6354 const astgen = gz.astgen; 6355 const tree = astgen.tree; 6356 const node_datas = tree.nodes.items(.data); 6357 6358 const lhs = try expr(gz, scope, coerced_bool_ri, node_datas[node].lhs); 6359 const bool_br = (try gz.addPlNodePayloadIndex(zir_tag, node, undefined)).toIndex().?; 6360 6361 var rhs_scope = gz.makeSubBlock(scope); 6362 defer rhs_scope.unstack(); 6363 const rhs = try fullBodyExpr(&rhs_scope, &rhs_scope.base, coerced_bool_ri, node_datas[node].rhs, .allow_branch_hint); 6364 if (!gz.refIsNoReturn(rhs)) { 6365 _ = try rhs_scope.addBreakWithSrcNode(.break_inline, bool_br, rhs, node_datas[node].rhs); 6366 } 6367 try rhs_scope.setBoolBrBody(bool_br, lhs); 6368 6369 const block_ref = bool_br.toRef(); 6370 return rvalue(gz, ri, block_ref, node); 6371 } 6372 6373 fn ifExpr( 6374 parent_gz: *GenZir, 6375 scope: *Scope, 6376 ri: ResultInfo, 6377 node: Ast.Node.Index, 6378 if_full: Ast.full.If, 6379 ) InnerError!Zir.Inst.Ref { 6380 const astgen = parent_gz.astgen; 6381 const tree = astgen.tree; 6382 const token_tags = tree.tokens.items(.tag); 6383 6384 const do_err_trace = astgen.fn_block != null and if_full.error_token != null; 6385 6386 const need_rl = astgen.nodes_need_rl.contains(node); 6387 const block_ri: ResultInfo = if (need_rl) ri else .{ 6388 .rl = switch (ri.rl) { 6389 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 6390 .inferred_ptr => .none, 6391 else => ri.rl, 6392 }, 6393 .ctx = ri.ctx, 6394 }; 6395 // We need to call `rvalue` to write through to the pointer only if we had a 6396 // result pointer and aren't forwarding it. 6397 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 6398 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 6399 6400 var block_scope = parent_gz.makeSubBlock(scope); 6401 block_scope.setBreakResultInfo(block_ri); 6402 defer block_scope.unstack(); 6403 6404 const payload_is_ref = if (if_full.payload_token) |payload_token| 6405 token_tags[payload_token] == .asterisk 6406 else 6407 false; 6408 6409 try emitDbgNode(parent_gz, if_full.ast.cond_expr); 6410 const cond: struct { 6411 inst: Zir.Inst.Ref, 6412 bool_bit: Zir.Inst.Ref, 6413 } = c: { 6414 if (if_full.error_token) |_| { 6415 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none, .ctx = .error_handling_expr }; 6416 const err_union = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr); 6417 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err; 6418 break :c .{ 6419 .inst = err_union, 6420 .bool_bit = try block_scope.addUnNode(tag, err_union, if_full.ast.cond_expr), 6421 }; 6422 } else if (if_full.payload_token) |_| { 6423 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none }; 6424 const optional = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr); 6425 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null; 6426 break :c .{ 6427 .inst = optional, 6428 .bool_bit = try block_scope.addUnNode(tag, optional, if_full.ast.cond_expr), 6429 }; 6430 } else { 6431 const cond = try expr(&block_scope, &block_scope.base, coerced_bool_ri, if_full.ast.cond_expr); 6432 break :c .{ 6433 .inst = cond, 6434 .bool_bit = cond, 6435 }; 6436 } 6437 }; 6438 6439 const condbr = try block_scope.addCondBr(.condbr, node); 6440 6441 const block = try parent_gz.makeBlockInst(.block, node); 6442 try block_scope.setBlockBody(block); 6443 // block_scope unstacked now, can add new instructions to parent_gz 6444 try parent_gz.instructions.append(astgen.gpa, block); 6445 6446 var then_scope = parent_gz.makeSubBlock(scope); 6447 defer then_scope.unstack(); 6448 6449 var payload_val_scope: Scope.LocalVal = undefined; 6450 6451 const then_node = if_full.ast.then_expr; 6452 const then_sub_scope = s: { 6453 if (if_full.error_token != null) { 6454 if (if_full.payload_token) |payload_token| { 6455 const tag: Zir.Inst.Tag = if (payload_is_ref) 6456 .err_union_payload_unsafe_ptr 6457 else 6458 .err_union_payload_unsafe; 6459 const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node); 6460 const token_name_index = payload_token + @intFromBool(payload_is_ref); 6461 const ident_name = try astgen.identAsString(token_name_index); 6462 const token_name_str = tree.tokenSlice(token_name_index); 6463 if (mem.eql(u8, "_", token_name_str)) { 6464 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 6465 break :s &then_scope.base; 6466 } 6467 try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str, .capture); 6468 payload_val_scope = .{ 6469 .parent = &then_scope.base, 6470 .gen_zir = &then_scope, 6471 .name = ident_name, 6472 .inst = payload_inst, 6473 .token_src = token_name_index, 6474 .id_cat = .capture, 6475 }; 6476 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst); 6477 break :s &payload_val_scope.base; 6478 } else { 6479 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node); 6480 break :s &then_scope.base; 6481 } 6482 } else if (if_full.payload_token) |payload_token| { 6483 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token; 6484 const tag: Zir.Inst.Tag = if (payload_is_ref) 6485 .optional_payload_unsafe_ptr 6486 else 6487 .optional_payload_unsafe; 6488 const ident_bytes = tree.tokenSlice(ident_token); 6489 if (mem.eql(u8, "_", ident_bytes)) { 6490 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 6491 break :s &then_scope.base; 6492 } 6493 const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node); 6494 const ident_name = try astgen.identAsString(ident_token); 6495 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture); 6496 payload_val_scope = .{ 6497 .parent = &then_scope.base, 6498 .gen_zir = &then_scope, 6499 .name = ident_name, 6500 .inst = payload_inst, 6501 .token_src = ident_token, 6502 .id_cat = .capture, 6503 }; 6504 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst); 6505 break :s &payload_val_scope.base; 6506 } else { 6507 break :s &then_scope.base; 6508 } 6509 }; 6510 6511 const then_result = try fullBodyExpr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node, .allow_branch_hint); 6512 try checkUsed(parent_gz, &then_scope.base, then_sub_scope); 6513 if (!then_scope.endsWithNoReturn()) { 6514 _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, then_node); 6515 } 6516 6517 var else_scope = parent_gz.makeSubBlock(scope); 6518 defer else_scope.unstack(); 6519 6520 // We know that the operand (almost certainly) modified the error return trace, 6521 // so signal to Sema that it should save the new index for restoring later. 6522 if (do_err_trace and nodeMayAppendToErrorTrace(tree, if_full.ast.cond_expr)) 6523 _ = try else_scope.addSaveErrRetIndex(.always); 6524 6525 const else_node = if_full.ast.else_expr; 6526 if (else_node != 0) { 6527 const sub_scope = s: { 6528 if (if_full.error_token) |error_token| { 6529 const tag: Zir.Inst.Tag = if (payload_is_ref) 6530 .err_union_code_ptr 6531 else 6532 .err_union_code; 6533 const payload_inst = try else_scope.addUnNode(tag, cond.inst, if_full.ast.cond_expr); 6534 const ident_name = try astgen.identAsString(error_token); 6535 const error_token_str = tree.tokenSlice(error_token); 6536 if (mem.eql(u8, "_", error_token_str)) 6537 break :s &else_scope.base; 6538 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str, .capture); 6539 payload_val_scope = .{ 6540 .parent = &else_scope.base, 6541 .gen_zir = &else_scope, 6542 .name = ident_name, 6543 .inst = payload_inst, 6544 .token_src = error_token, 6545 .id_cat = .capture, 6546 }; 6547 try else_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst); 6548 break :s &payload_val_scope.base; 6549 } else { 6550 break :s &else_scope.base; 6551 } 6552 }; 6553 const else_result = try fullBodyExpr(&else_scope, sub_scope, block_scope.break_result_info, else_node, .allow_branch_hint); 6554 if (!else_scope.endsWithNoReturn()) { 6555 // As our last action before the break, "pop" the error trace if needed 6556 if (do_err_trace) 6557 try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, else_node, else_result); 6558 _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, else_node); 6559 } 6560 try checkUsed(parent_gz, &else_scope.base, sub_scope); 6561 } else { 6562 const result = try rvalue(&else_scope, ri, .void_value, node); 6563 _ = try else_scope.addBreak(.@"break", block, result); 6564 } 6565 6566 try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope); 6567 6568 if (need_result_rvalue) { 6569 return rvalue(parent_gz, ri, block.toRef(), node); 6570 } else { 6571 return block.toRef(); 6572 } 6573 } 6574 6575 /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`. 6576 fn setCondBrPayload( 6577 condbr: Zir.Inst.Index, 6578 cond: Zir.Inst.Ref, 6579 then_scope: *GenZir, 6580 else_scope: *GenZir, 6581 ) !void { 6582 defer then_scope.unstack(); 6583 defer else_scope.unstack(); 6584 const astgen = then_scope.astgen; 6585 const then_body = then_scope.instructionsSliceUpto(else_scope); 6586 const else_body = else_scope.instructionsSlice(); 6587 const then_body_len = astgen.countBodyLenAfterFixups(then_body); 6588 const else_body_len = astgen.countBodyLenAfterFixups(else_body); 6589 try astgen.extra.ensureUnusedCapacity( 6590 astgen.gpa, 6591 @typeInfo(Zir.Inst.CondBr).@"struct".fields.len + then_body_len + else_body_len, 6592 ); 6593 6594 const zir_datas = astgen.instructions.items(.data); 6595 zir_datas[@intFromEnum(condbr)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{ 6596 .condition = cond, 6597 .then_body_len = then_body_len, 6598 .else_body_len = else_body_len, 6599 }); 6600 astgen.appendBodyWithFixups(then_body); 6601 astgen.appendBodyWithFixups(else_body); 6602 } 6603 6604 fn whileExpr( 6605 parent_gz: *GenZir, 6606 scope: *Scope, 6607 ri: ResultInfo, 6608 node: Ast.Node.Index, 6609 while_full: Ast.full.While, 6610 is_statement: bool, 6611 ) InnerError!Zir.Inst.Ref { 6612 const astgen = parent_gz.astgen; 6613 const tree = astgen.tree; 6614 const token_tags = tree.tokens.items(.tag); 6615 const token_starts = tree.tokens.items(.start); 6616 6617 const need_rl = astgen.nodes_need_rl.contains(node); 6618 const block_ri: ResultInfo = if (need_rl) ri else .{ 6619 .rl = switch (ri.rl) { 6620 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 6621 .inferred_ptr => .none, 6622 else => ri.rl, 6623 }, 6624 .ctx = ri.ctx, 6625 }; 6626 // We need to call `rvalue` to write through to the pointer only if we had a 6627 // result pointer and aren't forwarding it. 6628 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 6629 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 6630 6631 if (while_full.label_token) |label_token| { 6632 try astgen.checkLabelRedefinition(scope, label_token); 6633 } 6634 6635 const is_inline = while_full.inline_token != null; 6636 if (parent_gz.is_comptime and is_inline) { 6637 try astgen.appendErrorTok(while_full.inline_token.?, "redundant inline keyword in comptime scope", .{}); 6638 } 6639 const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop; 6640 const loop_block = try parent_gz.makeBlockInst(loop_tag, node); 6641 try parent_gz.instructions.append(astgen.gpa, loop_block); 6642 6643 var loop_scope = parent_gz.makeSubBlock(scope); 6644 loop_scope.is_inline = is_inline; 6645 loop_scope.setBreakResultInfo(block_ri); 6646 defer loop_scope.unstack(); 6647 6648 var cond_scope = parent_gz.makeSubBlock(&loop_scope.base); 6649 defer cond_scope.unstack(); 6650 6651 const payload_is_ref = if (while_full.payload_token) |payload_token| 6652 token_tags[payload_token] == .asterisk 6653 else 6654 false; 6655 6656 try emitDbgNode(parent_gz, while_full.ast.cond_expr); 6657 const cond: struct { 6658 inst: Zir.Inst.Ref, 6659 bool_bit: Zir.Inst.Ref, 6660 } = c: { 6661 if (while_full.error_token) |_| { 6662 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none }; 6663 const err_union = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr, .normal); 6664 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err; 6665 break :c .{ 6666 .inst = err_union, 6667 .bool_bit = try cond_scope.addUnNode(tag, err_union, while_full.ast.cond_expr), 6668 }; 6669 } else if (while_full.payload_token) |_| { 6670 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none }; 6671 const optional = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr, .normal); 6672 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null; 6673 break :c .{ 6674 .inst = optional, 6675 .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.cond_expr), 6676 }; 6677 } else { 6678 const cond = try fullBodyExpr(&cond_scope, &cond_scope.base, coerced_bool_ri, while_full.ast.cond_expr, .normal); 6679 break :c .{ 6680 .inst = cond, 6681 .bool_bit = cond, 6682 }; 6683 } 6684 }; 6685 6686 const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; 6687 const condbr = try cond_scope.addCondBr(condbr_tag, node); 6688 const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block; 6689 const cond_block = try loop_scope.makeBlockInst(block_tag, node); 6690 try cond_scope.setBlockBody(cond_block); 6691 // cond_scope unstacked now, can add new instructions to loop_scope 6692 try loop_scope.instructions.append(astgen.gpa, cond_block); 6693 6694 // make scope now but don't stack on parent_gz until loop_scope 6695 // gets unstacked after cont_expr is emitted and added below 6696 var then_scope = parent_gz.makeSubBlock(&cond_scope.base); 6697 then_scope.instructions_top = GenZir.unstacked_top; 6698 defer then_scope.unstack(); 6699 6700 var dbg_var_name: Zir.NullTerminatedString = .empty; 6701 var dbg_var_inst: Zir.Inst.Ref = undefined; 6702 var opt_payload_inst: Zir.Inst.OptionalIndex = .none; 6703 var payload_val_scope: Scope.LocalVal = undefined; 6704 const then_sub_scope = s: { 6705 if (while_full.error_token != null) { 6706 if (while_full.payload_token) |payload_token| { 6707 const tag: Zir.Inst.Tag = if (payload_is_ref) 6708 .err_union_payload_unsafe_ptr 6709 else 6710 .err_union_payload_unsafe; 6711 // will add this instruction to then_scope.instructions below 6712 const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr); 6713 opt_payload_inst = payload_inst.toOptional(); 6714 const ident_token = payload_token + @intFromBool(payload_is_ref); 6715 const ident_bytes = tree.tokenSlice(ident_token); 6716 if (mem.eql(u8, "_", ident_bytes)) { 6717 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 6718 break :s &then_scope.base; 6719 } 6720 const ident_name = try astgen.identAsString(ident_token); 6721 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture); 6722 payload_val_scope = .{ 6723 .parent = &then_scope.base, 6724 .gen_zir = &then_scope, 6725 .name = ident_name, 6726 .inst = payload_inst.toRef(), 6727 .token_src = ident_token, 6728 .id_cat = .capture, 6729 }; 6730 dbg_var_name = ident_name; 6731 dbg_var_inst = payload_inst.toRef(); 6732 break :s &payload_val_scope.base; 6733 } else { 6734 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node); 6735 break :s &then_scope.base; 6736 } 6737 } else if (while_full.payload_token) |payload_token| { 6738 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token; 6739 const tag: Zir.Inst.Tag = if (payload_is_ref) 6740 .optional_payload_unsafe_ptr 6741 else 6742 .optional_payload_unsafe; 6743 // will add this instruction to then_scope.instructions below 6744 const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr); 6745 opt_payload_inst = payload_inst.toOptional(); 6746 const ident_name = try astgen.identAsString(ident_token); 6747 const ident_bytes = tree.tokenSlice(ident_token); 6748 if (mem.eql(u8, "_", ident_bytes)) { 6749 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 6750 break :s &then_scope.base; 6751 } 6752 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture); 6753 payload_val_scope = .{ 6754 .parent = &then_scope.base, 6755 .gen_zir = &then_scope, 6756 .name = ident_name, 6757 .inst = payload_inst.toRef(), 6758 .token_src = ident_token, 6759 .id_cat = .capture, 6760 }; 6761 dbg_var_name = ident_name; 6762 dbg_var_inst = payload_inst.toRef(); 6763 break :s &payload_val_scope.base; 6764 } else { 6765 break :s &then_scope.base; 6766 } 6767 }; 6768 6769 var continue_scope = parent_gz.makeSubBlock(then_sub_scope); 6770 continue_scope.instructions_top = GenZir.unstacked_top; 6771 defer continue_scope.unstack(); 6772 const continue_block = try then_scope.makeBlockInst(block_tag, node); 6773 6774 const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; 6775 _ = try loop_scope.addNode(repeat_tag, node); 6776 6777 try loop_scope.setBlockBody(loop_block); 6778 loop_scope.break_block = loop_block.toOptional(); 6779 loop_scope.continue_block = continue_block.toOptional(); 6780 if (while_full.label_token) |label_token| { 6781 loop_scope.label = .{ 6782 .token = label_token, 6783 .block_inst = loop_block, 6784 }; 6785 } 6786 6787 // done adding instructions to loop_scope, can now stack then_scope 6788 then_scope.instructions_top = then_scope.instructions.items.len; 6789 6790 const then_node = while_full.ast.then_expr; 6791 if (opt_payload_inst.unwrap()) |payload_inst| { 6792 try then_scope.instructions.append(astgen.gpa, payload_inst); 6793 } 6794 if (dbg_var_name != .empty) try then_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst); 6795 try then_scope.instructions.append(astgen.gpa, continue_block); 6796 // This code could be improved to avoid emitting the continue expr when there 6797 // are no jumps to it. This happens when the last statement of a while body is noreturn 6798 // and there are no `continue` statements. 6799 // Tracking issue: https://github.com/ziglang/zig/issues/9185 6800 if (while_full.ast.cont_expr != 0) { 6801 _ = try unusedResultExpr(&then_scope, then_sub_scope, while_full.ast.cont_expr); 6802 } 6803 6804 continue_scope.instructions_top = continue_scope.instructions.items.len; 6805 { 6806 try emitDbgNode(&continue_scope, then_node); 6807 const unused_result = try fullBodyExpr(&continue_scope, &continue_scope.base, .{ .rl = .none }, then_node, .allow_branch_hint); 6808 _ = try addEnsureResult(&continue_scope, unused_result, then_node); 6809 } 6810 try checkUsed(parent_gz, &then_scope.base, then_sub_scope); 6811 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; 6812 if (!continue_scope.endsWithNoReturn()) { 6813 astgen.advanceSourceCursor(token_starts[tree.lastToken(then_node)]); 6814 try emitDbgStmt(parent_gz, .{ astgen.source_line - parent_gz.decl_line, astgen.source_column }); 6815 _ = try parent_gz.add(.{ 6816 .tag = .extended, 6817 .data = .{ .extended = .{ 6818 .opcode = .dbg_empty_stmt, 6819 .small = undefined, 6820 .operand = undefined, 6821 } }, 6822 }); 6823 _ = try continue_scope.addBreak(break_tag, continue_block, .void_value); 6824 } 6825 try continue_scope.setBlockBody(continue_block); 6826 _ = try then_scope.addBreak(break_tag, cond_block, .void_value); 6827 6828 var else_scope = parent_gz.makeSubBlock(&cond_scope.base); 6829 defer else_scope.unstack(); 6830 6831 const else_node = while_full.ast.else_expr; 6832 if (else_node != 0) { 6833 const sub_scope = s: { 6834 if (while_full.error_token) |error_token| { 6835 const tag: Zir.Inst.Tag = if (payload_is_ref) 6836 .err_union_code_ptr 6837 else 6838 .err_union_code; 6839 const else_payload_inst = try else_scope.addUnNode(tag, cond.inst, while_full.ast.cond_expr); 6840 const ident_name = try astgen.identAsString(error_token); 6841 const ident_bytes = tree.tokenSlice(error_token); 6842 if (mem.eql(u8, ident_bytes, "_")) 6843 break :s &else_scope.base; 6844 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, ident_bytes, .capture); 6845 payload_val_scope = .{ 6846 .parent = &else_scope.base, 6847 .gen_zir = &else_scope, 6848 .name = ident_name, 6849 .inst = else_payload_inst, 6850 .token_src = error_token, 6851 .id_cat = .capture, 6852 }; 6853 try else_scope.addDbgVar(.dbg_var_val, ident_name, else_payload_inst); 6854 break :s &payload_val_scope.base; 6855 } else { 6856 break :s &else_scope.base; 6857 } 6858 }; 6859 // Remove the continue block and break block so that `continue` and `break` 6860 // control flow apply to outer loops; not this one. 6861 loop_scope.continue_block = .none; 6862 loop_scope.break_block = .none; 6863 const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint); 6864 if (is_statement) { 6865 _ = try addEnsureResult(&else_scope, else_result, else_node); 6866 } 6867 6868 try checkUsed(parent_gz, &else_scope.base, sub_scope); 6869 if (!else_scope.endsWithNoReturn()) { 6870 _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node); 6871 } 6872 } else { 6873 const result = try rvalue(&else_scope, ri, .void_value, node); 6874 _ = try else_scope.addBreak(break_tag, loop_block, result); 6875 } 6876 6877 if (loop_scope.label) |some| { 6878 if (!some.used) { 6879 try astgen.appendErrorTok(some.token, "unused while loop label", .{}); 6880 } 6881 } 6882 6883 try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope); 6884 6885 const result = if (need_result_rvalue) 6886 try rvalue(parent_gz, ri, loop_block.toRef(), node) 6887 else 6888 loop_block.toRef(); 6889 6890 if (is_statement) { 6891 _ = try parent_gz.addUnNode(.ensure_result_used, result, node); 6892 } 6893 6894 return result; 6895 } 6896 6897 fn forExpr( 6898 parent_gz: *GenZir, 6899 scope: *Scope, 6900 ri: ResultInfo, 6901 node: Ast.Node.Index, 6902 for_full: Ast.full.For, 6903 is_statement: bool, 6904 ) InnerError!Zir.Inst.Ref { 6905 const astgen = parent_gz.astgen; 6906 6907 if (for_full.label_token) |label_token| { 6908 try astgen.checkLabelRedefinition(scope, label_token); 6909 } 6910 6911 const need_rl = astgen.nodes_need_rl.contains(node); 6912 const block_ri: ResultInfo = if (need_rl) ri else .{ 6913 .rl = switch (ri.rl) { 6914 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 6915 .inferred_ptr => .none, 6916 else => ri.rl, 6917 }, 6918 .ctx = ri.ctx, 6919 }; 6920 // We need to call `rvalue` to write through to the pointer only if we had a 6921 // result pointer and aren't forwarding it. 6922 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 6923 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 6924 6925 const is_inline = for_full.inline_token != null; 6926 if (parent_gz.is_comptime and is_inline) { 6927 try astgen.appendErrorTok(for_full.inline_token.?, "redundant inline keyword in comptime scope", .{}); 6928 } 6929 const tree = astgen.tree; 6930 const token_tags = tree.tokens.items(.tag); 6931 const token_starts = tree.tokens.items(.start); 6932 const node_tags = tree.nodes.items(.tag); 6933 const node_data = tree.nodes.items(.data); 6934 const gpa = astgen.gpa; 6935 6936 // For counters, this is the start value; for indexables, this is the base 6937 // pointer that can be used with elem_ptr and similar instructions. 6938 // Special value `none` means that this is a counter and its start value is 6939 // zero, indicating that the main index counter can be used directly. 6940 const indexables = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len); 6941 defer gpa.free(indexables); 6942 // elements of this array can be `none`, indicating no length check. 6943 const lens = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len); 6944 defer gpa.free(lens); 6945 6946 // We will use a single zero-based counter no matter how many indexables there are. 6947 const index_ptr = blk: { 6948 const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc; 6949 const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node); 6950 // initialize to zero 6951 _ = try parent_gz.addPlNode(.store_node, node, Zir.Inst.Bin{ 6952 .lhs = index_ptr, 6953 .rhs = .zero_usize, 6954 }); 6955 break :blk index_ptr; 6956 }; 6957 6958 var any_len_checks = false; 6959 6960 { 6961 var capture_token = for_full.payload_token; 6962 for (for_full.ast.inputs, indexables, lens) |input, *indexable_ref, *len_ref| { 6963 const capture_is_ref = token_tags[capture_token] == .asterisk; 6964 const ident_tok = capture_token + @intFromBool(capture_is_ref); 6965 const is_discard = mem.eql(u8, tree.tokenSlice(ident_tok), "_"); 6966 6967 if (is_discard and capture_is_ref) { 6968 return astgen.failTok(capture_token, "pointer modifier invalid on discard", .{}); 6969 } 6970 // Skip over the comma, and on to the next capture (or the ending pipe character). 6971 capture_token = ident_tok + 2; 6972 6973 try emitDbgNode(parent_gz, input); 6974 if (node_tags[input] == .for_range) { 6975 if (capture_is_ref) { 6976 return astgen.failTok(ident_tok, "cannot capture reference to range", .{}); 6977 } 6978 const start_node = node_data[input].lhs; 6979 const start_val = try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, start_node); 6980 6981 const end_node = node_data[input].rhs; 6982 const end_val = if (end_node != 0) 6983 try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_data[input].rhs) 6984 else 6985 .none; 6986 6987 if (end_val == .none and is_discard) { 6988 try astgen.appendErrorTok(ident_tok, "discard of unbounded counter", .{}); 6989 } 6990 6991 const start_is_zero = nodeIsTriviallyZero(tree, start_node); 6992 const range_len = if (end_val == .none or start_is_zero) 6993 end_val 6994 else 6995 try parent_gz.addPlNode(.sub, input, Zir.Inst.Bin{ 6996 .lhs = end_val, 6997 .rhs = start_val, 6998 }); 6999 7000 any_len_checks = any_len_checks or range_len != .none; 7001 indexable_ref.* = if (start_is_zero) .none else start_val; 7002 len_ref.* = range_len; 7003 } else { 7004 const indexable = try expr(parent_gz, scope, .{ .rl = .none }, input); 7005 7006 any_len_checks = true; 7007 indexable_ref.* = indexable; 7008 len_ref.* = indexable; 7009 } 7010 } 7011 } 7012 7013 if (!any_len_checks) { 7014 return astgen.failNode(node, "unbounded for loop", .{}); 7015 } 7016 7017 // We use a dedicated ZIR instruction to assert the lengths to assist with 7018 // nicer error reporting as well as fewer ZIR bytes emitted. 7019 const len: Zir.Inst.Ref = len: { 7020 const lens_len: u32 = @intCast(lens.len); 7021 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).@"struct".fields.len + lens_len); 7022 const len = try parent_gz.addPlNode(.for_len, node, Zir.Inst.MultiOp{ 7023 .operands_len = lens_len, 7024 }); 7025 appendRefsAssumeCapacity(astgen, lens); 7026 break :len len; 7027 }; 7028 7029 const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop; 7030 const loop_block = try parent_gz.makeBlockInst(loop_tag, node); 7031 try parent_gz.instructions.append(gpa, loop_block); 7032 7033 var loop_scope = parent_gz.makeSubBlock(scope); 7034 loop_scope.is_inline = is_inline; 7035 loop_scope.setBreakResultInfo(block_ri); 7036 defer loop_scope.unstack(); 7037 7038 // We need to finish loop_scope later once we have the deferred refs from then_scope. However, the 7039 // load must be removed from instructions in the meantime or it appears to be part of parent_gz. 7040 const index = try loop_scope.addUnNode(.load, index_ptr, node); 7041 _ = loop_scope.instructions.pop(); 7042 7043 var cond_scope = parent_gz.makeSubBlock(&loop_scope.base); 7044 defer cond_scope.unstack(); 7045 7046 // Check the condition. 7047 const cond = try cond_scope.addPlNode(.cmp_lt, node, Zir.Inst.Bin{ 7048 .lhs = index, 7049 .rhs = len, 7050 }); 7051 7052 const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; 7053 const condbr = try cond_scope.addCondBr(condbr_tag, node); 7054 const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block; 7055 const cond_block = try loop_scope.makeBlockInst(block_tag, node); 7056 try cond_scope.setBlockBody(cond_block); 7057 7058 loop_scope.break_block = loop_block.toOptional(); 7059 loop_scope.continue_block = cond_block.toOptional(); 7060 if (for_full.label_token) |label_token| { 7061 loop_scope.label = .{ 7062 .token = label_token, 7063 .block_inst = loop_block, 7064 }; 7065 } 7066 7067 const then_node = for_full.ast.then_expr; 7068 var then_scope = parent_gz.makeSubBlock(&cond_scope.base); 7069 defer then_scope.unstack(); 7070 7071 const capture_scopes = try gpa.alloc(Scope.LocalVal, for_full.ast.inputs.len); 7072 defer gpa.free(capture_scopes); 7073 7074 const then_sub_scope = blk: { 7075 var capture_token = for_full.payload_token; 7076 var capture_sub_scope: *Scope = &then_scope.base; 7077 for (for_full.ast.inputs, indexables, capture_scopes) |input, indexable_ref, *capture_scope| { 7078 const capture_is_ref = token_tags[capture_token] == .asterisk; 7079 const ident_tok = capture_token + @intFromBool(capture_is_ref); 7080 const capture_name = tree.tokenSlice(ident_tok); 7081 // Skip over the comma, and on to the next capture (or the ending pipe character). 7082 capture_token = ident_tok + 2; 7083 7084 if (mem.eql(u8, capture_name, "_")) continue; 7085 7086 const name_str_index = try astgen.identAsString(ident_tok); 7087 try astgen.detectLocalShadowing(capture_sub_scope, name_str_index, ident_tok, capture_name, .capture); 7088 7089 const capture_inst = inst: { 7090 const is_counter = node_tags[input] == .for_range; 7091 7092 if (indexable_ref == .none) { 7093 // Special case: the main index can be used directly. 7094 assert(is_counter); 7095 assert(!capture_is_ref); 7096 break :inst index; 7097 } 7098 7099 // For counters, we add the index variable to the start value; for 7100 // indexables, we use it as an element index. This is so similar 7101 // that they can share the same code paths, branching only on the 7102 // ZIR tag. 7103 const switch_cond = (@as(u2, @intFromBool(capture_is_ref)) << 1) | @intFromBool(is_counter); 7104 const tag: Zir.Inst.Tag = switch (switch_cond) { 7105 0b00 => .elem_val, 7106 0b01 => .add, 7107 0b10 => .elem_ptr, 7108 0b11 => unreachable, // compile error emitted already 7109 }; 7110 break :inst try then_scope.addPlNode(tag, input, Zir.Inst.Bin{ 7111 .lhs = indexable_ref, 7112 .rhs = index, 7113 }); 7114 }; 7115 7116 capture_scope.* = .{ 7117 .parent = capture_sub_scope, 7118 .gen_zir = &then_scope, 7119 .name = name_str_index, 7120 .inst = capture_inst, 7121 .token_src = ident_tok, 7122 .id_cat = .capture, 7123 }; 7124 7125 try then_scope.addDbgVar(.dbg_var_val, name_str_index, capture_inst); 7126 capture_sub_scope = &capture_scope.base; 7127 } 7128 7129 break :blk capture_sub_scope; 7130 }; 7131 7132 const then_result = try fullBodyExpr(&then_scope, then_sub_scope, .{ .rl = .none }, then_node, .allow_branch_hint); 7133 _ = try addEnsureResult(&then_scope, then_result, then_node); 7134 7135 try checkUsed(parent_gz, &then_scope.base, then_sub_scope); 7136 7137 astgen.advanceSourceCursor(token_starts[tree.lastToken(then_node)]); 7138 try emitDbgStmt(parent_gz, .{ astgen.source_line - parent_gz.decl_line, astgen.source_column }); 7139 _ = try parent_gz.add(.{ 7140 .tag = .extended, 7141 .data = .{ .extended = .{ 7142 .opcode = .dbg_empty_stmt, 7143 .small = undefined, 7144 .operand = undefined, 7145 } }, 7146 }); 7147 7148 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; 7149 _ = try then_scope.addBreak(break_tag, cond_block, .void_value); 7150 7151 var else_scope = parent_gz.makeSubBlock(&cond_scope.base); 7152 defer else_scope.unstack(); 7153 7154 const else_node = for_full.ast.else_expr; 7155 if (else_node != 0) { 7156 const sub_scope = &else_scope.base; 7157 // Remove the continue block and break block so that `continue` and `break` 7158 // control flow apply to outer loops; not this one. 7159 loop_scope.continue_block = .none; 7160 loop_scope.break_block = .none; 7161 const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint); 7162 if (is_statement) { 7163 _ = try addEnsureResult(&else_scope, else_result, else_node); 7164 } 7165 if (!else_scope.endsWithNoReturn()) { 7166 _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node); 7167 } 7168 } else { 7169 const result = try rvalue(&else_scope, ri, .void_value, node); 7170 _ = try else_scope.addBreak(break_tag, loop_block, result); 7171 } 7172 7173 if (loop_scope.label) |some| { 7174 if (!some.used) { 7175 try astgen.appendErrorTok(some.token, "unused for loop label", .{}); 7176 } 7177 } 7178 7179 try setCondBrPayload(condbr, cond, &then_scope, &else_scope); 7180 7181 // then_block and else_block unstacked now, can resurrect loop_scope to finally finish it 7182 { 7183 loop_scope.instructions_top = loop_scope.instructions.items.len; 7184 try loop_scope.instructions.appendSlice(gpa, &.{ index.toIndex().?, cond_block }); 7185 7186 // Increment the index variable. 7187 const index_plus_one = try loop_scope.addPlNode(.add_unsafe, node, Zir.Inst.Bin{ 7188 .lhs = index, 7189 .rhs = .one_usize, 7190 }); 7191 _ = try loop_scope.addPlNode(.store_node, node, Zir.Inst.Bin{ 7192 .lhs = index_ptr, 7193 .rhs = index_plus_one, 7194 }); 7195 7196 const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; 7197 _ = try loop_scope.addNode(repeat_tag, node); 7198 7199 try loop_scope.setBlockBody(loop_block); 7200 } 7201 7202 const result = if (need_result_rvalue) 7203 try rvalue(parent_gz, ri, loop_block.toRef(), node) 7204 else 7205 loop_block.toRef(); 7206 7207 if (is_statement) { 7208 _ = try parent_gz.addUnNode(.ensure_result_used, result, node); 7209 } 7210 return result; 7211 } 7212 7213 fn switchExprErrUnion( 7214 parent_gz: *GenZir, 7215 scope: *Scope, 7216 ri: ResultInfo, 7217 catch_or_if_node: Ast.Node.Index, 7218 node_ty: enum { @"catch", @"if" }, 7219 ) InnerError!Zir.Inst.Ref { 7220 const astgen = parent_gz.astgen; 7221 const gpa = astgen.gpa; 7222 const tree = astgen.tree; 7223 const node_datas = tree.nodes.items(.data); 7224 const node_tags = tree.nodes.items(.tag); 7225 const main_tokens = tree.nodes.items(.main_token); 7226 const token_tags = tree.tokens.items(.tag); 7227 7228 const if_full = switch (node_ty) { 7229 .@"catch" => undefined, 7230 .@"if" => tree.fullIf(catch_or_if_node).?, 7231 }; 7232 7233 const switch_node, const operand_node, const error_payload = switch (node_ty) { 7234 .@"catch" => .{ 7235 node_datas[catch_or_if_node].rhs, 7236 node_datas[catch_or_if_node].lhs, 7237 main_tokens[catch_or_if_node] + 2, 7238 }, 7239 .@"if" => .{ 7240 if_full.ast.else_expr, 7241 if_full.ast.cond_expr, 7242 if_full.error_token.?, 7243 }, 7244 }; 7245 assert(node_tags[switch_node] == .@"switch" or node_tags[switch_node] == .switch_comma); 7246 7247 const do_err_trace = astgen.fn_block != null; 7248 7249 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); 7250 const case_nodes = tree.extra_data[extra.start..extra.end]; 7251 7252 const need_rl = astgen.nodes_need_rl.contains(catch_or_if_node); 7253 const block_ri: ResultInfo = if (need_rl) ri else .{ 7254 .rl = switch (ri.rl) { 7255 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, catch_or_if_node)).? }, 7256 .inferred_ptr => .none, 7257 else => ri.rl, 7258 }, 7259 .ctx = ri.ctx, 7260 }; 7261 7262 const payload_is_ref = switch (node_ty) { 7263 .@"if" => if_full.payload_token != null and token_tags[if_full.payload_token.?] == .asterisk, 7264 .@"catch" => ri.rl == .ref or ri.rl == .ref_coerced_ty, 7265 }; 7266 7267 // We need to call `rvalue` to write through to the pointer only if we had a 7268 // result pointer and aren't forwarding it. 7269 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 7270 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 7271 var scalar_cases_len: u32 = 0; 7272 var multi_cases_len: u32 = 0; 7273 var inline_cases_len: u32 = 0; 7274 var has_else = false; 7275 var else_node: Ast.Node.Index = 0; 7276 var else_src: ?Ast.TokenIndex = null; 7277 for (case_nodes) |case_node| { 7278 const case = tree.fullSwitchCase(case_node).?; 7279 7280 if (case.ast.values.len == 0) { 7281 const case_src = case.ast.arrow_token - 1; 7282 if (else_src) |src| { 7283 return astgen.failTokNotes( 7284 case_src, 7285 "multiple else prongs in switch expression", 7286 .{}, 7287 &[_]u32{ 7288 try astgen.errNoteTok( 7289 src, 7290 "previous else prong here", 7291 .{}, 7292 ), 7293 }, 7294 ); 7295 } 7296 has_else = true; 7297 else_node = case_node; 7298 else_src = case_src; 7299 continue; 7300 } else if (case.ast.values.len == 1 and 7301 node_tags[case.ast.values[0]] == .identifier and 7302 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")) 7303 { 7304 const case_src = case.ast.arrow_token - 1; 7305 return astgen.failTokNotes( 7306 case_src, 7307 "'_' prong is not allowed when switching on errors", 7308 .{}, 7309 &[_]u32{ 7310 try astgen.errNoteTok( 7311 case_src, 7312 "consider using 'else'", 7313 .{}, 7314 ), 7315 }, 7316 ); 7317 } 7318 7319 for (case.ast.values) |val| { 7320 if (node_tags[val] == .string_literal) 7321 return astgen.failNode(val, "cannot switch on strings", .{}); 7322 } 7323 7324 if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) { 7325 scalar_cases_len += 1; 7326 } else { 7327 multi_cases_len += 1; 7328 } 7329 if (case.inline_token != null) { 7330 inline_cases_len += 1; 7331 } 7332 } 7333 7334 const operand_ri: ResultInfo = .{ 7335 .rl = if (payload_is_ref) .ref else .none, 7336 .ctx = .error_handling_expr, 7337 }; 7338 7339 astgen.advanceSourceCursorToNode(operand_node); 7340 const operand_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; 7341 7342 const raw_operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, switch_node); 7343 const item_ri: ResultInfo = .{ .rl = .none }; 7344 7345 // This contains the data that goes into the `extra` array for the SwitchBlockErrUnion, except 7346 // the first cases_nodes.len slots are a table that indexes payloads later in the array, 7347 // with the non-error and else case indices coming first, then scalar_cases_len indexes, then 7348 // multi_cases_len indexes 7349 const payloads = &astgen.scratch; 7350 const scratch_top = astgen.scratch.items.len; 7351 const case_table_start = scratch_top; 7352 const scalar_case_table = case_table_start + 1 + @intFromBool(has_else); 7353 const multi_case_table = scalar_case_table + scalar_cases_len; 7354 const case_table_end = multi_case_table + multi_cases_len; 7355 7356 try astgen.scratch.resize(gpa, case_table_end); 7357 defer astgen.scratch.items.len = scratch_top; 7358 7359 var block_scope = parent_gz.makeSubBlock(scope); 7360 // block_scope not used for collecting instructions 7361 block_scope.instructions_top = GenZir.unstacked_top; 7362 block_scope.setBreakResultInfo(block_ri); 7363 7364 // Sema expects a dbg_stmt immediately before switch_block_err_union 7365 try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc); 7366 // This gets added to the parent block later, after the item expressions. 7367 const switch_block = try parent_gz.makeBlockInst(.switch_block_err_union, switch_node); 7368 7369 // We re-use this same scope for all cases, including the special prong, if any. 7370 var case_scope = parent_gz.makeSubBlock(&block_scope.base); 7371 case_scope.instructions_top = GenZir.unstacked_top; 7372 7373 { 7374 const body_len_index: u32 = @intCast(payloads.items.len); 7375 payloads.items[case_table_start] = body_len_index; 7376 try payloads.resize(gpa, body_len_index + 1); // body_len 7377 7378 case_scope.instructions_top = parent_gz.instructions.items.len; 7379 defer case_scope.unstack(); 7380 7381 const unwrap_payload_tag: Zir.Inst.Tag = if (payload_is_ref) 7382 .err_union_payload_unsafe_ptr 7383 else 7384 .err_union_payload_unsafe; 7385 7386 const unwrapped_payload = try case_scope.addUnNode( 7387 unwrap_payload_tag, 7388 raw_operand, 7389 catch_or_if_node, 7390 ); 7391 7392 switch (node_ty) { 7393 .@"catch" => { 7394 const case_result = switch (ri.rl) { 7395 .ref, .ref_coerced_ty => unwrapped_payload, 7396 else => try rvalue( 7397 &case_scope, 7398 block_scope.break_result_info, 7399 unwrapped_payload, 7400 catch_or_if_node, 7401 ), 7402 }; 7403 _ = try case_scope.addBreakWithSrcNode( 7404 .@"break", 7405 switch_block, 7406 case_result, 7407 catch_or_if_node, 7408 ); 7409 }, 7410 .@"if" => { 7411 var payload_val_scope: Scope.LocalVal = undefined; 7412 7413 const then_node = if_full.ast.then_expr; 7414 const then_sub_scope = s: { 7415 assert(if_full.error_token != null); 7416 if (if_full.payload_token) |payload_token| { 7417 const token_name_index = payload_token + @intFromBool(payload_is_ref); 7418 const ident_name = try astgen.identAsString(token_name_index); 7419 const token_name_str = tree.tokenSlice(token_name_index); 7420 if (mem.eql(u8, "_", token_name_str)) 7421 break :s &case_scope.base; 7422 try astgen.detectLocalShadowing( 7423 &case_scope.base, 7424 ident_name, 7425 token_name_index, 7426 token_name_str, 7427 .capture, 7428 ); 7429 payload_val_scope = .{ 7430 .parent = &case_scope.base, 7431 .gen_zir = &case_scope, 7432 .name = ident_name, 7433 .inst = unwrapped_payload, 7434 .token_src = token_name_index, 7435 .id_cat = .capture, 7436 }; 7437 try case_scope.addDbgVar(.dbg_var_val, ident_name, unwrapped_payload); 7438 break :s &payload_val_scope.base; 7439 } else { 7440 _ = try case_scope.addUnNode( 7441 .ensure_err_union_payload_void, 7442 raw_operand, 7443 catch_or_if_node, 7444 ); 7445 break :s &case_scope.base; 7446 } 7447 }; 7448 const then_result = try expr( 7449 &case_scope, 7450 then_sub_scope, 7451 block_scope.break_result_info, 7452 then_node, 7453 ); 7454 try checkUsed(parent_gz, &case_scope.base, then_sub_scope); 7455 if (!case_scope.endsWithNoReturn()) { 7456 _ = try case_scope.addBreakWithSrcNode( 7457 .@"break", 7458 switch_block, 7459 then_result, 7460 then_node, 7461 ); 7462 } 7463 }, 7464 } 7465 7466 const case_slice = case_scope.instructionsSlice(); 7467 const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, &.{switch_block}); 7468 try payloads.ensureUnusedCapacity(gpa, body_len); 7469 const capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = switch (node_ty) { 7470 .@"catch" => .none, 7471 .@"if" => if (if_full.payload_token == null) 7472 .none 7473 else if (payload_is_ref) 7474 .by_ref 7475 else 7476 .by_val, 7477 }; 7478 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{ 7479 .body_len = @intCast(body_len), 7480 .capture = capture, 7481 .is_inline = false, 7482 .has_tag_capture = false, 7483 }); 7484 appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, &.{switch_block}); 7485 } 7486 7487 const err_name = blk: { 7488 const err_str = tree.tokenSlice(error_payload); 7489 if (mem.eql(u8, err_str, "_")) { 7490 // This is fatal because we already know we're switching on the captured error. 7491 return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{}); 7492 } 7493 const err_name = try astgen.identAsString(error_payload); 7494 try astgen.detectLocalShadowing(scope, err_name, error_payload, err_str, .capture); 7495 7496 break :blk err_name; 7497 }; 7498 7499 // allocate a shared dummy instruction for the error capture 7500 const err_inst = err_inst: { 7501 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 7502 try astgen.instructions.append(astgen.gpa, .{ 7503 .tag = .extended, 7504 .data = .{ .extended = .{ 7505 .opcode = .value_placeholder, 7506 .small = undefined, 7507 .operand = undefined, 7508 } }, 7509 }); 7510 break :err_inst inst; 7511 }; 7512 7513 // In this pass we generate all the item and prong expressions for error cases. 7514 var multi_case_index: u32 = 0; 7515 var scalar_case_index: u32 = 0; 7516 var any_uses_err_capture = false; 7517 for (case_nodes) |case_node| { 7518 const case = tree.fullSwitchCase(case_node).?; 7519 7520 const is_multi_case = case.ast.values.len > 1 or 7521 (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range); 7522 7523 var dbg_var_name: Zir.NullTerminatedString = .empty; 7524 var dbg_var_inst: Zir.Inst.Ref = undefined; 7525 var err_scope: Scope.LocalVal = undefined; 7526 var capture_scope: Scope.LocalVal = undefined; 7527 7528 const sub_scope = blk: { 7529 err_scope = .{ 7530 .parent = &case_scope.base, 7531 .gen_zir = &case_scope, 7532 .name = err_name, 7533 .inst = err_inst.toRef(), 7534 .token_src = error_payload, 7535 .id_cat = .capture, 7536 }; 7537 7538 const capture_token = case.payload_token orelse break :blk &err_scope.base; 7539 if (token_tags[capture_token] != .identifier) { 7540 return astgen.failTok(capture_token + 1, "error set cannot be captured by reference", .{}); 7541 } 7542 7543 const capture_slice = tree.tokenSlice(capture_token); 7544 if (mem.eql(u8, capture_slice, "_")) { 7545 try astgen.appendErrorTok(capture_token, "discard of error capture; omit it instead", .{}); 7546 } 7547 const tag_name = try astgen.identAsString(capture_token); 7548 try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture); 7549 7550 capture_scope = .{ 7551 .parent = &case_scope.base, 7552 .gen_zir = &case_scope, 7553 .name = tag_name, 7554 .inst = switch_block.toRef(), 7555 .token_src = capture_token, 7556 .id_cat = .capture, 7557 }; 7558 dbg_var_name = tag_name; 7559 dbg_var_inst = switch_block.toRef(); 7560 7561 err_scope.parent = &capture_scope.base; 7562 7563 break :blk &err_scope.base; 7564 }; 7565 7566 const header_index: u32 = @intCast(payloads.items.len); 7567 const body_len_index = if (is_multi_case) blk: { 7568 payloads.items[multi_case_table + multi_case_index] = header_index; 7569 multi_case_index += 1; 7570 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len 7571 7572 // items 7573 var items_len: u32 = 0; 7574 for (case.ast.values) |item_node| { 7575 if (node_tags[item_node] == .switch_range) continue; 7576 items_len += 1; 7577 7578 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node); 7579 try payloads.append(gpa, @intFromEnum(item_inst)); 7580 } 7581 7582 // ranges 7583 var ranges_len: u32 = 0; 7584 for (case.ast.values) |range| { 7585 if (node_tags[range] != .switch_range) continue; 7586 ranges_len += 1; 7587 7588 const first = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].lhs); 7589 const last = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].rhs); 7590 try payloads.appendSlice(gpa, &[_]u32{ 7591 @intFromEnum(first), @intFromEnum(last), 7592 }); 7593 } 7594 7595 payloads.items[header_index] = items_len; 7596 payloads.items[header_index + 1] = ranges_len; 7597 break :blk header_index + 2; 7598 } else if (case_node == else_node) blk: { 7599 payloads.items[case_table_start + 1] = header_index; 7600 try payloads.resize(gpa, header_index + 1); // body_len 7601 break :blk header_index; 7602 } else blk: { 7603 payloads.items[scalar_case_table + scalar_case_index] = header_index; 7604 scalar_case_index += 1; 7605 try payloads.resize(gpa, header_index + 2); // item, body_len 7606 const item_node = case.ast.values[0]; 7607 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node); 7608 payloads.items[header_index] = @intFromEnum(item_inst); 7609 break :blk header_index + 1; 7610 }; 7611 7612 { 7613 // temporarily stack case_scope on parent_gz 7614 case_scope.instructions_top = parent_gz.instructions.items.len; 7615 defer case_scope.unstack(); 7616 7617 if (do_err_trace and nodeMayAppendToErrorTrace(tree, operand_node)) 7618 _ = try case_scope.addSaveErrRetIndex(.always); 7619 7620 if (dbg_var_name != .empty) { 7621 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst); 7622 } 7623 7624 const target_expr_node = case.ast.target_expr; 7625 const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint); 7626 // check capture_scope, not err_scope to avoid false positive unused error capture 7627 try checkUsed(parent_gz, &case_scope.base, err_scope.parent); 7628 const uses_err = err_scope.used != 0 or err_scope.discarded != 0; 7629 if (uses_err) { 7630 try case_scope.addDbgVar(.dbg_var_val, err_name, err_inst.toRef()); 7631 any_uses_err_capture = true; 7632 } 7633 7634 if (!parent_gz.refIsNoReturn(case_result)) { 7635 if (do_err_trace) 7636 try restoreErrRetIndex( 7637 &case_scope, 7638 .{ .block = switch_block }, 7639 block_scope.break_result_info, 7640 target_expr_node, 7641 case_result, 7642 ); 7643 7644 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node); 7645 } 7646 7647 const case_slice = case_scope.instructionsSlice(); 7648 const extra_insts: []const Zir.Inst.Index = if (uses_err) &.{ switch_block, err_inst } else &.{switch_block}; 7649 const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, extra_insts); 7650 try payloads.ensureUnusedCapacity(gpa, body_len); 7651 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{ 7652 .body_len = @intCast(body_len), 7653 .capture = if (case.payload_token != null) .by_val else .none, 7654 .is_inline = case.inline_token != null, 7655 .has_tag_capture = false, 7656 }); 7657 appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, extra_insts); 7658 } 7659 } 7660 // Now that the item expressions are generated we can add this. 7661 try parent_gz.instructions.append(gpa, switch_block); 7662 7663 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlockErrUnion).@"struct".fields.len + 7664 @intFromBool(multi_cases_len != 0) + 7665 payloads.items.len - case_table_end + 7666 (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len); 7667 7668 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlockErrUnion{ 7669 .operand = raw_operand, 7670 .bits = Zir.Inst.SwitchBlockErrUnion.Bits{ 7671 .has_multi_cases = multi_cases_len != 0, 7672 .has_else = has_else, 7673 .scalar_cases_len = @intCast(scalar_cases_len), 7674 .any_uses_err_capture = any_uses_err_capture, 7675 .payload_is_ref = payload_is_ref, 7676 }, 7677 .main_src_node_offset = parent_gz.nodeIndexToRelative(catch_or_if_node), 7678 }); 7679 7680 if (multi_cases_len != 0) { 7681 astgen.extra.appendAssumeCapacity(multi_cases_len); 7682 } 7683 7684 if (any_uses_err_capture) { 7685 astgen.extra.appendAssumeCapacity(@intFromEnum(err_inst)); 7686 } 7687 7688 const zir_datas = astgen.instructions.items(.data); 7689 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index; 7690 7691 for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| { 7692 var body_len_index = start_index; 7693 var end_index = start_index; 7694 const table_index = case_table_start + i; 7695 if (table_index < scalar_case_table) { 7696 end_index += 1; 7697 } else if (table_index < multi_case_table) { 7698 body_len_index += 1; 7699 end_index += 2; 7700 } else { 7701 body_len_index += 2; 7702 const items_len = payloads.items[start_index]; 7703 const ranges_len = payloads.items[start_index + 1]; 7704 end_index += 3 + items_len + 2 * ranges_len; 7705 } 7706 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]); 7707 end_index += prong_info.body_len; 7708 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]); 7709 } 7710 7711 if (need_result_rvalue) { 7712 return rvalue(parent_gz, ri, switch_block.toRef(), switch_node); 7713 } else { 7714 return switch_block.toRef(); 7715 } 7716 } 7717 7718 fn switchExpr( 7719 parent_gz: *GenZir, 7720 scope: *Scope, 7721 ri: ResultInfo, 7722 node: Ast.Node.Index, 7723 switch_full: Ast.full.Switch, 7724 ) InnerError!Zir.Inst.Ref { 7725 const astgen = parent_gz.astgen; 7726 const gpa = astgen.gpa; 7727 const tree = astgen.tree; 7728 const node_datas = tree.nodes.items(.data); 7729 const node_tags = tree.nodes.items(.tag); 7730 const main_tokens = tree.nodes.items(.main_token); 7731 const token_tags = tree.tokens.items(.tag); 7732 const operand_node = switch_full.ast.condition; 7733 const case_nodes = switch_full.ast.cases; 7734 7735 const need_rl = astgen.nodes_need_rl.contains(node); 7736 const block_ri: ResultInfo = if (need_rl) ri else .{ 7737 .rl = switch (ri.rl) { 7738 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 7739 .inferred_ptr => .none, 7740 else => ri.rl, 7741 }, 7742 .ctx = ri.ctx, 7743 }; 7744 // We need to call `rvalue` to write through to the pointer only if we had a 7745 // result pointer and aren't forwarding it. 7746 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 7747 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 7748 7749 if (switch_full.label_token) |label_token| { 7750 try astgen.checkLabelRedefinition(scope, label_token); 7751 } 7752 7753 // We perform two passes over the AST. This first pass is to collect information 7754 // for the following variables, make note of the special prong AST node index, 7755 // and bail out with a compile error if there are multiple special prongs present. 7756 var any_payload_is_ref = false; 7757 var any_has_tag_capture = false; 7758 var any_non_inline_capture = false; 7759 var scalar_cases_len: u32 = 0; 7760 var multi_cases_len: u32 = 0; 7761 var inline_cases_len: u32 = 0; 7762 var special_prong: Zir.SpecialProng = .none; 7763 var special_node: Ast.Node.Index = 0; 7764 var else_src: ?Ast.TokenIndex = null; 7765 var underscore_src: ?Ast.TokenIndex = null; 7766 for (case_nodes) |case_node| { 7767 const case = tree.fullSwitchCase(case_node).?; 7768 if (case.payload_token) |payload_token| { 7769 const ident = if (token_tags[payload_token] == .asterisk) blk: { 7770 any_payload_is_ref = true; 7771 break :blk payload_token + 1; 7772 } else payload_token; 7773 if (token_tags[ident + 1] == .comma) { 7774 any_has_tag_capture = true; 7775 } 7776 7777 // If the first capture is ignored, then there is no runtime-known 7778 // capture, as the tag capture must be for an inline prong. 7779 // This check isn't perfect, because for things like enums, the 7780 // first prong *is* comptime-known for inline prongs! But such 7781 // knowledge requires semantic analysis. 7782 if (!mem.eql(u8, tree.tokenSlice(ident), "_")) { 7783 any_non_inline_capture = true; 7784 } 7785 } 7786 // Check for else/`_` prong. 7787 if (case.ast.values.len == 0) { 7788 const case_src = case.ast.arrow_token - 1; 7789 if (else_src) |src| { 7790 return astgen.failTokNotes( 7791 case_src, 7792 "multiple else prongs in switch expression", 7793 .{}, 7794 &[_]u32{ 7795 try astgen.errNoteTok( 7796 src, 7797 "previous else prong here", 7798 .{}, 7799 ), 7800 }, 7801 ); 7802 } else if (underscore_src) |some_underscore| { 7803 return astgen.failNodeNotes( 7804 node, 7805 "else and '_' prong in switch expression", 7806 .{}, 7807 &[_]u32{ 7808 try astgen.errNoteTok( 7809 case_src, 7810 "else prong here", 7811 .{}, 7812 ), 7813 try astgen.errNoteTok( 7814 some_underscore, 7815 "'_' prong here", 7816 .{}, 7817 ), 7818 }, 7819 ); 7820 } 7821 special_node = case_node; 7822 special_prong = .@"else"; 7823 else_src = case_src; 7824 continue; 7825 } else if (case.ast.values.len == 1 and 7826 node_tags[case.ast.values[0]] == .identifier and 7827 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")) 7828 { 7829 const case_src = case.ast.arrow_token - 1; 7830 if (underscore_src) |src| { 7831 return astgen.failTokNotes( 7832 case_src, 7833 "multiple '_' prongs in switch expression", 7834 .{}, 7835 &[_]u32{ 7836 try astgen.errNoteTok( 7837 src, 7838 "previous '_' prong here", 7839 .{}, 7840 ), 7841 }, 7842 ); 7843 } else if (else_src) |some_else| { 7844 return astgen.failNodeNotes( 7845 node, 7846 "else and '_' prong in switch expression", 7847 .{}, 7848 &[_]u32{ 7849 try astgen.errNoteTok( 7850 some_else, 7851 "else prong here", 7852 .{}, 7853 ), 7854 try astgen.errNoteTok( 7855 case_src, 7856 "'_' prong here", 7857 .{}, 7858 ), 7859 }, 7860 ); 7861 } 7862 if (case.inline_token != null) { 7863 return astgen.failTok(case_src, "cannot inline '_' prong", .{}); 7864 } 7865 special_node = case_node; 7866 special_prong = .under; 7867 underscore_src = case_src; 7868 continue; 7869 } 7870 7871 for (case.ast.values) |val| { 7872 if (node_tags[val] == .string_literal) 7873 return astgen.failNode(val, "cannot switch on strings", .{}); 7874 } 7875 7876 if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) { 7877 scalar_cases_len += 1; 7878 } else { 7879 multi_cases_len += 1; 7880 } 7881 if (case.inline_token != null) { 7882 inline_cases_len += 1; 7883 } 7884 } 7885 7886 const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none }; 7887 7888 astgen.advanceSourceCursorToNode(operand_node); 7889 const operand_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; 7890 7891 const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node); 7892 const item_ri: ResultInfo = .{ .rl = .none }; 7893 7894 // If this switch is labeled, it may have `continue`s targeting it, and thus we need the operand type 7895 // to provide a result type. 7896 const raw_operand_ty_ref = if (switch_full.label_token != null) t: { 7897 break :t try parent_gz.addUnNode(.typeof, raw_operand, operand_node); 7898 } else undefined; 7899 7900 // This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti, 7901 // except the first cases_nodes.len slots are a table that indexes payloads later in the array, with 7902 // the special case index coming first, then scalar_case_len indexes, then multi_cases_len indexes 7903 const payloads = &astgen.scratch; 7904 const scratch_top = astgen.scratch.items.len; 7905 const case_table_start = scratch_top; 7906 const scalar_case_table = case_table_start + @intFromBool(special_prong != .none); 7907 const multi_case_table = scalar_case_table + scalar_cases_len; 7908 const case_table_end = multi_case_table + multi_cases_len; 7909 try astgen.scratch.resize(gpa, case_table_end); 7910 defer astgen.scratch.items.len = scratch_top; 7911 7912 var block_scope = parent_gz.makeSubBlock(scope); 7913 // block_scope not used for collecting instructions 7914 block_scope.instructions_top = GenZir.unstacked_top; 7915 block_scope.setBreakResultInfo(block_ri); 7916 7917 // Sema expects a dbg_stmt immediately before switch_block(_ref) 7918 try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc); 7919 // This gets added to the parent block later, after the item expressions. 7920 const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block; 7921 const switch_block = try parent_gz.makeBlockInst(switch_tag, node); 7922 7923 if (switch_full.label_token) |label_token| { 7924 block_scope.continue_block = switch_block.toOptional(); 7925 block_scope.continue_result_info = .{ 7926 .rl = if (any_payload_is_ref) 7927 .{ .ref_coerced_ty = raw_operand_ty_ref } 7928 else 7929 .{ .coerced_ty = raw_operand_ty_ref }, 7930 }; 7931 7932 block_scope.label = .{ 7933 .token = label_token, 7934 .block_inst = switch_block, 7935 }; 7936 // `break` can target this via `label.block_inst` 7937 // `break_result_info` already set by `setBreakResultInfo` 7938 } 7939 7940 // We re-use this same scope for all cases, including the special prong, if any. 7941 var case_scope = parent_gz.makeSubBlock(&block_scope.base); 7942 case_scope.instructions_top = GenZir.unstacked_top; 7943 7944 // If any prong has an inline tag capture, allocate a shared dummy instruction for it 7945 const tag_inst = if (any_has_tag_capture) tag_inst: { 7946 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 7947 try astgen.instructions.append(astgen.gpa, .{ 7948 .tag = .extended, 7949 .data = .{ .extended = .{ 7950 .opcode = .value_placeholder, 7951 .small = undefined, 7952 .operand = undefined, 7953 } }, 7954 }); 7955 break :tag_inst inst; 7956 } else undefined; 7957 7958 // In this pass we generate all the item and prong expressions. 7959 var multi_case_index: u32 = 0; 7960 var scalar_case_index: u32 = 0; 7961 for (case_nodes) |case_node| { 7962 const case = tree.fullSwitchCase(case_node).?; 7963 7964 const is_multi_case = case.ast.values.len > 1 or 7965 (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range); 7966 7967 var dbg_var_name: Zir.NullTerminatedString = .empty; 7968 var dbg_var_inst: Zir.Inst.Ref = undefined; 7969 var dbg_var_tag_name: Zir.NullTerminatedString = .empty; 7970 var dbg_var_tag_inst: Zir.Inst.Ref = undefined; 7971 var has_tag_capture = false; 7972 var capture_val_scope: Scope.LocalVal = undefined; 7973 var tag_scope: Scope.LocalVal = undefined; 7974 7975 var capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = .none; 7976 7977 const sub_scope = blk: { 7978 const payload_token = case.payload_token orelse break :blk &case_scope.base; 7979 const ident = if (token_tags[payload_token] == .asterisk) 7980 payload_token + 1 7981 else 7982 payload_token; 7983 7984 const is_ptr = ident != payload_token; 7985 capture = if (is_ptr) .by_ref else .by_val; 7986 7987 const ident_slice = tree.tokenSlice(ident); 7988 var payload_sub_scope: *Scope = undefined; 7989 if (mem.eql(u8, ident_slice, "_")) { 7990 if (is_ptr) { 7991 return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 7992 } 7993 payload_sub_scope = &case_scope.base; 7994 } else { 7995 const capture_name = try astgen.identAsString(ident); 7996 try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture); 7997 capture_val_scope = .{ 7998 .parent = &case_scope.base, 7999 .gen_zir = &case_scope, 8000 .name = capture_name, 8001 .inst = switch_block.toRef(), 8002 .token_src = ident, 8003 .id_cat = .capture, 8004 }; 8005 dbg_var_name = capture_name; 8006 dbg_var_inst = switch_block.toRef(); 8007 payload_sub_scope = &capture_val_scope.base; 8008 } 8009 8010 const tag_token = if (token_tags[ident + 1] == .comma) 8011 ident + 2 8012 else 8013 break :blk payload_sub_scope; 8014 const tag_slice = tree.tokenSlice(tag_token); 8015 if (mem.eql(u8, tag_slice, "_")) { 8016 try astgen.appendErrorTok(tag_token, "discard of tag capture; omit it instead", .{}); 8017 } else if (case.inline_token == null) { 8018 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{}); 8019 } 8020 const tag_name = try astgen.identAsString(tag_token); 8021 try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture"); 8022 8023 assert(any_has_tag_capture); 8024 has_tag_capture = true; 8025 8026 tag_scope = .{ 8027 .parent = payload_sub_scope, 8028 .gen_zir = &case_scope, 8029 .name = tag_name, 8030 .inst = tag_inst.toRef(), 8031 .token_src = tag_token, 8032 .id_cat = .@"switch tag capture", 8033 }; 8034 dbg_var_tag_name = tag_name; 8035 dbg_var_tag_inst = tag_inst.toRef(); 8036 break :blk &tag_scope.base; 8037 }; 8038 8039 const header_index: u32 = @intCast(payloads.items.len); 8040 const body_len_index = if (is_multi_case) blk: { 8041 payloads.items[multi_case_table + multi_case_index] = header_index; 8042 multi_case_index += 1; 8043 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len 8044 8045 // items 8046 var items_len: u32 = 0; 8047 for (case.ast.values) |item_node| { 8048 if (node_tags[item_node] == .switch_range) continue; 8049 items_len += 1; 8050 8051 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node); 8052 try payloads.append(gpa, @intFromEnum(item_inst)); 8053 } 8054 8055 // ranges 8056 var ranges_len: u32 = 0; 8057 for (case.ast.values) |range| { 8058 if (node_tags[range] != .switch_range) continue; 8059 ranges_len += 1; 8060 8061 const first = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].lhs); 8062 const last = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].rhs); 8063 try payloads.appendSlice(gpa, &[_]u32{ 8064 @intFromEnum(first), @intFromEnum(last), 8065 }); 8066 } 8067 8068 payloads.items[header_index] = items_len; 8069 payloads.items[header_index + 1] = ranges_len; 8070 break :blk header_index + 2; 8071 } else if (case_node == special_node) blk: { 8072 payloads.items[case_table_start] = header_index; 8073 try payloads.resize(gpa, header_index + 1); // body_len 8074 break :blk header_index; 8075 } else blk: { 8076 payloads.items[scalar_case_table + scalar_case_index] = header_index; 8077 scalar_case_index += 1; 8078 try payloads.resize(gpa, header_index + 2); // item, body_len 8079 const item_node = case.ast.values[0]; 8080 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node); 8081 payloads.items[header_index] = @intFromEnum(item_inst); 8082 break :blk header_index + 1; 8083 }; 8084 8085 { 8086 // temporarily stack case_scope on parent_gz 8087 case_scope.instructions_top = parent_gz.instructions.items.len; 8088 defer case_scope.unstack(); 8089 8090 if (dbg_var_name != .empty) { 8091 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst); 8092 } 8093 if (dbg_var_tag_name != .empty) { 8094 try case_scope.addDbgVar(.dbg_var_val, dbg_var_tag_name, dbg_var_tag_inst); 8095 } 8096 const target_expr_node = case.ast.target_expr; 8097 const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint); 8098 try checkUsed(parent_gz, &case_scope.base, sub_scope); 8099 if (!parent_gz.refIsNoReturn(case_result)) { 8100 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node); 8101 } 8102 8103 const case_slice = case_scope.instructionsSlice(); 8104 const extra_insts: []const Zir.Inst.Index = if (has_tag_capture) &.{ switch_block, tag_inst } else &.{switch_block}; 8105 const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, extra_insts); 8106 try payloads.ensureUnusedCapacity(gpa, body_len); 8107 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{ 8108 .body_len = @intCast(body_len), 8109 .capture = capture, 8110 .is_inline = case.inline_token != null, 8111 .has_tag_capture = has_tag_capture, 8112 }); 8113 appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, extra_insts); 8114 } 8115 } 8116 8117 if (switch_full.label_token) |label_token| if (!block_scope.label.?.used) { 8118 try astgen.appendErrorTok(label_token, "unused switch label", .{}); 8119 }; 8120 8121 // Now that the item expressions are generated we can add this. 8122 try parent_gz.instructions.append(gpa, switch_block); 8123 8124 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).@"struct".fields.len + 8125 @intFromBool(multi_cases_len != 0) + 8126 @intFromBool(any_has_tag_capture) + 8127 payloads.items.len - case_table_end + 8128 (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len); 8129 8130 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{ 8131 .operand = raw_operand, 8132 .bits = Zir.Inst.SwitchBlock.Bits{ 8133 .has_multi_cases = multi_cases_len != 0, 8134 .has_else = special_prong == .@"else", 8135 .has_under = special_prong == .under, 8136 .any_has_tag_capture = any_has_tag_capture, 8137 .any_non_inline_capture = any_non_inline_capture, 8138 .has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue, 8139 .scalar_cases_len = @intCast(scalar_cases_len), 8140 }, 8141 }); 8142 8143 if (multi_cases_len != 0) { 8144 astgen.extra.appendAssumeCapacity(multi_cases_len); 8145 } 8146 8147 if (any_has_tag_capture) { 8148 astgen.extra.appendAssumeCapacity(@intFromEnum(tag_inst)); 8149 } 8150 8151 const zir_datas = astgen.instructions.items(.data); 8152 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index; 8153 8154 for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| { 8155 var body_len_index = start_index; 8156 var end_index = start_index; 8157 const table_index = case_table_start + i; 8158 if (table_index < scalar_case_table) { 8159 end_index += 1; 8160 } else if (table_index < multi_case_table) { 8161 body_len_index += 1; 8162 end_index += 2; 8163 } else { 8164 body_len_index += 2; 8165 const items_len = payloads.items[start_index]; 8166 const ranges_len = payloads.items[start_index + 1]; 8167 end_index += 3 + items_len + 2 * ranges_len; 8168 } 8169 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]); 8170 end_index += prong_info.body_len; 8171 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]); 8172 } 8173 8174 if (need_result_rvalue) { 8175 return rvalue(parent_gz, ri, switch_block.toRef(), node); 8176 } else { 8177 return switch_block.toRef(); 8178 } 8179 } 8180 8181 fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 8182 const astgen = gz.astgen; 8183 const tree = astgen.tree; 8184 const node_datas = tree.nodes.items(.data); 8185 const node_tags = tree.nodes.items(.tag); 8186 8187 if (astgen.fn_block == null) { 8188 return astgen.failNode(node, "'return' outside function scope", .{}); 8189 } 8190 8191 if (gz.any_defer_node != 0) { 8192 return astgen.failNodeNotes(node, "cannot return from defer expression", .{}, &.{ 8193 try astgen.errNoteNode( 8194 gz.any_defer_node, 8195 "defer expression here", 8196 .{}, 8197 ), 8198 }); 8199 } 8200 8201 // Ensure debug line/column information is emitted for this return expression. 8202 // Then we will save the line/column so that we can emit another one that goes 8203 // "backwards" because we want to evaluate the operand, but then put the debug 8204 // info back at the return keyword for error return tracing. 8205 if (!gz.is_comptime) { 8206 try emitDbgNode(gz, node); 8207 } 8208 const ret_lc: LineColumn = .{ astgen.source_line - gz.decl_line, astgen.source_column }; 8209 8210 const defer_outer = &astgen.fn_block.?.base; 8211 8212 const operand_node = node_datas[node].lhs; 8213 if (operand_node == 0) { 8214 // Returning a void value; skip error defers. 8215 try genDefers(gz, defer_outer, scope, .normal_only); 8216 8217 // As our last action before the return, "pop" the error trace if needed 8218 _ = try gz.addRestoreErrRetIndex(.ret, .always, node); 8219 8220 _ = try gz.addUnNode(.ret_node, .void_value, node); 8221 return Zir.Inst.Ref.unreachable_value; 8222 } 8223 8224 if (node_tags[operand_node] == .error_value) { 8225 // Hot path for `return error.Foo`. This bypasses result location logic as well as logic 8226 // for detecting whether to add something to the function's inferred error set. 8227 const ident_token = node_datas[operand_node].rhs; 8228 const err_name_str_index = try astgen.identAsString(ident_token); 8229 const defer_counts = countDefers(defer_outer, scope); 8230 if (!defer_counts.need_err_code) { 8231 try genDefers(gz, defer_outer, scope, .both_sans_err); 8232 try emitDbgStmt(gz, ret_lc); 8233 _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token); 8234 return Zir.Inst.Ref.unreachable_value; 8235 } 8236 const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token); 8237 try genDefers(gz, defer_outer, scope, .{ .both = err_code }); 8238 try emitDbgStmt(gz, ret_lc); 8239 _ = try gz.addUnNode(.ret_node, err_code, node); 8240 return Zir.Inst.Ref.unreachable_value; 8241 } 8242 8243 const ri: ResultInfo = if (astgen.nodes_need_rl.contains(node)) .{ 8244 .rl = .{ .ptr = .{ .inst = try gz.addNode(.ret_ptr, node) } }, 8245 .ctx = .@"return", 8246 } else .{ 8247 .rl = .{ .coerced_ty = astgen.fn_ret_ty }, 8248 .ctx = .@"return", 8249 }; 8250 const prev_anon_name_strategy = gz.anon_name_strategy; 8251 gz.anon_name_strategy = .func; 8252 const operand = try reachableExpr(gz, scope, ri, operand_node, node); 8253 gz.anon_name_strategy = prev_anon_name_strategy; 8254 8255 switch (nodeMayEvalToError(tree, operand_node)) { 8256 .never => { 8257 // Returning a value that cannot be an error; skip error defers. 8258 try genDefers(gz, defer_outer, scope, .normal_only); 8259 8260 // As our last action before the return, "pop" the error trace if needed 8261 _ = try gz.addRestoreErrRetIndex(.ret, .always, node); 8262 8263 try emitDbgStmt(gz, ret_lc); 8264 try gz.addRet(ri, operand, node); 8265 return Zir.Inst.Ref.unreachable_value; 8266 }, 8267 .always => { 8268 // Value is always an error. Emit both error defers and regular defers. 8269 const err_code = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand; 8270 try genDefers(gz, defer_outer, scope, .{ .both = err_code }); 8271 try emitDbgStmt(gz, ret_lc); 8272 try gz.addRet(ri, operand, node); 8273 return Zir.Inst.Ref.unreachable_value; 8274 }, 8275 .maybe => { 8276 const defer_counts = countDefers(defer_outer, scope); 8277 if (!defer_counts.have_err) { 8278 // Only regular defers; no branch needed. 8279 try genDefers(gz, defer_outer, scope, .normal_only); 8280 try emitDbgStmt(gz, ret_lc); 8281 8282 // As our last action before the return, "pop" the error trace if needed 8283 const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand; 8284 _ = try gz.addRestoreErrRetIndex(.ret, .{ .if_non_error = result }, node); 8285 8286 try gz.addRet(ri, operand, node); 8287 return Zir.Inst.Ref.unreachable_value; 8288 } 8289 8290 // Emit conditional branch for generating errdefers. 8291 const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand; 8292 const is_non_err = try gz.addUnNode(.ret_is_non_err, result, node); 8293 const condbr = try gz.addCondBr(.condbr, node); 8294 8295 var then_scope = gz.makeSubBlock(scope); 8296 defer then_scope.unstack(); 8297 8298 try genDefers(&then_scope, defer_outer, scope, .normal_only); 8299 8300 // As our last action before the return, "pop" the error trace if needed 8301 _ = try then_scope.addRestoreErrRetIndex(.ret, .always, node); 8302 8303 try emitDbgStmt(&then_scope, ret_lc); 8304 try then_scope.addRet(ri, operand, node); 8305 8306 var else_scope = gz.makeSubBlock(scope); 8307 defer else_scope.unstack(); 8308 8309 const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{ 8310 .both = try else_scope.addUnNode(.err_union_code, result, node), 8311 }; 8312 try genDefers(&else_scope, defer_outer, scope, which_ones); 8313 try emitDbgStmt(&else_scope, ret_lc); 8314 try else_scope.addRet(ri, operand, node); 8315 8316 try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope); 8317 8318 return Zir.Inst.Ref.unreachable_value; 8319 }, 8320 } 8321 } 8322 8323 /// Parses the string `buf` as a base 10 integer of type `u16`. 8324 /// 8325 /// Unlike std.fmt.parseInt, does not allow the '_' character in `buf`. 8326 fn parseBitCount(buf: []const u8) std.fmt.ParseIntError!u16 { 8327 if (buf.len == 0) return error.InvalidCharacter; 8328 8329 var x: u16 = 0; 8330 8331 for (buf) |c| { 8332 const digit = switch (c) { 8333 '0'...'9' => c - '0', 8334 else => return error.InvalidCharacter, 8335 }; 8336 8337 if (x != 0) x = try std.math.mul(u16, x, 10); 8338 x = try std.math.add(u16, x, digit); 8339 } 8340 8341 return x; 8342 } 8343 8344 fn identifier( 8345 gz: *GenZir, 8346 scope: *Scope, 8347 ri: ResultInfo, 8348 ident: Ast.Node.Index, 8349 ) InnerError!Zir.Inst.Ref { 8350 const astgen = gz.astgen; 8351 const tree = astgen.tree; 8352 const main_tokens = tree.nodes.items(.main_token); 8353 8354 const ident_token = main_tokens[ident]; 8355 const ident_name_raw = tree.tokenSlice(ident_token); 8356 if (mem.eql(u8, ident_name_raw, "_")) { 8357 return astgen.failNode(ident, "'_' used as an identifier without @\"_\" syntax", .{}); 8358 } 8359 8360 // if not @"" syntax, just use raw token slice 8361 if (ident_name_raw[0] != '@') { 8362 if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| { 8363 return rvalue(gz, ri, zir_const_ref, ident); 8364 } 8365 8366 if (ident_name_raw.len >= 2) integer: { 8367 const first_c = ident_name_raw[0]; 8368 if (first_c == 'i' or first_c == 'u') { 8369 const signedness: std.builtin.Signedness = switch (first_c == 'i') { 8370 true => .signed, 8371 false => .unsigned, 8372 }; 8373 if (ident_name_raw.len >= 3 and ident_name_raw[1] == '0') { 8374 return astgen.failNode( 8375 ident, 8376 "primitive integer type '{s}' has leading zero", 8377 .{ident_name_raw}, 8378 ); 8379 } 8380 const bit_count = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) { 8381 error.Overflow => return astgen.failNode( 8382 ident, 8383 "primitive integer type '{s}' exceeds maximum bit width of 65535", 8384 .{ident_name_raw}, 8385 ), 8386 error.InvalidCharacter => break :integer, 8387 }; 8388 const result = try gz.add(.{ 8389 .tag = .int_type, 8390 .data = .{ .int_type = .{ 8391 .src_node = gz.nodeIndexToRelative(ident), 8392 .signedness = signedness, 8393 .bit_count = bit_count, 8394 } }, 8395 }); 8396 return rvalue(gz, ri, result, ident); 8397 } 8398 } 8399 } 8400 8401 // Local variables, including function parameters. 8402 return localVarRef(gz, scope, ri, ident, ident_token); 8403 } 8404 8405 fn localVarRef( 8406 gz: *GenZir, 8407 scope: *Scope, 8408 ri: ResultInfo, 8409 ident: Ast.Node.Index, 8410 ident_token: Ast.TokenIndex, 8411 ) InnerError!Zir.Inst.Ref { 8412 const astgen = gz.astgen; 8413 const name_str_index = try astgen.identAsString(ident_token); 8414 var s = scope; 8415 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already 8416 var found_needs_tunnel: bool = undefined; // defined when `found_already != null` 8417 var found_namespaces_out: u32 = undefined; // defined when `found_already != null` 8418 8419 // The number of namespaces above `gz` we currently are 8420 var num_namespaces_out: u32 = 0; 8421 // defined by `num_namespaces_out != 0` 8422 var capturing_namespace: *Scope.Namespace = undefined; 8423 8424 while (true) switch (s.tag) { 8425 .local_val => { 8426 const local_val = s.cast(Scope.LocalVal).?; 8427 8428 if (local_val.name == name_str_index) { 8429 // Locals cannot shadow anything, so we do not need to look for ambiguous 8430 // references in this case. 8431 if (ri.rl == .discard and ri.ctx == .assignment) { 8432 local_val.discarded = ident_token; 8433 } else { 8434 local_val.used = ident_token; 8435 } 8436 8437 const value_inst = if (num_namespaces_out != 0) try tunnelThroughClosure( 8438 gz, 8439 ident, 8440 num_namespaces_out, 8441 .{ .ref = local_val.inst }, 8442 .{ .token = local_val.token_src }, 8443 ) else local_val.inst; 8444 8445 return rvalueNoCoercePreRef(gz, ri, value_inst, ident); 8446 } 8447 s = local_val.parent; 8448 }, 8449 .local_ptr => { 8450 const local_ptr = s.cast(Scope.LocalPtr).?; 8451 if (local_ptr.name == name_str_index) { 8452 if (ri.rl == .discard and ri.ctx == .assignment) { 8453 local_ptr.discarded = ident_token; 8454 } else { 8455 local_ptr.used = ident_token; 8456 } 8457 8458 // Can't close over a runtime variable 8459 if (num_namespaces_out != 0 and !local_ptr.maybe_comptime and !gz.is_typeof) { 8460 const ident_name = try astgen.identifierTokenString(ident_token); 8461 return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{ 8462 try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}), 8463 try astgen.errNoteNode(capturing_namespace.node, "crosses namespace boundary here", .{}), 8464 }); 8465 } 8466 8467 switch (ri.rl) { 8468 .ref, .ref_coerced_ty => { 8469 const ptr_inst = if (num_namespaces_out != 0) try tunnelThroughClosure( 8470 gz, 8471 ident, 8472 num_namespaces_out, 8473 .{ .ref = local_ptr.ptr }, 8474 .{ .token = local_ptr.token_src }, 8475 ) else local_ptr.ptr; 8476 local_ptr.used_as_lvalue = true; 8477 return ptr_inst; 8478 }, 8479 else => { 8480 const val_inst = if (num_namespaces_out != 0) try tunnelThroughClosure( 8481 gz, 8482 ident, 8483 num_namespaces_out, 8484 .{ .ref_load = local_ptr.ptr }, 8485 .{ .token = local_ptr.token_src }, 8486 ) else try gz.addUnNode(.load, local_ptr.ptr, ident); 8487 return rvalueNoCoercePreRef(gz, ri, val_inst, ident); 8488 }, 8489 } 8490 } 8491 s = local_ptr.parent; 8492 }, 8493 .gen_zir => s = s.cast(GenZir).?.parent, 8494 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, 8495 .namespace => { 8496 const ns = s.cast(Scope.Namespace).?; 8497 if (ns.decls.get(name_str_index)) |i| { 8498 if (found_already) |f| { 8499 return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{ 8500 try astgen.errNoteNode(f, "declared here", .{}), 8501 try astgen.errNoteNode(i, "also declared here", .{}), 8502 }); 8503 } 8504 // We found a match but must continue looking for ambiguous references to decls. 8505 found_already = i; 8506 found_needs_tunnel = ns.maybe_generic; 8507 found_namespaces_out = num_namespaces_out; 8508 } 8509 num_namespaces_out += 1; 8510 capturing_namespace = ns; 8511 s = ns.parent; 8512 }, 8513 .top => break, 8514 }; 8515 if (found_already == null) { 8516 const ident_name = try astgen.identifierTokenString(ident_token); 8517 return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name}); 8518 } 8519 8520 // Decl references happen by name rather than ZIR index so that when unrelated 8521 // decls are modified, ZIR code containing references to them can be unmodified. 8522 8523 if (found_namespaces_out > 0 and found_needs_tunnel) { 8524 switch (ri.rl) { 8525 .ref, .ref_coerced_ty => return tunnelThroughClosure( 8526 gz, 8527 ident, 8528 found_namespaces_out, 8529 .{ .decl_ref = name_str_index }, 8530 .{ .node = found_already.? }, 8531 ), 8532 else => { 8533 const result = try tunnelThroughClosure( 8534 gz, 8535 ident, 8536 found_namespaces_out, 8537 .{ .decl_val = name_str_index }, 8538 .{ .node = found_already.? }, 8539 ); 8540 return rvalueNoCoercePreRef(gz, ri, result, ident); 8541 }, 8542 } 8543 } 8544 8545 switch (ri.rl) { 8546 .ref, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token), 8547 else => { 8548 const result = try gz.addStrTok(.decl_val, name_str_index, ident_token); 8549 return rvalueNoCoercePreRef(gz, ri, result, ident); 8550 }, 8551 } 8552 } 8553 8554 /// Access a ZIR instruction through closure. May tunnel through arbitrarily 8555 /// many namespaces, adding closure captures as required. 8556 /// Returns the index of the `closure_get` instruction added to `gz`. 8557 fn tunnelThroughClosure( 8558 gz: *GenZir, 8559 /// The node which references the value to be captured. 8560 inner_ref_node: Ast.Node.Index, 8561 /// The number of namespaces being tunnelled through. At least 1. 8562 num_tunnels: u32, 8563 /// The value being captured. 8564 value: union(enum) { 8565 ref: Zir.Inst.Ref, 8566 ref_load: Zir.Inst.Ref, 8567 decl_val: Zir.NullTerminatedString, 8568 decl_ref: Zir.NullTerminatedString, 8569 }, 8570 /// The location of the value's declaration. 8571 decl_src: union(enum) { 8572 token: Ast.TokenIndex, 8573 node: Ast.Node.Index, 8574 }, 8575 ) !Zir.Inst.Ref { 8576 switch (value) { 8577 .ref => |v| if (v.toIndex() == null) return v, // trivial value; do not need tunnel 8578 .ref_load => |v| assert(v.toIndex() != null), // there are no constant pointer refs 8579 .decl_val, .decl_ref => {}, 8580 } 8581 8582 const astgen = gz.astgen; 8583 const gpa = astgen.gpa; 8584 8585 // Otherwise we need a tunnel. First, figure out the path of namespaces we 8586 // are tunneling through. This is usually only going to be one or two, so 8587 // use an SFBA to optimize for the common case. 8588 var sfba = std.heap.stackFallback(@sizeOf(usize) * 2, astgen.arena); 8589 var intermediate_tunnels = try sfba.get().alloc(*Scope.Namespace, num_tunnels - 1); 8590 8591 const root_ns = ns: { 8592 var i: usize = num_tunnels - 1; 8593 var scope: *Scope = gz.parent; 8594 while (i > 0) { 8595 if (scope.cast(Scope.Namespace)) |mid_ns| { 8596 i -= 1; 8597 intermediate_tunnels[i] = mid_ns; 8598 } 8599 scope = scope.parent().?; 8600 } 8601 while (true) { 8602 if (scope.cast(Scope.Namespace)) |ns| break :ns ns; 8603 scope = scope.parent().?; 8604 } 8605 }; 8606 8607 // Now that we know the scopes we're tunneling through, begin adding 8608 // captures as required, starting with the outermost namespace. 8609 const root_capture = Zir.Inst.Capture.wrap(switch (value) { 8610 .ref => |v| .{ .instruction = v.toIndex().? }, 8611 .ref_load => |v| .{ .instruction_load = v.toIndex().? }, 8612 .decl_val => |str| .{ .decl_val = str }, 8613 .decl_ref => |str| .{ .decl_ref = str }, 8614 }); 8615 var cur_capture_index = std.math.cast( 8616 u16, 8617 (try root_ns.captures.getOrPut(gpa, root_capture)).index, 8618 ) orelse return astgen.failNodeNotes(root_ns.node, "this compiler implementation only supports up to 65536 captures per namespace", .{}, &.{ 8619 switch (decl_src) { 8620 .token => |t| try astgen.errNoteTok(t, "captured value here", .{}), 8621 .node => |n| try astgen.errNoteNode(n, "captured value here", .{}), 8622 }, 8623 try astgen.errNoteNode(inner_ref_node, "value used here", .{}), 8624 }); 8625 8626 for (intermediate_tunnels) |tunnel_ns| { 8627 cur_capture_index = std.math.cast( 8628 u16, 8629 (try tunnel_ns.captures.getOrPut(gpa, Zir.Inst.Capture.wrap(.{ .nested = cur_capture_index }))).index, 8630 ) orelse return astgen.failNodeNotes(tunnel_ns.node, "this compiler implementation only supports up to 65536 captures per namespace", .{}, &.{ 8631 switch (decl_src) { 8632 .token => |t| try astgen.errNoteTok(t, "captured value here", .{}), 8633 .node => |n| try astgen.errNoteNode(n, "captured value here", .{}), 8634 }, 8635 try astgen.errNoteNode(inner_ref_node, "value used here", .{}), 8636 }); 8637 } 8638 8639 // Incorporate the capture index into the source hash, so that changes in 8640 // the order of captures cause suitable re-analysis. 8641 astgen.src_hasher.update(std.mem.asBytes(&cur_capture_index)); 8642 8643 // Add an instruction to get the value from the closure. 8644 return gz.addExtendedNodeSmall(.closure_get, inner_ref_node, cur_capture_index); 8645 } 8646 8647 fn stringLiteral( 8648 gz: *GenZir, 8649 ri: ResultInfo, 8650 node: Ast.Node.Index, 8651 ) InnerError!Zir.Inst.Ref { 8652 const astgen = gz.astgen; 8653 const tree = astgen.tree; 8654 const main_tokens = tree.nodes.items(.main_token); 8655 const str_lit_token = main_tokens[node]; 8656 const str = try astgen.strLitAsString(str_lit_token); 8657 const result = try gz.add(.{ 8658 .tag = .str, 8659 .data = .{ .str = .{ 8660 .start = str.index, 8661 .len = str.len, 8662 } }, 8663 }); 8664 return rvalue(gz, ri, result, node); 8665 } 8666 8667 fn multilineStringLiteral( 8668 gz: *GenZir, 8669 ri: ResultInfo, 8670 node: Ast.Node.Index, 8671 ) InnerError!Zir.Inst.Ref { 8672 const astgen = gz.astgen; 8673 const str = try astgen.strLitNodeAsString(node); 8674 const result = try gz.add(.{ 8675 .tag = .str, 8676 .data = .{ .str = .{ 8677 .start = str.index, 8678 .len = str.len, 8679 } }, 8680 }); 8681 return rvalue(gz, ri, result, node); 8682 } 8683 8684 fn charLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 8685 const astgen = gz.astgen; 8686 const tree = astgen.tree; 8687 const main_tokens = tree.nodes.items(.main_token); 8688 const main_token = main_tokens[node]; 8689 const slice = tree.tokenSlice(main_token); 8690 8691 switch (std.zig.parseCharLiteral(slice)) { 8692 .success => |codepoint| { 8693 const result = try gz.addInt(codepoint); 8694 return rvalue(gz, ri, result, node); 8695 }, 8696 .failure => |err| return astgen.failWithStrLitError(err, main_token, slice, 0), 8697 } 8698 } 8699 8700 const Sign = enum { negative, positive }; 8701 8702 fn numberLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index, source_node: Ast.Node.Index, sign: Sign) InnerError!Zir.Inst.Ref { 8703 const astgen = gz.astgen; 8704 const tree = astgen.tree; 8705 const main_tokens = tree.nodes.items(.main_token); 8706 const num_token = main_tokens[node]; 8707 const bytes = tree.tokenSlice(num_token); 8708 8709 const result: Zir.Inst.Ref = switch (std.zig.parseNumberLiteral(bytes)) { 8710 .int => |num| switch (num) { 8711 0 => if (sign == .positive) .zero else return astgen.failTokNotes( 8712 num_token, 8713 "integer literal '-0' is ambiguous", 8714 .{}, 8715 &.{ 8716 try astgen.errNoteTok(num_token, "use '0' for an integer zero", .{}), 8717 try astgen.errNoteTok(num_token, "use '-0.0' for a floating-point signed zero", .{}), 8718 }, 8719 ), 8720 1 => { 8721 // Handle the negation here! 8722 const result: Zir.Inst.Ref = switch (sign) { 8723 .positive => .one, 8724 .negative => .negative_one, 8725 }; 8726 return rvalue(gz, ri, result, source_node); 8727 }, 8728 else => try gz.addInt(num), 8729 }, 8730 .big_int => |base| big: { 8731 const gpa = astgen.gpa; 8732 var big_int = try std.math.big.int.Managed.init(gpa); 8733 defer big_int.deinit(); 8734 const prefix_offset: usize = if (base == .decimal) 0 else 2; 8735 big_int.setString(@intFromEnum(base), bytes[prefix_offset..]) catch |err| switch (err) { 8736 error.InvalidCharacter => unreachable, // caught in `parseNumberLiteral` 8737 error.InvalidBase => unreachable, // we only pass 16, 8, 2, see above 8738 error.OutOfMemory => return error.OutOfMemory, 8739 }; 8740 8741 const limbs = big_int.limbs[0..big_int.len()]; 8742 assert(big_int.isPositive()); 8743 break :big try gz.addIntBig(limbs); 8744 }, 8745 .float => { 8746 const unsigned_float_number = std.fmt.parseFloat(f128, bytes) catch |err| switch (err) { 8747 error.InvalidCharacter => unreachable, // validated by tokenizer 8748 }; 8749 const float_number = switch (sign) { 8750 .negative => -unsigned_float_number, 8751 .positive => unsigned_float_number, 8752 }; 8753 // If the value fits into a f64 without losing any precision, store it that way. 8754 @setFloatMode(.strict); 8755 const smaller_float: f64 = @floatCast(float_number); 8756 const bigger_again: f128 = smaller_float; 8757 if (bigger_again == float_number) { 8758 const result = try gz.addFloat(smaller_float); 8759 return rvalue(gz, ri, result, source_node); 8760 } 8761 // We need to use 128 bits. Break the float into 4 u32 values so we can 8762 // put it into the `extra` array. 8763 const int_bits: u128 = @bitCast(float_number); 8764 const result = try gz.addPlNode(.float128, node, Zir.Inst.Float128{ 8765 .piece0 = @truncate(int_bits), 8766 .piece1 = @truncate(int_bits >> 32), 8767 .piece2 = @truncate(int_bits >> 64), 8768 .piece3 = @truncate(int_bits >> 96), 8769 }); 8770 return rvalue(gz, ri, result, source_node); 8771 }, 8772 .failure => |err| return astgen.failWithNumberError(err, num_token, bytes), 8773 }; 8774 8775 if (sign == .positive) { 8776 return rvalue(gz, ri, result, source_node); 8777 } else { 8778 const negated = try gz.addUnNode(.negate, result, source_node); 8779 return rvalue(gz, ri, negated, source_node); 8780 } 8781 } 8782 8783 fn failWithNumberError(astgen: *AstGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) InnerError { 8784 const is_float = std.mem.indexOfScalar(u8, bytes, '.') != null; 8785 switch (err) { 8786 .leading_zero => if (is_float) { 8787 return astgen.failTok(token, "number '{s}' has leading zero", .{bytes}); 8788 } else { 8789 return astgen.failTokNotes(token, "number '{s}' has leading zero", .{bytes}, &.{ 8790 try astgen.errNoteTok(token, "use '0o' prefix for octal literals", .{}), 8791 }); 8792 }, 8793 .digit_after_base => return astgen.failTok(token, "expected a digit after base prefix", .{}), 8794 .upper_case_base => |i| return astgen.failOff(token, @intCast(i), "base prefix must be lowercase", .{}), 8795 .invalid_float_base => |i| return astgen.failOff(token, @intCast(i), "invalid base for float literal", .{}), 8796 .repeated_underscore => |i| return astgen.failOff(token, @intCast(i), "repeated digit separator", .{}), 8797 .invalid_underscore_after_special => |i| return astgen.failOff(token, @intCast(i), "expected digit before digit separator", .{}), 8798 .invalid_digit => |info| return astgen.failOff(token, @intCast(info.i), "invalid digit '{c}' for {s} base", .{ bytes[info.i], @tagName(info.base) }), 8799 .invalid_digit_exponent => |i| return astgen.failOff(token, @intCast(i), "invalid digit '{c}' in exponent", .{bytes[i]}), 8800 .duplicate_exponent => |i| return astgen.failOff(token, @intCast(i), "duplicate exponent", .{}), 8801 .exponent_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before exponent", .{}), 8802 .special_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before '{c}'", .{bytes[i]}), 8803 .trailing_special => |i| return astgen.failOff(token, @intCast(i), "expected digit after '{c}'", .{bytes[i - 1]}), 8804 .trailing_underscore => |i| return astgen.failOff(token, @intCast(i), "trailing digit separator", .{}), 8805 .duplicate_period => unreachable, // Validated by tokenizer 8806 .invalid_character => unreachable, // Validated by tokenizer 8807 .invalid_exponent_sign => |i| { 8808 assert(bytes.len >= 2 and bytes[0] == '0' and bytes[1] == 'x'); // Validated by tokenizer 8809 return astgen.failOff(token, @intCast(i), "sign '{c}' cannot follow digit '{c}' in hex base", .{ bytes[i], bytes[i - 1] }); 8810 }, 8811 .period_after_exponent => |i| return astgen.failOff(token, @intCast(i), "unexpected period after exponent", .{}), 8812 } 8813 } 8814 8815 fn asmExpr( 8816 gz: *GenZir, 8817 scope: *Scope, 8818 ri: ResultInfo, 8819 node: Ast.Node.Index, 8820 full: Ast.full.Asm, 8821 ) InnerError!Zir.Inst.Ref { 8822 const astgen = gz.astgen; 8823 const tree = astgen.tree; 8824 const main_tokens = tree.nodes.items(.main_token); 8825 const node_datas = tree.nodes.items(.data); 8826 const node_tags = tree.nodes.items(.tag); 8827 const token_tags = tree.tokens.items(.tag); 8828 8829 const TagAndTmpl = struct { tag: Zir.Inst.Extended, tmpl: Zir.NullTerminatedString }; 8830 const tag_and_tmpl: TagAndTmpl = switch (node_tags[full.ast.template]) { 8831 .string_literal => .{ 8832 .tag = .@"asm", 8833 .tmpl = (try astgen.strLitAsString(main_tokens[full.ast.template])).index, 8834 }, 8835 .multiline_string_literal => .{ 8836 .tag = .@"asm", 8837 .tmpl = (try astgen.strLitNodeAsString(full.ast.template)).index, 8838 }, 8839 else => .{ 8840 .tag = .asm_expr, 8841 .tmpl = @enumFromInt(@intFromEnum(try comptimeExpr(gz, scope, .{ .rl = .none }, full.ast.template))), 8842 }, 8843 }; 8844 8845 // See https://github.com/ziglang/zig/issues/215 and related issues discussing 8846 // possible inline assembly improvements. Until then here is status quo AstGen 8847 // for assembly syntax. It's used by std lib crypto aesni.zig. 8848 const is_container_asm = astgen.fn_block == null; 8849 if (is_container_asm) { 8850 if (full.volatile_token) |t| 8851 return astgen.failTok(t, "volatile is meaningless on global assembly", .{}); 8852 if (full.outputs.len != 0 or full.inputs.len != 0 or full.first_clobber != null) 8853 return astgen.failNode(node, "global assembly cannot have inputs, outputs, or clobbers", .{}); 8854 } else { 8855 if (full.outputs.len == 0 and full.volatile_token == null) { 8856 return astgen.failNode(node, "assembly expression with no output must be marked volatile", .{}); 8857 } 8858 } 8859 if (full.outputs.len > 32) { 8860 return astgen.failNode(full.outputs[32], "too many asm outputs", .{}); 8861 } 8862 var outputs_buffer: [32]Zir.Inst.Asm.Output = undefined; 8863 const outputs = outputs_buffer[0..full.outputs.len]; 8864 8865 var output_type_bits: u32 = 0; 8866 8867 for (full.outputs, 0..) |output_node, i| { 8868 const symbolic_name = main_tokens[output_node]; 8869 const name = try astgen.identAsString(symbolic_name); 8870 const constraint_token = symbolic_name + 2; 8871 const constraint = (try astgen.strLitAsString(constraint_token)).index; 8872 const has_arrow = token_tags[symbolic_name + 4] == .arrow; 8873 if (has_arrow) { 8874 if (output_type_bits != 0) { 8875 return astgen.failNode(output_node, "inline assembly allows up to one output value", .{}); 8876 } 8877 output_type_bits |= @as(u32, 1) << @intCast(i); 8878 const out_type_node = node_datas[output_node].lhs; 8879 const out_type_inst = try typeExpr(gz, scope, out_type_node); 8880 outputs[i] = .{ 8881 .name = name, 8882 .constraint = constraint, 8883 .operand = out_type_inst, 8884 }; 8885 } else { 8886 const ident_token = symbolic_name + 4; 8887 // TODO have a look at #215 and related issues and decide how to 8888 // handle outputs. Do we want this to be identifiers? 8889 // Or maybe we want to force this to be expressions with a pointer type. 8890 outputs[i] = .{ 8891 .name = name, 8892 .constraint = constraint, 8893 .operand = try localVarRef(gz, scope, .{ .rl = .ref }, node, ident_token), 8894 }; 8895 } 8896 } 8897 8898 if (full.inputs.len > 32) { 8899 return astgen.failNode(full.inputs[32], "too many asm inputs", .{}); 8900 } 8901 var inputs_buffer: [32]Zir.Inst.Asm.Input = undefined; 8902 const inputs = inputs_buffer[0..full.inputs.len]; 8903 8904 for (full.inputs, 0..) |input_node, i| { 8905 const symbolic_name = main_tokens[input_node]; 8906 const name = try astgen.identAsString(symbolic_name); 8907 const constraint_token = symbolic_name + 2; 8908 const constraint = (try astgen.strLitAsString(constraint_token)).index; 8909 const operand = try expr(gz, scope, .{ .rl = .none }, node_datas[input_node].lhs); 8910 inputs[i] = .{ 8911 .name = name, 8912 .constraint = constraint, 8913 .operand = operand, 8914 }; 8915 } 8916 8917 var clobbers_buffer: [32]u32 = undefined; 8918 var clobber_i: usize = 0; 8919 if (full.first_clobber) |first_clobber| clobbers: { 8920 // asm ("foo" ::: "a", "b") 8921 // asm ("foo" ::: "a", "b",) 8922 var tok_i = first_clobber; 8923 while (true) : (tok_i += 1) { 8924 if (clobber_i >= clobbers_buffer.len) { 8925 return astgen.failTok(tok_i, "too many asm clobbers", .{}); 8926 } 8927 clobbers_buffer[clobber_i] = @intFromEnum((try astgen.strLitAsString(tok_i)).index); 8928 clobber_i += 1; 8929 tok_i += 1; 8930 switch (token_tags[tok_i]) { 8931 .r_paren => break :clobbers, 8932 .comma => { 8933 if (token_tags[tok_i + 1] == .r_paren) { 8934 break :clobbers; 8935 } else { 8936 continue; 8937 } 8938 }, 8939 else => unreachable, 8940 } 8941 } 8942 } 8943 8944 const result = try gz.addAsm(.{ 8945 .tag = tag_and_tmpl.tag, 8946 .node = node, 8947 .asm_source = tag_and_tmpl.tmpl, 8948 .is_volatile = full.volatile_token != null, 8949 .output_type_bits = output_type_bits, 8950 .outputs = outputs, 8951 .inputs = inputs, 8952 .clobbers = clobbers_buffer[0..clobber_i], 8953 }); 8954 return rvalue(gz, ri, result, node); 8955 } 8956 8957 fn as( 8958 gz: *GenZir, 8959 scope: *Scope, 8960 ri: ResultInfo, 8961 node: Ast.Node.Index, 8962 lhs: Ast.Node.Index, 8963 rhs: Ast.Node.Index, 8964 ) InnerError!Zir.Inst.Ref { 8965 const dest_type = try typeExpr(gz, scope, lhs); 8966 const result = try reachableExpr(gz, scope, .{ .rl = .{ .ty = dest_type } }, rhs, node); 8967 return rvalue(gz, ri, result, node); 8968 } 8969 8970 fn unionInit( 8971 gz: *GenZir, 8972 scope: *Scope, 8973 ri: ResultInfo, 8974 node: Ast.Node.Index, 8975 params: []const Ast.Node.Index, 8976 ) InnerError!Zir.Inst.Ref { 8977 const union_type = try typeExpr(gz, scope, params[0]); 8978 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]); 8979 const field_type = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{ 8980 .container_type = union_type, 8981 .field_name = field_name, 8982 }); 8983 const init = try reachableExpr(gz, scope, .{ .rl = .{ .ty = field_type } }, params[2], node); 8984 const result = try gz.addPlNode(.union_init, node, Zir.Inst.UnionInit{ 8985 .union_type = union_type, 8986 .init = init, 8987 .field_name = field_name, 8988 }); 8989 return rvalue(gz, ri, result, node); 8990 } 8991 8992 fn bitCast( 8993 gz: *GenZir, 8994 scope: *Scope, 8995 ri: ResultInfo, 8996 node: Ast.Node.Index, 8997 operand_node: Ast.Node.Index, 8998 ) InnerError!Zir.Inst.Ref { 8999 const dest_type = try ri.rl.resultTypeForCast(gz, node, "@bitCast"); 9000 const operand = try reachableExpr(gz, scope, .{ .rl = .none }, operand_node, node); 9001 const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{ 9002 .lhs = dest_type, 9003 .rhs = operand, 9004 }); 9005 return rvalue(gz, ri, result, node); 9006 } 9007 9008 /// Handle one or more nested pointer cast builtins: 9009 /// * @ptrCast 9010 /// * @alignCast 9011 /// * @addrSpaceCast 9012 /// * @constCast 9013 /// * @volatileCast 9014 /// Any sequence of such builtins is treated as a single operation. This allowed 9015 /// for sequences like `@ptrCast(@alignCast(ptr))` to work correctly despite the 9016 /// intermediate result type being unknown. 9017 fn ptrCast( 9018 gz: *GenZir, 9019 scope: *Scope, 9020 ri: ResultInfo, 9021 root_node: Ast.Node.Index, 9022 ) InnerError!Zir.Inst.Ref { 9023 const astgen = gz.astgen; 9024 const tree = astgen.tree; 9025 const main_tokens = tree.nodes.items(.main_token); 9026 const node_datas = tree.nodes.items(.data); 9027 const node_tags = tree.nodes.items(.tag); 9028 9029 const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?; 9030 var flags: Zir.Inst.FullPtrCastFlags = .{}; 9031 9032 // Note that all pointer cast builtins have one parameter, so we only need 9033 // to handle `builtin_call_two`. 9034 var node = root_node; 9035 while (true) { 9036 switch (node_tags[node]) { 9037 .builtin_call_two, .builtin_call_two_comma => {}, 9038 .grouped_expression => { 9039 // Handle the chaining even with redundant parentheses 9040 node = node_datas[node].lhs; 9041 continue; 9042 }, 9043 else => break, 9044 } 9045 9046 if (node_datas[node].lhs == 0) break; // 0 args 9047 9048 const builtin_token = main_tokens[node]; 9049 const builtin_name = tree.tokenSlice(builtin_token); 9050 const info = BuiltinFn.list.get(builtin_name) orelse break; 9051 if (node_datas[node].rhs == 0) { 9052 // 1 arg 9053 if (info.param_count != 1) break; 9054 9055 switch (info.tag) { 9056 else => break, 9057 inline .ptr_cast, 9058 .align_cast, 9059 .addrspace_cast, 9060 .const_cast, 9061 .volatile_cast, 9062 => |tag| { 9063 if (@field(flags, @tagName(tag))) { 9064 return astgen.failNode(node, "redundant {s}", .{builtin_name}); 9065 } 9066 @field(flags, @tagName(tag)) = true; 9067 }, 9068 } 9069 9070 node = node_datas[node].lhs; 9071 } else { 9072 // 2 args 9073 if (info.param_count != 2) break; 9074 9075 switch (info.tag) { 9076 else => break, 9077 .field_parent_ptr => { 9078 if (flags.ptr_cast) break; 9079 9080 const flags_int: FlagsInt = @bitCast(flags); 9081 const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node); 9082 const parent_ptr_type = try ri.rl.resultTypeForCast(gz, root_node, "@alignCast"); 9083 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, node_datas[node].lhs); 9084 const field_ptr = try expr(gz, scope, .{ .rl = .none }, node_datas[node].rhs); 9085 try emitDbgStmt(gz, cursor); 9086 const result = try gz.addExtendedPayloadSmall(.field_parent_ptr, flags_int, Zir.Inst.FieldParentPtr{ 9087 .src_node = gz.nodeIndexToRelative(node), 9088 .parent_ptr_type = parent_ptr_type, 9089 .field_name = field_name, 9090 .field_ptr = field_ptr, 9091 }); 9092 return rvalue(gz, ri, result, root_node); 9093 }, 9094 } 9095 } 9096 } 9097 9098 const flags_int: FlagsInt = @bitCast(flags); 9099 assert(flags_int != 0); 9100 9101 const ptr_only: Zir.Inst.FullPtrCastFlags = .{ .ptr_cast = true }; 9102 if (flags_int == @as(FlagsInt, @bitCast(ptr_only))) { 9103 // Special case: simpler representation 9104 return typeCast(gz, scope, ri, root_node, node, .ptr_cast, "@ptrCast"); 9105 } 9106 9107 const no_result_ty_flags: Zir.Inst.FullPtrCastFlags = .{ 9108 .const_cast = true, 9109 .volatile_cast = true, 9110 }; 9111 if ((flags_int & ~@as(FlagsInt, @bitCast(no_result_ty_flags))) == 0) { 9112 // Result type not needed 9113 const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node); 9114 const operand = try expr(gz, scope, .{ .rl = .none }, node); 9115 try emitDbgStmt(gz, cursor); 9116 const result = try gz.addExtendedPayloadSmall(.ptr_cast_no_dest, flags_int, Zir.Inst.UnNode{ 9117 .node = gz.nodeIndexToRelative(root_node), 9118 .operand = operand, 9119 }); 9120 return rvalue(gz, ri, result, root_node); 9121 } 9122 9123 // Full cast including result type 9124 9125 const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node); 9126 const result_type = try ri.rl.resultTypeForCast(gz, root_node, flags.needResultTypeBuiltinName()); 9127 const operand = try expr(gz, scope, .{ .rl = .none }, node); 9128 try emitDbgStmt(gz, cursor); 9129 const result = try gz.addExtendedPayloadSmall(.ptr_cast_full, flags_int, Zir.Inst.BinNode{ 9130 .node = gz.nodeIndexToRelative(root_node), 9131 .lhs = result_type, 9132 .rhs = operand, 9133 }); 9134 return rvalue(gz, ri, result, root_node); 9135 } 9136 9137 fn typeOf( 9138 gz: *GenZir, 9139 scope: *Scope, 9140 ri: ResultInfo, 9141 node: Ast.Node.Index, 9142 args: []const Ast.Node.Index, 9143 ) InnerError!Zir.Inst.Ref { 9144 const astgen = gz.astgen; 9145 if (args.len < 1) { 9146 return astgen.failNode(node, "expected at least 1 argument, found 0", .{}); 9147 } 9148 const gpa = astgen.gpa; 9149 if (args.len == 1) { 9150 const typeof_inst = try gz.makeBlockInst(.typeof_builtin, node); 9151 9152 var typeof_scope = gz.makeSubBlock(scope); 9153 typeof_scope.is_comptime = false; 9154 typeof_scope.is_typeof = true; 9155 typeof_scope.c_import = false; 9156 defer typeof_scope.unstack(); 9157 9158 const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, args[0], node); 9159 if (!gz.refIsNoReturn(ty_expr)) { 9160 _ = try typeof_scope.addBreak(.break_inline, typeof_inst, ty_expr); 9161 } 9162 try typeof_scope.setBlockBody(typeof_inst); 9163 9164 // typeof_scope unstacked now, can add new instructions to gz 9165 try gz.instructions.append(gpa, typeof_inst); 9166 return rvalue(gz, ri, typeof_inst.toRef(), node); 9167 } 9168 const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len; 9169 const payload_index = try reserveExtra(astgen, payload_size + args.len); 9170 const args_index = payload_index + payload_size; 9171 9172 const typeof_inst = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, args.len); 9173 9174 var typeof_scope = gz.makeSubBlock(scope); 9175 typeof_scope.is_comptime = false; 9176 9177 for (args, 0..) |arg, i| { 9178 const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, arg, node); 9179 astgen.extra.items[args_index + i] = @intFromEnum(param_ref); 9180 } 9181 _ = try typeof_scope.addBreak(.break_inline, typeof_inst.toIndex().?, .void_value); 9182 9183 const body = typeof_scope.instructionsSlice(); 9184 const body_len = astgen.countBodyLenAfterFixups(body); 9185 astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{ 9186 .body_len = @intCast(body_len), 9187 .body_index = @intCast(astgen.extra.items.len), 9188 .src_node = gz.nodeIndexToRelative(node), 9189 }); 9190 try astgen.extra.ensureUnusedCapacity(gpa, body_len); 9191 astgen.appendBodyWithFixups(body); 9192 typeof_scope.unstack(); 9193 9194 return rvalue(gz, ri, typeof_inst, node); 9195 } 9196 9197 fn minMax( 9198 gz: *GenZir, 9199 scope: *Scope, 9200 ri: ResultInfo, 9201 node: Ast.Node.Index, 9202 args: []const Ast.Node.Index, 9203 comptime op: enum { min, max }, 9204 ) InnerError!Zir.Inst.Ref { 9205 const astgen = gz.astgen; 9206 if (args.len < 2) { 9207 return astgen.failNode(node, "expected at least 2 arguments, found {}", .{args.len}); 9208 } 9209 if (args.len == 2) { 9210 const tag: Zir.Inst.Tag = switch (op) { 9211 .min => .min, 9212 .max => .max, 9213 }; 9214 const a = try expr(gz, scope, .{ .rl = .none }, args[0]); 9215 const b = try expr(gz, scope, .{ .rl = .none }, args[1]); 9216 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 9217 .lhs = a, 9218 .rhs = b, 9219 }); 9220 return rvalue(gz, ri, result, node); 9221 } 9222 const payload_index = try addExtra(astgen, Zir.Inst.NodeMultiOp{ 9223 .src_node = gz.nodeIndexToRelative(node), 9224 }); 9225 var extra_index = try reserveExtra(gz.astgen, args.len); 9226 for (args) |arg| { 9227 const arg_ref = try expr(gz, scope, .{ .rl = .none }, arg); 9228 astgen.extra.items[extra_index] = @intFromEnum(arg_ref); 9229 extra_index += 1; 9230 } 9231 const tag: Zir.Inst.Extended = switch (op) { 9232 .min => .min_multi, 9233 .max => .max_multi, 9234 }; 9235 const result = try gz.addExtendedMultiOpPayloadIndex(tag, payload_index, args.len); 9236 return rvalue(gz, ri, result, node); 9237 } 9238 9239 fn builtinCall( 9240 gz: *GenZir, 9241 scope: *Scope, 9242 ri: ResultInfo, 9243 node: Ast.Node.Index, 9244 params: []const Ast.Node.Index, 9245 allow_branch_hint: bool, 9246 ) InnerError!Zir.Inst.Ref { 9247 const astgen = gz.astgen; 9248 const tree = astgen.tree; 9249 const main_tokens = tree.nodes.items(.main_token); 9250 9251 const builtin_token = main_tokens[node]; 9252 const builtin_name = tree.tokenSlice(builtin_token); 9253 9254 // We handle the different builtins manually because they have different semantics depending 9255 // on the function. For example, `@as` and others participate in result location semantics, 9256 // and `@cImport` creates a special scope that collects a .c source code text buffer. 9257 // Also, some builtins have a variable number of parameters. 9258 9259 const info = BuiltinFn.list.get(builtin_name) orelse { 9260 return astgen.failNode(node, "invalid builtin function: '{s}'", .{ 9261 builtin_name, 9262 }); 9263 }; 9264 if (info.param_count) |expected| { 9265 if (expected != params.len) { 9266 const s = if (expected == 1) "" else "s"; 9267 return astgen.failNode(node, "expected {d} argument{s}, found {d}", .{ 9268 expected, s, params.len, 9269 }); 9270 } 9271 } 9272 9273 // Check function scope-only builtins 9274 9275 if (astgen.fn_block == null and info.illegal_outside_function) 9276 return astgen.failNode(node, "'{s}' outside function scope", .{builtin_name}); 9277 9278 switch (info.tag) { 9279 .branch_hint => { 9280 if (!allow_branch_hint) { 9281 return astgen.failNode(node, "'@branchHint' must appear as the first statement in a function or conditional branch", .{}); 9282 } 9283 const hint_ty = try gz.addBuiltinValue(node, .branch_hint); 9284 const hint_val = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = hint_ty } }, params[0]); 9285 _ = try gz.addExtendedPayload(.branch_hint, Zir.Inst.UnNode{ 9286 .node = gz.nodeIndexToRelative(node), 9287 .operand = hint_val, 9288 }); 9289 return rvalue(gz, ri, .void_value, node); 9290 }, 9291 .import => { 9292 const node_tags = tree.nodes.items(.tag); 9293 const operand_node = params[0]; 9294 9295 if (node_tags[operand_node] != .string_literal) { 9296 // Spec reference: https://github.com/ziglang/zig/issues/2206 9297 return astgen.failNode(operand_node, "@import operand must be a string literal", .{}); 9298 } 9299 const str_lit_token = main_tokens[operand_node]; 9300 const str = try astgen.strLitAsString(str_lit_token); 9301 const str_slice = astgen.string_bytes.items[@intFromEnum(str.index)..][0..str.len]; 9302 if (mem.indexOfScalar(u8, str_slice, 0) != null) { 9303 return astgen.failTok(str_lit_token, "import path cannot contain null bytes", .{}); 9304 } else if (str.len == 0) { 9305 return astgen.failTok(str_lit_token, "import path cannot be empty", .{}); 9306 } 9307 const result = try gz.addStrTok(.import, str.index, str_lit_token); 9308 const gop = try astgen.imports.getOrPut(astgen.gpa, str.index); 9309 if (!gop.found_existing) { 9310 gop.value_ptr.* = str_lit_token; 9311 } 9312 return rvalue(gz, ri, result, node); 9313 }, 9314 .compile_log => { 9315 const payload_index = try addExtra(gz.astgen, Zir.Inst.NodeMultiOp{ 9316 .src_node = gz.nodeIndexToRelative(node), 9317 }); 9318 var extra_index = try reserveExtra(gz.astgen, params.len); 9319 for (params) |param| { 9320 const param_ref = try expr(gz, scope, .{ .rl = .none }, param); 9321 astgen.extra.items[extra_index] = @intFromEnum(param_ref); 9322 extra_index += 1; 9323 } 9324 const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log, payload_index, params.len); 9325 return rvalue(gz, ri, result, node); 9326 }, 9327 .field => { 9328 if (ri.rl == .ref or ri.rl == .ref_coerced_ty) { 9329 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{ 9330 .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]), 9331 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]), 9332 }); 9333 } 9334 const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{ 9335 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), 9336 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]), 9337 }); 9338 return rvalue(gz, ri, result, node); 9339 }, 9340 .FieldType => { 9341 const ty_inst = try typeExpr(gz, scope, params[0]); 9342 const name_inst = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]); 9343 const result = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{ 9344 .container_type = ty_inst, 9345 .field_name = name_inst, 9346 }); 9347 return rvalue(gz, ri, result, node); 9348 }, 9349 9350 // zig fmt: off 9351 .as => return as( gz, scope, ri, node, params[0], params[1]), 9352 .bit_cast => return bitCast( gz, scope, ri, node, params[0]), 9353 .TypeOf => return typeOf( gz, scope, ri, node, params), 9354 .union_init => return unionInit(gz, scope, ri, node, params), 9355 .c_import => return cImport( gz, scope, node, params[0]), 9356 .min => return minMax( gz, scope, ri, node, params, .min), 9357 .max => return minMax( gz, scope, ri, node, params, .max), 9358 // zig fmt: on 9359 9360 .@"export" => { 9361 const exported = try expr(gz, scope, .{ .rl = .none }, params[0]); 9362 const export_options_ty = try gz.addBuiltinValue(node, .export_options); 9363 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = export_options_ty } }, params[1]); 9364 _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{ 9365 .exported = exported, 9366 .options = options, 9367 }); 9368 return rvalue(gz, ri, .void_value, node); 9369 }, 9370 .@"extern" => { 9371 const type_inst = try typeExpr(gz, scope, params[0]); 9372 const extern_options_ty = try gz.addBuiltinValue(node, .extern_options); 9373 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = extern_options_ty } }, params[1]); 9374 const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{ 9375 .node = gz.nodeIndexToRelative(node), 9376 .lhs = type_inst, 9377 .rhs = options, 9378 }); 9379 return rvalue(gz, ri, result, node); 9380 }, 9381 .set_float_mode => { 9382 const float_mode_ty = try gz.addBuiltinValue(node, .float_mode); 9383 const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_mode_ty } }, params[0]); 9384 _ = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{ 9385 .node = gz.nodeIndexToRelative(node), 9386 .operand = order, 9387 }); 9388 return rvalue(gz, ri, .void_value, node); 9389 }, 9390 9391 .src => { 9392 // Incorporate the source location into the source hash, so that 9393 // changes in the source location of `@src()` result in re-analysis. 9394 astgen.src_hasher.update( 9395 std.mem.asBytes(&astgen.source_line) ++ 9396 std.mem.asBytes(&astgen.source_column), 9397 ); 9398 9399 const token_starts = tree.tokens.items(.start); 9400 const node_start = token_starts[tree.firstToken(node)]; 9401 astgen.advanceSourceCursor(node_start); 9402 const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.Src{ 9403 .node = gz.nodeIndexToRelative(node), 9404 .line = astgen.source_line, 9405 .column = astgen.source_column, 9406 }); 9407 return rvalue(gz, ri, result, node); 9408 }, 9409 9410 // zig fmt: off 9411 .This => return rvalue(gz, ri, try gz.addNodeExtended(.this, node), node), 9412 .return_address => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr, node), node), 9413 .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node), 9414 .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node), 9415 .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node), 9416 .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node), 9417 .disable_instrumentation => return rvalue(gz, ri, try gz.addNodeExtended(.disable_instrumentation, node), node), 9418 9419 .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info), 9420 .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of), 9421 .bit_size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .bit_size_of), 9422 .align_of => return simpleUnOpType(gz, scope, ri, node, params[0], .align_of), 9423 9424 .int_from_ptr => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_ptr), 9425 .compile_error => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .compile_error), 9426 .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota), 9427 .int_from_enum => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_enum), 9428 .int_from_bool => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_bool), 9429 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file), 9430 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name), 9431 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, params[0], .set_runtime_safety), 9432 .sqrt => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sqrt), 9433 .sin => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sin), 9434 .cos => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .cos), 9435 .tan => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tan), 9436 .exp => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .exp), 9437 .exp2 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .exp2), 9438 .log => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log), 9439 .log2 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log2), 9440 .log10 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log10), 9441 .abs => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .abs), 9442 .floor => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .floor), 9443 .ceil => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .ceil), 9444 .trunc => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .trunc), 9445 .round => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .round), 9446 .tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name), 9447 .type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name), 9448 .Frame => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_type), 9449 .frame_size => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_size), 9450 9451 .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name), 9452 .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name), 9453 .ptr_from_int => return typeCast(gz, scope, ri, node, params[0], .ptr_from_int, builtin_name), 9454 .enum_from_int => return typeCast(gz, scope, ri, node, params[0], .enum_from_int, builtin_name), 9455 .float_cast => return typeCast(gz, scope, ri, node, params[0], .float_cast, builtin_name), 9456 .int_cast => return typeCast(gz, scope, ri, node, params[0], .int_cast, builtin_name), 9457 .truncate => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name), 9458 // zig fmt: on 9459 9460 .in_comptime => if (gz.is_comptime) { 9461 return astgen.failNode(node, "redundant '@inComptime' in comptime scope", .{}); 9462 } else { 9463 return rvalue(gz, ri, try gz.addNodeExtended(.in_comptime, node), node); 9464 }, 9465 9466 .Type => { 9467 const type_info_ty = try gz.addBuiltinValue(node, .type_info); 9468 const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = type_info_ty } }, params[0]); 9469 9470 const gpa = gz.astgen.gpa; 9471 9472 try gz.instructions.ensureUnusedCapacity(gpa, 1); 9473 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 9474 9475 const payload_index = try gz.astgen.addExtra(Zir.Inst.Reify{ 9476 .node = node, // Absolute node index -- see the definition of `Reify`. 9477 .operand = operand, 9478 .src_line = astgen.source_line, 9479 }); 9480 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 9481 gz.astgen.instructions.appendAssumeCapacity(.{ 9482 .tag = .extended, 9483 .data = .{ .extended = .{ 9484 .opcode = .reify, 9485 .small = @intFromEnum(gz.anon_name_strategy), 9486 .operand = payload_index, 9487 } }, 9488 }); 9489 gz.instructions.appendAssumeCapacity(new_index); 9490 const result = new_index.toRef(); 9491 return rvalue(gz, ri, result, node); 9492 }, 9493 .panic => { 9494 try emitDbgNode(gz, node); 9495 return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .panic); 9496 }, 9497 .trap => { 9498 try emitDbgNode(gz, node); 9499 _ = try gz.addNode(.trap, node); 9500 return rvalue(gz, ri, .unreachable_value, node); 9501 }, 9502 .int_from_error => { 9503 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]); 9504 const result = try gz.addExtendedPayload(.int_from_error, Zir.Inst.UnNode{ 9505 .node = gz.nodeIndexToRelative(node), 9506 .operand = operand, 9507 }); 9508 return rvalue(gz, ri, result, node); 9509 }, 9510 .error_from_int => { 9511 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]); 9512 const result = try gz.addExtendedPayload(.error_from_int, Zir.Inst.UnNode{ 9513 .node = gz.nodeIndexToRelative(node), 9514 .operand = operand, 9515 }); 9516 return rvalue(gz, ri, result, node); 9517 }, 9518 .error_cast => { 9519 try emitDbgNode(gz, node); 9520 9521 const result = try gz.addExtendedPayload(.error_cast, Zir.Inst.BinNode{ 9522 .lhs = try ri.rl.resultTypeForCast(gz, node, builtin_name), 9523 .rhs = try expr(gz, scope, .{ .rl = .none }, params[0]), 9524 .node = gz.nodeIndexToRelative(node), 9525 }); 9526 return rvalue(gz, ri, result, node); 9527 }, 9528 .ptr_cast, 9529 .align_cast, 9530 .addrspace_cast, 9531 .const_cast, 9532 .volatile_cast, 9533 => return ptrCast(gz, scope, ri, node), 9534 9535 // zig fmt: off 9536 .has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl), 9537 .has_field => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_field), 9538 9539 .clz => return bitBuiltin(gz, scope, ri, node, params[0], .clz), 9540 .ctz => return bitBuiltin(gz, scope, ri, node, params[0], .ctz), 9541 .pop_count => return bitBuiltin(gz, scope, ri, node, params[0], .pop_count), 9542 .byte_swap => return bitBuiltin(gz, scope, ri, node, params[0], .byte_swap), 9543 .bit_reverse => return bitBuiltin(gz, scope, ri, node, params[0], .bit_reverse), 9544 9545 .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact), 9546 .div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor), 9547 .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc), 9548 .mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod), 9549 .rem => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem), 9550 9551 .shl_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shl_exact), 9552 .shr_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shr_exact), 9553 9554 .bit_offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .bit_offset_of), 9555 .offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .offset_of), 9556 9557 .c_undef => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_undef), 9558 .c_include => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_include), 9559 9560 .cmpxchg_strong => return cmpxchg(gz, scope, ri, node, params, 1), 9561 .cmpxchg_weak => return cmpxchg(gz, scope, ri, node, params, 0), 9562 // zig fmt: on 9563 9564 .wasm_memory_size => { 9565 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]); 9566 const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{ 9567 .node = gz.nodeIndexToRelative(node), 9568 .operand = operand, 9569 }); 9570 return rvalue(gz, ri, result, node); 9571 }, 9572 .wasm_memory_grow => { 9573 const index_arg = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]); 9574 const delta_arg = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[1]); 9575 const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{ 9576 .node = gz.nodeIndexToRelative(node), 9577 .lhs = index_arg, 9578 .rhs = delta_arg, 9579 }); 9580 return rvalue(gz, ri, result, node); 9581 }, 9582 .c_define => { 9583 if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{}); 9584 const name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0]); 9585 const value = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]); 9586 const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{ 9587 .node = gz.nodeIndexToRelative(node), 9588 .lhs = name, 9589 .rhs = value, 9590 }); 9591 return rvalue(gz, ri, result, node); 9592 }, 9593 9594 .splat => { 9595 const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name); 9596 const elem_type = try gz.addUnNode(.vec_arr_elem_type, result_type, node); 9597 const scalar = try expr(gz, scope, .{ .rl = .{ .ty = elem_type } }, params[0]); 9598 const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{ 9599 .lhs = result_type, 9600 .rhs = scalar, 9601 }); 9602 return rvalue(gz, ri, result, node); 9603 }, 9604 .reduce => { 9605 const reduce_op_ty = try gz.addBuiltinValue(node, .reduce_op); 9606 const op = try expr(gz, scope, .{ .rl = .{ .coerced_ty = reduce_op_ty } }, params[0]); 9607 const scalar = try expr(gz, scope, .{ .rl = .none }, params[1]); 9608 const result = try gz.addPlNode(.reduce, node, Zir.Inst.Bin{ 9609 .lhs = op, 9610 .rhs = scalar, 9611 }); 9612 return rvalue(gz, ri, result, node); 9613 }, 9614 9615 .add_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .add_with_overflow), 9616 .sub_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .sub_with_overflow), 9617 .mul_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .mul_with_overflow), 9618 .shl_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .shl_with_overflow), 9619 9620 .atomic_load => { 9621 const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order); 9622 const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{ 9623 // zig fmt: off 9624 .elem_type = try typeExpr(gz, scope, params[0]), 9625 .ptr = try expr (gz, scope, .{ .rl = .none }, params[1]), 9626 .ordering = try expr (gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[2]), 9627 // zig fmt: on 9628 }); 9629 return rvalue(gz, ri, result, node); 9630 }, 9631 .atomic_rmw => { 9632 const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order); 9633 const atomic_rmw_op_type = try gz.addBuiltinValue(node, .atomic_rmw_op); 9634 const int_type = try typeExpr(gz, scope, params[0]); 9635 const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{ 9636 // zig fmt: off 9637 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), 9638 .operation = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_rmw_op_type } }, params[2]), 9639 .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[3]), 9640 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[4]), 9641 // zig fmt: on 9642 }); 9643 return rvalue(gz, ri, result, node); 9644 }, 9645 .atomic_store => { 9646 const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order); 9647 const int_type = try typeExpr(gz, scope, params[0]); 9648 _ = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{ 9649 // zig fmt: off 9650 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), 9651 .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]), 9652 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[3]), 9653 // zig fmt: on 9654 }); 9655 return rvalue(gz, ri, .void_value, node); 9656 }, 9657 .mul_add => { 9658 const float_type = try typeExpr(gz, scope, params[0]); 9659 const mulend1 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[1]); 9660 const mulend2 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[2]); 9661 const addend = try expr(gz, scope, .{ .rl = .{ .ty = float_type } }, params[3]); 9662 const result = try gz.addPlNode(.mul_add, node, Zir.Inst.MulAdd{ 9663 .mulend1 = mulend1, 9664 .mulend2 = mulend2, 9665 .addend = addend, 9666 }); 9667 return rvalue(gz, ri, result, node); 9668 }, 9669 .call => { 9670 const call_modifier_ty = try gz.addBuiltinValue(node, .call_modifier); 9671 const modifier = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = call_modifier_ty } }, params[0]); 9672 const callee = try expr(gz, scope, .{ .rl = .none }, params[1]); 9673 const args = try expr(gz, scope, .{ .rl = .none }, params[2]); 9674 const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{ 9675 .modifier = modifier, 9676 .callee = callee, 9677 .args = args, 9678 .flags = .{ 9679 .is_nosuspend = gz.nosuspend_node != 0, 9680 .ensure_result_used = false, 9681 }, 9682 }); 9683 return rvalue(gz, ri, result, node); 9684 }, 9685 .field_parent_ptr => { 9686 const parent_ptr_type = try ri.rl.resultTypeForCast(gz, node, builtin_name); 9687 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0]); 9688 const result = try gz.addExtendedPayloadSmall(.field_parent_ptr, 0, Zir.Inst.FieldParentPtr{ 9689 .src_node = gz.nodeIndexToRelative(node), 9690 .parent_ptr_type = parent_ptr_type, 9691 .field_name = field_name, 9692 .field_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), 9693 }); 9694 return rvalue(gz, ri, result, node); 9695 }, 9696 .memcpy => { 9697 _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{ 9698 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), 9699 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]), 9700 }); 9701 return rvalue(gz, ri, .void_value, node); 9702 }, 9703 .memset => { 9704 const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]); 9705 const lhs_ty = try gz.addUnNode(.typeof, lhs, params[0]); 9706 const elem_ty = try gz.addUnNode(.indexable_ptr_elem_type, lhs_ty, params[0]); 9707 _ = try gz.addPlNode(.memset, node, Zir.Inst.Bin{ 9708 .lhs = lhs, 9709 .rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = elem_ty } }, params[1]), 9710 }); 9711 return rvalue(gz, ri, .void_value, node); 9712 }, 9713 .shuffle => { 9714 const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{ 9715 .elem_type = try typeExpr(gz, scope, params[0]), 9716 .a = try expr(gz, scope, .{ .rl = .none }, params[1]), 9717 .b = try expr(gz, scope, .{ .rl = .none }, params[2]), 9718 .mask = try comptimeExpr(gz, scope, .{ .rl = .none }, params[3]), 9719 }); 9720 return rvalue(gz, ri, result, node); 9721 }, 9722 .select => { 9723 const result = try gz.addExtendedPayload(.select, Zir.Inst.Select{ 9724 .node = gz.nodeIndexToRelative(node), 9725 .elem_type = try typeExpr(gz, scope, params[0]), 9726 .pred = try expr(gz, scope, .{ .rl = .none }, params[1]), 9727 .a = try expr(gz, scope, .{ .rl = .none }, params[2]), 9728 .b = try expr(gz, scope, .{ .rl = .none }, params[3]), 9729 }); 9730 return rvalue(gz, ri, result, node); 9731 }, 9732 .async_call => { 9733 const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{ 9734 .node = gz.nodeIndexToRelative(node), 9735 .frame_buffer = try expr(gz, scope, .{ .rl = .none }, params[0]), 9736 .result_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), 9737 .fn_ptr = try expr(gz, scope, .{ .rl = .none }, params[2]), 9738 .args = try expr(gz, scope, .{ .rl = .none }, params[3]), 9739 }); 9740 return rvalue(gz, ri, result, node); 9741 }, 9742 .Vector => { 9743 const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{ 9744 .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]), 9745 .rhs = try typeExpr(gz, scope, params[1]), 9746 }); 9747 return rvalue(gz, ri, result, node); 9748 }, 9749 .prefetch => { 9750 const prefetch_options_ty = try gz.addBuiltinValue(node, .prefetch_options); 9751 const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]); 9752 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = prefetch_options_ty } }, params[1]); 9753 _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{ 9754 .node = gz.nodeIndexToRelative(node), 9755 .lhs = ptr, 9756 .rhs = options, 9757 }); 9758 return rvalue(gz, ri, .void_value, node); 9759 }, 9760 .c_va_arg => { 9761 const result = try gz.addExtendedPayload(.c_va_arg, Zir.Inst.BinNode{ 9762 .node = gz.nodeIndexToRelative(node), 9763 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), 9764 .rhs = try typeExpr(gz, scope, params[1]), 9765 }); 9766 return rvalue(gz, ri, result, node); 9767 }, 9768 .c_va_copy => { 9769 const result = try gz.addExtendedPayload(.c_va_copy, Zir.Inst.UnNode{ 9770 .node = gz.nodeIndexToRelative(node), 9771 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]), 9772 }); 9773 return rvalue(gz, ri, result, node); 9774 }, 9775 .c_va_end => { 9776 const result = try gz.addExtendedPayload(.c_va_end, Zir.Inst.UnNode{ 9777 .node = gz.nodeIndexToRelative(node), 9778 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]), 9779 }); 9780 return rvalue(gz, ri, result, node); 9781 }, 9782 .c_va_start => { 9783 if (!astgen.fn_var_args) { 9784 return astgen.failNode(node, "'@cVaStart' in a non-variadic function", .{}); 9785 } 9786 return rvalue(gz, ri, try gz.addNodeExtended(.c_va_start, node), node); 9787 }, 9788 9789 .work_item_id => { 9790 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]); 9791 const result = try gz.addExtendedPayload(.work_item_id, Zir.Inst.UnNode{ 9792 .node = gz.nodeIndexToRelative(node), 9793 .operand = operand, 9794 }); 9795 return rvalue(gz, ri, result, node); 9796 }, 9797 .work_group_size => { 9798 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]); 9799 const result = try gz.addExtendedPayload(.work_group_size, Zir.Inst.UnNode{ 9800 .node = gz.nodeIndexToRelative(node), 9801 .operand = operand, 9802 }); 9803 return rvalue(gz, ri, result, node); 9804 }, 9805 .work_group_id => { 9806 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]); 9807 const result = try gz.addExtendedPayload(.work_group_id, Zir.Inst.UnNode{ 9808 .node = gz.nodeIndexToRelative(node), 9809 .operand = operand, 9810 }); 9811 return rvalue(gz, ri, result, node); 9812 }, 9813 } 9814 } 9815 9816 fn hasDeclOrField( 9817 gz: *GenZir, 9818 scope: *Scope, 9819 ri: ResultInfo, 9820 node: Ast.Node.Index, 9821 lhs_node: Ast.Node.Index, 9822 rhs_node: Ast.Node.Index, 9823 tag: Zir.Inst.Tag, 9824 ) InnerError!Zir.Inst.Ref { 9825 const container_type = try typeExpr(gz, scope, lhs_node); 9826 const name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, rhs_node); 9827 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 9828 .lhs = container_type, 9829 .rhs = name, 9830 }); 9831 return rvalue(gz, ri, result, node); 9832 } 9833 9834 fn typeCast( 9835 gz: *GenZir, 9836 scope: *Scope, 9837 ri: ResultInfo, 9838 node: Ast.Node.Index, 9839 operand_node: Ast.Node.Index, 9840 tag: Zir.Inst.Tag, 9841 builtin_name: []const u8, 9842 ) InnerError!Zir.Inst.Ref { 9843 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 9844 const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name); 9845 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node); 9846 9847 try emitDbgStmt(gz, cursor); 9848 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 9849 .lhs = result_type, 9850 .rhs = operand, 9851 }); 9852 return rvalue(gz, ri, result, node); 9853 } 9854 9855 fn simpleUnOpType( 9856 gz: *GenZir, 9857 scope: *Scope, 9858 ri: ResultInfo, 9859 node: Ast.Node.Index, 9860 operand_node: Ast.Node.Index, 9861 tag: Zir.Inst.Tag, 9862 ) InnerError!Zir.Inst.Ref { 9863 const operand = try typeExpr(gz, scope, operand_node); 9864 const result = try gz.addUnNode(tag, operand, node); 9865 return rvalue(gz, ri, result, node); 9866 } 9867 9868 fn simpleUnOp( 9869 gz: *GenZir, 9870 scope: *Scope, 9871 ri: ResultInfo, 9872 node: Ast.Node.Index, 9873 operand_ri: ResultInfo, 9874 operand_node: Ast.Node.Index, 9875 tag: Zir.Inst.Tag, 9876 ) InnerError!Zir.Inst.Ref { 9877 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 9878 const operand = if (tag == .compile_error) 9879 try comptimeExpr(gz, scope, operand_ri, operand_node) 9880 else 9881 try expr(gz, scope, operand_ri, operand_node); 9882 switch (tag) { 9883 .tag_name, .error_name, .int_from_ptr => try emitDbgStmt(gz, cursor), 9884 else => {}, 9885 } 9886 const result = try gz.addUnNode(tag, operand, node); 9887 return rvalue(gz, ri, result, node); 9888 } 9889 9890 fn negation( 9891 gz: *GenZir, 9892 scope: *Scope, 9893 ri: ResultInfo, 9894 node: Ast.Node.Index, 9895 ) InnerError!Zir.Inst.Ref { 9896 const astgen = gz.astgen; 9897 const tree = astgen.tree; 9898 const node_tags = tree.nodes.items(.tag); 9899 const node_datas = tree.nodes.items(.data); 9900 9901 // Check for float literal as the sub-expression because we want to preserve 9902 // its negativity rather than having it go through comptime subtraction. 9903 const operand_node = node_datas[node].lhs; 9904 if (node_tags[operand_node] == .number_literal) { 9905 return numberLiteral(gz, ri, operand_node, node, .negative); 9906 } 9907 9908 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node); 9909 const result = try gz.addUnNode(.negate, operand, node); 9910 return rvalue(gz, ri, result, node); 9911 } 9912 9913 fn cmpxchg( 9914 gz: *GenZir, 9915 scope: *Scope, 9916 ri: ResultInfo, 9917 node: Ast.Node.Index, 9918 params: []const Ast.Node.Index, 9919 small: u16, 9920 ) InnerError!Zir.Inst.Ref { 9921 const int_type = try typeExpr(gz, scope, params[0]); 9922 const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order); 9923 const result = try gz.addExtendedPayloadSmall(.cmpxchg, small, Zir.Inst.Cmpxchg{ 9924 // zig fmt: off 9925 .node = gz.nodeIndexToRelative(node), 9926 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), 9927 .expected_value = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]), 9928 .new_value = try expr(gz, scope, .{ .rl = .{ .coerced_ty = int_type } }, params[3]), 9929 .success_order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[4]), 9930 .failure_order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[5]), 9931 // zig fmt: on 9932 }); 9933 return rvalue(gz, ri, result, node); 9934 } 9935 9936 fn bitBuiltin( 9937 gz: *GenZir, 9938 scope: *Scope, 9939 ri: ResultInfo, 9940 node: Ast.Node.Index, 9941 operand_node: Ast.Node.Index, 9942 tag: Zir.Inst.Tag, 9943 ) InnerError!Zir.Inst.Ref { 9944 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node); 9945 const result = try gz.addUnNode(tag, operand, node); 9946 return rvalue(gz, ri, result, node); 9947 } 9948 9949 fn divBuiltin( 9950 gz: *GenZir, 9951 scope: *Scope, 9952 ri: ResultInfo, 9953 node: Ast.Node.Index, 9954 lhs_node: Ast.Node.Index, 9955 rhs_node: Ast.Node.Index, 9956 tag: Zir.Inst.Tag, 9957 ) InnerError!Zir.Inst.Ref { 9958 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 9959 const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node); 9960 const rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node); 9961 9962 try emitDbgStmt(gz, cursor); 9963 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); 9964 return rvalue(gz, ri, result, node); 9965 } 9966 9967 fn simpleCBuiltin( 9968 gz: *GenZir, 9969 scope: *Scope, 9970 ri: ResultInfo, 9971 node: Ast.Node.Index, 9972 operand_node: Ast.Node.Index, 9973 tag: Zir.Inst.Extended, 9974 ) InnerError!Zir.Inst.Ref { 9975 const name: []const u8 = if (tag == .c_undef) "C undef" else "C include"; 9976 if (!gz.c_import) return gz.astgen.failNode(node, "{s} valid only inside C import block", .{name}); 9977 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, operand_node); 9978 _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{ 9979 .node = gz.nodeIndexToRelative(node), 9980 .operand = operand, 9981 }); 9982 return rvalue(gz, ri, .void_value, node); 9983 } 9984 9985 fn offsetOf( 9986 gz: *GenZir, 9987 scope: *Scope, 9988 ri: ResultInfo, 9989 node: Ast.Node.Index, 9990 lhs_node: Ast.Node.Index, 9991 rhs_node: Ast.Node.Index, 9992 tag: Zir.Inst.Tag, 9993 ) InnerError!Zir.Inst.Ref { 9994 const type_inst = try typeExpr(gz, scope, lhs_node); 9995 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, rhs_node); 9996 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 9997 .lhs = type_inst, 9998 .rhs = field_name, 9999 }); 10000 return rvalue(gz, ri, result, node); 10001 } 10002 10003 fn shiftOp( 10004 gz: *GenZir, 10005 scope: *Scope, 10006 ri: ResultInfo, 10007 node: Ast.Node.Index, 10008 lhs_node: Ast.Node.Index, 10009 rhs_node: Ast.Node.Index, 10010 tag: Zir.Inst.Tag, 10011 ) InnerError!Zir.Inst.Ref { 10012 const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node); 10013 10014 const cursor = switch (gz.astgen.tree.nodes.items(.tag)[node]) { 10015 .shl, .shr => maybeAdvanceSourceCursorToMainToken(gz, node), 10016 else => undefined, 10017 }; 10018 10019 const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node); 10020 const rhs = try expr(gz, scope, .{ .rl = .{ .ty = log2_int_type }, .ctx = .shift_op }, rhs_node); 10021 10022 switch (gz.astgen.tree.nodes.items(.tag)[node]) { 10023 .shl, .shr => try emitDbgStmt(gz, cursor), 10024 else => undefined, 10025 } 10026 10027 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 10028 .lhs = lhs, 10029 .rhs = rhs, 10030 }); 10031 return rvalue(gz, ri, result, node); 10032 } 10033 10034 fn cImport( 10035 gz: *GenZir, 10036 scope: *Scope, 10037 node: Ast.Node.Index, 10038 body_node: Ast.Node.Index, 10039 ) InnerError!Zir.Inst.Ref { 10040 const astgen = gz.astgen; 10041 const gpa = astgen.gpa; 10042 10043 if (gz.c_import) return gz.astgen.failNode(node, "cannot nest @cImport", .{}); 10044 10045 var block_scope = gz.makeSubBlock(scope); 10046 block_scope.is_comptime = true; 10047 block_scope.c_import = true; 10048 defer block_scope.unstack(); 10049 10050 const block_inst = try gz.makeBlockInst(.c_import, node); 10051 const block_result = try fullBodyExpr(&block_scope, &block_scope.base, .{ .rl = .none }, body_node, .normal); 10052 _ = try gz.addUnNode(.ensure_result_used, block_result, node); 10053 if (!gz.refIsNoReturn(block_result)) { 10054 _ = try block_scope.addBreak(.break_inline, block_inst, .void_value); 10055 } 10056 try block_scope.setBlockBody(block_inst); 10057 // block_scope unstacked now, can add new instructions to gz 10058 try gz.instructions.append(gpa, block_inst); 10059 10060 return block_inst.toRef(); 10061 } 10062 10063 fn overflowArithmetic( 10064 gz: *GenZir, 10065 scope: *Scope, 10066 ri: ResultInfo, 10067 node: Ast.Node.Index, 10068 params: []const Ast.Node.Index, 10069 tag: Zir.Inst.Extended, 10070 ) InnerError!Zir.Inst.Ref { 10071 const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]); 10072 const rhs = try expr(gz, scope, .{ .rl = .none }, params[1]); 10073 const result = try gz.addExtendedPayload(tag, Zir.Inst.BinNode{ 10074 .node = gz.nodeIndexToRelative(node), 10075 .lhs = lhs, 10076 .rhs = rhs, 10077 }); 10078 return rvalue(gz, ri, result, node); 10079 } 10080 10081 fn callExpr( 10082 gz: *GenZir, 10083 scope: *Scope, 10084 ri: ResultInfo, 10085 node: Ast.Node.Index, 10086 call: Ast.full.Call, 10087 ) InnerError!Zir.Inst.Ref { 10088 const astgen = gz.astgen; 10089 10090 const callee = try calleeExpr(gz, scope, ri.rl, call.ast.fn_expr); 10091 const modifier: std.builtin.CallModifier = blk: { 10092 if (gz.is_comptime) { 10093 break :blk .compile_time; 10094 } 10095 if (call.async_token != null) { 10096 break :blk .async_kw; 10097 } 10098 if (gz.nosuspend_node != 0) { 10099 break :blk .no_async; 10100 } 10101 break :blk .auto; 10102 }; 10103 10104 { 10105 astgen.advanceSourceCursor(astgen.tree.tokens.items(.start)[call.ast.lparen]); 10106 const line = astgen.source_line - gz.decl_line; 10107 const column = astgen.source_column; 10108 // Sema expects a dbg_stmt immediately before call, 10109 try emitDbgStmtForceCurrentIndex(gz, .{ line, column }); 10110 } 10111 10112 switch (callee) { 10113 .direct => |obj| assert(obj != .none), 10114 .field => |field| assert(field.obj_ptr != .none), 10115 } 10116 assert(node != 0); 10117 10118 const call_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 10119 const call_inst = call_index.toRef(); 10120 try gz.astgen.instructions.append(astgen.gpa, undefined); 10121 try gz.instructions.append(astgen.gpa, call_index); 10122 10123 const scratch_top = astgen.scratch.items.len; 10124 defer astgen.scratch.items.len = scratch_top; 10125 10126 var scratch_index = scratch_top; 10127 try astgen.scratch.resize(astgen.gpa, scratch_top + call.ast.params.len); 10128 10129 for (call.ast.params) |param_node| { 10130 var arg_block = gz.makeSubBlock(scope); 10131 defer arg_block.unstack(); 10132 10133 // `call_inst` is reused to provide the param type. 10134 const arg_ref = try fullBodyExpr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node, .normal); 10135 _ = try arg_block.addBreakWithSrcNode(.break_inline, call_index, arg_ref, param_node); 10136 10137 const body = arg_block.instructionsSlice(); 10138 try astgen.scratch.ensureUnusedCapacity(astgen.gpa, countBodyLenAfterFixups(astgen, body)); 10139 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 10140 10141 astgen.scratch.items[scratch_index] = @intCast(astgen.scratch.items.len - scratch_top); 10142 scratch_index += 1; 10143 } 10144 10145 // If our result location is a try/catch/error-union-if/return, a function argument, 10146 // or an initializer for a `const` variable, the error trace propagates. 10147 // Otherwise, it should always be popped (handled in Sema). 10148 const propagate_error_trace = switch (ri.ctx) { 10149 .error_handling_expr, .@"return", .fn_arg, .const_init => true, 10150 else => false, 10151 }; 10152 10153 switch (callee) { 10154 .direct => |callee_obj| { 10155 const payload_index = try addExtra(astgen, Zir.Inst.Call{ 10156 .callee = callee_obj, 10157 .flags = .{ 10158 .pop_error_return_trace = !propagate_error_trace, 10159 .packed_modifier = @intCast(@intFromEnum(modifier)), 10160 .args_len = @intCast(call.ast.params.len), 10161 }, 10162 }); 10163 if (call.ast.params.len != 0) { 10164 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]); 10165 } 10166 gz.astgen.instructions.set(@intFromEnum(call_index), .{ 10167 .tag = .call, 10168 .data = .{ .pl_node = .{ 10169 .src_node = gz.nodeIndexToRelative(node), 10170 .payload_index = payload_index, 10171 } }, 10172 }); 10173 }, 10174 .field => |callee_field| { 10175 const payload_index = try addExtra(astgen, Zir.Inst.FieldCall{ 10176 .obj_ptr = callee_field.obj_ptr, 10177 .field_name_start = callee_field.field_name_start, 10178 .flags = .{ 10179 .pop_error_return_trace = !propagate_error_trace, 10180 .packed_modifier = @intCast(@intFromEnum(modifier)), 10181 .args_len = @intCast(call.ast.params.len), 10182 }, 10183 }); 10184 if (call.ast.params.len != 0) { 10185 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]); 10186 } 10187 gz.astgen.instructions.set(@intFromEnum(call_index), .{ 10188 .tag = .field_call, 10189 .data = .{ .pl_node = .{ 10190 .src_node = gz.nodeIndexToRelative(node), 10191 .payload_index = payload_index, 10192 } }, 10193 }); 10194 }, 10195 } 10196 return rvalue(gz, ri, call_inst, node); // TODO function call with result location 10197 } 10198 10199 const Callee = union(enum) { 10200 field: struct { 10201 /// A *pointer* to the object the field is fetched on, so that we can 10202 /// promote the lvalue to an address if the first parameter requires it. 10203 obj_ptr: Zir.Inst.Ref, 10204 /// Offset into `string_bytes`. 10205 field_name_start: Zir.NullTerminatedString, 10206 }, 10207 direct: Zir.Inst.Ref, 10208 }; 10209 10210 /// calleeExpr generates the function part of a call expression (f in f(x)), but 10211 /// *not* the callee argument to the @call() builtin. Its purpose is to 10212 /// distinguish between standard calls and method call syntax `a.b()`. Thus, if 10213 /// the lhs is a field access, we return using the `field` union field; 10214 /// otherwise, we use the `direct` union field. 10215 fn calleeExpr( 10216 gz: *GenZir, 10217 scope: *Scope, 10218 call_rl: ResultInfo.Loc, 10219 node: Ast.Node.Index, 10220 ) InnerError!Callee { 10221 const astgen = gz.astgen; 10222 const tree = astgen.tree; 10223 10224 const tag = tree.nodes.items(.tag)[node]; 10225 switch (tag) { 10226 .field_access => { 10227 const main_tokens = tree.nodes.items(.main_token); 10228 const node_datas = tree.nodes.items(.data); 10229 const object_node = node_datas[node].lhs; 10230 const dot_token = main_tokens[node]; 10231 const field_ident = dot_token + 1; 10232 const str_index = try astgen.identAsString(field_ident); 10233 // Capture the object by reference so we can promote it to an 10234 // address in Sema if needed. 10235 const lhs = try expr(gz, scope, .{ .rl = .ref }, object_node); 10236 10237 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 10238 try emitDbgStmt(gz, cursor); 10239 10240 return .{ .field = .{ 10241 .obj_ptr = lhs, 10242 .field_name_start = str_index, 10243 } }; 10244 }, 10245 .enum_literal => if (try call_rl.resultType(gz, node)) |res_ty| { 10246 // Decl literal call syntax, e.g. 10247 // `const foo: T = .init();` 10248 // Look up `init` in `T`, but don't try and coerce it. 10249 const str_index = try astgen.identAsString(tree.nodes.items(.main_token)[node]); 10250 const callee = try gz.addPlNode(.decl_literal_no_coerce, node, Zir.Inst.Field{ 10251 .lhs = res_ty, 10252 .field_name_start = str_index, 10253 }); 10254 return .{ .direct = callee }; 10255 } else { 10256 return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) }; 10257 }, 10258 else => return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) }, 10259 } 10260 } 10261 10262 const primitive_instrs = std.StaticStringMap(Zir.Inst.Ref).initComptime(.{ 10263 .{ "anyerror", .anyerror_type }, 10264 .{ "anyframe", .anyframe_type }, 10265 .{ "anyopaque", .anyopaque_type }, 10266 .{ "bool", .bool_type }, 10267 .{ "c_int", .c_int_type }, 10268 .{ "c_long", .c_long_type }, 10269 .{ "c_longdouble", .c_longdouble_type }, 10270 .{ "c_longlong", .c_longlong_type }, 10271 .{ "c_char", .c_char_type }, 10272 .{ "c_short", .c_short_type }, 10273 .{ "c_uint", .c_uint_type }, 10274 .{ "c_ulong", .c_ulong_type }, 10275 .{ "c_ulonglong", .c_ulonglong_type }, 10276 .{ "c_ushort", .c_ushort_type }, 10277 .{ "comptime_float", .comptime_float_type }, 10278 .{ "comptime_int", .comptime_int_type }, 10279 .{ "f128", .f128_type }, 10280 .{ "f16", .f16_type }, 10281 .{ "f32", .f32_type }, 10282 .{ "f64", .f64_type }, 10283 .{ "f80", .f80_type }, 10284 .{ "false", .bool_false }, 10285 .{ "i16", .i16_type }, 10286 .{ "i32", .i32_type }, 10287 .{ "i64", .i64_type }, 10288 .{ "i128", .i128_type }, 10289 .{ "i8", .i8_type }, 10290 .{ "isize", .isize_type }, 10291 .{ "noreturn", .noreturn_type }, 10292 .{ "null", .null_value }, 10293 .{ "true", .bool_true }, 10294 .{ "type", .type_type }, 10295 .{ "u16", .u16_type }, 10296 .{ "u29", .u29_type }, 10297 .{ "u32", .u32_type }, 10298 .{ "u64", .u64_type }, 10299 .{ "u128", .u128_type }, 10300 .{ "u1", .u1_type }, 10301 .{ "u8", .u8_type }, 10302 .{ "undefined", .undef }, 10303 .{ "usize", .usize_type }, 10304 .{ "void", .void_type }, 10305 }); 10306 10307 comptime { 10308 // These checks ensure that std.zig.primitives stays in sync with the primitive->Zir map. 10309 const primitives = std.zig.primitives; 10310 for (primitive_instrs.keys(), primitive_instrs.values()) |key, value| { 10311 if (!primitives.isPrimitive(key)) { 10312 @compileError("std.zig.isPrimitive() is not aware of Zir instr '" ++ @tagName(value) ++ "'"); 10313 } 10314 } 10315 for (primitives.names.keys()) |key| { 10316 if (primitive_instrs.get(key) == null) { 10317 @compileError("std.zig.primitives entry '" ++ key ++ "' does not have a corresponding Zir instr"); 10318 } 10319 } 10320 } 10321 10322 fn nodeIsTriviallyZero(tree: *const Ast, node: Ast.Node.Index) bool { 10323 const node_tags = tree.nodes.items(.tag); 10324 const main_tokens = tree.nodes.items(.main_token); 10325 10326 switch (node_tags[node]) { 10327 .number_literal => { 10328 const ident = main_tokens[node]; 10329 return switch (std.zig.parseNumberLiteral(tree.tokenSlice(ident))) { 10330 .int => |number| switch (number) { 10331 0 => true, 10332 else => false, 10333 }, 10334 else => false, 10335 }; 10336 }, 10337 else => return false, 10338 } 10339 } 10340 10341 fn nodeMayAppendToErrorTrace(tree: *const Ast, start_node: Ast.Node.Index) bool { 10342 const node_tags = tree.nodes.items(.tag); 10343 const node_datas = tree.nodes.items(.data); 10344 10345 var node = start_node; 10346 while (true) { 10347 switch (node_tags[node]) { 10348 // These don't have the opportunity to call any runtime functions. 10349 .error_value, 10350 .identifier, 10351 .@"comptime", 10352 => return false, 10353 10354 // Forward the question to the LHS sub-expression. 10355 .grouped_expression, 10356 .@"try", 10357 .@"nosuspend", 10358 .unwrap_optional, 10359 => node = node_datas[node].lhs, 10360 10361 // Anything that does not eval to an error is guaranteed to pop any 10362 // additions to the error trace, so it effectively does not append. 10363 else => return nodeMayEvalToError(tree, start_node) != .never, 10364 } 10365 } 10366 } 10367 10368 fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.EvalToError { 10369 const node_tags = tree.nodes.items(.tag); 10370 const node_datas = tree.nodes.items(.data); 10371 const main_tokens = tree.nodes.items(.main_token); 10372 const token_tags = tree.tokens.items(.tag); 10373 10374 var node = start_node; 10375 while (true) { 10376 switch (node_tags[node]) { 10377 .root, 10378 .@"usingnamespace", 10379 .test_decl, 10380 .switch_case, 10381 .switch_case_inline, 10382 .switch_case_one, 10383 .switch_case_inline_one, 10384 .container_field_init, 10385 .container_field_align, 10386 .container_field, 10387 .asm_output, 10388 .asm_input, 10389 => unreachable, 10390 10391 .error_value => return .always, 10392 10393 .@"asm", 10394 .asm_simple, 10395 .identifier, 10396 .field_access, 10397 .deref, 10398 .array_access, 10399 .while_simple, 10400 .while_cont, 10401 .for_simple, 10402 .if_simple, 10403 .@"while", 10404 .@"if", 10405 .@"for", 10406 .@"switch", 10407 .switch_comma, 10408 .call_one, 10409 .call_one_comma, 10410 .async_call_one, 10411 .async_call_one_comma, 10412 .call, 10413 .call_comma, 10414 .async_call, 10415 .async_call_comma, 10416 => return .maybe, 10417 10418 .@"return", 10419 .@"break", 10420 .@"continue", 10421 .bit_not, 10422 .bool_not, 10423 .global_var_decl, 10424 .local_var_decl, 10425 .simple_var_decl, 10426 .aligned_var_decl, 10427 .@"defer", 10428 .@"errdefer", 10429 .address_of, 10430 .optional_type, 10431 .negation, 10432 .negation_wrap, 10433 .@"resume", 10434 .array_type, 10435 .array_type_sentinel, 10436 .ptr_type_aligned, 10437 .ptr_type_sentinel, 10438 .ptr_type, 10439 .ptr_type_bit_range, 10440 .@"suspend", 10441 .fn_proto_simple, 10442 .fn_proto_multi, 10443 .fn_proto_one, 10444 .fn_proto, 10445 .fn_decl, 10446 .anyframe_type, 10447 .anyframe_literal, 10448 .number_literal, 10449 .enum_literal, 10450 .string_literal, 10451 .multiline_string_literal, 10452 .char_literal, 10453 .unreachable_literal, 10454 .error_set_decl, 10455 .container_decl, 10456 .container_decl_trailing, 10457 .container_decl_two, 10458 .container_decl_two_trailing, 10459 .container_decl_arg, 10460 .container_decl_arg_trailing, 10461 .tagged_union, 10462 .tagged_union_trailing, 10463 .tagged_union_two, 10464 .tagged_union_two_trailing, 10465 .tagged_union_enum_tag, 10466 .tagged_union_enum_tag_trailing, 10467 .add, 10468 .add_wrap, 10469 .add_sat, 10470 .array_cat, 10471 .array_mult, 10472 .assign, 10473 .assign_destructure, 10474 .assign_bit_and, 10475 .assign_bit_or, 10476 .assign_shl, 10477 .assign_shl_sat, 10478 .assign_shr, 10479 .assign_bit_xor, 10480 .assign_div, 10481 .assign_sub, 10482 .assign_sub_wrap, 10483 .assign_sub_sat, 10484 .assign_mod, 10485 .assign_add, 10486 .assign_add_wrap, 10487 .assign_add_sat, 10488 .assign_mul, 10489 .assign_mul_wrap, 10490 .assign_mul_sat, 10491 .bang_equal, 10492 .bit_and, 10493 .bit_or, 10494 .shl, 10495 .shl_sat, 10496 .shr, 10497 .bit_xor, 10498 .bool_and, 10499 .bool_or, 10500 .div, 10501 .equal_equal, 10502 .error_union, 10503 .greater_or_equal, 10504 .greater_than, 10505 .less_or_equal, 10506 .less_than, 10507 .merge_error_sets, 10508 .mod, 10509 .mul, 10510 .mul_wrap, 10511 .mul_sat, 10512 .switch_range, 10513 .for_range, 10514 .sub, 10515 .sub_wrap, 10516 .sub_sat, 10517 .slice, 10518 .slice_open, 10519 .slice_sentinel, 10520 .array_init_one, 10521 .array_init_one_comma, 10522 .array_init_dot_two, 10523 .array_init_dot_two_comma, 10524 .array_init_dot, 10525 .array_init_dot_comma, 10526 .array_init, 10527 .array_init_comma, 10528 .struct_init_one, 10529 .struct_init_one_comma, 10530 .struct_init_dot_two, 10531 .struct_init_dot_two_comma, 10532 .struct_init_dot, 10533 .struct_init_dot_comma, 10534 .struct_init, 10535 .struct_init_comma, 10536 => return .never, 10537 10538 // Forward the question to the LHS sub-expression. 10539 .grouped_expression, 10540 .@"try", 10541 .@"await", 10542 .@"comptime", 10543 .@"nosuspend", 10544 .unwrap_optional, 10545 => node = node_datas[node].lhs, 10546 10547 // LHS sub-expression may still be an error under the outer optional or error union 10548 .@"catch", 10549 .@"orelse", 10550 => return .maybe, 10551 10552 .block_two, 10553 .block_two_semicolon, 10554 .block, 10555 .block_semicolon, 10556 => { 10557 const lbrace = main_tokens[node]; 10558 if (token_tags[lbrace - 1] == .colon) { 10559 // Labeled blocks may need a memory location to forward 10560 // to their break statements. 10561 return .maybe; 10562 } else { 10563 return .never; 10564 } 10565 }, 10566 10567 .builtin_call, 10568 .builtin_call_comma, 10569 .builtin_call_two, 10570 .builtin_call_two_comma, 10571 => { 10572 const builtin_token = main_tokens[node]; 10573 const builtin_name = tree.tokenSlice(builtin_token); 10574 // If the builtin is an invalid name, we don't cause an error here; instead 10575 // let it pass, and the error will be "invalid builtin function" later. 10576 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return .maybe; 10577 return builtin_info.eval_to_error; 10578 }, 10579 } 10580 } 10581 } 10582 10583 /// Returns `true` if it is known the type expression has more than one possible value; 10584 /// `false` otherwise. 10585 fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.Index) bool { 10586 const node_tags = tree.nodes.items(.tag); 10587 const node_datas = tree.nodes.items(.data); 10588 10589 var node = start_node; 10590 while (true) { 10591 switch (node_tags[node]) { 10592 .root, 10593 .@"usingnamespace", 10594 .test_decl, 10595 .switch_case, 10596 .switch_case_inline, 10597 .switch_case_one, 10598 .switch_case_inline_one, 10599 .container_field_init, 10600 .container_field_align, 10601 .container_field, 10602 .asm_output, 10603 .asm_input, 10604 .global_var_decl, 10605 .local_var_decl, 10606 .simple_var_decl, 10607 .aligned_var_decl, 10608 => unreachable, 10609 10610 .@"return", 10611 .@"break", 10612 .@"continue", 10613 .bit_not, 10614 .bool_not, 10615 .@"defer", 10616 .@"errdefer", 10617 .address_of, 10618 .negation, 10619 .negation_wrap, 10620 .@"resume", 10621 .array_type, 10622 .@"suspend", 10623 .fn_decl, 10624 .anyframe_literal, 10625 .number_literal, 10626 .enum_literal, 10627 .string_literal, 10628 .multiline_string_literal, 10629 .char_literal, 10630 .unreachable_literal, 10631 .error_set_decl, 10632 .container_decl, 10633 .container_decl_trailing, 10634 .container_decl_two, 10635 .container_decl_two_trailing, 10636 .container_decl_arg, 10637 .container_decl_arg_trailing, 10638 .tagged_union, 10639 .tagged_union_trailing, 10640 .tagged_union_two, 10641 .tagged_union_two_trailing, 10642 .tagged_union_enum_tag, 10643 .tagged_union_enum_tag_trailing, 10644 .@"asm", 10645 .asm_simple, 10646 .add, 10647 .add_wrap, 10648 .add_sat, 10649 .array_cat, 10650 .array_mult, 10651 .assign, 10652 .assign_destructure, 10653 .assign_bit_and, 10654 .assign_bit_or, 10655 .assign_shl, 10656 .assign_shl_sat, 10657 .assign_shr, 10658 .assign_bit_xor, 10659 .assign_div, 10660 .assign_sub, 10661 .assign_sub_wrap, 10662 .assign_sub_sat, 10663 .assign_mod, 10664 .assign_add, 10665 .assign_add_wrap, 10666 .assign_add_sat, 10667 .assign_mul, 10668 .assign_mul_wrap, 10669 .assign_mul_sat, 10670 .bang_equal, 10671 .bit_and, 10672 .bit_or, 10673 .shl, 10674 .shl_sat, 10675 .shr, 10676 .bit_xor, 10677 .bool_and, 10678 .bool_or, 10679 .div, 10680 .equal_equal, 10681 .error_union, 10682 .greater_or_equal, 10683 .greater_than, 10684 .less_or_equal, 10685 .less_than, 10686 .merge_error_sets, 10687 .mod, 10688 .mul, 10689 .mul_wrap, 10690 .mul_sat, 10691 .switch_range, 10692 .for_range, 10693 .field_access, 10694 .sub, 10695 .sub_wrap, 10696 .sub_sat, 10697 .slice, 10698 .slice_open, 10699 .slice_sentinel, 10700 .deref, 10701 .array_access, 10702 .error_value, 10703 .while_simple, 10704 .while_cont, 10705 .for_simple, 10706 .if_simple, 10707 .@"catch", 10708 .@"orelse", 10709 .array_init_one, 10710 .array_init_one_comma, 10711 .array_init_dot_two, 10712 .array_init_dot_two_comma, 10713 .array_init_dot, 10714 .array_init_dot_comma, 10715 .array_init, 10716 .array_init_comma, 10717 .struct_init_one, 10718 .struct_init_one_comma, 10719 .struct_init_dot_two, 10720 .struct_init_dot_two_comma, 10721 .struct_init_dot, 10722 .struct_init_dot_comma, 10723 .struct_init, 10724 .struct_init_comma, 10725 .@"while", 10726 .@"if", 10727 .@"for", 10728 .@"switch", 10729 .switch_comma, 10730 .call_one, 10731 .call_one_comma, 10732 .async_call_one, 10733 .async_call_one_comma, 10734 .call, 10735 .call_comma, 10736 .async_call, 10737 .async_call_comma, 10738 .block_two, 10739 .block_two_semicolon, 10740 .block, 10741 .block_semicolon, 10742 .builtin_call, 10743 .builtin_call_comma, 10744 .builtin_call_two, 10745 .builtin_call_two_comma, 10746 // these are function bodies, not pointers 10747 .fn_proto_simple, 10748 .fn_proto_multi, 10749 .fn_proto_one, 10750 .fn_proto, 10751 => return false, 10752 10753 // Forward the question to the LHS sub-expression. 10754 .grouped_expression, 10755 .@"try", 10756 .@"await", 10757 .@"comptime", 10758 .@"nosuspend", 10759 .unwrap_optional, 10760 => node = node_datas[node].lhs, 10761 10762 .ptr_type_aligned, 10763 .ptr_type_sentinel, 10764 .ptr_type, 10765 .ptr_type_bit_range, 10766 .optional_type, 10767 .anyframe_type, 10768 .array_type_sentinel, 10769 => return true, 10770 10771 .identifier => { 10772 const main_tokens = tree.nodes.items(.main_token); 10773 const ident_bytes = tree.tokenSlice(main_tokens[node]); 10774 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) { 10775 .anyerror_type, 10776 .anyframe_type, 10777 .anyopaque_type, 10778 .bool_type, 10779 .c_int_type, 10780 .c_long_type, 10781 .c_longdouble_type, 10782 .c_longlong_type, 10783 .c_char_type, 10784 .c_short_type, 10785 .c_uint_type, 10786 .c_ulong_type, 10787 .c_ulonglong_type, 10788 .c_ushort_type, 10789 .comptime_float_type, 10790 .comptime_int_type, 10791 .f16_type, 10792 .f32_type, 10793 .f64_type, 10794 .f80_type, 10795 .f128_type, 10796 .i16_type, 10797 .i32_type, 10798 .i64_type, 10799 .i128_type, 10800 .i8_type, 10801 .isize_type, 10802 .type_type, 10803 .u16_type, 10804 .u29_type, 10805 .u32_type, 10806 .u64_type, 10807 .u128_type, 10808 .u1_type, 10809 .u8_type, 10810 .usize_type, 10811 => return true, 10812 10813 .void_type, 10814 .bool_false, 10815 .bool_true, 10816 .null_value, 10817 .undef, 10818 .noreturn_type, 10819 => return false, 10820 10821 else => unreachable, // that's all the values from `primitives`. 10822 } else { 10823 return false; 10824 } 10825 }, 10826 } 10827 } 10828 } 10829 10830 /// Returns `true` if it is known the expression is a type that cannot be used at runtime; 10831 /// `false` otherwise. 10832 fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { 10833 const node_tags = tree.nodes.items(.tag); 10834 const node_datas = tree.nodes.items(.data); 10835 10836 var node = start_node; 10837 while (true) { 10838 switch (node_tags[node]) { 10839 .root, 10840 .@"usingnamespace", 10841 .test_decl, 10842 .switch_case, 10843 .switch_case_inline, 10844 .switch_case_one, 10845 .switch_case_inline_one, 10846 .container_field_init, 10847 .container_field_align, 10848 .container_field, 10849 .asm_output, 10850 .asm_input, 10851 .global_var_decl, 10852 .local_var_decl, 10853 .simple_var_decl, 10854 .aligned_var_decl, 10855 => unreachable, 10856 10857 .@"return", 10858 .@"break", 10859 .@"continue", 10860 .bit_not, 10861 .bool_not, 10862 .@"defer", 10863 .@"errdefer", 10864 .address_of, 10865 .negation, 10866 .negation_wrap, 10867 .@"resume", 10868 .array_type, 10869 .@"suspend", 10870 .fn_decl, 10871 .anyframe_literal, 10872 .number_literal, 10873 .enum_literal, 10874 .string_literal, 10875 .multiline_string_literal, 10876 .char_literal, 10877 .unreachable_literal, 10878 .error_set_decl, 10879 .container_decl, 10880 .container_decl_trailing, 10881 .container_decl_two, 10882 .container_decl_two_trailing, 10883 .container_decl_arg, 10884 .container_decl_arg_trailing, 10885 .tagged_union, 10886 .tagged_union_trailing, 10887 .tagged_union_two, 10888 .tagged_union_two_trailing, 10889 .tagged_union_enum_tag, 10890 .tagged_union_enum_tag_trailing, 10891 .@"asm", 10892 .asm_simple, 10893 .add, 10894 .add_wrap, 10895 .add_sat, 10896 .array_cat, 10897 .array_mult, 10898 .assign, 10899 .assign_destructure, 10900 .assign_bit_and, 10901 .assign_bit_or, 10902 .assign_shl, 10903 .assign_shl_sat, 10904 .assign_shr, 10905 .assign_bit_xor, 10906 .assign_div, 10907 .assign_sub, 10908 .assign_sub_wrap, 10909 .assign_sub_sat, 10910 .assign_mod, 10911 .assign_add, 10912 .assign_add_wrap, 10913 .assign_add_sat, 10914 .assign_mul, 10915 .assign_mul_wrap, 10916 .assign_mul_sat, 10917 .bang_equal, 10918 .bit_and, 10919 .bit_or, 10920 .shl, 10921 .shl_sat, 10922 .shr, 10923 .bit_xor, 10924 .bool_and, 10925 .bool_or, 10926 .div, 10927 .equal_equal, 10928 .error_union, 10929 .greater_or_equal, 10930 .greater_than, 10931 .less_or_equal, 10932 .less_than, 10933 .merge_error_sets, 10934 .mod, 10935 .mul, 10936 .mul_wrap, 10937 .mul_sat, 10938 .switch_range, 10939 .for_range, 10940 .field_access, 10941 .sub, 10942 .sub_wrap, 10943 .sub_sat, 10944 .slice, 10945 .slice_open, 10946 .slice_sentinel, 10947 .deref, 10948 .array_access, 10949 .error_value, 10950 .while_simple, 10951 .while_cont, 10952 .for_simple, 10953 .if_simple, 10954 .@"catch", 10955 .@"orelse", 10956 .array_init_one, 10957 .array_init_one_comma, 10958 .array_init_dot_two, 10959 .array_init_dot_two_comma, 10960 .array_init_dot, 10961 .array_init_dot_comma, 10962 .array_init, 10963 .array_init_comma, 10964 .struct_init_one, 10965 .struct_init_one_comma, 10966 .struct_init_dot_two, 10967 .struct_init_dot_two_comma, 10968 .struct_init_dot, 10969 .struct_init_dot_comma, 10970 .struct_init, 10971 .struct_init_comma, 10972 .@"while", 10973 .@"if", 10974 .@"for", 10975 .@"switch", 10976 .switch_comma, 10977 .call_one, 10978 .call_one_comma, 10979 .async_call_one, 10980 .async_call_one_comma, 10981 .call, 10982 .call_comma, 10983 .async_call, 10984 .async_call_comma, 10985 .block_two, 10986 .block_two_semicolon, 10987 .block, 10988 .block_semicolon, 10989 .builtin_call, 10990 .builtin_call_comma, 10991 .builtin_call_two, 10992 .builtin_call_two_comma, 10993 .ptr_type_aligned, 10994 .ptr_type_sentinel, 10995 .ptr_type, 10996 .ptr_type_bit_range, 10997 .optional_type, 10998 .anyframe_type, 10999 .array_type_sentinel, 11000 => return false, 11001 11002 // these are function bodies, not pointers 11003 .fn_proto_simple, 11004 .fn_proto_multi, 11005 .fn_proto_one, 11006 .fn_proto, 11007 => return true, 11008 11009 // Forward the question to the LHS sub-expression. 11010 .grouped_expression, 11011 .@"try", 11012 .@"await", 11013 .@"comptime", 11014 .@"nosuspend", 11015 .unwrap_optional, 11016 => node = node_datas[node].lhs, 11017 11018 .identifier => { 11019 const main_tokens = tree.nodes.items(.main_token); 11020 const ident_bytes = tree.tokenSlice(main_tokens[node]); 11021 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) { 11022 .anyerror_type, 11023 .anyframe_type, 11024 .anyopaque_type, 11025 .bool_type, 11026 .c_int_type, 11027 .c_long_type, 11028 .c_longdouble_type, 11029 .c_longlong_type, 11030 .c_char_type, 11031 .c_short_type, 11032 .c_uint_type, 11033 .c_ulong_type, 11034 .c_ulonglong_type, 11035 .c_ushort_type, 11036 .f16_type, 11037 .f32_type, 11038 .f64_type, 11039 .f80_type, 11040 .f128_type, 11041 .i16_type, 11042 .i32_type, 11043 .i64_type, 11044 .i128_type, 11045 .i8_type, 11046 .isize_type, 11047 .u16_type, 11048 .u29_type, 11049 .u32_type, 11050 .u64_type, 11051 .u128_type, 11052 .u1_type, 11053 .u8_type, 11054 .usize_type, 11055 .void_type, 11056 .bool_false, 11057 .bool_true, 11058 .null_value, 11059 .undef, 11060 .noreturn_type, 11061 => return false, 11062 11063 .comptime_float_type, 11064 .comptime_int_type, 11065 .type_type, 11066 => return true, 11067 11068 else => unreachable, // that's all the values from `primitives`. 11069 } else { 11070 return false; 11071 } 11072 }, 11073 } 11074 } 11075 } 11076 11077 /// Returns `true` if the node uses `gz.anon_name_strategy`. 11078 fn nodeUsesAnonNameStrategy(tree: *const Ast, node: Ast.Node.Index) bool { 11079 const node_tags = tree.nodes.items(.tag); 11080 switch (node_tags[node]) { 11081 .container_decl, 11082 .container_decl_trailing, 11083 .container_decl_two, 11084 .container_decl_two_trailing, 11085 .container_decl_arg, 11086 .container_decl_arg_trailing, 11087 .tagged_union, 11088 .tagged_union_trailing, 11089 .tagged_union_two, 11090 .tagged_union_two_trailing, 11091 .tagged_union_enum_tag, 11092 .tagged_union_enum_tag_trailing, 11093 => return true, 11094 .builtin_call_two, .builtin_call_two_comma, .builtin_call, .builtin_call_comma => { 11095 const builtin_token = tree.nodes.items(.main_token)[node]; 11096 const builtin_name = tree.tokenSlice(builtin_token); 11097 return std.mem.eql(u8, builtin_name, "@Type"); 11098 }, 11099 else => return false, 11100 } 11101 } 11102 11103 /// Applies `rl` semantics to `result`. Expressions which do not do their own handling of 11104 /// result locations must call this function on their result. 11105 /// As an example, if `ri.rl` is `.ptr`, it will write the result to the pointer. 11106 /// If `ri.rl` is `.ty`, it will coerce the result to the type. 11107 /// Assumes nothing stacked on `gz`. 11108 fn rvalue( 11109 gz: *GenZir, 11110 ri: ResultInfo, 11111 raw_result: Zir.Inst.Ref, 11112 src_node: Ast.Node.Index, 11113 ) InnerError!Zir.Inst.Ref { 11114 return rvalueInner(gz, ri, raw_result, src_node, true); 11115 } 11116 11117 /// Like `rvalue`, but refuses to perform coercions before taking references for 11118 /// the `ref_coerced_ty` result type. This is used for local variables which do 11119 /// not have `alloc`s, because we want variables to have consistent addresses, 11120 /// i.e. we want them to act like lvalues. 11121 fn rvalueNoCoercePreRef( 11122 gz: *GenZir, 11123 ri: ResultInfo, 11124 raw_result: Zir.Inst.Ref, 11125 src_node: Ast.Node.Index, 11126 ) InnerError!Zir.Inst.Ref { 11127 return rvalueInner(gz, ri, raw_result, src_node, false); 11128 } 11129 11130 fn rvalueInner( 11131 gz: *GenZir, 11132 ri: ResultInfo, 11133 raw_result: Zir.Inst.Ref, 11134 src_node: Ast.Node.Index, 11135 allow_coerce_pre_ref: bool, 11136 ) InnerError!Zir.Inst.Ref { 11137 const result = r: { 11138 if (raw_result.toIndex()) |result_index| { 11139 const zir_tags = gz.astgen.instructions.items(.tag); 11140 const data = gz.astgen.instructions.items(.data)[@intFromEnum(result_index)]; 11141 if (zir_tags[@intFromEnum(result_index)].isAlwaysVoid(data)) { 11142 break :r Zir.Inst.Ref.void_value; 11143 } 11144 } 11145 break :r raw_result; 11146 }; 11147 if (gz.endsWithNoReturn()) return result; 11148 switch (ri.rl) { 11149 .none, .coerced_ty => return result, 11150 .discard => { 11151 // Emit a compile error for discarding error values. 11152 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); 11153 return .void_value; 11154 }, 11155 .ref, .ref_coerced_ty => { 11156 const coerced_result = if (allow_coerce_pre_ref and ri.rl == .ref_coerced_ty) res: { 11157 const ptr_ty = ri.rl.ref_coerced_ty; 11158 break :res try gz.addPlNode(.coerce_ptr_elem_ty, src_node, Zir.Inst.Bin{ 11159 .lhs = ptr_ty, 11160 .rhs = result, 11161 }); 11162 } else result; 11163 // We need a pointer but we have a value. 11164 // Unfortunately it's not quite as simple as directly emitting a ref 11165 // instruction here because we need subsequent address-of operator on 11166 // const locals to return the same address. 11167 const astgen = gz.astgen; 11168 const tree = astgen.tree; 11169 const src_token = tree.firstToken(src_node); 11170 const result_index = coerced_result.toIndex() orelse 11171 return gz.addUnTok(.ref, coerced_result, src_token); 11172 const gop = try astgen.ref_table.getOrPut(astgen.gpa, result_index); 11173 if (!gop.found_existing) { 11174 gop.value_ptr.* = try gz.makeUnTok(.ref, coerced_result, src_token); 11175 } 11176 return gop.value_ptr.*.toRef(); 11177 }, 11178 .ty => |ty_inst| { 11179 // Quickly eliminate some common, unnecessary type coercion. 11180 const as_ty = @as(u64, @intFromEnum(Zir.Inst.Ref.type_type)) << 32; 11181 const as_bool = @as(u64, @intFromEnum(Zir.Inst.Ref.bool_type)) << 32; 11182 const as_void = @as(u64, @intFromEnum(Zir.Inst.Ref.void_type)) << 32; 11183 const as_comptime_int = @as(u64, @intFromEnum(Zir.Inst.Ref.comptime_int_type)) << 32; 11184 const as_usize = @as(u64, @intFromEnum(Zir.Inst.Ref.usize_type)) << 32; 11185 const as_u8 = @as(u64, @intFromEnum(Zir.Inst.Ref.u8_type)) << 32; 11186 switch ((@as(u64, @intFromEnum(ty_inst)) << 32) | @as(u64, @intFromEnum(result))) { 11187 as_ty | @intFromEnum(Zir.Inst.Ref.u1_type), 11188 as_ty | @intFromEnum(Zir.Inst.Ref.u8_type), 11189 as_ty | @intFromEnum(Zir.Inst.Ref.i8_type), 11190 as_ty | @intFromEnum(Zir.Inst.Ref.u16_type), 11191 as_ty | @intFromEnum(Zir.Inst.Ref.u29_type), 11192 as_ty | @intFromEnum(Zir.Inst.Ref.i16_type), 11193 as_ty | @intFromEnum(Zir.Inst.Ref.u32_type), 11194 as_ty | @intFromEnum(Zir.Inst.Ref.i32_type), 11195 as_ty | @intFromEnum(Zir.Inst.Ref.u64_type), 11196 as_ty | @intFromEnum(Zir.Inst.Ref.i64_type), 11197 as_ty | @intFromEnum(Zir.Inst.Ref.u128_type), 11198 as_ty | @intFromEnum(Zir.Inst.Ref.i128_type), 11199 as_ty | @intFromEnum(Zir.Inst.Ref.usize_type), 11200 as_ty | @intFromEnum(Zir.Inst.Ref.isize_type), 11201 as_ty | @intFromEnum(Zir.Inst.Ref.c_char_type), 11202 as_ty | @intFromEnum(Zir.Inst.Ref.c_short_type), 11203 as_ty | @intFromEnum(Zir.Inst.Ref.c_ushort_type), 11204 as_ty | @intFromEnum(Zir.Inst.Ref.c_int_type), 11205 as_ty | @intFromEnum(Zir.Inst.Ref.c_uint_type), 11206 as_ty | @intFromEnum(Zir.Inst.Ref.c_long_type), 11207 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulong_type), 11208 as_ty | @intFromEnum(Zir.Inst.Ref.c_longlong_type), 11209 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulonglong_type), 11210 as_ty | @intFromEnum(Zir.Inst.Ref.c_longdouble_type), 11211 as_ty | @intFromEnum(Zir.Inst.Ref.f16_type), 11212 as_ty | @intFromEnum(Zir.Inst.Ref.f32_type), 11213 as_ty | @intFromEnum(Zir.Inst.Ref.f64_type), 11214 as_ty | @intFromEnum(Zir.Inst.Ref.f80_type), 11215 as_ty | @intFromEnum(Zir.Inst.Ref.f128_type), 11216 as_ty | @intFromEnum(Zir.Inst.Ref.anyopaque_type), 11217 as_ty | @intFromEnum(Zir.Inst.Ref.bool_type), 11218 as_ty | @intFromEnum(Zir.Inst.Ref.void_type), 11219 as_ty | @intFromEnum(Zir.Inst.Ref.type_type), 11220 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_type), 11221 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_int_type), 11222 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_float_type), 11223 as_ty | @intFromEnum(Zir.Inst.Ref.noreturn_type), 11224 as_ty | @intFromEnum(Zir.Inst.Ref.anyframe_type), 11225 as_ty | @intFromEnum(Zir.Inst.Ref.null_type), 11226 as_ty | @intFromEnum(Zir.Inst.Ref.undefined_type), 11227 as_ty | @intFromEnum(Zir.Inst.Ref.enum_literal_type), 11228 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_u8_type), 11229 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_type), 11230 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_sentinel_0_type), 11231 as_ty | @intFromEnum(Zir.Inst.Ref.single_const_pointer_to_comptime_int_type), 11232 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_type), 11233 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_sentinel_0_type), 11234 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_void_error_union_type), 11235 as_ty | @intFromEnum(Zir.Inst.Ref.generic_poison_type), 11236 as_ty | @intFromEnum(Zir.Inst.Ref.empty_tuple_type), 11237 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero), 11238 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one), 11239 as_comptime_int | @intFromEnum(Zir.Inst.Ref.negative_one), 11240 as_usize | @intFromEnum(Zir.Inst.Ref.zero_usize), 11241 as_usize | @intFromEnum(Zir.Inst.Ref.one_usize), 11242 as_u8 | @intFromEnum(Zir.Inst.Ref.zero_u8), 11243 as_u8 | @intFromEnum(Zir.Inst.Ref.one_u8), 11244 as_u8 | @intFromEnum(Zir.Inst.Ref.four_u8), 11245 as_bool | @intFromEnum(Zir.Inst.Ref.bool_true), 11246 as_bool | @intFromEnum(Zir.Inst.Ref.bool_false), 11247 as_void | @intFromEnum(Zir.Inst.Ref.void_value), 11248 => return result, // type of result is already correct 11249 11250 as_usize | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_usize, 11251 as_u8 | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_u8, 11252 as_usize | @intFromEnum(Zir.Inst.Ref.one) => return .one_usize, 11253 as_u8 | @intFromEnum(Zir.Inst.Ref.one) => return .one_u8, 11254 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero, 11255 as_u8 | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero_u8, 11256 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one, 11257 as_u8 | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one_u8, 11258 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_u8) => return .zero, 11259 as_usize | @intFromEnum(Zir.Inst.Ref.zero_u8) => return .zero_usize, 11260 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_u8) => return .one, 11261 as_usize | @intFromEnum(Zir.Inst.Ref.one_u8) => return .one_usize, 11262 11263 // Need an explicit type coercion instruction. 11264 else => return gz.addPlNode(ri.zirTag(), src_node, Zir.Inst.As{ 11265 .dest_type = ty_inst, 11266 .operand = result, 11267 }), 11268 } 11269 }, 11270 .ptr => |ptr_res| { 11271 _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{ 11272 .lhs = ptr_res.inst, 11273 .rhs = result, 11274 }); 11275 return .void_value; 11276 }, 11277 .inferred_ptr => |alloc| { 11278 _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{ 11279 .lhs = alloc, 11280 .rhs = result, 11281 }); 11282 return .void_value; 11283 }, 11284 .destructure => |destructure| { 11285 const components = destructure.components; 11286 _ = try gz.addPlNode(.validate_destructure, src_node, Zir.Inst.ValidateDestructure{ 11287 .operand = result, 11288 .destructure_node = gz.nodeIndexToRelative(destructure.src_node), 11289 .expect_len = @intCast(components.len), 11290 }); 11291 for (components, 0..) |component, i| { 11292 if (component == .discard) continue; 11293 const elem_val = try gz.add(.{ 11294 .tag = .elem_val_imm, 11295 .data = .{ .elem_val_imm = .{ 11296 .operand = result, 11297 .idx = @intCast(i), 11298 } }, 11299 }); 11300 switch (component) { 11301 .typed_ptr => |ptr_res| { 11302 _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{ 11303 .lhs = ptr_res.inst, 11304 .rhs = elem_val, 11305 }); 11306 }, 11307 .inferred_ptr => |ptr_inst| { 11308 _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{ 11309 .lhs = ptr_inst, 11310 .rhs = elem_val, 11311 }); 11312 }, 11313 .discard => unreachable, 11314 } 11315 } 11316 return .void_value; 11317 }, 11318 } 11319 } 11320 11321 /// Given an identifier token, obtain the string for it. 11322 /// If the token uses @"" syntax, parses as a string, reports errors if applicable, 11323 /// and allocates the result within `astgen.arena`. 11324 /// Otherwise, returns a reference to the source code bytes directly. 11325 /// See also `appendIdentStr` and `parseStrLit`. 11326 fn identifierTokenString(astgen: *AstGen, token: Ast.TokenIndex) InnerError![]const u8 { 11327 const tree = astgen.tree; 11328 const token_tags = tree.tokens.items(.tag); 11329 assert(token_tags[token] == .identifier); 11330 const ident_name = tree.tokenSlice(token); 11331 if (!mem.startsWith(u8, ident_name, "@")) { 11332 return ident_name; 11333 } 11334 var buf: ArrayListUnmanaged(u8) = .empty; 11335 defer buf.deinit(astgen.gpa); 11336 try astgen.parseStrLit(token, &buf, ident_name, 1); 11337 if (mem.indexOfScalar(u8, buf.items, 0) != null) { 11338 return astgen.failTok(token, "identifier cannot contain null bytes", .{}); 11339 } else if (buf.items.len == 0) { 11340 return astgen.failTok(token, "identifier cannot be empty", .{}); 11341 } 11342 const duped = try astgen.arena.dupe(u8, buf.items); 11343 return duped; 11344 } 11345 11346 /// Given an identifier token, obtain the string for it (possibly parsing as a string 11347 /// literal if it is @"" syntax), and append the string to `buf`. 11348 /// See also `identifierTokenString` and `parseStrLit`. 11349 fn appendIdentStr( 11350 astgen: *AstGen, 11351 token: Ast.TokenIndex, 11352 buf: *ArrayListUnmanaged(u8), 11353 ) InnerError!void { 11354 const tree = astgen.tree; 11355 const token_tags = tree.tokens.items(.tag); 11356 assert(token_tags[token] == .identifier); 11357 const ident_name = tree.tokenSlice(token); 11358 if (!mem.startsWith(u8, ident_name, "@")) { 11359 return buf.appendSlice(astgen.gpa, ident_name); 11360 } else { 11361 const start = buf.items.len; 11362 try astgen.parseStrLit(token, buf, ident_name, 1); 11363 const slice = buf.items[start..]; 11364 if (mem.indexOfScalar(u8, slice, 0) != null) { 11365 return astgen.failTok(token, "identifier cannot contain null bytes", .{}); 11366 } else if (slice.len == 0) { 11367 return astgen.failTok(token, "identifier cannot be empty", .{}); 11368 } 11369 } 11370 } 11371 11372 /// Appends the result to `buf`. 11373 fn parseStrLit( 11374 astgen: *AstGen, 11375 token: Ast.TokenIndex, 11376 buf: *ArrayListUnmanaged(u8), 11377 bytes: []const u8, 11378 offset: u32, 11379 ) InnerError!void { 11380 const raw_string = bytes[offset..]; 11381 var buf_managed = buf.toManaged(astgen.gpa); 11382 const result = std.zig.string_literal.parseWrite(buf_managed.writer(), raw_string); 11383 buf.* = buf_managed.moveToUnmanaged(); 11384 switch (try result) { 11385 .success => return, 11386 .failure => |err| return astgen.failWithStrLitError(err, token, bytes, offset), 11387 } 11388 } 11389 11390 fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError { 11391 const raw_string = bytes[offset..]; 11392 return err.lower(raw_string, offset, AstGen.failOff, .{ astgen, token }); 11393 } 11394 11395 fn failNode( 11396 astgen: *AstGen, 11397 node: Ast.Node.Index, 11398 comptime format: []const u8, 11399 args: anytype, 11400 ) InnerError { 11401 return astgen.failNodeNotes(node, format, args, &[0]u32{}); 11402 } 11403 11404 fn appendErrorNode( 11405 astgen: *AstGen, 11406 node: Ast.Node.Index, 11407 comptime format: []const u8, 11408 args: anytype, 11409 ) Allocator.Error!void { 11410 try astgen.appendErrorNodeNotes(node, format, args, &[0]u32{}); 11411 } 11412 11413 fn appendErrorNodeNotes( 11414 astgen: *AstGen, 11415 node: Ast.Node.Index, 11416 comptime format: []const u8, 11417 args: anytype, 11418 notes: []const u32, 11419 ) Allocator.Error!void { 11420 @branchHint(.cold); 11421 const string_bytes = &astgen.string_bytes; 11422 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len); 11423 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args); 11424 const notes_index: u32 = if (notes.len != 0) blk: { 11425 const notes_start = astgen.extra.items.len; 11426 try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len); 11427 astgen.extra.appendAssumeCapacity(@intCast(notes.len)); 11428 astgen.extra.appendSliceAssumeCapacity(notes); 11429 break :blk @intCast(notes_start); 11430 } else 0; 11431 try astgen.compile_errors.append(astgen.gpa, .{ 11432 .msg = msg, 11433 .node = node, 11434 .token = 0, 11435 .byte_offset = 0, 11436 .notes = notes_index, 11437 }); 11438 } 11439 11440 fn failNodeNotes( 11441 astgen: *AstGen, 11442 node: Ast.Node.Index, 11443 comptime format: []const u8, 11444 args: anytype, 11445 notes: []const u32, 11446 ) InnerError { 11447 try appendErrorNodeNotes(astgen, node, format, args, notes); 11448 return error.AnalysisFail; 11449 } 11450 11451 fn failTok( 11452 astgen: *AstGen, 11453 token: Ast.TokenIndex, 11454 comptime format: []const u8, 11455 args: anytype, 11456 ) InnerError { 11457 return astgen.failTokNotes(token, format, args, &[0]u32{}); 11458 } 11459 11460 fn appendErrorTok( 11461 astgen: *AstGen, 11462 token: Ast.TokenIndex, 11463 comptime format: []const u8, 11464 args: anytype, 11465 ) !void { 11466 try astgen.appendErrorTokNotesOff(token, 0, format, args, &[0]u32{}); 11467 } 11468 11469 fn failTokNotes( 11470 astgen: *AstGen, 11471 token: Ast.TokenIndex, 11472 comptime format: []const u8, 11473 args: anytype, 11474 notes: []const u32, 11475 ) InnerError { 11476 try appendErrorTokNotesOff(astgen, token, 0, format, args, notes); 11477 return error.AnalysisFail; 11478 } 11479 11480 fn appendErrorTokNotes( 11481 astgen: *AstGen, 11482 token: Ast.TokenIndex, 11483 comptime format: []const u8, 11484 args: anytype, 11485 notes: []const u32, 11486 ) !void { 11487 return appendErrorTokNotesOff(astgen, token, 0, format, args, notes); 11488 } 11489 11490 /// Same as `fail`, except given a token plus an offset from its starting byte 11491 /// offset. 11492 fn failOff( 11493 astgen: *AstGen, 11494 token: Ast.TokenIndex, 11495 byte_offset: u32, 11496 comptime format: []const u8, 11497 args: anytype, 11498 ) InnerError { 11499 try appendErrorTokNotesOff(astgen, token, byte_offset, format, args, &.{}); 11500 return error.AnalysisFail; 11501 } 11502 11503 fn appendErrorTokNotesOff( 11504 astgen: *AstGen, 11505 token: Ast.TokenIndex, 11506 byte_offset: u32, 11507 comptime format: []const u8, 11508 args: anytype, 11509 notes: []const u32, 11510 ) !void { 11511 @branchHint(.cold); 11512 const gpa = astgen.gpa; 11513 const string_bytes = &astgen.string_bytes; 11514 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len); 11515 try string_bytes.writer(gpa).print(format ++ "\x00", args); 11516 const notes_index: u32 = if (notes.len != 0) blk: { 11517 const notes_start = astgen.extra.items.len; 11518 try astgen.extra.ensureTotalCapacity(gpa, notes_start + 1 + notes.len); 11519 astgen.extra.appendAssumeCapacity(@intCast(notes.len)); 11520 astgen.extra.appendSliceAssumeCapacity(notes); 11521 break :blk @intCast(notes_start); 11522 } else 0; 11523 try astgen.compile_errors.append(gpa, .{ 11524 .msg = msg, 11525 .node = 0, 11526 .token = token, 11527 .byte_offset = byte_offset, 11528 .notes = notes_index, 11529 }); 11530 } 11531 11532 fn errNoteTok( 11533 astgen: *AstGen, 11534 token: Ast.TokenIndex, 11535 comptime format: []const u8, 11536 args: anytype, 11537 ) Allocator.Error!u32 { 11538 return errNoteTokOff(astgen, token, 0, format, args); 11539 } 11540 11541 fn errNoteTokOff( 11542 astgen: *AstGen, 11543 token: Ast.TokenIndex, 11544 byte_offset: u32, 11545 comptime format: []const u8, 11546 args: anytype, 11547 ) Allocator.Error!u32 { 11548 @branchHint(.cold); 11549 const string_bytes = &astgen.string_bytes; 11550 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len); 11551 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args); 11552 return astgen.addExtra(Zir.Inst.CompileErrors.Item{ 11553 .msg = msg, 11554 .node = 0, 11555 .token = token, 11556 .byte_offset = byte_offset, 11557 .notes = 0, 11558 }); 11559 } 11560 11561 fn errNoteNode( 11562 astgen: *AstGen, 11563 node: Ast.Node.Index, 11564 comptime format: []const u8, 11565 args: anytype, 11566 ) Allocator.Error!u32 { 11567 @branchHint(.cold); 11568 const string_bytes = &astgen.string_bytes; 11569 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len); 11570 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args); 11571 return astgen.addExtra(Zir.Inst.CompileErrors.Item{ 11572 .msg = msg, 11573 .node = node, 11574 .token = 0, 11575 .byte_offset = 0, 11576 .notes = 0, 11577 }); 11578 } 11579 11580 fn identAsString(astgen: *AstGen, ident_token: Ast.TokenIndex) !Zir.NullTerminatedString { 11581 const gpa = astgen.gpa; 11582 const string_bytes = &astgen.string_bytes; 11583 const str_index: u32 = @intCast(string_bytes.items.len); 11584 try astgen.appendIdentStr(ident_token, string_bytes); 11585 const key: []const u8 = string_bytes.items[str_index..]; 11586 const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{ 11587 .bytes = string_bytes, 11588 }, StringIndexContext{ 11589 .bytes = string_bytes, 11590 }); 11591 if (gop.found_existing) { 11592 string_bytes.shrinkRetainingCapacity(str_index); 11593 return @enumFromInt(gop.key_ptr.*); 11594 } else { 11595 gop.key_ptr.* = str_index; 11596 try string_bytes.append(gpa, 0); 11597 return @enumFromInt(str_index); 11598 } 11599 } 11600 11601 const IndexSlice = struct { index: Zir.NullTerminatedString, len: u32 }; 11602 11603 fn strLitAsString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !IndexSlice { 11604 const gpa = astgen.gpa; 11605 const string_bytes = &astgen.string_bytes; 11606 const str_index: u32 = @intCast(string_bytes.items.len); 11607 const token_bytes = astgen.tree.tokenSlice(str_lit_token); 11608 try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0); 11609 const key: []const u8 = string_bytes.items[str_index..]; 11610 if (std.mem.indexOfScalar(u8, key, 0)) |_| return .{ 11611 .index = @enumFromInt(str_index), 11612 .len = @intCast(key.len), 11613 }; 11614 const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{ 11615 .bytes = string_bytes, 11616 }, StringIndexContext{ 11617 .bytes = string_bytes, 11618 }); 11619 if (gop.found_existing) { 11620 string_bytes.shrinkRetainingCapacity(str_index); 11621 return .{ 11622 .index = @enumFromInt(gop.key_ptr.*), 11623 .len = @intCast(key.len), 11624 }; 11625 } else { 11626 gop.key_ptr.* = str_index; 11627 // Still need a null byte because we are using the same table 11628 // to lookup null terminated strings, so if we get a match, it has to 11629 // be null terminated for that to work. 11630 try string_bytes.append(gpa, 0); 11631 return .{ 11632 .index = @enumFromInt(str_index), 11633 .len = @intCast(key.len), 11634 }; 11635 } 11636 } 11637 11638 fn strLitNodeAsString(astgen: *AstGen, node: Ast.Node.Index) !IndexSlice { 11639 const tree = astgen.tree; 11640 const node_datas = tree.nodes.items(.data); 11641 11642 const start = node_datas[node].lhs; 11643 const end = node_datas[node].rhs; 11644 11645 const gpa = astgen.gpa; 11646 const string_bytes = &astgen.string_bytes; 11647 const str_index = string_bytes.items.len; 11648 11649 // First line: do not append a newline. 11650 var tok_i = start; 11651 { 11652 const slice = tree.tokenSlice(tok_i); 11653 const line_bytes = slice[2..]; 11654 try string_bytes.appendSlice(gpa, line_bytes); 11655 tok_i += 1; 11656 } 11657 // Following lines: each line prepends a newline. 11658 while (tok_i <= end) : (tok_i += 1) { 11659 const slice = tree.tokenSlice(tok_i); 11660 const line_bytes = slice[2..]; 11661 try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1); 11662 string_bytes.appendAssumeCapacity('\n'); 11663 string_bytes.appendSliceAssumeCapacity(line_bytes); 11664 } 11665 const len = string_bytes.items.len - str_index; 11666 try string_bytes.append(gpa, 0); 11667 return IndexSlice{ 11668 .index = @enumFromInt(str_index), 11669 .len = @intCast(len), 11670 }; 11671 } 11672 11673 fn testNameString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !Zir.NullTerminatedString { 11674 const gpa = astgen.gpa; 11675 const string_bytes = &astgen.string_bytes; 11676 const str_index: u32 = @intCast(string_bytes.items.len); 11677 const token_bytes = astgen.tree.tokenSlice(str_lit_token); 11678 try string_bytes.append(gpa, 0); // Indicates this is a test. 11679 try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0); 11680 const slice = string_bytes.items[str_index + 1 ..]; 11681 if (mem.indexOfScalar(u8, slice, 0) != null) { 11682 return astgen.failTok(str_lit_token, "test name cannot contain null bytes", .{}); 11683 } else if (slice.len == 0) { 11684 return astgen.failTok(str_lit_token, "empty test name must be omitted", .{}); 11685 } 11686 try string_bytes.append(gpa, 0); 11687 return @enumFromInt(str_index); 11688 } 11689 11690 const Scope = struct { 11691 tag: Tag, 11692 11693 fn cast(base: *Scope, comptime T: type) ?*T { 11694 if (T == Defer) { 11695 switch (base.tag) { 11696 .defer_normal, .defer_error => return @alignCast(@fieldParentPtr("base", base)), 11697 else => return null, 11698 } 11699 } 11700 if (T == Namespace) { 11701 switch (base.tag) { 11702 .namespace => return @alignCast(@fieldParentPtr("base", base)), 11703 else => return null, 11704 } 11705 } 11706 if (base.tag != T.base_tag) 11707 return null; 11708 11709 return @alignCast(@fieldParentPtr("base", base)); 11710 } 11711 11712 fn parent(base: *Scope) ?*Scope { 11713 return switch (base.tag) { 11714 .gen_zir => base.cast(GenZir).?.parent, 11715 .local_val => base.cast(LocalVal).?.parent, 11716 .local_ptr => base.cast(LocalPtr).?.parent, 11717 .defer_normal, .defer_error => base.cast(Defer).?.parent, 11718 .namespace => base.cast(Namespace).?.parent, 11719 .top => null, 11720 }; 11721 } 11722 11723 const Tag = enum { 11724 gen_zir, 11725 local_val, 11726 local_ptr, 11727 defer_normal, 11728 defer_error, 11729 namespace, 11730 top, 11731 }; 11732 11733 /// The category of identifier. These tag names are user-visible in compile errors. 11734 const IdCat = enum { 11735 @"function parameter", 11736 @"local constant", 11737 @"local variable", 11738 @"switch tag capture", 11739 capture, 11740 }; 11741 11742 /// This is always a `const` local and importantly the `inst` is a value type, not a pointer. 11743 /// This structure lives as long as the AST generation of the Block 11744 /// node that contains the variable. 11745 const LocalVal = struct { 11746 const base_tag: Tag = .local_val; 11747 base: Scope = Scope{ .tag = base_tag }, 11748 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11749 parent: *Scope, 11750 gen_zir: *GenZir, 11751 inst: Zir.Inst.Ref, 11752 /// Source location of the corresponding variable declaration. 11753 token_src: Ast.TokenIndex, 11754 /// Track the first identifier where it is referenced. 11755 /// 0 means never referenced. 11756 used: Ast.TokenIndex = 0, 11757 /// Track the identifier where it is discarded, like this `_ = foo;`. 11758 /// 0 means never discarded. 11759 discarded: Ast.TokenIndex = 0, 11760 /// String table index. 11761 name: Zir.NullTerminatedString, 11762 id_cat: IdCat, 11763 }; 11764 11765 /// This could be a `const` or `var` local. It has a pointer instead of a value. 11766 /// This structure lives as long as the AST generation of the Block 11767 /// node that contains the variable. 11768 const LocalPtr = struct { 11769 const base_tag: Tag = .local_ptr; 11770 base: Scope = Scope{ .tag = base_tag }, 11771 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11772 parent: *Scope, 11773 gen_zir: *GenZir, 11774 ptr: Zir.Inst.Ref, 11775 /// Source location of the corresponding variable declaration. 11776 token_src: Ast.TokenIndex, 11777 /// Track the first identifier where it is referenced. 11778 /// 0 means never referenced. 11779 used: Ast.TokenIndex = 0, 11780 /// Track the identifier where it is discarded, like this `_ = foo;`. 11781 /// 0 means never discarded. 11782 discarded: Ast.TokenIndex = 0, 11783 /// Whether this value is used as an lvalue after initialization. 11784 /// If not, we know it can be `const`, so will emit a compile error if it is `var`. 11785 used_as_lvalue: bool = false, 11786 /// String table index. 11787 name: Zir.NullTerminatedString, 11788 id_cat: IdCat, 11789 /// true means we find out during Sema whether the value is comptime. 11790 /// false means it is already known at AstGen the value is runtime-known. 11791 maybe_comptime: bool, 11792 }; 11793 11794 const Defer = struct { 11795 base: Scope, 11796 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11797 parent: *Scope, 11798 index: u32, 11799 len: u32, 11800 remapped_err_code: Zir.Inst.OptionalIndex = .none, 11801 }; 11802 11803 /// Represents a global scope that has any number of declarations in it. 11804 /// Each declaration has this as the parent scope. 11805 const Namespace = struct { 11806 const base_tag: Tag = .namespace; 11807 base: Scope = Scope{ .tag = base_tag }, 11808 11809 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11810 parent: *Scope, 11811 /// Maps string table index to the source location of declaration, 11812 /// for the purposes of reporting name shadowing compile errors. 11813 decls: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .empty, 11814 node: Ast.Node.Index, 11815 inst: Zir.Inst.Index, 11816 maybe_generic: bool, 11817 11818 /// The astgen scope containing this namespace. 11819 /// Only valid during astgen. 11820 declaring_gz: ?*GenZir, 11821 11822 /// Set of captures used by this namespace. 11823 captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Capture, void) = .empty, 11824 11825 fn deinit(self: *Namespace, gpa: Allocator) void { 11826 self.decls.deinit(gpa); 11827 self.captures.deinit(gpa); 11828 self.* = undefined; 11829 } 11830 }; 11831 11832 const Top = struct { 11833 const base_tag: Scope.Tag = .top; 11834 base: Scope = Scope{ .tag = base_tag }, 11835 }; 11836 }; 11837 11838 /// This is a temporary structure; references to it are valid only 11839 /// while constructing a `Zir`. 11840 const GenZir = struct { 11841 const base_tag: Scope.Tag = .gen_zir; 11842 base: Scope = Scope{ .tag = base_tag }, 11843 /// Whether we're already in a scope known to be comptime. This is set 11844 /// whenever we know Sema will analyze the current block with `is_comptime`, 11845 /// for instance when we're within a `struct_decl` or a `block_comptime`. 11846 is_comptime: bool, 11847 /// Whether we're in an expression within a `@TypeOf` operand. In this case, closure of runtime 11848 /// variables is permitted where it is usually not. 11849 is_typeof: bool = false, 11850 /// This is set to true for a `GenZir` of a `block_inline`, indicating that 11851 /// exits from this block should use `break_inline` rather than `break`. 11852 is_inline: bool = false, 11853 c_import: bool = false, 11854 /// How decls created in this scope should be named. 11855 anon_name_strategy: Zir.Inst.NameStrategy = .anon, 11856 /// The containing decl AST node. 11857 decl_node_index: Ast.Node.Index, 11858 /// The containing decl line index, absolute. 11859 decl_line: u32, 11860 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11861 parent: *Scope, 11862 /// All `GenZir` scopes for the same ZIR share this. 11863 astgen: *AstGen, 11864 /// Keeps track of the list of instructions in this scope. Possibly shared. 11865 /// Indexes to instructions in `astgen`. 11866 instructions: *ArrayListUnmanaged(Zir.Inst.Index), 11867 /// A sub-block may share its instructions ArrayList with containing GenZir, 11868 /// if use is strictly nested. This saves prior size of list for unstacking. 11869 instructions_top: usize, 11870 label: ?Label = null, 11871 break_block: Zir.Inst.OptionalIndex = .none, 11872 continue_block: Zir.Inst.OptionalIndex = .none, 11873 /// Only valid when setBreakResultInfo is called. 11874 break_result_info: AstGen.ResultInfo = undefined, 11875 continue_result_info: AstGen.ResultInfo = undefined, 11876 11877 suspend_node: Ast.Node.Index = 0, 11878 nosuspend_node: Ast.Node.Index = 0, 11879 /// Set if this GenZir is a defer. 11880 cur_defer_node: Ast.Node.Index = 0, 11881 // Set if this GenZir is a defer or it is inside a defer. 11882 any_defer_node: Ast.Node.Index = 0, 11883 11884 const unstacked_top = std.math.maxInt(usize); 11885 /// Call unstack before adding any new instructions to containing GenZir. 11886 fn unstack(self: *GenZir) void { 11887 if (self.instructions_top != unstacked_top) { 11888 self.instructions.items.len = self.instructions_top; 11889 self.instructions_top = unstacked_top; 11890 } 11891 } 11892 11893 fn isEmpty(self: *const GenZir) bool { 11894 return (self.instructions_top == unstacked_top) or 11895 (self.instructions.items.len == self.instructions_top); 11896 } 11897 11898 fn instructionsSlice(self: *const GenZir) []Zir.Inst.Index { 11899 return if (self.instructions_top == unstacked_top) 11900 &[0]Zir.Inst.Index{} 11901 else 11902 self.instructions.items[self.instructions_top..]; 11903 } 11904 11905 fn instructionsSliceUpto(self: *const GenZir, stacked_gz: *GenZir) []Zir.Inst.Index { 11906 return if (self.instructions_top == unstacked_top) 11907 &[0]Zir.Inst.Index{} 11908 else if (self.instructions == stacked_gz.instructions and stacked_gz.instructions_top != unstacked_top) 11909 self.instructions.items[self.instructions_top..stacked_gz.instructions_top] 11910 else 11911 self.instructions.items[self.instructions_top..]; 11912 } 11913 11914 fn instructionsSliceUptoOpt(gz: *const GenZir, maybe_stacked_gz: ?*GenZir) []Zir.Inst.Index { 11915 if (maybe_stacked_gz) |stacked_gz| { 11916 return gz.instructionsSliceUpto(stacked_gz); 11917 } else { 11918 return gz.instructionsSlice(); 11919 } 11920 } 11921 11922 fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir { 11923 return .{ 11924 .is_comptime = gz.is_comptime, 11925 .is_typeof = gz.is_typeof, 11926 .c_import = gz.c_import, 11927 .decl_node_index = gz.decl_node_index, 11928 .decl_line = gz.decl_line, 11929 .parent = scope, 11930 .astgen = gz.astgen, 11931 .suspend_node = gz.suspend_node, 11932 .nosuspend_node = gz.nosuspend_node, 11933 .any_defer_node = gz.any_defer_node, 11934 .instructions = gz.instructions, 11935 .instructions_top = gz.instructions.items.len, 11936 }; 11937 } 11938 11939 const Label = struct { 11940 token: Ast.TokenIndex, 11941 block_inst: Zir.Inst.Index, 11942 used: bool = false, 11943 used_for_continue: bool = false, 11944 }; 11945 11946 /// Assumes nothing stacked on `gz`. 11947 fn endsWithNoReturn(gz: GenZir) bool { 11948 if (gz.isEmpty()) return false; 11949 const tags = gz.astgen.instructions.items(.tag); 11950 const last_inst = gz.instructions.items[gz.instructions.items.len - 1]; 11951 return tags[@intFromEnum(last_inst)].isNoReturn(); 11952 } 11953 11954 /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`. 11955 fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool { 11956 if (inst_ref == .unreachable_value) return true; 11957 if (inst_ref.toIndex()) |inst_index| { 11958 return gz.astgen.instructions.items(.tag)[@intFromEnum(inst_index)].isNoReturn(); 11959 } 11960 return false; 11961 } 11962 11963 fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) i32 { 11964 return @as(i32, @bitCast(node_index)) - @as(i32, @bitCast(gz.decl_node_index)); 11965 } 11966 11967 fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) u32 { 11968 return token - gz.srcToken(); 11969 } 11970 11971 fn srcToken(gz: GenZir) Ast.TokenIndex { 11972 return gz.astgen.tree.firstToken(gz.decl_node_index); 11973 } 11974 11975 fn setBreakResultInfo(gz: *GenZir, parent_ri: AstGen.ResultInfo) void { 11976 // Depending on whether the result location is a pointer or value, different 11977 // ZIR needs to be generated. In the former case we rely on storing to the 11978 // pointer to communicate the result, and use breakvoid; in the latter case 11979 // the block break instructions will have the result values. 11980 switch (parent_ri.rl) { 11981 .coerced_ty => |ty_inst| { 11982 // Type coercion needs to happen before breaks. 11983 gz.break_result_info = .{ .rl = .{ .ty = ty_inst }, .ctx = parent_ri.ctx }; 11984 }, 11985 .discard => { 11986 // We don't forward the result context here. This prevents 11987 // "unnecessary discard" errors from being caused by expressions 11988 // far from the actual discard, such as a `break` from a 11989 // discarded block. 11990 gz.break_result_info = .{ .rl = .discard }; 11991 }, 11992 else => { 11993 gz.break_result_info = parent_ri; 11994 }, 11995 } 11996 } 11997 11998 /// Assumes nothing stacked on `gz`. Unstacks `gz`. 11999 fn setBoolBrBody(gz: *GenZir, bool_br: Zir.Inst.Index, bool_br_lhs: Zir.Inst.Ref) !void { 12000 const astgen = gz.astgen; 12001 const gpa = astgen.gpa; 12002 const body = gz.instructionsSlice(); 12003 const body_len = astgen.countBodyLenAfterFixups(body); 12004 try astgen.extra.ensureUnusedCapacity( 12005 gpa, 12006 @typeInfo(Zir.Inst.BoolBr).@"struct".fields.len + body_len, 12007 ); 12008 const zir_datas = astgen.instructions.items(.data); 12009 zir_datas[@intFromEnum(bool_br)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.BoolBr{ 12010 .lhs = bool_br_lhs, 12011 .body_len = body_len, 12012 }); 12013 astgen.appendBodyWithFixups(body); 12014 gz.unstack(); 12015 } 12016 12017 /// Assumes nothing stacked on `gz`. Unstacks `gz`. 12018 fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void { 12019 const astgen = gz.astgen; 12020 const gpa = astgen.gpa; 12021 const body = gz.instructionsSlice(); 12022 const body_len = astgen.countBodyLenAfterFixups(body); 12023 try astgen.extra.ensureUnusedCapacity( 12024 gpa, 12025 @typeInfo(Zir.Inst.Block).@"struct".fields.len + body_len, 12026 ); 12027 const zir_datas = astgen.instructions.items(.data); 12028 zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity( 12029 Zir.Inst.Block{ .body_len = body_len }, 12030 ); 12031 astgen.appendBodyWithFixups(body); 12032 gz.unstack(); 12033 } 12034 12035 /// Assumes nothing stacked on `gz`. Unstacks `gz`. 12036 fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void { 12037 const astgen = gz.astgen; 12038 const gpa = astgen.gpa; 12039 const body = gz.instructionsSlice(); 12040 const body_len = astgen.countBodyLenAfterFixups(body); 12041 try astgen.extra.ensureUnusedCapacity( 12042 gpa, 12043 @typeInfo(Zir.Inst.Try).@"struct".fields.len + body_len, 12044 ); 12045 const zir_datas = astgen.instructions.items(.data); 12046 zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity( 12047 Zir.Inst.Try{ 12048 .operand = operand, 12049 .body_len = body_len, 12050 }, 12051 ); 12052 astgen.appendBodyWithFixups(body); 12053 gz.unstack(); 12054 } 12055 12056 /// Must be called with the following stack set up: 12057 /// * gz (bottom) 12058 /// * ret_gz 12059 /// * cc_gz 12060 /// * body_gz (top) 12061 /// Unstacks all of those except for `gz`. 12062 fn addFunc( 12063 gz: *GenZir, 12064 args: struct { 12065 src_node: Ast.Node.Index, 12066 lbrace_line: u32 = 0, 12067 lbrace_column: u32 = 0, 12068 param_block: Zir.Inst.Index, 12069 12070 ret_gz: ?*GenZir, 12071 body_gz: ?*GenZir, 12072 cc_gz: ?*GenZir, 12073 12074 ret_param_refs: []Zir.Inst.Index, 12075 param_insts: []Zir.Inst.Index, // refs to params in `body_gz` should still be in `astgen.ref_table` 12076 12077 cc_ref: Zir.Inst.Ref, 12078 ret_ref: Zir.Inst.Ref, 12079 12080 lib_name: Zir.NullTerminatedString, 12081 noalias_bits: u32, 12082 is_var_args: bool, 12083 is_inferred_error: bool, 12084 is_test: bool, 12085 is_extern: bool, 12086 is_noinline: bool, 12087 12088 /// Ignored if `body_gz == null`. 12089 proto_hash: std.zig.SrcHash, 12090 }, 12091 ) !Zir.Inst.Ref { 12092 assert(args.src_node != 0); 12093 const astgen = gz.astgen; 12094 const gpa = astgen.gpa; 12095 const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref; 12096 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12097 12098 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12099 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12100 12101 const body, const cc_body, const ret_body = bodies: { 12102 var stacked_gz: ?*GenZir = null; 12103 const body: []const Zir.Inst.Index = if (args.body_gz) |body_gz| body: { 12104 const body = body_gz.instructionsSliceUptoOpt(stacked_gz); 12105 stacked_gz = body_gz; 12106 break :body body; 12107 } else &.{}; 12108 const cc_body: []const Zir.Inst.Index = if (args.cc_gz) |cc_gz| body: { 12109 const cc_body = cc_gz.instructionsSliceUptoOpt(stacked_gz); 12110 stacked_gz = cc_gz; 12111 break :body cc_body; 12112 } else &.{}; 12113 const ret_body: []const Zir.Inst.Index = if (args.ret_gz) |ret_gz| body: { 12114 const ret_body = ret_gz.instructionsSliceUptoOpt(stacked_gz); 12115 stacked_gz = ret_gz; 12116 break :body ret_body; 12117 } else &.{}; 12118 break :bodies .{ body, cc_body, ret_body }; 12119 }; 12120 12121 var src_locs_and_hash_buffer: [7]u32 = undefined; 12122 const src_locs_and_hash: []const u32 = if (args.body_gz != null) src_locs_and_hash: { 12123 const tree = astgen.tree; 12124 const node_tags = tree.nodes.items(.tag); 12125 const node_datas = tree.nodes.items(.data); 12126 const token_starts = tree.tokens.items(.start); 12127 const fn_decl = args.src_node; 12128 assert(node_tags[fn_decl] == .fn_decl or node_tags[fn_decl] == .test_decl); 12129 const block = node_datas[fn_decl].rhs; 12130 const rbrace_start = token_starts[tree.lastToken(block)]; 12131 astgen.advanceSourceCursor(rbrace_start); 12132 const rbrace_line: u32 = @intCast(astgen.source_line - gz.decl_line); 12133 const rbrace_column: u32 = @intCast(astgen.source_column); 12134 12135 const columns = args.lbrace_column | (rbrace_column << 16); 12136 12137 const proto_hash_arr: [4]u32 = @bitCast(args.proto_hash); 12138 12139 src_locs_and_hash_buffer = .{ 12140 args.lbrace_line, 12141 rbrace_line, 12142 columns, 12143 proto_hash_arr[0], 12144 proto_hash_arr[1], 12145 proto_hash_arr[2], 12146 proto_hash_arr[3], 12147 }; 12148 break :src_locs_and_hash &src_locs_and_hash_buffer; 12149 } else &.{}; 12150 12151 const body_len = astgen.countBodyLenAfterFixupsExtraRefs(body, args.param_insts); 12152 12153 const tag: Zir.Inst.Tag, const payload_index: u32 = if (args.cc_ref != .none or args.lib_name != .empty or 12154 args.is_var_args or args.is_test or args.is_extern or 12155 args.noalias_bits != 0 or args.is_noinline) 12156 inst_info: { 12157 try astgen.extra.ensureUnusedCapacity( 12158 gpa, 12159 @typeInfo(Zir.Inst.FuncFancy).@"struct".fields.len + 12160 fancyFnExprExtraLen(astgen, &.{}, cc_body, args.cc_ref) + 12161 fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) + 12162 body_len + src_locs_and_hash.len + 12163 @intFromBool(args.lib_name != .empty) + 12164 @intFromBool(args.noalias_bits != 0), 12165 ); 12166 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{ 12167 .param_block = args.param_block, 12168 .body_len = body_len, 12169 .bits = .{ 12170 .is_var_args = args.is_var_args, 12171 .is_inferred_error = args.is_inferred_error, 12172 .is_test = args.is_test, 12173 .is_extern = args.is_extern, 12174 .is_noinline = args.is_noinline, 12175 .has_lib_name = args.lib_name != .empty, 12176 .has_any_noalias = args.noalias_bits != 0, 12177 12178 .has_cc_ref = args.cc_ref != .none, 12179 .has_ret_ty_ref = ret_ref != .none, 12180 12181 .has_cc_body = cc_body.len != 0, 12182 .has_ret_ty_body = ret_body.len != 0, 12183 }, 12184 }); 12185 if (args.lib_name != .empty) { 12186 astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name)); 12187 } 12188 12189 const zir_datas = astgen.instructions.items(.data); 12190 if (cc_body.len != 0) { 12191 astgen.extra.appendAssumeCapacity(astgen.countBodyLenAfterFixups(cc_body)); 12192 astgen.appendBodyWithFixups(cc_body); 12193 const break_extra = zir_datas[@intFromEnum(cc_body[cc_body.len - 1])].@"break".payload_index; 12194 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] = 12195 @intFromEnum(new_index); 12196 } else if (args.cc_ref != .none) { 12197 astgen.extra.appendAssumeCapacity(@intFromEnum(args.cc_ref)); 12198 } 12199 if (ret_body.len != 0) { 12200 astgen.extra.appendAssumeCapacity( 12201 astgen.countBodyLenAfterFixups(args.ret_param_refs) + 12202 astgen.countBodyLenAfterFixups(ret_body), 12203 ); 12204 astgen.appendBodyWithFixups(args.ret_param_refs); 12205 astgen.appendBodyWithFixups(ret_body); 12206 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index; 12207 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] = 12208 @intFromEnum(new_index); 12209 } else if (ret_ref != .none) { 12210 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref)); 12211 } 12212 12213 if (args.noalias_bits != 0) { 12214 astgen.extra.appendAssumeCapacity(args.noalias_bits); 12215 } 12216 12217 astgen.appendBodyWithFixupsExtraRefsArrayList(&astgen.extra, body, args.param_insts); 12218 astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash); 12219 12220 break :inst_info .{ .func_fancy, payload_index }; 12221 } else inst_info: { 12222 try astgen.extra.ensureUnusedCapacity( 12223 gpa, 12224 @typeInfo(Zir.Inst.Func).@"struct".fields.len + 1 + 12225 fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) + 12226 body_len + src_locs_and_hash.len, 12227 ); 12228 12229 const ret_body_len = if (ret_body.len != 0) 12230 countBodyLenAfterFixups(astgen, args.ret_param_refs) + countBodyLenAfterFixups(astgen, ret_body) 12231 else 12232 @intFromBool(ret_ref != .none); 12233 12234 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{ 12235 .param_block = args.param_block, 12236 .ret_body_len = ret_body_len, 12237 .body_len = body_len, 12238 }); 12239 const zir_datas = astgen.instructions.items(.data); 12240 if (ret_body.len != 0) { 12241 astgen.appendBodyWithFixups(args.ret_param_refs); 12242 astgen.appendBodyWithFixups(ret_body); 12243 12244 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index; 12245 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] = 12246 @intFromEnum(new_index); 12247 } else if (ret_ref != .none) { 12248 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref)); 12249 } 12250 astgen.appendBodyWithFixupsExtraRefsArrayList(&astgen.extra, body, args.param_insts); 12251 astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash); 12252 12253 break :inst_info .{ 12254 if (args.is_inferred_error) .func_inferred else .func, 12255 payload_index, 12256 }; 12257 }; 12258 12259 // Order is important when unstacking. 12260 if (args.body_gz) |body_gz| body_gz.unstack(); 12261 if (args.cc_gz) |cc_gz| cc_gz.unstack(); 12262 if (args.ret_gz) |ret_gz| ret_gz.unstack(); 12263 12264 astgen.instructions.appendAssumeCapacity(.{ 12265 .tag = tag, 12266 .data = .{ .pl_node = .{ 12267 .src_node = gz.nodeIndexToRelative(args.src_node), 12268 .payload_index = payload_index, 12269 } }, 12270 }); 12271 gz.instructions.appendAssumeCapacity(new_index); 12272 return new_index.toRef(); 12273 } 12274 12275 fn fancyFnExprExtraLen(astgen: *AstGen, param_refs_body: []const Zir.Inst.Index, main_body: []const Zir.Inst.Index, ref: Zir.Inst.Ref) u32 { 12276 return countBodyLenAfterFixups(astgen, param_refs_body) + 12277 countBodyLenAfterFixups(astgen, main_body) + 12278 // If there is a body, we need an element for its length; otherwise, if there is a ref, we need to include that. 12279 @intFromBool(main_body.len > 0 or ref != .none); 12280 } 12281 12282 fn addVar(gz: *GenZir, args: struct { 12283 align_inst: Zir.Inst.Ref, 12284 lib_name: Zir.NullTerminatedString, 12285 var_type: Zir.Inst.Ref, 12286 init: Zir.Inst.Ref, 12287 is_extern: bool, 12288 is_const: bool, 12289 is_threadlocal: bool, 12290 }) !Zir.Inst.Ref { 12291 const astgen = gz.astgen; 12292 const gpa = astgen.gpa; 12293 12294 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12295 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12296 12297 try astgen.extra.ensureUnusedCapacity( 12298 gpa, 12299 @typeInfo(Zir.Inst.ExtendedVar).@"struct".fields.len + 12300 @intFromBool(args.lib_name != .empty) + 12301 @intFromBool(args.align_inst != .none) + 12302 @intFromBool(args.init != .none), 12303 ); 12304 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedVar{ 12305 .var_type = args.var_type, 12306 }); 12307 if (args.lib_name != .empty) { 12308 astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name)); 12309 } 12310 if (args.align_inst != .none) { 12311 astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst)); 12312 } 12313 if (args.init != .none) { 12314 astgen.extra.appendAssumeCapacity(@intFromEnum(args.init)); 12315 } 12316 12317 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12318 astgen.instructions.appendAssumeCapacity(.{ 12319 .tag = .extended, 12320 .data = .{ .extended = .{ 12321 .opcode = .variable, 12322 .small = @bitCast(Zir.Inst.ExtendedVar.Small{ 12323 .has_lib_name = args.lib_name != .empty, 12324 .has_align = args.align_inst != .none, 12325 .has_init = args.init != .none, 12326 .is_extern = args.is_extern, 12327 .is_const = args.is_const, 12328 .is_threadlocal = args.is_threadlocal, 12329 }), 12330 .operand = payload_index, 12331 } }, 12332 }); 12333 gz.instructions.appendAssumeCapacity(new_index); 12334 return new_index.toRef(); 12335 } 12336 12337 fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref { 12338 return gz.add(.{ 12339 .tag = .int, 12340 .data = .{ .int = integer }, 12341 }); 12342 } 12343 12344 fn addIntBig(gz: *GenZir, limbs: []const std.math.big.Limb) !Zir.Inst.Ref { 12345 const astgen = gz.astgen; 12346 const gpa = astgen.gpa; 12347 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12348 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12349 try astgen.string_bytes.ensureUnusedCapacity(gpa, @sizeOf(std.math.big.Limb) * limbs.len); 12350 12351 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12352 astgen.instructions.appendAssumeCapacity(.{ 12353 .tag = .int_big, 12354 .data = .{ .str = .{ 12355 .start = @enumFromInt(astgen.string_bytes.items.len), 12356 .len = @intCast(limbs.len), 12357 } }, 12358 }); 12359 gz.instructions.appendAssumeCapacity(new_index); 12360 astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs)); 12361 return new_index.toRef(); 12362 } 12363 12364 fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref { 12365 return gz.add(.{ 12366 .tag = .float, 12367 .data = .{ .float = number }, 12368 }); 12369 } 12370 12371 fn addUnNode( 12372 gz: *GenZir, 12373 tag: Zir.Inst.Tag, 12374 operand: Zir.Inst.Ref, 12375 /// Absolute node index. This function does the conversion to offset from Decl. 12376 src_node: Ast.Node.Index, 12377 ) !Zir.Inst.Ref { 12378 assert(operand != .none); 12379 return gz.add(.{ 12380 .tag = tag, 12381 .data = .{ .un_node = .{ 12382 .operand = operand, 12383 .src_node = gz.nodeIndexToRelative(src_node), 12384 } }, 12385 }); 12386 } 12387 12388 fn makeUnNode( 12389 gz: *GenZir, 12390 tag: Zir.Inst.Tag, 12391 operand: Zir.Inst.Ref, 12392 /// Absolute node index. This function does the conversion to offset from Decl. 12393 src_node: Ast.Node.Index, 12394 ) !Zir.Inst.Index { 12395 assert(operand != .none); 12396 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12397 try gz.astgen.instructions.append(gz.astgen.gpa, .{ 12398 .tag = tag, 12399 .data = .{ .un_node = .{ 12400 .operand = operand, 12401 .src_node = gz.nodeIndexToRelative(src_node), 12402 } }, 12403 }); 12404 return new_index; 12405 } 12406 12407 fn addPlNode( 12408 gz: *GenZir, 12409 tag: Zir.Inst.Tag, 12410 /// Absolute node index. This function does the conversion to offset from Decl. 12411 src_node: Ast.Node.Index, 12412 extra: anytype, 12413 ) !Zir.Inst.Ref { 12414 const gpa = gz.astgen.gpa; 12415 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12416 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 12417 12418 const payload_index = try gz.astgen.addExtra(extra); 12419 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12420 gz.astgen.instructions.appendAssumeCapacity(.{ 12421 .tag = tag, 12422 .data = .{ .pl_node = .{ 12423 .src_node = gz.nodeIndexToRelative(src_node), 12424 .payload_index = payload_index, 12425 } }, 12426 }); 12427 gz.instructions.appendAssumeCapacity(new_index); 12428 return new_index.toRef(); 12429 } 12430 12431 fn addPlNodePayloadIndex( 12432 gz: *GenZir, 12433 tag: Zir.Inst.Tag, 12434 /// Absolute node index. This function does the conversion to offset from Decl. 12435 src_node: Ast.Node.Index, 12436 payload_index: u32, 12437 ) !Zir.Inst.Ref { 12438 return try gz.add(.{ 12439 .tag = tag, 12440 .data = .{ .pl_node = .{ 12441 .src_node = gz.nodeIndexToRelative(src_node), 12442 .payload_index = payload_index, 12443 } }, 12444 }); 12445 } 12446 12447 /// Supports `param_gz` stacked on `gz`. Assumes nothing stacked on `param_gz`. Unstacks `param_gz`. 12448 fn addParam( 12449 gz: *GenZir, 12450 param_gz: *GenZir, 12451 /// Previous parameters, which might be referenced in `param_gz` (the new parameter type). 12452 /// `ref`s of these instructions will be put into this param's type body, and removed from `AstGen.ref_table`. 12453 prev_param_insts: []const Zir.Inst.Index, 12454 tag: Zir.Inst.Tag, 12455 /// Absolute token index. This function does the conversion to Decl offset. 12456 abs_tok_index: Ast.TokenIndex, 12457 name: Zir.NullTerminatedString, 12458 ) !Zir.Inst.Index { 12459 const gpa = gz.astgen.gpa; 12460 const param_body = param_gz.instructionsSlice(); 12461 const body_len = gz.astgen.countBodyLenAfterFixupsExtraRefs(param_body, prev_param_insts); 12462 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 12463 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).@"struct".fields.len + body_len); 12464 12465 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{ 12466 .name = name, 12467 .body_len = @intCast(body_len), 12468 }); 12469 gz.astgen.appendBodyWithFixupsExtraRefsArrayList(&gz.astgen.extra, param_body, prev_param_insts); 12470 param_gz.unstack(); 12471 12472 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12473 gz.astgen.instructions.appendAssumeCapacity(.{ 12474 .tag = tag, 12475 .data = .{ .pl_tok = .{ 12476 .src_tok = gz.tokenIndexToRelative(abs_tok_index), 12477 .payload_index = payload_index, 12478 } }, 12479 }); 12480 gz.instructions.appendAssumeCapacity(new_index); 12481 return new_index; 12482 } 12483 12484 fn addBuiltinValue(gz: *GenZir, src_node: Ast.Node.Index, val: Zir.Inst.BuiltinValue) !Zir.Inst.Ref { 12485 return addExtendedNodeSmall(gz, .builtin_value, src_node, @intFromEnum(val)); 12486 } 12487 12488 fn addExtendedPayload(gz: *GenZir, opcode: Zir.Inst.Extended, extra: anytype) !Zir.Inst.Ref { 12489 return addExtendedPayloadSmall(gz, opcode, undefined, extra); 12490 } 12491 12492 fn addExtendedPayloadSmall( 12493 gz: *GenZir, 12494 opcode: Zir.Inst.Extended, 12495 small: u16, 12496 extra: anytype, 12497 ) !Zir.Inst.Ref { 12498 const gpa = gz.astgen.gpa; 12499 12500 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12501 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 12502 12503 const payload_index = try gz.astgen.addExtra(extra); 12504 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12505 gz.astgen.instructions.appendAssumeCapacity(.{ 12506 .tag = .extended, 12507 .data = .{ .extended = .{ 12508 .opcode = opcode, 12509 .small = small, 12510 .operand = payload_index, 12511 } }, 12512 }); 12513 gz.instructions.appendAssumeCapacity(new_index); 12514 return new_index.toRef(); 12515 } 12516 12517 fn addExtendedMultiOp( 12518 gz: *GenZir, 12519 opcode: Zir.Inst.Extended, 12520 node: Ast.Node.Index, 12521 operands: []const Zir.Inst.Ref, 12522 ) !Zir.Inst.Ref { 12523 const astgen = gz.astgen; 12524 const gpa = astgen.gpa; 12525 12526 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12527 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12528 try astgen.extra.ensureUnusedCapacity( 12529 gpa, 12530 @typeInfo(Zir.Inst.NodeMultiOp).@"struct".fields.len + operands.len, 12531 ); 12532 12533 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{ 12534 .src_node = gz.nodeIndexToRelative(node), 12535 }); 12536 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12537 astgen.instructions.appendAssumeCapacity(.{ 12538 .tag = .extended, 12539 .data = .{ .extended = .{ 12540 .opcode = opcode, 12541 .small = @intCast(operands.len), 12542 .operand = payload_index, 12543 } }, 12544 }); 12545 gz.instructions.appendAssumeCapacity(new_index); 12546 astgen.appendRefsAssumeCapacity(operands); 12547 return new_index.toRef(); 12548 } 12549 12550 fn addExtendedMultiOpPayloadIndex( 12551 gz: *GenZir, 12552 opcode: Zir.Inst.Extended, 12553 payload_index: u32, 12554 trailing_len: usize, 12555 ) !Zir.Inst.Ref { 12556 const astgen = gz.astgen; 12557 const gpa = astgen.gpa; 12558 12559 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12560 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12561 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12562 astgen.instructions.appendAssumeCapacity(.{ 12563 .tag = .extended, 12564 .data = .{ .extended = .{ 12565 .opcode = opcode, 12566 .small = @intCast(trailing_len), 12567 .operand = payload_index, 12568 } }, 12569 }); 12570 gz.instructions.appendAssumeCapacity(new_index); 12571 return new_index.toRef(); 12572 } 12573 12574 fn addExtendedNodeSmall( 12575 gz: *GenZir, 12576 opcode: Zir.Inst.Extended, 12577 src_node: Ast.Node.Index, 12578 small: u16, 12579 ) !Zir.Inst.Ref { 12580 const astgen = gz.astgen; 12581 const gpa = astgen.gpa; 12582 12583 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12584 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12585 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12586 astgen.instructions.appendAssumeCapacity(.{ 12587 .tag = .extended, 12588 .data = .{ .extended = .{ 12589 .opcode = opcode, 12590 .small = small, 12591 .operand = @bitCast(gz.nodeIndexToRelative(src_node)), 12592 } }, 12593 }); 12594 gz.instructions.appendAssumeCapacity(new_index); 12595 return new_index.toRef(); 12596 } 12597 12598 fn addUnTok( 12599 gz: *GenZir, 12600 tag: Zir.Inst.Tag, 12601 operand: Zir.Inst.Ref, 12602 /// Absolute token index. This function does the conversion to Decl offset. 12603 abs_tok_index: Ast.TokenIndex, 12604 ) !Zir.Inst.Ref { 12605 assert(operand != .none); 12606 return gz.add(.{ 12607 .tag = tag, 12608 .data = .{ .un_tok = .{ 12609 .operand = operand, 12610 .src_tok = gz.tokenIndexToRelative(abs_tok_index), 12611 } }, 12612 }); 12613 } 12614 12615 fn makeUnTok( 12616 gz: *GenZir, 12617 tag: Zir.Inst.Tag, 12618 operand: Zir.Inst.Ref, 12619 /// Absolute token index. This function does the conversion to Decl offset. 12620 abs_tok_index: Ast.TokenIndex, 12621 ) !Zir.Inst.Index { 12622 const astgen = gz.astgen; 12623 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12624 assert(operand != .none); 12625 try astgen.instructions.append(astgen.gpa, .{ 12626 .tag = tag, 12627 .data = .{ .un_tok = .{ 12628 .operand = operand, 12629 .src_tok = gz.tokenIndexToRelative(abs_tok_index), 12630 } }, 12631 }); 12632 return new_index; 12633 } 12634 12635 fn addStrTok( 12636 gz: *GenZir, 12637 tag: Zir.Inst.Tag, 12638 str_index: Zir.NullTerminatedString, 12639 /// Absolute token index. This function does the conversion to Decl offset. 12640 abs_tok_index: Ast.TokenIndex, 12641 ) !Zir.Inst.Ref { 12642 return gz.add(.{ 12643 .tag = tag, 12644 .data = .{ .str_tok = .{ 12645 .start = str_index, 12646 .src_tok = gz.tokenIndexToRelative(abs_tok_index), 12647 } }, 12648 }); 12649 } 12650 12651 fn addSaveErrRetIndex( 12652 gz: *GenZir, 12653 cond: union(enum) { 12654 always: void, 12655 if_of_error_type: Zir.Inst.Ref, 12656 }, 12657 ) !Zir.Inst.Index { 12658 return gz.addAsIndex(.{ 12659 .tag = .save_err_ret_index, 12660 .data = .{ .save_err_ret_index = .{ 12661 .operand = switch (cond) { 12662 .if_of_error_type => |x| x, 12663 else => .none, 12664 }, 12665 } }, 12666 }); 12667 } 12668 12669 const BranchTarget = union(enum) { 12670 ret, 12671 block: Zir.Inst.Index, 12672 }; 12673 12674 fn addRestoreErrRetIndex( 12675 gz: *GenZir, 12676 bt: BranchTarget, 12677 cond: union(enum) { 12678 always: void, 12679 if_non_error: Zir.Inst.Ref, 12680 }, 12681 src_node: Ast.Node.Index, 12682 ) !Zir.Inst.Index { 12683 switch (cond) { 12684 .always => return gz.addAsIndex(.{ 12685 .tag = .restore_err_ret_index_unconditional, 12686 .data = .{ .un_node = .{ 12687 .operand = switch (bt) { 12688 .ret => .none, 12689 .block => |b| b.toRef(), 12690 }, 12691 .src_node = gz.nodeIndexToRelative(src_node), 12692 } }, 12693 }), 12694 .if_non_error => |operand| switch (bt) { 12695 .ret => return gz.addAsIndex(.{ 12696 .tag = .restore_err_ret_index_fn_entry, 12697 .data = .{ .un_node = .{ 12698 .operand = operand, 12699 .src_node = gz.nodeIndexToRelative(src_node), 12700 } }, 12701 }), 12702 .block => |block| return (try gz.addExtendedPayload( 12703 .restore_err_ret_index, 12704 Zir.Inst.RestoreErrRetIndex{ 12705 .src_node = gz.nodeIndexToRelative(src_node), 12706 .block = block.toRef(), 12707 .operand = operand, 12708 }, 12709 )).toIndex().?, 12710 }, 12711 } 12712 } 12713 12714 fn addBreak( 12715 gz: *GenZir, 12716 tag: Zir.Inst.Tag, 12717 block_inst: Zir.Inst.Index, 12718 operand: Zir.Inst.Ref, 12719 ) !Zir.Inst.Index { 12720 const gpa = gz.astgen.gpa; 12721 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12722 12723 const new_index = try gz.makeBreak(tag, block_inst, operand); 12724 gz.instructions.appendAssumeCapacity(new_index); 12725 return new_index; 12726 } 12727 12728 fn makeBreak( 12729 gz: *GenZir, 12730 tag: Zir.Inst.Tag, 12731 block_inst: Zir.Inst.Index, 12732 operand: Zir.Inst.Ref, 12733 ) !Zir.Inst.Index { 12734 return gz.makeBreakCommon(tag, block_inst, operand, null); 12735 } 12736 12737 fn addBreakWithSrcNode( 12738 gz: *GenZir, 12739 tag: Zir.Inst.Tag, 12740 block_inst: Zir.Inst.Index, 12741 operand: Zir.Inst.Ref, 12742 operand_src_node: Ast.Node.Index, 12743 ) !Zir.Inst.Index { 12744 const gpa = gz.astgen.gpa; 12745 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12746 12747 const new_index = try gz.makeBreakWithSrcNode(tag, block_inst, operand, operand_src_node); 12748 gz.instructions.appendAssumeCapacity(new_index); 12749 return new_index; 12750 } 12751 12752 fn makeBreakWithSrcNode( 12753 gz: *GenZir, 12754 tag: Zir.Inst.Tag, 12755 block_inst: Zir.Inst.Index, 12756 operand: Zir.Inst.Ref, 12757 operand_src_node: Ast.Node.Index, 12758 ) !Zir.Inst.Index { 12759 return gz.makeBreakCommon(tag, block_inst, operand, operand_src_node); 12760 } 12761 12762 fn makeBreakCommon( 12763 gz: *GenZir, 12764 tag: Zir.Inst.Tag, 12765 block_inst: Zir.Inst.Index, 12766 operand: Zir.Inst.Ref, 12767 operand_src_node: ?Ast.Node.Index, 12768 ) !Zir.Inst.Index { 12769 const gpa = gz.astgen.gpa; 12770 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 12771 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Break).@"struct".fields.len); 12772 12773 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12774 gz.astgen.instructions.appendAssumeCapacity(.{ 12775 .tag = tag, 12776 .data = .{ .@"break" = .{ 12777 .operand = operand, 12778 .payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Break{ 12779 .operand_src_node = if (operand_src_node) |src_node| 12780 gz.nodeIndexToRelative(src_node) 12781 else 12782 Zir.Inst.Break.no_src_node, 12783 .block_inst = block_inst, 12784 }), 12785 } }, 12786 }); 12787 return new_index; 12788 } 12789 12790 fn addBin( 12791 gz: *GenZir, 12792 tag: Zir.Inst.Tag, 12793 lhs: Zir.Inst.Ref, 12794 rhs: Zir.Inst.Ref, 12795 ) !Zir.Inst.Ref { 12796 assert(lhs != .none); 12797 assert(rhs != .none); 12798 return gz.add(.{ 12799 .tag = tag, 12800 .data = .{ .bin = .{ 12801 .lhs = lhs, 12802 .rhs = rhs, 12803 } }, 12804 }); 12805 } 12806 12807 fn addDefer(gz: *GenZir, index: u32, len: u32) !void { 12808 _ = try gz.add(.{ 12809 .tag = .@"defer", 12810 .data = .{ .@"defer" = .{ 12811 .index = index, 12812 .len = len, 12813 } }, 12814 }); 12815 } 12816 12817 fn addDecl( 12818 gz: *GenZir, 12819 tag: Zir.Inst.Tag, 12820 decl_index: u32, 12821 src_node: Ast.Node.Index, 12822 ) !Zir.Inst.Ref { 12823 return gz.add(.{ 12824 .tag = tag, 12825 .data = .{ .pl_node = .{ 12826 .src_node = gz.nodeIndexToRelative(src_node), 12827 .payload_index = decl_index, 12828 } }, 12829 }); 12830 } 12831 12832 fn addNode( 12833 gz: *GenZir, 12834 tag: Zir.Inst.Tag, 12835 /// Absolute node index. This function does the conversion to offset from Decl. 12836 src_node: Ast.Node.Index, 12837 ) !Zir.Inst.Ref { 12838 return gz.add(.{ 12839 .tag = tag, 12840 .data = .{ .node = gz.nodeIndexToRelative(src_node) }, 12841 }); 12842 } 12843 12844 fn addInstNode( 12845 gz: *GenZir, 12846 tag: Zir.Inst.Tag, 12847 inst: Zir.Inst.Index, 12848 /// Absolute node index. This function does the conversion to offset from Decl. 12849 src_node: Ast.Node.Index, 12850 ) !Zir.Inst.Ref { 12851 return gz.add(.{ 12852 .tag = tag, 12853 .data = .{ .inst_node = .{ 12854 .inst = inst, 12855 .src_node = gz.nodeIndexToRelative(src_node), 12856 } }, 12857 }); 12858 } 12859 12860 fn addNodeExtended( 12861 gz: *GenZir, 12862 opcode: Zir.Inst.Extended, 12863 /// Absolute node index. This function does the conversion to offset from Decl. 12864 src_node: Ast.Node.Index, 12865 ) !Zir.Inst.Ref { 12866 return gz.add(.{ 12867 .tag = .extended, 12868 .data = .{ .extended = .{ 12869 .opcode = opcode, 12870 .small = undefined, 12871 .operand = @bitCast(gz.nodeIndexToRelative(src_node)), 12872 } }, 12873 }); 12874 } 12875 12876 fn addAllocExtended( 12877 gz: *GenZir, 12878 args: struct { 12879 /// Absolute node index. This function does the conversion to offset from Decl. 12880 node: Ast.Node.Index, 12881 type_inst: Zir.Inst.Ref, 12882 align_inst: Zir.Inst.Ref, 12883 is_const: bool, 12884 is_comptime: bool, 12885 }, 12886 ) !Zir.Inst.Ref { 12887 const astgen = gz.astgen; 12888 const gpa = astgen.gpa; 12889 12890 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12891 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12892 try astgen.extra.ensureUnusedCapacity( 12893 gpa, 12894 @typeInfo(Zir.Inst.AllocExtended).@"struct".fields.len + 12895 @intFromBool(args.type_inst != .none) + 12896 @intFromBool(args.align_inst != .none), 12897 ); 12898 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.AllocExtended{ 12899 .src_node = gz.nodeIndexToRelative(args.node), 12900 }); 12901 if (args.type_inst != .none) { 12902 astgen.extra.appendAssumeCapacity(@intFromEnum(args.type_inst)); 12903 } 12904 if (args.align_inst != .none) { 12905 astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst)); 12906 } 12907 12908 const has_type: u4 = @intFromBool(args.type_inst != .none); 12909 const has_align: u4 = @intFromBool(args.align_inst != .none); 12910 const is_const: u4 = @intFromBool(args.is_const); 12911 const is_comptime: u4 = @intFromBool(args.is_comptime); 12912 const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3); 12913 12914 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12915 astgen.instructions.appendAssumeCapacity(.{ 12916 .tag = .extended, 12917 .data = .{ .extended = .{ 12918 .opcode = .alloc, 12919 .small = small, 12920 .operand = payload_index, 12921 } }, 12922 }); 12923 gz.instructions.appendAssumeCapacity(new_index); 12924 return new_index.toRef(); 12925 } 12926 12927 fn addAsm( 12928 gz: *GenZir, 12929 args: struct { 12930 tag: Zir.Inst.Extended, 12931 /// Absolute node index. This function does the conversion to offset from Decl. 12932 node: Ast.Node.Index, 12933 asm_source: Zir.NullTerminatedString, 12934 output_type_bits: u32, 12935 is_volatile: bool, 12936 outputs: []const Zir.Inst.Asm.Output, 12937 inputs: []const Zir.Inst.Asm.Input, 12938 clobbers: []const u32, 12939 }, 12940 ) !Zir.Inst.Ref { 12941 const astgen = gz.astgen; 12942 const gpa = astgen.gpa; 12943 12944 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12945 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12946 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Asm).@"struct".fields.len + 12947 args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).@"struct".fields.len + 12948 args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).@"struct".fields.len + 12949 args.clobbers.len); 12950 12951 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{ 12952 .src_node = gz.nodeIndexToRelative(args.node), 12953 .asm_source = args.asm_source, 12954 .output_type_bits = args.output_type_bits, 12955 }); 12956 for (args.outputs) |output| { 12957 _ = gz.astgen.addExtraAssumeCapacity(output); 12958 } 12959 for (args.inputs) |input| { 12960 _ = gz.astgen.addExtraAssumeCapacity(input); 12961 } 12962 gz.astgen.extra.appendSliceAssumeCapacity(args.clobbers); 12963 12964 // * 0b00000000_000XXXXX - `outputs_len`. 12965 // * 0b000000XX_XXX00000 - `inputs_len`. 12966 // * 0b0XXXXX00_00000000 - `clobbers_len`. 12967 // * 0bX0000000_00000000 - is volatile 12968 const small: u16 = @as(u16, @intCast(args.outputs.len)) | 12969 @as(u16, @intCast(args.inputs.len << 5)) | 12970 @as(u16, @intCast(args.clobbers.len << 10)) | 12971 (@as(u16, @intFromBool(args.is_volatile)) << 15); 12972 12973 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12974 astgen.instructions.appendAssumeCapacity(.{ 12975 .tag = .extended, 12976 .data = .{ .extended = .{ 12977 .opcode = args.tag, 12978 .small = small, 12979 .operand = payload_index, 12980 } }, 12981 }); 12982 gz.instructions.appendAssumeCapacity(new_index); 12983 return new_index.toRef(); 12984 } 12985 12986 /// Note that this returns a `Zir.Inst.Index` not a ref. 12987 /// Does *not* append the block instruction to the scope. 12988 /// Leaves the `payload_index` field undefined. 12989 fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index { 12990 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12991 const gpa = gz.astgen.gpa; 12992 try gz.astgen.instructions.append(gpa, .{ 12993 .tag = tag, 12994 .data = .{ .pl_node = .{ 12995 .src_node = gz.nodeIndexToRelative(node), 12996 .payload_index = undefined, 12997 } }, 12998 }); 12999 return new_index; 13000 } 13001 13002 /// Note that this returns a `Zir.Inst.Index` not a ref. 13003 /// Does *not* append the block instruction to the scope. 13004 /// Leaves the `payload_index` field undefined. Use `setDeclaration` to finalize. 13005 fn makeDeclaration(gz: *GenZir, node: Ast.Node.Index) !Zir.Inst.Index { 13006 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 13007 try gz.astgen.instructions.append(gz.astgen.gpa, .{ 13008 .tag = .declaration, 13009 .data = .{ .declaration = .{ 13010 .src_node = node, 13011 .payload_index = undefined, 13012 } }, 13013 }); 13014 return new_index; 13015 } 13016 13017 /// Note that this returns a `Zir.Inst.Index` not a ref. 13018 /// Leaves the `payload_index` field undefined. 13019 fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index { 13020 const gpa = gz.astgen.gpa; 13021 try gz.instructions.ensureUnusedCapacity(gpa, 1); 13022 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 13023 try gz.astgen.instructions.append(gpa, .{ 13024 .tag = tag, 13025 .data = .{ .pl_node = .{ 13026 .src_node = gz.nodeIndexToRelative(node), 13027 .payload_index = undefined, 13028 } }, 13029 }); 13030 gz.instructions.appendAssumeCapacity(new_index); 13031 return new_index; 13032 } 13033 13034 fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct { 13035 src_node: Ast.Node.Index, 13036 captures_len: u32, 13037 fields_len: u32, 13038 decls_len: u32, 13039 has_backing_int: bool, 13040 layout: std.builtin.Type.ContainerLayout, 13041 known_non_opv: bool, 13042 known_comptime_only: bool, 13043 any_comptime_fields: bool, 13044 any_default_inits: bool, 13045 any_aligned_fields: bool, 13046 fields_hash: std.zig.SrcHash, 13047 }) !void { 13048 const astgen = gz.astgen; 13049 const gpa = astgen.gpa; 13050 13051 // Node 0 is valid for the root `struct_decl` of a file! 13052 assert(args.src_node != 0 or gz.parent.tag == .top); 13053 13054 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); 13055 13056 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len + 3); 13057 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.StructDecl{ 13058 .fields_hash_0 = fields_hash_arr[0], 13059 .fields_hash_1 = fields_hash_arr[1], 13060 .fields_hash_2 = fields_hash_arr[2], 13061 .fields_hash_3 = fields_hash_arr[3], 13062 .src_line = astgen.source_line, 13063 .src_node = args.src_node, 13064 }); 13065 13066 if (args.captures_len != 0) { 13067 astgen.extra.appendAssumeCapacity(args.captures_len); 13068 } 13069 if (args.fields_len != 0) { 13070 astgen.extra.appendAssumeCapacity(args.fields_len); 13071 } 13072 if (args.decls_len != 0) { 13073 astgen.extra.appendAssumeCapacity(args.decls_len); 13074 } 13075 astgen.instructions.set(@intFromEnum(inst), .{ 13076 .tag = .extended, 13077 .data = .{ .extended = .{ 13078 .opcode = .struct_decl, 13079 .small = @bitCast(Zir.Inst.StructDecl.Small{ 13080 .has_captures_len = args.captures_len != 0, 13081 .has_fields_len = args.fields_len != 0, 13082 .has_decls_len = args.decls_len != 0, 13083 .has_backing_int = args.has_backing_int, 13084 .known_non_opv = args.known_non_opv, 13085 .known_comptime_only = args.known_comptime_only, 13086 .name_strategy = gz.anon_name_strategy, 13087 .layout = args.layout, 13088 .any_comptime_fields = args.any_comptime_fields, 13089 .any_default_inits = args.any_default_inits, 13090 .any_aligned_fields = args.any_aligned_fields, 13091 }), 13092 .operand = payload_index, 13093 } }, 13094 }); 13095 } 13096 13097 fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct { 13098 src_node: Ast.Node.Index, 13099 tag_type: Zir.Inst.Ref, 13100 captures_len: u32, 13101 body_len: u32, 13102 fields_len: u32, 13103 decls_len: u32, 13104 layout: std.builtin.Type.ContainerLayout, 13105 auto_enum_tag: bool, 13106 any_aligned_fields: bool, 13107 fields_hash: std.zig.SrcHash, 13108 }) !void { 13109 const astgen = gz.astgen; 13110 const gpa = astgen.gpa; 13111 13112 assert(args.src_node != 0); 13113 13114 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); 13115 13116 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).@"struct".fields.len + 5); 13117 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.UnionDecl{ 13118 .fields_hash_0 = fields_hash_arr[0], 13119 .fields_hash_1 = fields_hash_arr[1], 13120 .fields_hash_2 = fields_hash_arr[2], 13121 .fields_hash_3 = fields_hash_arr[3], 13122 .src_line = astgen.source_line, 13123 .src_node = args.src_node, 13124 }); 13125 13126 if (args.tag_type != .none) { 13127 astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type)); 13128 } 13129 if (args.captures_len != 0) { 13130 astgen.extra.appendAssumeCapacity(args.captures_len); 13131 } 13132 if (args.body_len != 0) { 13133 astgen.extra.appendAssumeCapacity(args.body_len); 13134 } 13135 if (args.fields_len != 0) { 13136 astgen.extra.appendAssumeCapacity(args.fields_len); 13137 } 13138 if (args.decls_len != 0) { 13139 astgen.extra.appendAssumeCapacity(args.decls_len); 13140 } 13141 astgen.instructions.set(@intFromEnum(inst), .{ 13142 .tag = .extended, 13143 .data = .{ .extended = .{ 13144 .opcode = .union_decl, 13145 .small = @bitCast(Zir.Inst.UnionDecl.Small{ 13146 .has_tag_type = args.tag_type != .none, 13147 .has_captures_len = args.captures_len != 0, 13148 .has_body_len = args.body_len != 0, 13149 .has_fields_len = args.fields_len != 0, 13150 .has_decls_len = args.decls_len != 0, 13151 .name_strategy = gz.anon_name_strategy, 13152 .layout = args.layout, 13153 .auto_enum_tag = args.auto_enum_tag, 13154 .any_aligned_fields = args.any_aligned_fields, 13155 }), 13156 .operand = payload_index, 13157 } }, 13158 }); 13159 } 13160 13161 fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct { 13162 src_node: Ast.Node.Index, 13163 tag_type: Zir.Inst.Ref, 13164 captures_len: u32, 13165 body_len: u32, 13166 fields_len: u32, 13167 decls_len: u32, 13168 nonexhaustive: bool, 13169 fields_hash: std.zig.SrcHash, 13170 }) !void { 13171 const astgen = gz.astgen; 13172 const gpa = astgen.gpa; 13173 13174 assert(args.src_node != 0); 13175 13176 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); 13177 13178 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.EnumDecl).@"struct".fields.len + 5); 13179 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.EnumDecl{ 13180 .fields_hash_0 = fields_hash_arr[0], 13181 .fields_hash_1 = fields_hash_arr[1], 13182 .fields_hash_2 = fields_hash_arr[2], 13183 .fields_hash_3 = fields_hash_arr[3], 13184 .src_line = astgen.source_line, 13185 .src_node = args.src_node, 13186 }); 13187 13188 if (args.tag_type != .none) { 13189 astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type)); 13190 } 13191 if (args.captures_len != 0) { 13192 astgen.extra.appendAssumeCapacity(args.captures_len); 13193 } 13194 if (args.body_len != 0) { 13195 astgen.extra.appendAssumeCapacity(args.body_len); 13196 } 13197 if (args.fields_len != 0) { 13198 astgen.extra.appendAssumeCapacity(args.fields_len); 13199 } 13200 if (args.decls_len != 0) { 13201 astgen.extra.appendAssumeCapacity(args.decls_len); 13202 } 13203 astgen.instructions.set(@intFromEnum(inst), .{ 13204 .tag = .extended, 13205 .data = .{ .extended = .{ 13206 .opcode = .enum_decl, 13207 .small = @bitCast(Zir.Inst.EnumDecl.Small{ 13208 .has_tag_type = args.tag_type != .none, 13209 .has_captures_len = args.captures_len != 0, 13210 .has_body_len = args.body_len != 0, 13211 .has_fields_len = args.fields_len != 0, 13212 .has_decls_len = args.decls_len != 0, 13213 .name_strategy = gz.anon_name_strategy, 13214 .nonexhaustive = args.nonexhaustive, 13215 }), 13216 .operand = payload_index, 13217 } }, 13218 }); 13219 } 13220 13221 fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct { 13222 src_node: Ast.Node.Index, 13223 captures_len: u32, 13224 decls_len: u32, 13225 }) !void { 13226 const astgen = gz.astgen; 13227 const gpa = astgen.gpa; 13228 13229 assert(args.src_node != 0); 13230 13231 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).@"struct".fields.len + 2); 13232 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.OpaqueDecl{ 13233 .src_line = astgen.source_line, 13234 .src_node = args.src_node, 13235 }); 13236 13237 if (args.captures_len != 0) { 13238 astgen.extra.appendAssumeCapacity(args.captures_len); 13239 } 13240 if (args.decls_len != 0) { 13241 astgen.extra.appendAssumeCapacity(args.decls_len); 13242 } 13243 astgen.instructions.set(@intFromEnum(inst), .{ 13244 .tag = .extended, 13245 .data = .{ .extended = .{ 13246 .opcode = .opaque_decl, 13247 .small = @bitCast(Zir.Inst.OpaqueDecl.Small{ 13248 .has_captures_len = args.captures_len != 0, 13249 .has_decls_len = args.decls_len != 0, 13250 .name_strategy = gz.anon_name_strategy, 13251 }), 13252 .operand = payload_index, 13253 } }, 13254 }); 13255 } 13256 13257 fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref { 13258 return (try gz.addAsIndex(inst)).toRef(); 13259 } 13260 13261 fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index { 13262 const gpa = gz.astgen.gpa; 13263 try gz.instructions.ensureUnusedCapacity(gpa, 1); 13264 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 13265 13266 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 13267 gz.astgen.instructions.appendAssumeCapacity(inst); 13268 gz.instructions.appendAssumeCapacity(new_index); 13269 return new_index; 13270 } 13271 13272 fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index { 13273 const gpa = gz.astgen.gpa; 13274 try gz.instructions.ensureUnusedCapacity(gpa, 1); 13275 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 13276 13277 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 13278 gz.astgen.instructions.len += 1; 13279 gz.instructions.appendAssumeCapacity(new_index); 13280 return new_index; 13281 } 13282 13283 fn addRet(gz: *GenZir, ri: ResultInfo, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void { 13284 switch (ri.rl) { 13285 .ptr => |ptr_res| _ = try gz.addUnNode(.ret_load, ptr_res.inst, node), 13286 .coerced_ty => _ = try gz.addUnNode(.ret_node, operand, node), 13287 else => unreachable, 13288 } 13289 } 13290 13291 fn addDbgVar(gz: *GenZir, tag: Zir.Inst.Tag, name: Zir.NullTerminatedString, inst: Zir.Inst.Ref) !void { 13292 if (gz.is_comptime) return; 13293 13294 _ = try gz.add(.{ .tag = tag, .data = .{ 13295 .str_op = .{ 13296 .str = name, 13297 .operand = inst, 13298 }, 13299 } }); 13300 } 13301 }; 13302 13303 /// This can only be for short-lived references; the memory becomes invalidated 13304 /// when another string is added. 13305 fn nullTerminatedString(astgen: AstGen, index: Zir.NullTerminatedString) [*:0]const u8 { 13306 return @ptrCast(astgen.string_bytes.items[@intFromEnum(index)..]); 13307 } 13308 13309 /// Local variables shadowing detection, including function parameters. 13310 fn detectLocalShadowing( 13311 astgen: *AstGen, 13312 scope: *Scope, 13313 ident_name: Zir.NullTerminatedString, 13314 name_token: Ast.TokenIndex, 13315 token_bytes: []const u8, 13316 id_cat: Scope.IdCat, 13317 ) !void { 13318 const gpa = astgen.gpa; 13319 if (token_bytes[0] != '@' and isPrimitive(token_bytes)) { 13320 return astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{ 13321 token_bytes, 13322 }, &[_]u32{ 13323 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{ 13324 token_bytes, 13325 }), 13326 }); 13327 } 13328 13329 var s = scope; 13330 var outer_scope = false; 13331 while (true) switch (s.tag) { 13332 .local_val => { 13333 const local_val = s.cast(Scope.LocalVal).?; 13334 if (local_val.name == ident_name) { 13335 const name_slice = mem.span(astgen.nullTerminatedString(ident_name)); 13336 const name = try gpa.dupe(u8, name_slice); 13337 defer gpa.free(name); 13338 if (outer_scope) { 13339 return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{ 13340 @tagName(id_cat), name, @tagName(local_val.id_cat), 13341 }, &[_]u32{ 13342 try astgen.errNoteTok( 13343 local_val.token_src, 13344 "previous declaration here", 13345 .{}, 13346 ), 13347 }); 13348 } 13349 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{ 13350 @tagName(local_val.id_cat), name, 13351 }, &[_]u32{ 13352 try astgen.errNoteTok( 13353 local_val.token_src, 13354 "previous declaration here", 13355 .{}, 13356 ), 13357 }); 13358 } 13359 s = local_val.parent; 13360 }, 13361 .local_ptr => { 13362 const local_ptr = s.cast(Scope.LocalPtr).?; 13363 if (local_ptr.name == ident_name) { 13364 const name_slice = mem.span(astgen.nullTerminatedString(ident_name)); 13365 const name = try gpa.dupe(u8, name_slice); 13366 defer gpa.free(name); 13367 if (outer_scope) { 13368 return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{ 13369 @tagName(id_cat), name, @tagName(local_ptr.id_cat), 13370 }, &[_]u32{ 13371 try astgen.errNoteTok( 13372 local_ptr.token_src, 13373 "previous declaration here", 13374 .{}, 13375 ), 13376 }); 13377 } 13378 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{ 13379 @tagName(local_ptr.id_cat), name, 13380 }, &[_]u32{ 13381 try astgen.errNoteTok( 13382 local_ptr.token_src, 13383 "previous declaration here", 13384 .{}, 13385 ), 13386 }); 13387 } 13388 s = local_ptr.parent; 13389 }, 13390 .namespace => { 13391 outer_scope = true; 13392 const ns = s.cast(Scope.Namespace).?; 13393 const decl_node = ns.decls.get(ident_name) orelse { 13394 s = ns.parent; 13395 continue; 13396 }; 13397 const name_slice = mem.span(astgen.nullTerminatedString(ident_name)); 13398 const name = try gpa.dupe(u8, name_slice); 13399 defer gpa.free(name); 13400 return astgen.failTokNotes(name_token, "{s} shadows declaration of '{s}'", .{ 13401 @tagName(id_cat), name, 13402 }, &[_]u32{ 13403 try astgen.errNoteNode(decl_node, "declared here", .{}), 13404 }); 13405 }, 13406 .gen_zir => { 13407 s = s.cast(GenZir).?.parent; 13408 outer_scope = true; 13409 }, 13410 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, 13411 .top => break, 13412 }; 13413 } 13414 13415 const LineColumn = struct { u32, u32 }; 13416 13417 /// Advances the source cursor to the main token of `node` if not in comptime scope. 13418 /// Usually paired with `emitDbgStmt`. 13419 fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) LineColumn { 13420 if (gz.is_comptime) return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column }; 13421 13422 const tree = gz.astgen.tree; 13423 const token_starts = tree.tokens.items(.start); 13424 const main_tokens = tree.nodes.items(.main_token); 13425 const node_start = token_starts[main_tokens[node]]; 13426 gz.astgen.advanceSourceCursor(node_start); 13427 13428 return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column }; 13429 } 13430 13431 /// Advances the source cursor to the beginning of `node`. 13432 fn advanceSourceCursorToNode(astgen: *AstGen, node: Ast.Node.Index) void { 13433 const tree = astgen.tree; 13434 const token_starts = tree.tokens.items(.start); 13435 const node_start = token_starts[tree.firstToken(node)]; 13436 astgen.advanceSourceCursor(node_start); 13437 } 13438 13439 /// Advances the source cursor to an absolute byte offset `end` in the file. 13440 fn advanceSourceCursor(astgen: *AstGen, end: usize) void { 13441 const source = astgen.tree.source; 13442 var i = astgen.source_offset; 13443 var line = astgen.source_line; 13444 var column = astgen.source_column; 13445 assert(i <= end); 13446 while (i < end) : (i += 1) { 13447 if (source[i] == '\n') { 13448 line += 1; 13449 column = 0; 13450 } else { 13451 column += 1; 13452 } 13453 } 13454 astgen.source_offset = i; 13455 astgen.source_line = line; 13456 astgen.source_column = column; 13457 } 13458 13459 const SourceCursor = struct { 13460 offset: u32, 13461 line: u32, 13462 column: u32, 13463 }; 13464 13465 /// Get the current source cursor, to be restored later with `restoreSourceCursor`. 13466 /// This is useful when analyzing source code out-of-order. 13467 fn saveSourceCursor(astgen: *const AstGen) SourceCursor { 13468 return .{ 13469 .offset = astgen.source_offset, 13470 .line = astgen.source_line, 13471 .column = astgen.source_column, 13472 }; 13473 } 13474 fn restoreSourceCursor(astgen: *AstGen, cursor: SourceCursor) void { 13475 astgen.source_offset = cursor.offset; 13476 astgen.source_line = cursor.line; 13477 astgen.source_column = cursor.column; 13478 } 13479 13480 /// Detects name conflicts for decls and fields, and populates `namespace.decls` with all named declarations. 13481 /// Returns the number of declarations in the namespace, including unnamed declarations (e.g. `comptime` decls). 13482 fn scanContainer( 13483 astgen: *AstGen, 13484 namespace: *Scope.Namespace, 13485 members: []const Ast.Node.Index, 13486 container_kind: enum { @"struct", @"union", @"enum", @"opaque" }, 13487 ) !u32 { 13488 const gpa = astgen.gpa; 13489 const tree = astgen.tree; 13490 const node_tags = tree.nodes.items(.tag); 13491 const main_tokens = tree.nodes.items(.main_token); 13492 const token_tags = tree.tokens.items(.tag); 13493 13494 var any_invalid_declarations = false; 13495 13496 // This type forms a linked list of source tokens declaring the same name. 13497 const NameEntry = struct { 13498 tok: Ast.TokenIndex, 13499 /// Using a linked list here simplifies memory management, and is acceptable since 13500 ///ewntries are only allocated in error situations. The entries are allocated into the 13501 /// AstGen arena. 13502 next: ?*@This(), 13503 }; 13504 13505 // The maps below are allocated into this SFBA to avoid using the GPA for small namespaces. 13506 var sfba_state = std.heap.stackFallback(512, astgen.gpa); 13507 const sfba = sfba_state.get(); 13508 13509 var names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty; 13510 var test_names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty; 13511 var decltest_names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty; 13512 defer { 13513 names.deinit(sfba); 13514 test_names.deinit(sfba); 13515 decltest_names.deinit(sfba); 13516 } 13517 13518 var any_duplicates = false; 13519 var decl_count: u32 = 0; 13520 for (members) |member_node| { 13521 const Kind = enum { decl, field }; 13522 const kind: Kind, const name_token = switch (node_tags[member_node]) { 13523 .container_field_init, 13524 .container_field_align, 13525 .container_field, 13526 => blk: { 13527 var full = tree.fullContainerField(member_node).?; 13528 switch (container_kind) { 13529 .@"struct", .@"opaque" => {}, 13530 .@"union", .@"enum" => full.convertToNonTupleLike(astgen.tree.nodes), 13531 } 13532 if (full.ast.tuple_like) continue; 13533 break :blk .{ .field, full.ast.main_token }; 13534 }, 13535 13536 .global_var_decl, 13537 .local_var_decl, 13538 .simple_var_decl, 13539 .aligned_var_decl, 13540 => blk: { 13541 decl_count += 1; 13542 break :blk .{ .decl, main_tokens[member_node] + 1 }; 13543 }, 13544 13545 .fn_proto_simple, 13546 .fn_proto_multi, 13547 .fn_proto_one, 13548 .fn_proto, 13549 .fn_decl, 13550 => blk: { 13551 decl_count += 1; 13552 const ident = main_tokens[member_node] + 1; 13553 if (token_tags[ident] != .identifier) { 13554 try astgen.appendErrorNode(member_node, "missing function name", .{}); 13555 any_invalid_declarations = true; 13556 continue; 13557 } 13558 break :blk .{ .decl, ident }; 13559 }, 13560 13561 .@"comptime", .@"usingnamespace" => { 13562 decl_count += 1; 13563 continue; 13564 }, 13565 13566 .test_decl => { 13567 decl_count += 1; 13568 // We don't want shadowing detection here, and test names work a bit differently, so 13569 // we must do the redeclaration detection ourselves. 13570 const test_name_token = main_tokens[member_node] + 1; 13571 const new_ent: NameEntry = .{ 13572 .tok = test_name_token, 13573 .next = null, 13574 }; 13575 switch (token_tags[test_name_token]) { 13576 else => {}, // unnamed test 13577 .string_literal => { 13578 const name = try astgen.strLitAsString(test_name_token); 13579 const gop = try test_names.getOrPut(sfba, name.index); 13580 if (gop.found_existing) { 13581 var e = gop.value_ptr; 13582 while (e.next) |n| e = n; 13583 e.next = try astgen.arena.create(NameEntry); 13584 e.next.?.* = new_ent; 13585 any_duplicates = true; 13586 } else { 13587 gop.value_ptr.* = new_ent; 13588 } 13589 }, 13590 .identifier => { 13591 const name = try astgen.identAsString(test_name_token); 13592 const gop = try decltest_names.getOrPut(sfba, name); 13593 if (gop.found_existing) { 13594 var e = gop.value_ptr; 13595 while (e.next) |n| e = n; 13596 e.next = try astgen.arena.create(NameEntry); 13597 e.next.?.* = new_ent; 13598 any_duplicates = true; 13599 } else { 13600 gop.value_ptr.* = new_ent; 13601 } 13602 }, 13603 } 13604 continue; 13605 }, 13606 13607 else => unreachable, 13608 }; 13609 13610 const name_str_index = try astgen.identAsString(name_token); 13611 13612 if (kind == .decl) { 13613 // Put the name straight into `decls`, even if there are compile errors. 13614 // This avoids incorrect "undeclared identifier" errors later on. 13615 try namespace.decls.put(gpa, name_str_index, member_node); 13616 } 13617 13618 { 13619 const gop = try names.getOrPut(sfba, name_str_index); 13620 const new_ent: NameEntry = .{ 13621 .tok = name_token, 13622 .next = null, 13623 }; 13624 if (gop.found_existing) { 13625 var e = gop.value_ptr; 13626 while (e.next) |n| e = n; 13627 e.next = try astgen.arena.create(NameEntry); 13628 e.next.?.* = new_ent; 13629 any_duplicates = true; 13630 continue; 13631 } else { 13632 gop.value_ptr.* = new_ent; 13633 } 13634 } 13635 13636 // For fields, we only needed the duplicate check! Decls have some more checks to do, though. 13637 switch (kind) { 13638 .decl => {}, 13639 .field => continue, 13640 } 13641 13642 const token_bytes = astgen.tree.tokenSlice(name_token); 13643 if (token_bytes[0] != '@' and isPrimitive(token_bytes)) { 13644 try astgen.appendErrorTokNotes(name_token, "name shadows primitive '{s}'", .{ 13645 token_bytes, 13646 }, &.{ 13647 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{ 13648 token_bytes, 13649 }), 13650 }); 13651 any_invalid_declarations = true; 13652 continue; 13653 } 13654 13655 var s = namespace.parent; 13656 while (true) switch (s.tag) { 13657 .local_val => { 13658 const local_val = s.cast(Scope.LocalVal).?; 13659 if (local_val.name == name_str_index) { 13660 try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{ 13661 token_bytes, @tagName(local_val.id_cat), 13662 }, &.{ 13663 try astgen.errNoteTok( 13664 local_val.token_src, 13665 "previous declaration here", 13666 .{}, 13667 ), 13668 }); 13669 any_invalid_declarations = true; 13670 break; 13671 } 13672 s = local_val.parent; 13673 }, 13674 .local_ptr => { 13675 const local_ptr = s.cast(Scope.LocalPtr).?; 13676 if (local_ptr.name == name_str_index) { 13677 try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{ 13678 token_bytes, @tagName(local_ptr.id_cat), 13679 }, &.{ 13680 try astgen.errNoteTok( 13681 local_ptr.token_src, 13682 "previous declaration here", 13683 .{}, 13684 ), 13685 }); 13686 any_invalid_declarations = true; 13687 break; 13688 } 13689 s = local_ptr.parent; 13690 }, 13691 .namespace => s = s.cast(Scope.Namespace).?.parent, 13692 .gen_zir => s = s.cast(GenZir).?.parent, 13693 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, 13694 .top => break, 13695 }; 13696 } 13697 13698 if (!any_duplicates) { 13699 if (any_invalid_declarations) return error.AnalysisFail; 13700 return decl_count; 13701 } 13702 13703 for (names.keys(), names.values()) |name, first| { 13704 if (first.next == null) continue; 13705 var notes: std.ArrayListUnmanaged(u32) = .empty; 13706 var prev: NameEntry = first; 13707 while (prev.next) |cur| : (prev = cur.*) { 13708 try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate name here", .{})); 13709 } 13710 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)})); 13711 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name))); 13712 try astgen.appendErrorTokNotes(first.tok, "duplicate {s} member name '{s}'", .{ @tagName(container_kind), name_duped }, notes.items); 13713 any_invalid_declarations = true; 13714 } 13715 13716 for (test_names.keys(), test_names.values()) |name, first| { 13717 if (first.next == null) continue; 13718 var notes: std.ArrayListUnmanaged(u32) = .empty; 13719 var prev: NameEntry = first; 13720 while (prev.next) |cur| : (prev = cur.*) { 13721 try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate test here", .{})); 13722 } 13723 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)})); 13724 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name))); 13725 try astgen.appendErrorTokNotes(first.tok, "duplicate test name '{s}'", .{name_duped}, notes.items); 13726 any_invalid_declarations = true; 13727 } 13728 13729 for (decltest_names.keys(), decltest_names.values()) |name, first| { 13730 if (first.next == null) continue; 13731 var notes: std.ArrayListUnmanaged(u32) = .empty; 13732 var prev: NameEntry = first; 13733 while (prev.next) |cur| : (prev = cur.*) { 13734 try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate decltest here", .{})); 13735 } 13736 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)})); 13737 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name))); 13738 try astgen.appendErrorTokNotes(first.tok, "duplicate decltest '{s}'", .{name_duped}, notes.items); 13739 any_invalid_declarations = true; 13740 } 13741 13742 assert(any_invalid_declarations); 13743 return error.AnalysisFail; 13744 } 13745 13746 /// Assumes capacity for body has already been added. Needed capacity taking into 13747 /// account fixups can be found with `countBodyLenAfterFixups`. 13748 fn appendBodyWithFixups(astgen: *AstGen, body: []const Zir.Inst.Index) void { 13749 return appendBodyWithFixupsArrayList(astgen, &astgen.extra, body); 13750 } 13751 13752 fn appendBodyWithFixupsArrayList( 13753 astgen: *AstGen, 13754 list: *std.ArrayListUnmanaged(u32), 13755 body: []const Zir.Inst.Index, 13756 ) void { 13757 astgen.appendBodyWithFixupsExtraRefsArrayList(list, body, &.{}); 13758 } 13759 13760 fn appendBodyWithFixupsExtraRefsArrayList( 13761 astgen: *AstGen, 13762 list: *std.ArrayListUnmanaged(u32), 13763 body: []const Zir.Inst.Index, 13764 extra_refs: []const Zir.Inst.Index, 13765 ) void { 13766 for (extra_refs) |extra_inst| { 13767 if (astgen.ref_table.fetchRemove(extra_inst)) |kv| { 13768 appendPossiblyRefdBodyInst(astgen, list, kv.value); 13769 } 13770 } 13771 for (body) |body_inst| { 13772 appendPossiblyRefdBodyInst(astgen, list, body_inst); 13773 } 13774 } 13775 13776 fn appendPossiblyRefdBodyInst( 13777 astgen: *AstGen, 13778 list: *std.ArrayListUnmanaged(u32), 13779 body_inst: Zir.Inst.Index, 13780 ) void { 13781 list.appendAssumeCapacity(@intFromEnum(body_inst)); 13782 const kv = astgen.ref_table.fetchRemove(body_inst) orelse return; 13783 const ref_inst = kv.value; 13784 return appendPossiblyRefdBodyInst(astgen, list, ref_inst); 13785 } 13786 13787 fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 { 13788 return astgen.countBodyLenAfterFixupsExtraRefs(body, &.{}); 13789 } 13790 13791 /// Return the number of instructions in `body` after prepending the `ref` instructions in `ref_table`. 13792 /// As well as all instructions in `body`, we also prepend `ref`s of any instruction in `extra_refs`. 13793 /// For instance, if an index has been reserved with a special meaning to a child block, it must be 13794 /// passed to `extra_refs` to ensure `ref`s of that index are added correctly. 13795 fn countBodyLenAfterFixupsExtraRefs(astgen: *AstGen, body: []const Zir.Inst.Index, extra_refs: []const Zir.Inst.Index) u32 { 13796 var count = body.len; 13797 for (body) |body_inst| { 13798 var check_inst = body_inst; 13799 while (astgen.ref_table.get(check_inst)) |ref_inst| { 13800 count += 1; 13801 check_inst = ref_inst; 13802 } 13803 } 13804 for (extra_refs) |extra_inst| { 13805 var check_inst = extra_inst; 13806 while (astgen.ref_table.get(check_inst)) |ref_inst| { 13807 count += 1; 13808 check_inst = ref_inst; 13809 } 13810 } 13811 return @intCast(count); 13812 } 13813 13814 fn emitDbgStmt(gz: *GenZir, lc: LineColumn) !void { 13815 if (gz.is_comptime) return; 13816 if (gz.instructions.items.len > gz.instructions_top) { 13817 const astgen = gz.astgen; 13818 const last = gz.instructions.items[gz.instructions.items.len - 1]; 13819 if (astgen.instructions.items(.tag)[@intFromEnum(last)] == .dbg_stmt) { 13820 astgen.instructions.items(.data)[@intFromEnum(last)].dbg_stmt = .{ 13821 .line = lc[0], 13822 .column = lc[1], 13823 }; 13824 return; 13825 } 13826 } 13827 13828 _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{ 13829 .dbg_stmt = .{ 13830 .line = lc[0], 13831 .column = lc[1], 13832 }, 13833 } }); 13834 } 13835 13836 /// In some cases, Sema expects us to generate a `dbg_stmt` at the instruction 13837 /// *index* directly preceding the next instruction (e.g. if a call is %10, it 13838 /// expects a dbg_stmt at %9). TODO: this logic may allow redundant dbg_stmt 13839 /// instructions; fix up Sema so we don't need it! 13840 fn emitDbgStmtForceCurrentIndex(gz: *GenZir, lc: LineColumn) !void { 13841 const astgen = gz.astgen; 13842 if (gz.instructions.items.len > gz.instructions_top and 13843 @intFromEnum(gz.instructions.items[gz.instructions.items.len - 1]) == astgen.instructions.len - 1) 13844 { 13845 const last = astgen.instructions.len - 1; 13846 if (astgen.instructions.items(.tag)[last] == .dbg_stmt) { 13847 astgen.instructions.items(.data)[last].dbg_stmt = .{ 13848 .line = lc[0], 13849 .column = lc[1], 13850 }; 13851 return; 13852 } 13853 } 13854 13855 _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{ 13856 .dbg_stmt = .{ 13857 .line = lc[0], 13858 .column = lc[1], 13859 }, 13860 } }); 13861 } 13862 13863 fn lowerAstErrors(astgen: *AstGen) !void { 13864 const gpa = astgen.gpa; 13865 const tree = astgen.tree; 13866 assert(tree.errors.len > 0); 13867 13868 var msg: std.ArrayListUnmanaged(u8) = .empty; 13869 defer msg.deinit(gpa); 13870 13871 var notes: std.ArrayListUnmanaged(u32) = .empty; 13872 defer notes.deinit(gpa); 13873 13874 var cur_err = tree.errors[0]; 13875 for (tree.errors[1..]) |err| { 13876 if (err.is_note) { 13877 try tree.renderError(err, msg.writer(gpa)); 13878 try notes.append(gpa, try astgen.errNoteTok(err.token, "{s}", .{msg.items})); 13879 } else { 13880 // Flush error 13881 const extra_offset = tree.errorOffset(cur_err); 13882 try tree.renderError(cur_err, msg.writer(gpa)); 13883 try astgen.appendErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items); 13884 notes.clearRetainingCapacity(); 13885 cur_err = err; 13886 13887 // TODO: `Parse` currently does not have good error recovery mechanisms, so the remaining errors could be bogus. 13888 // As such, we'll ignore all remaining errors for now. We should improve `Parse` so that we can report all the errors. 13889 return; 13890 } 13891 msg.clearRetainingCapacity(); 13892 } 13893 13894 // Flush error 13895 const extra_offset = tree.errorOffset(cur_err); 13896 try tree.renderError(cur_err, msg.writer(gpa)); 13897 try astgen.appendErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items); 13898 } 13899 13900 const DeclarationName = union(enum) { 13901 named: Ast.TokenIndex, 13902 named_test: Ast.TokenIndex, 13903 decltest: Ast.TokenIndex, 13904 unnamed_test, 13905 @"comptime", 13906 @"usingnamespace", 13907 }; 13908 13909 fn addFailedDeclaration( 13910 wip_members: *WipMembers, 13911 gz: *GenZir, 13912 name: DeclarationName, 13913 src_node: Ast.Node.Index, 13914 is_pub: bool, 13915 ) !void { 13916 const decl_inst = try gz.makeDeclaration(src_node); 13917 wip_members.nextDecl(decl_inst); 13918 var decl_gz = gz.makeSubBlock(&gz.base); // scope doesn't matter here 13919 _ = try decl_gz.add(.{ 13920 .tag = .extended, 13921 .data = .{ .extended = .{ 13922 .opcode = .astgen_error, 13923 .small = undefined, 13924 .operand = undefined, 13925 } }, 13926 }); 13927 try setDeclaration( 13928 decl_inst, 13929 @splat(0), // use a fixed hash to represent an AstGen failure; we don't care about source changes if AstGen still failed! 13930 name, 13931 gz.astgen.source_line, 13932 gz.astgen.source_column, 13933 is_pub, 13934 false, // we don't care about exports since semantic analysis will fail 13935 &decl_gz, 13936 null, 13937 ); 13938 } 13939 13940 /// Sets all extra data for a `declaration` instruction. 13941 /// Unstacks `value_gz`, `align_gz`, `linksection_gz`, and `addrspace_gz`. 13942 fn setDeclaration( 13943 decl_inst: Zir.Inst.Index, 13944 src_hash: std.zig.SrcHash, 13945 name: DeclarationName, 13946 src_line: u32, 13947 src_column: u32, 13948 is_pub: bool, 13949 is_export: bool, 13950 value_gz: *GenZir, 13951 /// May be `null` if all these blocks would be empty. 13952 /// If `null`, then `value_gz` must have nothing stacked on it. 13953 extra_gzs: ?struct { 13954 /// Must be stacked on `value_gz`. 13955 align_gz: *GenZir, 13956 /// Must be stacked on `align_gz`. 13957 linksection_gz: *GenZir, 13958 /// Must be stacked on `linksection_gz`, and have nothing stacked on it. 13959 addrspace_gz: *GenZir, 13960 }, 13961 ) !void { 13962 const astgen = value_gz.astgen; 13963 const gpa = astgen.gpa; 13964 13965 const empty_body: []Zir.Inst.Index = &.{}; 13966 const value_body, const align_body, const linksection_body, const addrspace_body = if (extra_gzs) |e| .{ 13967 value_gz.instructionsSliceUpto(e.align_gz), 13968 e.align_gz.instructionsSliceUpto(e.linksection_gz), 13969 e.linksection_gz.instructionsSliceUpto(e.addrspace_gz), 13970 e.addrspace_gz.instructionsSlice(), 13971 } else .{ value_gz.instructionsSlice(), empty_body, empty_body, empty_body }; 13972 13973 const value_len = astgen.countBodyLenAfterFixups(value_body); 13974 const align_len = astgen.countBodyLenAfterFixups(align_body); 13975 const linksection_len = astgen.countBodyLenAfterFixups(linksection_body); 13976 const addrspace_len = astgen.countBodyLenAfterFixups(addrspace_body); 13977 13978 const src_hash_arr: [4]u32 = @bitCast(src_hash); 13979 13980 const extra: Zir.Inst.Declaration = .{ 13981 .src_hash_0 = src_hash_arr[0], 13982 .src_hash_1 = src_hash_arr[1], 13983 .src_hash_2 = src_hash_arr[2], 13984 .src_hash_3 = src_hash_arr[3], 13985 .name = switch (name) { 13986 .named => |tok| @enumFromInt(@intFromEnum(try astgen.identAsString(tok))), 13987 .named_test => |tok| @enumFromInt(@intFromEnum(try astgen.testNameString(tok))), 13988 .decltest => |tok| @enumFromInt(str_idx: { 13989 const idx = astgen.string_bytes.items.len; 13990 try astgen.string_bytes.append(gpa, 0); // indicates this is a test 13991 try astgen.appendIdentStr(tok, &astgen.string_bytes); 13992 try astgen.string_bytes.append(gpa, 0); // end of the string 13993 break :str_idx idx; 13994 }), 13995 .unnamed_test => .unnamed_test, 13996 .@"comptime" => .@"comptime", 13997 .@"usingnamespace" => .@"usingnamespace", 13998 }, 13999 .src_line = src_line, 14000 .src_column = src_column, 14001 .flags = .{ 14002 .value_body_len = @intCast(value_len), 14003 .is_pub = is_pub, 14004 .is_export = is_export, 14005 .test_is_decltest = name == .decltest, 14006 .has_align_linksection_addrspace = align_len != 0 or linksection_len != 0 or addrspace_len != 0, 14007 }, 14008 }; 14009 astgen.instructions.items(.data)[@intFromEnum(decl_inst)].declaration.payload_index = try astgen.addExtra(extra); 14010 if (extra.flags.has_align_linksection_addrspace) { 14011 try astgen.extra.appendSlice(gpa, &.{ 14012 align_len, 14013 linksection_len, 14014 addrspace_len, 14015 }); 14016 } 14017 try astgen.extra.ensureUnusedCapacity(gpa, value_len + align_len + linksection_len + addrspace_len); 14018 astgen.appendBodyWithFixups(value_body); 14019 if (extra.flags.has_align_linksection_addrspace) { 14020 astgen.appendBodyWithFixups(align_body); 14021 astgen.appendBodyWithFixups(linksection_body); 14022 astgen.appendBodyWithFixups(addrspace_body); 14023 } 14024 14025 if (extra_gzs) |e| { 14026 e.addrspace_gz.unstack(); 14027 e.linksection_gz.unstack(); 14028 e.align_gz.unstack(); 14029 } 14030 value_gz.unstack(); 14031 } 14032 14033 /// Given a list of instructions, returns a list of all instructions which are a `ref` of one of the originals, 14034 /// from `astgen.ref_table`, non-recursively. The entries are removed from `astgen.ref_table`, and the returned 14035 /// slice can then be treated as its own body, to append `ref` instructions to a body other than the one they 14036 /// would normally exist in. 14037 /// 14038 /// This is used when lowering functions. Very rarely, the callconv expression, align expression, etc may reference 14039 /// function parameters via `¶m`; in this case, we need to lower to a `ref` instruction in the callconv/align/etc 14040 /// body, rather than in the declaration body. However, we don't append these bodies to `extra` until we've evaluated 14041 /// *all* of the bodies into a big `GenZir` stack. Therefore, we use this function to pull out these per-body `ref` 14042 /// instructions which must be emitted. 14043 fn fetchRemoveRefEntries(astgen: *AstGen, param_insts: []const Zir.Inst.Index) ![]Zir.Inst.Index { 14044 var refs: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty; 14045 for (param_insts) |param_inst| { 14046 if (astgen.ref_table.fetchRemove(param_inst)) |kv| { 14047 try refs.append(astgen.arena, kv.value); 14048 } 14049 } 14050 return refs.items; 14051 }