lib/std/zig/AstGen.zig (566809B) - 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 var arena = std.heap.ArenaAllocator.init(gpa); 134 defer arena.deinit(); 135 136 var nodes_need_rl = try AstRlAnnotate.annotate(gpa, arena.allocator(), tree); 137 defer nodes_need_rl.deinit(gpa); 138 139 var astgen: AstGen = .{ 140 .gpa = gpa, 141 .arena = arena.allocator(), 142 .tree = &tree, 143 .nodes_need_rl = &nodes_need_rl, 144 .src_hasher = undefined, // `structDeclInner` for the root struct will set this 145 }; 146 defer astgen.deinit(gpa); 147 148 // String table index 0 is reserved for `NullTerminatedString.empty`. 149 try astgen.string_bytes.append(gpa, 0); 150 151 // We expect at least as many ZIR instructions and extra data items 152 // as AST nodes. 153 try astgen.instructions.ensureTotalCapacity(gpa, tree.nodes.len); 154 155 // First few indexes of extra are reserved and set at the end. 156 const reserved_count = @typeInfo(Zir.ExtraIndex).@"enum".fields.len; 157 try astgen.extra.ensureTotalCapacity(gpa, tree.nodes.len + reserved_count); 158 astgen.extra.items.len += reserved_count; 159 160 var top_scope: Scope.Top = .{}; 161 162 var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty; 163 var gen_scope: GenZir = .{ 164 .is_comptime = true, 165 .parent = &top_scope.base, 166 .anon_name_strategy = .parent, 167 .decl_node_index = 0, 168 .decl_line = 0, 169 .astgen = &astgen, 170 .instructions = &gz_instructions, 171 .instructions_top = 0, 172 }; 173 defer gz_instructions.deinit(gpa); 174 175 // The AST -> ZIR lowering process assumes an AST that does not have any 176 // parse errors. 177 if (tree.errors.len == 0) { 178 if (AstGen.structDeclInner( 179 &gen_scope, 180 &gen_scope.base, 181 0, 182 tree.containerDeclRoot(), 183 .auto, 184 0, 185 )) |struct_decl_ref| { 186 assert(struct_decl_ref.toIndex().? == .main_struct_inst); 187 } else |err| switch (err) { 188 error.OutOfMemory => return error.OutOfMemory, 189 error.AnalysisFail => {}, // Handled via compile_errors below. 190 } 191 } else { 192 try lowerAstErrors(&astgen); 193 } 194 195 const err_index = @intFromEnum(Zir.ExtraIndex.compile_errors); 196 if (astgen.compile_errors.items.len == 0) { 197 astgen.extra.items[err_index] = 0; 198 } else { 199 try astgen.extra.ensureUnusedCapacity(gpa, 1 + astgen.compile_errors.items.len * 200 @typeInfo(Zir.Inst.CompileErrors.Item).@"struct".fields.len); 201 202 astgen.extra.items[err_index] = astgen.addExtraAssumeCapacity(Zir.Inst.CompileErrors{ 203 .items_len = @intCast(astgen.compile_errors.items.len), 204 }); 205 206 for (astgen.compile_errors.items) |item| { 207 _ = astgen.addExtraAssumeCapacity(item); 208 } 209 } 210 211 const imports_index = @intFromEnum(Zir.ExtraIndex.imports); 212 if (astgen.imports.count() == 0) { 213 astgen.extra.items[imports_index] = 0; 214 } else { 215 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Imports).@"struct".fields.len + 216 astgen.imports.count() * @typeInfo(Zir.Inst.Imports.Item).@"struct".fields.len); 217 218 astgen.extra.items[imports_index] = astgen.addExtraAssumeCapacity(Zir.Inst.Imports{ 219 .imports_len = @intCast(astgen.imports.count()), 220 }); 221 222 var it = astgen.imports.iterator(); 223 while (it.next()) |entry| { 224 _ = astgen.addExtraAssumeCapacity(Zir.Inst.Imports.Item{ 225 .name = entry.key_ptr.*, 226 .token = entry.value_ptr.*, 227 }); 228 } 229 } 230 231 return Zir{ 232 .instructions = astgen.instructions.toOwnedSlice(), 233 .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa), 234 .extra = try astgen.extra.toOwnedSlice(gpa), 235 }; 236 } 237 238 fn deinit(astgen: *AstGen, gpa: Allocator) void { 239 astgen.instructions.deinit(gpa); 240 astgen.extra.deinit(gpa); 241 astgen.string_table.deinit(gpa); 242 astgen.string_bytes.deinit(gpa); 243 astgen.compile_errors.deinit(gpa); 244 astgen.imports.deinit(gpa); 245 astgen.scratch.deinit(gpa); 246 astgen.ref_table.deinit(gpa); 247 } 248 249 const ResultInfo = struct { 250 /// The semantics requested for the result location 251 rl: Loc, 252 253 /// The "operator" consuming the result location 254 ctx: Context = .none, 255 256 /// Turns a `coerced_ty` back into a `ty`. Should be called at branch points 257 /// such as if and switch expressions. 258 fn br(ri: ResultInfo) ResultInfo { 259 return switch (ri.rl) { 260 .coerced_ty => |ty| .{ 261 .rl = .{ .ty = ty }, 262 .ctx = ri.ctx, 263 }, 264 else => ri, 265 }; 266 } 267 268 fn zirTag(ri: ResultInfo) Zir.Inst.Tag { 269 switch (ri.rl) { 270 .ty => return switch (ri.ctx) { 271 .shift_op => .as_shift_operand, 272 else => .as_node, 273 }, 274 else => unreachable, 275 } 276 } 277 278 const Loc = union(enum) { 279 /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the 280 /// expression should be generated. The result instruction from the expression must 281 /// be ignored. 282 discard, 283 /// The expression has an inferred type, and it will be evaluated as an rvalue. 284 none, 285 /// The expression will be coerced into this type, but it will be evaluated as an rvalue. 286 ty: Zir.Inst.Ref, 287 /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion, 288 /// so no `as` instruction needs to be emitted. 289 coerced_ty: Zir.Inst.Ref, 290 /// The expression must generate a pointer rather than a value. For example, the left hand side 291 /// of an assignment uses this kind of result location. 292 ref, 293 /// The expression must generate a pointer rather than a value, and the pointer will be coerced 294 /// by other code to this type, which is guaranteed by earlier instructions to be a pointer type. 295 ref_coerced_ty: Zir.Inst.Ref, 296 /// The expression must store its result into this typed pointer. The result instruction 297 /// from the expression must be ignored. 298 ptr: PtrResultLoc, 299 /// The expression must store its result into this allocation, which has an inferred type. 300 /// The result instruction from the expression must be ignored. 301 /// Always an instruction with tag `alloc_inferred`. 302 inferred_ptr: Zir.Inst.Ref, 303 /// The expression has a sequence of pointers to store its results into due to a destructure 304 /// operation. Each of these pointers may or may not have an inferred type. 305 destructure: struct { 306 /// The AST node of the destructure operation itself. 307 src_node: Ast.Node.Index, 308 /// The pointers to store results into. 309 components: []const DestructureComponent, 310 }, 311 312 const DestructureComponent = union(enum) { 313 typed_ptr: PtrResultLoc, 314 inferred_ptr: Zir.Inst.Ref, 315 discard, 316 }; 317 318 const PtrResultLoc = struct { 319 inst: Zir.Inst.Ref, 320 src_node: ?Ast.Node.Index = null, 321 }; 322 323 /// Find the result type for a cast builtin given the result location. 324 /// If the location does not have a known result type, returns `null`. 325 fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index) !?Zir.Inst.Ref { 326 return switch (rl) { 327 .discard, .none, .ref, .inferred_ptr, .destructure => null, 328 .ty, .coerced_ty => |ty_ref| ty_ref, 329 .ref_coerced_ty => |ptr_ty| try gz.addUnNode(.elem_type, ptr_ty, node), 330 .ptr => |ptr| { 331 const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node); 332 return try gz.addUnNode(.elem_type, ptr_ty, node); 333 }, 334 }; 335 } 336 337 /// Find the result type for a cast builtin given the result location. 338 /// If the location does not have a known result type, emits an error on 339 /// the given node. 340 fn resultTypeForCast(rl: Loc, gz: *GenZir, node: Ast.Node.Index, builtin_name: []const u8) !Zir.Inst.Ref { 341 const astgen = gz.astgen; 342 if (try rl.resultType(gz, node)) |ty| return ty; 343 switch (rl) { 344 .destructure => |destructure| return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ 345 try astgen.errNoteNode(destructure.src_node, "destructure expressions do not provide a single result type", .{}), 346 try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}), 347 }), 348 else => return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ 349 try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}), 350 }), 351 } 352 } 353 }; 354 355 const Context = enum { 356 /// The expression is the operand to a return expression. 357 @"return", 358 /// The expression is the input to an error-handling operator (if-else, try, or catch). 359 error_handling_expr, 360 /// The expression is the right-hand side of a shift operation. 361 shift_op, 362 /// The expression is an argument in a function call. 363 fn_arg, 364 /// The expression is the right-hand side of an initializer for a `const` variable 365 const_init, 366 /// The expression is the right-hand side of an assignment expression. 367 assignment, 368 /// No specific operator in particular. 369 none, 370 }; 371 }; 372 373 const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } }; 374 const coerced_linksection_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }; 375 const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } }; 376 const coerced_bool_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .bool_type } }; 377 378 fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 379 return comptimeExpr(gz, scope, coerced_type_ri, type_node); 380 } 381 382 fn reachableTypeExpr( 383 gz: *GenZir, 384 scope: *Scope, 385 type_node: Ast.Node.Index, 386 reachable_node: Ast.Node.Index, 387 ) InnerError!Zir.Inst.Ref { 388 return reachableExprComptime(gz, scope, coerced_type_ri, type_node, reachable_node, true); 389 } 390 391 /// Same as `expr` but fails with a compile error if the result type is `noreturn`. 392 fn reachableExpr( 393 gz: *GenZir, 394 scope: *Scope, 395 ri: ResultInfo, 396 node: Ast.Node.Index, 397 reachable_node: Ast.Node.Index, 398 ) InnerError!Zir.Inst.Ref { 399 return reachableExprComptime(gz, scope, ri, node, reachable_node, false); 400 } 401 402 fn reachableExprComptime( 403 gz: *GenZir, 404 scope: *Scope, 405 ri: ResultInfo, 406 node: Ast.Node.Index, 407 reachable_node: Ast.Node.Index, 408 force_comptime: bool, 409 ) InnerError!Zir.Inst.Ref { 410 const result_inst = if (force_comptime) 411 try comptimeExpr(gz, scope, ri, node) 412 else 413 try expr(gz, scope, ri, node); 414 415 if (gz.refIsNoReturn(result_inst)) { 416 try gz.astgen.appendErrorNodeNotes(reachable_node, "unreachable code", .{}, &[_]u32{ 417 try gz.astgen.errNoteNode(node, "control flow is diverted here", .{}), 418 }); 419 } 420 return result_inst; 421 } 422 423 fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 424 const astgen = gz.astgen; 425 const tree = astgen.tree; 426 const node_tags = tree.nodes.items(.tag); 427 const main_tokens = tree.nodes.items(.main_token); 428 switch (node_tags[node]) { 429 .root => unreachable, 430 .@"usingnamespace" => unreachable, 431 .test_decl => unreachable, 432 .global_var_decl => unreachable, 433 .local_var_decl => unreachable, 434 .simple_var_decl => unreachable, 435 .aligned_var_decl => unreachable, 436 .switch_case => unreachable, 437 .switch_case_inline => unreachable, 438 .switch_case_one => unreachable, 439 .switch_case_inline_one => unreachable, 440 .container_field_init => unreachable, 441 .container_field_align => unreachable, 442 .container_field => unreachable, 443 .asm_output => unreachable, 444 .asm_input => unreachable, 445 446 .assign, 447 .assign_destructure, 448 .assign_bit_and, 449 .assign_bit_or, 450 .assign_shl, 451 .assign_shl_sat, 452 .assign_shr, 453 .assign_bit_xor, 454 .assign_div, 455 .assign_sub, 456 .assign_sub_wrap, 457 .assign_sub_sat, 458 .assign_mod, 459 .assign_add, 460 .assign_add_wrap, 461 .assign_add_sat, 462 .assign_mul, 463 .assign_mul_wrap, 464 .assign_mul_sat, 465 .add, 466 .add_wrap, 467 .add_sat, 468 .sub, 469 .sub_wrap, 470 .sub_sat, 471 .mul, 472 .mul_wrap, 473 .mul_sat, 474 .div, 475 .mod, 476 .bit_and, 477 .bit_or, 478 .shl, 479 .shl_sat, 480 .shr, 481 .bit_xor, 482 .bang_equal, 483 .equal_equal, 484 .greater_than, 485 .greater_or_equal, 486 .less_than, 487 .less_or_equal, 488 .array_cat, 489 .array_mult, 490 .bool_and, 491 .bool_or, 492 .@"asm", 493 .asm_simple, 494 .string_literal, 495 .number_literal, 496 .call, 497 .call_comma, 498 .async_call, 499 .async_call_comma, 500 .call_one, 501 .call_one_comma, 502 .async_call_one, 503 .async_call_one_comma, 504 .unreachable_literal, 505 .@"return", 506 .@"if", 507 .if_simple, 508 .@"while", 509 .while_simple, 510 .while_cont, 511 .bool_not, 512 .address_of, 513 .optional_type, 514 .block, 515 .block_semicolon, 516 .block_two, 517 .block_two_semicolon, 518 .@"break", 519 .ptr_type_aligned, 520 .ptr_type_sentinel, 521 .ptr_type, 522 .ptr_type_bit_range, 523 .array_type, 524 .array_type_sentinel, 525 .enum_literal, 526 .multiline_string_literal, 527 .char_literal, 528 .@"defer", 529 .@"errdefer", 530 .@"catch", 531 .error_union, 532 .merge_error_sets, 533 .switch_range, 534 .for_range, 535 .@"await", 536 .bit_not, 537 .negation, 538 .negation_wrap, 539 .@"resume", 540 .@"try", 541 .slice, 542 .slice_open, 543 .slice_sentinel, 544 .array_init_one, 545 .array_init_one_comma, 546 .array_init_dot_two, 547 .array_init_dot_two_comma, 548 .array_init_dot, 549 .array_init_dot_comma, 550 .array_init, 551 .array_init_comma, 552 .struct_init_one, 553 .struct_init_one_comma, 554 .struct_init_dot_two, 555 .struct_init_dot_two_comma, 556 .struct_init_dot, 557 .struct_init_dot_comma, 558 .struct_init, 559 .struct_init_comma, 560 .@"switch", 561 .switch_comma, 562 .@"for", 563 .for_simple, 564 .@"suspend", 565 .@"continue", 566 .fn_proto_simple, 567 .fn_proto_multi, 568 .fn_proto_one, 569 .fn_proto, 570 .fn_decl, 571 .anyframe_type, 572 .anyframe_literal, 573 .error_set_decl, 574 .container_decl, 575 .container_decl_trailing, 576 .container_decl_two, 577 .container_decl_two_trailing, 578 .container_decl_arg, 579 .container_decl_arg_trailing, 580 .tagged_union, 581 .tagged_union_trailing, 582 .tagged_union_two, 583 .tagged_union_two_trailing, 584 .tagged_union_enum_tag, 585 .tagged_union_enum_tag_trailing, 586 .@"comptime", 587 .@"nosuspend", 588 .error_value, 589 => return astgen.failNode(node, "invalid left-hand side to assignment", .{}), 590 591 .builtin_call, 592 .builtin_call_comma, 593 .builtin_call_two, 594 .builtin_call_two_comma, 595 => { 596 const builtin_token = main_tokens[node]; 597 const builtin_name = tree.tokenSlice(builtin_token); 598 // If the builtin is an invalid name, we don't cause an error here; instead 599 // let it pass, and the error will be "invalid builtin function" later. 600 if (BuiltinFn.list.get(builtin_name)) |info| { 601 if (!info.allows_lvalue) { 602 return astgen.failNode(node, "invalid left-hand side to assignment", .{}); 603 } 604 } 605 }, 606 607 // These can be assigned to. 608 .unwrap_optional, 609 .deref, 610 .field_access, 611 .array_access, 612 .identifier, 613 .grouped_expression, 614 .@"orelse", 615 => {}, 616 } 617 return expr(gz, scope, .{ .rl = .ref }, node); 618 } 619 620 /// Turn Zig AST into untyped ZIR instructions. 621 /// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the 622 /// result instruction can be used to inspect whether it is isNoReturn() but that is it, 623 /// it must otherwise not be used. 624 fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 625 const astgen = gz.astgen; 626 const tree = astgen.tree; 627 const main_tokens = tree.nodes.items(.main_token); 628 const token_tags = tree.tokens.items(.tag); 629 const node_datas = tree.nodes.items(.data); 630 const node_tags = tree.nodes.items(.tag); 631 632 const prev_anon_name_strategy = gz.anon_name_strategy; 633 defer gz.anon_name_strategy = prev_anon_name_strategy; 634 if (!nodeUsesAnonNameStrategy(tree, node)) { 635 gz.anon_name_strategy = .anon; 636 } 637 638 switch (node_tags[node]) { 639 .root => unreachable, // Top-level declaration. 640 .@"usingnamespace" => unreachable, // Top-level declaration. 641 .test_decl => unreachable, // Top-level declaration. 642 .container_field_init => unreachable, // Top-level declaration. 643 .container_field_align => unreachable, // Top-level declaration. 644 .container_field => unreachable, // Top-level declaration. 645 .fn_decl => unreachable, // Top-level declaration. 646 647 .global_var_decl => unreachable, // Handled in `blockExpr`. 648 .local_var_decl => unreachable, // Handled in `blockExpr`. 649 .simple_var_decl => unreachable, // Handled in `blockExpr`. 650 .aligned_var_decl => unreachable, // Handled in `blockExpr`. 651 .@"defer" => unreachable, // Handled in `blockExpr`. 652 .@"errdefer" => unreachable, // Handled in `blockExpr`. 653 654 .switch_case => unreachable, // Handled in `switchExpr`. 655 .switch_case_inline => unreachable, // Handled in `switchExpr`. 656 .switch_case_one => unreachable, // Handled in `switchExpr`. 657 .switch_case_inline_one => unreachable, // Handled in `switchExpr`. 658 .switch_range => unreachable, // Handled in `switchExpr`. 659 660 .asm_output => unreachable, // Handled in `asmExpr`. 661 .asm_input => unreachable, // Handled in `asmExpr`. 662 663 .for_range => unreachable, // Handled in `forExpr`. 664 665 .assign => { 666 try assign(gz, scope, node); 667 return rvalue(gz, ri, .void_value, node); 668 }, 669 670 .assign_destructure => { 671 // Note that this variant does not declare any new var/const: that 672 // variant is handled by `blockExprStmts`. 673 try assignDestructure(gz, scope, node); 674 return rvalue(gz, ri, .void_value, node); 675 }, 676 677 .assign_shl => { 678 try assignShift(gz, scope, node, .shl); 679 return rvalue(gz, ri, .void_value, node); 680 }, 681 .assign_shl_sat => { 682 try assignShiftSat(gz, scope, node); 683 return rvalue(gz, ri, .void_value, node); 684 }, 685 .assign_shr => { 686 try assignShift(gz, scope, node, .shr); 687 return rvalue(gz, ri, .void_value, node); 688 }, 689 690 .assign_bit_and => { 691 try assignOp(gz, scope, node, .bit_and); 692 return rvalue(gz, ri, .void_value, node); 693 }, 694 .assign_bit_or => { 695 try assignOp(gz, scope, node, .bit_or); 696 return rvalue(gz, ri, .void_value, node); 697 }, 698 .assign_bit_xor => { 699 try assignOp(gz, scope, node, .xor); 700 return rvalue(gz, ri, .void_value, node); 701 }, 702 .assign_div => { 703 try assignOp(gz, scope, node, .div); 704 return rvalue(gz, ri, .void_value, node); 705 }, 706 .assign_sub => { 707 try assignOp(gz, scope, node, .sub); 708 return rvalue(gz, ri, .void_value, node); 709 }, 710 .assign_sub_wrap => { 711 try assignOp(gz, scope, node, .subwrap); 712 return rvalue(gz, ri, .void_value, node); 713 }, 714 .assign_sub_sat => { 715 try assignOp(gz, scope, node, .sub_sat); 716 return rvalue(gz, ri, .void_value, node); 717 }, 718 .assign_mod => { 719 try assignOp(gz, scope, node, .mod_rem); 720 return rvalue(gz, ri, .void_value, node); 721 }, 722 .assign_add => { 723 try assignOp(gz, scope, node, .add); 724 return rvalue(gz, ri, .void_value, node); 725 }, 726 .assign_add_wrap => { 727 try assignOp(gz, scope, node, .addwrap); 728 return rvalue(gz, ri, .void_value, node); 729 }, 730 .assign_add_sat => { 731 try assignOp(gz, scope, node, .add_sat); 732 return rvalue(gz, ri, .void_value, node); 733 }, 734 .assign_mul => { 735 try assignOp(gz, scope, node, .mul); 736 return rvalue(gz, ri, .void_value, node); 737 }, 738 .assign_mul_wrap => { 739 try assignOp(gz, scope, node, .mulwrap); 740 return rvalue(gz, ri, .void_value, node); 741 }, 742 .assign_mul_sat => { 743 try assignOp(gz, scope, node, .mul_sat); 744 return rvalue(gz, ri, .void_value, node); 745 }, 746 747 // zig fmt: off 748 .shl => return shiftOp(gz, scope, ri, node, node_datas[node].lhs, node_datas[node].rhs, .shl), 749 .shr => return shiftOp(gz, scope, ri, node, node_datas[node].lhs, node_datas[node].rhs, .shr), 750 751 .add => return simpleBinOp(gz, scope, ri, node, .add), 752 .add_wrap => return simpleBinOp(gz, scope, ri, node, .addwrap), 753 .add_sat => return simpleBinOp(gz, scope, ri, node, .add_sat), 754 .sub => return simpleBinOp(gz, scope, ri, node, .sub), 755 .sub_wrap => return simpleBinOp(gz, scope, ri, node, .subwrap), 756 .sub_sat => return simpleBinOp(gz, scope, ri, node, .sub_sat), 757 .mul => return simpleBinOp(gz, scope, ri, node, .mul), 758 .mul_wrap => return simpleBinOp(gz, scope, ri, node, .mulwrap), 759 .mul_sat => return simpleBinOp(gz, scope, ri, node, .mul_sat), 760 .div => return simpleBinOp(gz, scope, ri, node, .div), 761 .mod => return simpleBinOp(gz, scope, ri, node, .mod_rem), 762 .shl_sat => return simpleBinOp(gz, scope, ri, node, .shl_sat), 763 764 .bit_and => return simpleBinOp(gz, scope, ri, node, .bit_and), 765 .bit_or => return simpleBinOp(gz, scope, ri, node, .bit_or), 766 .bit_xor => return simpleBinOp(gz, scope, ri, node, .xor), 767 .bang_equal => return simpleBinOp(gz, scope, ri, node, .cmp_neq), 768 .equal_equal => return simpleBinOp(gz, scope, ri, node, .cmp_eq), 769 .greater_than => return simpleBinOp(gz, scope, ri, node, .cmp_gt), 770 .greater_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_gte), 771 .less_than => return simpleBinOp(gz, scope, ri, node, .cmp_lt), 772 .less_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_lte), 773 .array_cat => return simpleBinOp(gz, scope, ri, node, .array_cat), 774 775 .array_mult => { 776 // This syntax form does not currently use the result type in the language specification. 777 // However, the result type can be used to emit more optimal code for large multiplications by 778 // having Sema perform a coercion before the multiplication operation. 779 const result = try gz.addPlNode(.array_mul, node, Zir.Inst.ArrayMul{ 780 .res_ty = if (try ri.rl.resultType(gz, node)) |t| t else .none, 781 .lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs), 782 .rhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs), 783 }); 784 return rvalue(gz, ri, result, node); 785 }, 786 787 .error_union => return simpleBinOp(gz, scope, ri, node, .error_union_type), 788 .merge_error_sets => return simpleBinOp(gz, scope, ri, node, .merge_error_sets), 789 790 .bool_and => return boolBinOp(gz, scope, ri, node, .bool_br_and), 791 .bool_or => return boolBinOp(gz, scope, ri, node, .bool_br_or), 792 793 .bool_not => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, node_datas[node].lhs, .bool_not), 794 .bit_not => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .bit_not), 795 796 .negation => return negation(gz, scope, ri, node), 797 .negation_wrap => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .negate_wrap), 798 799 .identifier => return identifier(gz, scope, ri, node), 800 801 .asm_simple, 802 .@"asm", 803 => return asmExpr(gz, scope, ri, node, tree.fullAsm(node).?), 804 805 .string_literal => return stringLiteral(gz, ri, node), 806 .multiline_string_literal => return multilineStringLiteral(gz, ri, node), 807 808 .number_literal => return numberLiteral(gz, ri, node, node, .positive), 809 // zig fmt: on 810 811 .builtin_call_two, .builtin_call_two_comma => { 812 if (node_datas[node].lhs == 0) { 813 const params = [_]Ast.Node.Index{}; 814 return builtinCall(gz, scope, ri, node, ¶ms, false); 815 } else if (node_datas[node].rhs == 0) { 816 const params = [_]Ast.Node.Index{node_datas[node].lhs}; 817 return builtinCall(gz, scope, ri, node, ¶ms, false); 818 } else { 819 const params = [_]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; 820 return builtinCall(gz, scope, ri, node, ¶ms, false); 821 } 822 }, 823 .builtin_call, .builtin_call_comma => { 824 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; 825 return builtinCall(gz, scope, ri, node, params, false); 826 }, 827 828 .call_one, 829 .call_one_comma, 830 .async_call_one, 831 .async_call_one_comma, 832 .call, 833 .call_comma, 834 .async_call, 835 .async_call_comma, 836 => { 837 var buf: [1]Ast.Node.Index = undefined; 838 return callExpr(gz, scope, ri, node, tree.fullCall(&buf, node).?); 839 }, 840 841 .unreachable_literal => { 842 try emitDbgNode(gz, node); 843 _ = try gz.addAsIndex(.{ 844 .tag = .@"unreachable", 845 .data = .{ .@"unreachable" = .{ 846 .src_node = gz.nodeIndexToRelative(node), 847 } }, 848 }); 849 return Zir.Inst.Ref.unreachable_value; 850 }, 851 .@"return" => return ret(gz, scope, node), 852 .field_access => return fieldAccess(gz, scope, ri, node), 853 854 .if_simple, 855 .@"if", 856 => { 857 const if_full = tree.fullIf(node).?; 858 no_switch_on_err: { 859 const error_token = if_full.error_token orelse break :no_switch_on_err; 860 const full_switch = tree.fullSwitch(if_full.ast.else_expr) orelse break :no_switch_on_err; 861 if (full_switch.label_token != null) break :no_switch_on_err; 862 if (node_tags[full_switch.ast.condition] != .identifier) break :no_switch_on_err; 863 if (!mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(main_tokens[full_switch.ast.condition]))) break :no_switch_on_err; 864 return switchExprErrUnion(gz, scope, ri.br(), node, .@"if"); 865 } 866 return ifExpr(gz, scope, ri.br(), node, if_full); 867 }, 868 869 .while_simple, 870 .while_cont, 871 .@"while", 872 => return whileExpr(gz, scope, ri.br(), node, tree.fullWhile(node).?, false), 873 874 .for_simple, .@"for" => return forExpr(gz, scope, ri.br(), node, tree.fullFor(node).?, false), 875 876 .slice_open => { 877 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); 878 879 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 880 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs); 881 try emitDbgStmt(gz, cursor); 882 const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{ 883 .lhs = lhs, 884 .start = start, 885 }); 886 return rvalue(gz, ri, result, node); 887 }, 888 .slice => { 889 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice); 890 const lhs_node = node_datas[node].lhs; 891 const lhs_tag = node_tags[lhs_node]; 892 const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel; 893 const lhs_is_open_slice = lhs_tag == .slice_open or 894 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0); 895 if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) { 896 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs); 897 898 const start = if (lhs_is_slice_sentinel) start: { 899 const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel); 900 break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start); 901 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs); 902 903 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 904 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; 905 try emitDbgStmt(gz, cursor); 906 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{ 907 .lhs = lhs, 908 .start = start, 909 .len = len, 910 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node), 911 .sentinel = .none, 912 }); 913 return rvalue(gz, ri, result, node); 914 } 915 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); 916 917 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 918 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); 919 const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end); 920 try emitDbgStmt(gz, cursor); 921 const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{ 922 .lhs = lhs, 923 .start = start, 924 .end = end, 925 }); 926 return rvalue(gz, ri, result, node); 927 }, 928 .slice_sentinel => { 929 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel); 930 const lhs_node = node_datas[node].lhs; 931 const lhs_tag = node_tags[lhs_node]; 932 const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel; 933 const lhs_is_open_slice = lhs_tag == .slice_open or 934 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0); 935 if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) { 936 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs); 937 938 const start = if (lhs_is_slice_sentinel) start: { 939 const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel); 940 break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start); 941 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs); 942 943 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 944 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; 945 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel); 946 try emitDbgStmt(gz, cursor); 947 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{ 948 .lhs = lhs, 949 .start = start, 950 .len = len, 951 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node), 952 .sentinel = sentinel, 953 }); 954 return rvalue(gz, ri, result, node); 955 } 956 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); 957 958 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 959 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); 960 const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; 961 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel); 962 try emitDbgStmt(gz, cursor); 963 const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{ 964 .lhs = lhs, 965 .start = start, 966 .end = end, 967 .sentinel = sentinel, 968 }); 969 return rvalue(gz, ri, result, node); 970 }, 971 972 .deref => { 973 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs); 974 _ = try gz.addUnNode(.validate_deref, lhs, node); 975 switch (ri.rl) { 976 .ref, .ref_coerced_ty => return lhs, 977 else => { 978 const result = try gz.addUnNode(.load, lhs, node); 979 return rvalue(gz, ri, result, node); 980 }, 981 } 982 }, 983 .address_of => { 984 const operand_rl: ResultInfo.Loc = if (try ri.rl.resultType(gz, node)) |res_ty_inst| rl: { 985 _ = try gz.addUnTok(.validate_ref_ty, res_ty_inst, tree.firstToken(node)); 986 break :rl .{ .ref_coerced_ty = res_ty_inst }; 987 } else .ref; 988 const result = try expr(gz, scope, .{ .rl = operand_rl }, node_datas[node].lhs); 989 return rvalue(gz, ri, result, node); 990 }, 991 .optional_type => { 992 const operand = try typeExpr(gz, scope, node_datas[node].lhs); 993 const result = try gz.addUnNode(.optional_type, operand, node); 994 return rvalue(gz, ri, result, node); 995 }, 996 .unwrap_optional => switch (ri.rl) { 997 .ref, .ref_coerced_ty => { 998 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); 999 1000 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 1001 try emitDbgStmt(gz, cursor); 1002 1003 return gz.addUnNode(.optional_payload_safe_ptr, lhs, node); 1004 }, 1005 else => { 1006 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs); 1007 1008 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 1009 try emitDbgStmt(gz, cursor); 1010 1011 return rvalue(gz, ri, try gz.addUnNode(.optional_payload_safe, lhs, node), node); 1012 }, 1013 }, 1014 .block_two, .block_two_semicolon => { 1015 const statements = [2]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; 1016 if (node_datas[node].lhs == 0) { 1017 return blockExpr(gz, scope, ri, node, statements[0..0], .normal); 1018 } else if (node_datas[node].rhs == 0) { 1019 return blockExpr(gz, scope, ri, node, statements[0..1], .normal); 1020 } else { 1021 return blockExpr(gz, scope, ri, node, statements[0..2], .normal); 1022 } 1023 }, 1024 .block, .block_semicolon => { 1025 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; 1026 return blockExpr(gz, scope, ri, node, statements, .normal); 1027 }, 1028 .enum_literal => if (try ri.rl.resultType(gz, node)) |res_ty| { 1029 const str_index = try astgen.identAsString(main_tokens[node]); 1030 const res = try gz.addPlNode(.decl_literal, node, Zir.Inst.Field{ 1031 .lhs = res_ty, 1032 .field_name_start = str_index, 1033 }); 1034 switch (ri.rl) { 1035 .discard, .none, .ref => unreachable, // no result type 1036 .ty, .coerced_ty => return res, // `decl_literal` does the coercion for us 1037 .ref_coerced_ty, .ptr, .inferred_ptr, .destructure => return rvalue(gz, ri, res, node), 1038 } 1039 } else return simpleStrTok(gz, ri, main_tokens[node], node, .enum_literal), 1040 .error_value => return simpleStrTok(gz, ri, node_datas[node].rhs, node, .error_value), 1041 // TODO restore this when implementing https://github.com/ziglang/zig/issues/6025 1042 // .anyframe_literal => return rvalue(gz, ri, .anyframe_type, node), 1043 .anyframe_literal => { 1044 const result = try gz.addUnNode(.anyframe_type, .void_type, node); 1045 return rvalue(gz, ri, result, node); 1046 }, 1047 .anyframe_type => { 1048 const return_type = try typeExpr(gz, scope, node_datas[node].rhs); 1049 const result = try gz.addUnNode(.anyframe_type, return_type, node); 1050 return rvalue(gz, ri, result, node); 1051 }, 1052 .@"catch" => { 1053 const catch_token = main_tokens[node]; 1054 const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe) 1055 catch_token + 2 1056 else 1057 null; 1058 no_switch_on_err: { 1059 const capture_token = payload_token orelse break :no_switch_on_err; 1060 const full_switch = tree.fullSwitch(node_datas[node].rhs) orelse break :no_switch_on_err; 1061 if (full_switch.label_token != null) break :no_switch_on_err; 1062 if (node_tags[full_switch.ast.condition] != .identifier) break :no_switch_on_err; 1063 if (!mem.eql(u8, tree.tokenSlice(capture_token), tree.tokenSlice(main_tokens[full_switch.ast.condition]))) break :no_switch_on_err; 1064 return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch"); 1065 } 1066 switch (ri.rl) { 1067 .ref, .ref_coerced_ty => return orelseCatchExpr( 1068 gz, 1069 scope, 1070 ri, 1071 node, 1072 node_datas[node].lhs, 1073 .is_non_err_ptr, 1074 .err_union_payload_unsafe_ptr, 1075 .err_union_code_ptr, 1076 node_datas[node].rhs, 1077 payload_token, 1078 ), 1079 else => return orelseCatchExpr( 1080 gz, 1081 scope, 1082 ri, 1083 node, 1084 node_datas[node].lhs, 1085 .is_non_err, 1086 .err_union_payload_unsafe, 1087 .err_union_code, 1088 node_datas[node].rhs, 1089 payload_token, 1090 ), 1091 } 1092 }, 1093 .@"orelse" => switch (ri.rl) { 1094 .ref, .ref_coerced_ty => return orelseCatchExpr( 1095 gz, 1096 scope, 1097 ri, 1098 node, 1099 node_datas[node].lhs, 1100 .is_non_null_ptr, 1101 .optional_payload_unsafe_ptr, 1102 undefined, 1103 node_datas[node].rhs, 1104 null, 1105 ), 1106 else => return orelseCatchExpr( 1107 gz, 1108 scope, 1109 ri, 1110 node, 1111 node_datas[node].lhs, 1112 .is_non_null, 1113 .optional_payload_unsafe, 1114 undefined, 1115 node_datas[node].rhs, 1116 null, 1117 ), 1118 }, 1119 1120 .ptr_type_aligned, 1121 .ptr_type_sentinel, 1122 .ptr_type, 1123 .ptr_type_bit_range, 1124 => return ptrType(gz, scope, ri, node, tree.fullPtrType(node).?), 1125 1126 .container_decl, 1127 .container_decl_trailing, 1128 .container_decl_arg, 1129 .container_decl_arg_trailing, 1130 .container_decl_two, 1131 .container_decl_two_trailing, 1132 .tagged_union, 1133 .tagged_union_trailing, 1134 .tagged_union_enum_tag, 1135 .tagged_union_enum_tag_trailing, 1136 .tagged_union_two, 1137 .tagged_union_two_trailing, 1138 => { 1139 var buf: [2]Ast.Node.Index = undefined; 1140 return containerDecl(gz, scope, ri, node, tree.fullContainerDecl(&buf, node).?); 1141 }, 1142 1143 .@"break" => return breakExpr(gz, scope, node), 1144 .@"continue" => return continueExpr(gz, scope, node), 1145 .grouped_expression => return expr(gz, scope, ri, node_datas[node].lhs), 1146 .array_type => return arrayType(gz, scope, ri, node), 1147 .array_type_sentinel => return arrayTypeSentinel(gz, scope, ri, node), 1148 .char_literal => return charLiteral(gz, ri, node), 1149 .error_set_decl => return errorSetDecl(gz, ri, node), 1150 .array_access => return arrayAccess(gz, scope, ri, node), 1151 .@"comptime" => return comptimeExprAst(gz, scope, ri, node), 1152 .@"switch", .switch_comma => return switchExpr(gz, scope, ri.br(), node, tree.fullSwitch(node).?), 1153 1154 .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node), 1155 .@"suspend" => return suspendExpr(gz, scope, node), 1156 .@"await" => return awaitExpr(gz, scope, ri, node), 1157 .@"resume" => return resumeExpr(gz, scope, ri, node), 1158 1159 .@"try" => return tryExpr(gz, scope, ri, node, node_datas[node].lhs), 1160 1161 .array_init_one, 1162 .array_init_one_comma, 1163 .array_init_dot_two, 1164 .array_init_dot_two_comma, 1165 .array_init_dot, 1166 .array_init_dot_comma, 1167 .array_init, 1168 .array_init_comma, 1169 => { 1170 var buf: [2]Ast.Node.Index = undefined; 1171 return arrayInitExpr(gz, scope, ri, node, tree.fullArrayInit(&buf, node).?); 1172 }, 1173 1174 .struct_init_one, 1175 .struct_init_one_comma, 1176 .struct_init_dot_two, 1177 .struct_init_dot_two_comma, 1178 .struct_init_dot, 1179 .struct_init_dot_comma, 1180 .struct_init, 1181 .struct_init_comma, 1182 => { 1183 var buf: [2]Ast.Node.Index = undefined; 1184 return structInitExpr(gz, scope, ri, node, tree.fullStructInit(&buf, node).?); 1185 }, 1186 1187 .fn_proto_simple, 1188 .fn_proto_multi, 1189 .fn_proto_one, 1190 .fn_proto, 1191 => { 1192 var buf: [1]Ast.Node.Index = undefined; 1193 return fnProtoExpr(gz, scope, ri, node, tree.fullFnProto(&buf, node).?); 1194 }, 1195 } 1196 } 1197 1198 fn nosuspendExpr( 1199 gz: *GenZir, 1200 scope: *Scope, 1201 ri: ResultInfo, 1202 node: Ast.Node.Index, 1203 ) InnerError!Zir.Inst.Ref { 1204 const astgen = gz.astgen; 1205 const tree = astgen.tree; 1206 const node_datas = tree.nodes.items(.data); 1207 const body_node = node_datas[node].lhs; 1208 assert(body_node != 0); 1209 if (gz.nosuspend_node != 0) { 1210 try astgen.appendErrorNodeNotes(node, "redundant nosuspend block", .{}, &[_]u32{ 1211 try astgen.errNoteNode(gz.nosuspend_node, "other nosuspend block here", .{}), 1212 }); 1213 } 1214 gz.nosuspend_node = node; 1215 defer gz.nosuspend_node = 0; 1216 return expr(gz, scope, ri, body_node); 1217 } 1218 1219 fn suspendExpr( 1220 gz: *GenZir, 1221 scope: *Scope, 1222 node: Ast.Node.Index, 1223 ) InnerError!Zir.Inst.Ref { 1224 const astgen = gz.astgen; 1225 const gpa = astgen.gpa; 1226 const tree = astgen.tree; 1227 const node_datas = tree.nodes.items(.data); 1228 const body_node = node_datas[node].lhs; 1229 1230 if (gz.nosuspend_node != 0) { 1231 return astgen.failNodeNotes(node, "suspend inside nosuspend block", .{}, &[_]u32{ 1232 try astgen.errNoteNode(gz.nosuspend_node, "nosuspend block here", .{}), 1233 }); 1234 } 1235 if (gz.suspend_node != 0) { 1236 return astgen.failNodeNotes(node, "cannot suspend inside suspend block", .{}, &[_]u32{ 1237 try astgen.errNoteNode(gz.suspend_node, "other suspend block here", .{}), 1238 }); 1239 } 1240 assert(body_node != 0); 1241 1242 const suspend_inst = try gz.makeBlockInst(.suspend_block, node); 1243 try gz.instructions.append(gpa, suspend_inst); 1244 1245 var suspend_scope = gz.makeSubBlock(scope); 1246 suspend_scope.suspend_node = node; 1247 defer suspend_scope.unstack(); 1248 1249 const body_result = try fullBodyExpr(&suspend_scope, &suspend_scope.base, .{ .rl = .none }, body_node, .normal); 1250 if (!gz.refIsNoReturn(body_result)) { 1251 _ = try suspend_scope.addBreak(.break_inline, suspend_inst, .void_value); 1252 } 1253 try suspend_scope.setBlockBody(suspend_inst); 1254 1255 return suspend_inst.toRef(); 1256 } 1257 1258 fn awaitExpr( 1259 gz: *GenZir, 1260 scope: *Scope, 1261 ri: ResultInfo, 1262 node: Ast.Node.Index, 1263 ) InnerError!Zir.Inst.Ref { 1264 const astgen = gz.astgen; 1265 const tree = astgen.tree; 1266 const node_datas = tree.nodes.items(.data); 1267 const rhs_node = node_datas[node].lhs; 1268 1269 if (gz.suspend_node != 0) { 1270 return astgen.failNodeNotes(node, "cannot await inside suspend block", .{}, &[_]u32{ 1271 try astgen.errNoteNode(gz.suspend_node, "suspend block here", .{}), 1272 }); 1273 } 1274 const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node); 1275 const result = if (gz.nosuspend_node != 0) 1276 try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{ 1277 .node = gz.nodeIndexToRelative(node), 1278 .operand = operand, 1279 }) 1280 else 1281 try gz.addUnNode(.@"await", operand, node); 1282 1283 return rvalue(gz, ri, result, node); 1284 } 1285 1286 fn resumeExpr( 1287 gz: *GenZir, 1288 scope: *Scope, 1289 ri: ResultInfo, 1290 node: Ast.Node.Index, 1291 ) InnerError!Zir.Inst.Ref { 1292 const astgen = gz.astgen; 1293 const tree = astgen.tree; 1294 const node_datas = tree.nodes.items(.data); 1295 const rhs_node = node_datas[node].lhs; 1296 const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node); 1297 const result = try gz.addUnNode(.@"resume", operand, node); 1298 return rvalue(gz, ri, result, node); 1299 } 1300 1301 fn fnProtoExpr( 1302 gz: *GenZir, 1303 scope: *Scope, 1304 ri: ResultInfo, 1305 node: Ast.Node.Index, 1306 fn_proto: Ast.full.FnProto, 1307 ) InnerError!Zir.Inst.Ref { 1308 const astgen = gz.astgen; 1309 const tree = astgen.tree; 1310 const token_tags = tree.tokens.items(.tag); 1311 1312 if (fn_proto.name_token) |some| { 1313 return astgen.failTok(some, "function type cannot have a name", .{}); 1314 } 1315 1316 const is_extern = blk: { 1317 const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false; 1318 break :blk token_tags[maybe_extern_token] == .keyword_extern; 1319 }; 1320 assert(!is_extern); 1321 1322 var block_scope = gz.makeSubBlock(scope); 1323 defer block_scope.unstack(); 1324 1325 const block_inst = try gz.makeBlockInst(.block_inline, node); 1326 1327 var noalias_bits: u32 = 0; 1328 const is_var_args = is_var_args: { 1329 var param_type_i: usize = 0; 1330 var it = fn_proto.iterate(tree); 1331 while (it.next()) |param| : (param_type_i += 1) { 1332 const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) { 1333 .keyword_noalias => is_comptime: { 1334 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse 1335 return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{})); 1336 break :is_comptime false; 1337 }, 1338 .keyword_comptime => true, 1339 else => false, 1340 } else false; 1341 1342 const is_anytype = if (param.anytype_ellipsis3) |token| blk: { 1343 switch (token_tags[token]) { 1344 .keyword_anytype => break :blk true, 1345 .ellipsis3 => break :is_var_args true, 1346 else => unreachable, 1347 } 1348 } else false; 1349 1350 const param_name = if (param.name_token) |name_token| blk: { 1351 if (mem.eql(u8, "_", tree.tokenSlice(name_token))) 1352 break :blk .empty; 1353 1354 break :blk try astgen.identAsString(name_token); 1355 } else .empty; 1356 1357 if (is_anytype) { 1358 const name_token = param.name_token orelse param.anytype_ellipsis3.?; 1359 1360 const tag: Zir.Inst.Tag = if (is_comptime) 1361 .param_anytype_comptime 1362 else 1363 .param_anytype; 1364 _ = try block_scope.addStrTok(tag, param_name, name_token); 1365 } else { 1366 const param_type_node = param.type_expr; 1367 assert(param_type_node != 0); 1368 var param_gz = block_scope.makeSubBlock(scope); 1369 defer param_gz.unstack(); 1370 const param_type = try fullBodyExpr(¶m_gz, scope, coerced_type_ri, param_type_node, .normal); 1371 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1); 1372 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node); 1373 const main_tokens = tree.nodes.items(.main_token); 1374 const name_token = param.name_token orelse main_tokens[param_type_node]; 1375 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param; 1376 const param_inst = try block_scope.addParam(¶m_gz, tag, name_token, param_name, param.first_doc_comment); 1377 assert(param_inst_expected == param_inst); 1378 } 1379 } 1380 break :is_var_args false; 1381 }; 1382 1383 if (fn_proto.ast.align_expr != 0) { 1384 return astgen.failNode(fn_proto.ast.align_expr, "function type cannot have an alignment", .{}); 1385 } 1386 1387 if (fn_proto.ast.addrspace_expr != 0) { 1388 return astgen.failNode(fn_proto.ast.addrspace_expr, "function type cannot have an addrspace", .{}); 1389 } 1390 1391 if (fn_proto.ast.section_expr != 0) { 1392 return astgen.failNode(fn_proto.ast.section_expr, "function type cannot have a linksection", .{}); 1393 } 1394 1395 const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0) 1396 try expr( 1397 &block_scope, 1398 scope, 1399 .{ .rl = .{ .coerced_ty = try block_scope.addBuiltinValue(fn_proto.ast.callconv_expr, .calling_convention) } }, 1400 fn_proto.ast.callconv_expr, 1401 ) 1402 else 1403 Zir.Inst.Ref.none; 1404 1405 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; 1406 const is_inferred_error = token_tags[maybe_bang] == .bang; 1407 if (is_inferred_error) { 1408 return astgen.failTok(maybe_bang, "function type cannot have an inferred error set", .{}); 1409 } 1410 const ret_ty = try expr(&block_scope, scope, coerced_type_ri, fn_proto.ast.return_type); 1411 1412 const result = try block_scope.addFunc(.{ 1413 .src_node = fn_proto.ast.proto_node, 1414 1415 .cc_ref = cc, 1416 .cc_gz = null, 1417 .align_ref = .none, 1418 .align_gz = null, 1419 .ret_ref = ret_ty, 1420 .ret_gz = null, 1421 .section_ref = .none, 1422 .section_gz = null, 1423 .addrspace_ref = .none, 1424 .addrspace_gz = null, 1425 1426 .param_block = block_inst, 1427 .body_gz = null, 1428 .lib_name = .empty, 1429 .is_var_args = is_var_args, 1430 .is_inferred_error = false, 1431 .is_test = false, 1432 .is_extern = false, 1433 .is_noinline = false, 1434 .noalias_bits = noalias_bits, 1435 1436 .proto_hash = undefined, // ignored for `body_gz == null` 1437 }); 1438 1439 _ = try block_scope.addBreak(.break_inline, block_inst, result); 1440 try block_scope.setBlockBody(block_inst); 1441 try gz.instructions.append(astgen.gpa, block_inst); 1442 1443 return rvalue(gz, ri, block_inst.toRef(), fn_proto.ast.proto_node); 1444 } 1445 1446 fn arrayInitExpr( 1447 gz: *GenZir, 1448 scope: *Scope, 1449 ri: ResultInfo, 1450 node: Ast.Node.Index, 1451 array_init: Ast.full.ArrayInit, 1452 ) InnerError!Zir.Inst.Ref { 1453 const astgen = gz.astgen; 1454 const tree = astgen.tree; 1455 const node_tags = tree.nodes.items(.tag); 1456 const main_tokens = tree.nodes.items(.main_token); 1457 1458 assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init. 1459 1460 const array_ty: Zir.Inst.Ref, const elem_ty: Zir.Inst.Ref = inst: { 1461 if (array_init.ast.type_expr == 0) break :inst .{ .none, .none }; 1462 1463 infer: { 1464 const array_type: Ast.full.ArrayType = tree.fullArrayType(array_init.ast.type_expr) orelse break :infer; 1465 // This intentionally does not support `@"_"` syntax. 1466 if (node_tags[array_type.ast.elem_count] == .identifier and 1467 mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_")) 1468 { 1469 const len_inst = try gz.addInt(array_init.ast.elements.len); 1470 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type); 1471 if (array_type.ast.sentinel == 0) { 1472 const array_type_inst = try gz.addPlNode(.array_type, array_init.ast.type_expr, Zir.Inst.Bin{ 1473 .lhs = len_inst, 1474 .rhs = elem_type, 1475 }); 1476 break :inst .{ array_type_inst, elem_type }; 1477 } else { 1478 const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel); 1479 const array_type_inst = try gz.addPlNode( 1480 .array_type_sentinel, 1481 array_init.ast.type_expr, 1482 Zir.Inst.ArrayTypeSentinel{ 1483 .len = len_inst, 1484 .elem_type = elem_type, 1485 .sentinel = sentinel, 1486 }, 1487 ); 1488 break :inst .{ array_type_inst, elem_type }; 1489 } 1490 } 1491 } 1492 const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr); 1493 _ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{ 1494 .ty = array_type_inst, 1495 .init_count = @intCast(array_init.ast.elements.len), 1496 }); 1497 break :inst .{ array_type_inst, .none }; 1498 }; 1499 1500 if (array_ty != .none) { 1501 // Typed inits do not use RLS for language simplicity. 1502 switch (ri.rl) { 1503 .discard => { 1504 if (elem_ty != .none) { 1505 const elem_ri: ResultInfo = .{ .rl = .{ .ty = elem_ty } }; 1506 for (array_init.ast.elements) |elem_init| { 1507 _ = try expr(gz, scope, elem_ri, elem_init); 1508 } 1509 } else { 1510 for (array_init.ast.elements, 0..) |elem_init, i| { 1511 const this_elem_ty = try gz.add(.{ 1512 .tag = .array_init_elem_type, 1513 .data = .{ .bin = .{ 1514 .lhs = array_ty, 1515 .rhs = @enumFromInt(i), 1516 } }, 1517 }); 1518 _ = try expr(gz, scope, .{ .rl = .{ .ty = this_elem_ty } }, elem_init); 1519 } 1520 } 1521 return .void_value; 1522 }, 1523 .ref => return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, true), 1524 else => { 1525 const array_inst = try arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, false); 1526 return rvalue(gz, ri, array_inst, node); 1527 }, 1528 } 1529 } 1530 1531 switch (ri.rl) { 1532 .none => return arrayInitExprAnon(gz, scope, node, array_init.ast.elements), 1533 .discard => { 1534 for (array_init.ast.elements) |elem_init| { 1535 _ = try expr(gz, scope, .{ .rl = .discard }, elem_init); 1536 } 1537 return Zir.Inst.Ref.void_value; 1538 }, 1539 .ref => { 1540 const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements); 1541 return gz.addUnTok(.ref, result, tree.firstToken(node)); 1542 }, 1543 .ref_coerced_ty => |ptr_ty_inst| { 1544 const dest_arr_ty_inst = try gz.addPlNode(.validate_array_init_ref_ty, node, Zir.Inst.ArrayInitRefTy{ 1545 .ptr_ty = ptr_ty_inst, 1546 .elem_count = @intCast(array_init.ast.elements.len), 1547 }); 1548 return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, dest_arr_ty_inst, .none, true); 1549 }, 1550 .ty, .coerced_ty => |result_ty_inst| { 1551 _ = try gz.addPlNode(.validate_array_init_result_ty, node, Zir.Inst.ArrayInit{ 1552 .ty = result_ty_inst, 1553 .init_count = @intCast(array_init.ast.elements.len), 1554 }); 1555 return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, result_ty_inst, .none, false); 1556 }, 1557 .ptr => |ptr| { 1558 try arrayInitExprPtr(gz, scope, node, array_init.ast.elements, ptr.inst); 1559 return .void_value; 1560 }, 1561 .inferred_ptr => { 1562 // We can't get elem pointers of an untyped inferred alloc, so must perform a 1563 // standard anonymous initialization followed by an rvalue store. 1564 // See corresponding logic in structInitExpr. 1565 const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements); 1566 return rvalue(gz, ri, result, node); 1567 }, 1568 .destructure => |destructure| { 1569 // Untyped init - destructure directly into result pointers 1570 if (array_init.ast.elements.len != destructure.components.len) { 1571 return astgen.failNodeNotes(node, "expected {} elements for destructure, found {}", .{ 1572 destructure.components.len, 1573 array_init.ast.elements.len, 1574 }, &.{ 1575 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}), 1576 }); 1577 } 1578 for (array_init.ast.elements, destructure.components) |elem_init, ds_comp| { 1579 const elem_ri: ResultInfo = .{ .rl = switch (ds_comp) { 1580 .typed_ptr => |ptr_rl| .{ .ptr = ptr_rl }, 1581 .inferred_ptr => |ptr_inst| .{ .inferred_ptr = ptr_inst }, 1582 .discard => .discard, 1583 } }; 1584 _ = try expr(gz, scope, elem_ri, elem_init); 1585 } 1586 return .void_value; 1587 }, 1588 } 1589 } 1590 1591 /// An array initialization expression using an `array_init_anon` instruction. 1592 fn arrayInitExprAnon( 1593 gz: *GenZir, 1594 scope: *Scope, 1595 node: Ast.Node.Index, 1596 elements: []const Ast.Node.Index, 1597 ) InnerError!Zir.Inst.Ref { 1598 const astgen = gz.astgen; 1599 1600 const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{ 1601 .operands_len = @intCast(elements.len), 1602 }); 1603 var extra_index = try reserveExtra(astgen, elements.len); 1604 1605 for (elements) |elem_init| { 1606 const elem_ref = try expr(gz, scope, .{ .rl = .none }, elem_init); 1607 astgen.extra.items[extra_index] = @intFromEnum(elem_ref); 1608 extra_index += 1; 1609 } 1610 return try gz.addPlNodePayloadIndex(.array_init_anon, node, payload_index); 1611 } 1612 1613 /// An array initialization expression using an `array_init` or `array_init_ref` instruction. 1614 fn arrayInitExprTyped( 1615 gz: *GenZir, 1616 scope: *Scope, 1617 node: Ast.Node.Index, 1618 elements: []const Ast.Node.Index, 1619 ty_inst: Zir.Inst.Ref, 1620 maybe_elem_ty_inst: Zir.Inst.Ref, 1621 is_ref: bool, 1622 ) InnerError!Zir.Inst.Ref { 1623 const astgen = gz.astgen; 1624 1625 const len = elements.len + 1; // +1 for type 1626 const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{ 1627 .operands_len = @intCast(len), 1628 }); 1629 var extra_index = try reserveExtra(astgen, len); 1630 astgen.extra.items[extra_index] = @intFromEnum(ty_inst); 1631 extra_index += 1; 1632 1633 if (maybe_elem_ty_inst != .none) { 1634 const elem_ri: ResultInfo = .{ .rl = .{ .coerced_ty = maybe_elem_ty_inst } }; 1635 for (elements) |elem_init| { 1636 const elem_inst = try expr(gz, scope, elem_ri, elem_init); 1637 astgen.extra.items[extra_index] = @intFromEnum(elem_inst); 1638 extra_index += 1; 1639 } 1640 } else { 1641 for (elements, 0..) |elem_init, i| { 1642 const ri: ResultInfo = .{ .rl = .{ .coerced_ty = try gz.add(.{ 1643 .tag = .array_init_elem_type, 1644 .data = .{ .bin = .{ 1645 .lhs = ty_inst, 1646 .rhs = @enumFromInt(i), 1647 } }, 1648 }) } }; 1649 1650 const elem_inst = try expr(gz, scope, ri, elem_init); 1651 astgen.extra.items[extra_index] = @intFromEnum(elem_inst); 1652 extra_index += 1; 1653 } 1654 } 1655 1656 const tag: Zir.Inst.Tag = if (is_ref) .array_init_ref else .array_init; 1657 return try gz.addPlNodePayloadIndex(tag, node, payload_index); 1658 } 1659 1660 /// An array initialization expression using element pointers. 1661 fn arrayInitExprPtr( 1662 gz: *GenZir, 1663 scope: *Scope, 1664 node: Ast.Node.Index, 1665 elements: []const Ast.Node.Index, 1666 ptr_inst: Zir.Inst.Ref, 1667 ) InnerError!void { 1668 const astgen = gz.astgen; 1669 1670 const array_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node); 1671 1672 const payload_index = try addExtra(astgen, Zir.Inst.Block{ 1673 .body_len = @intCast(elements.len), 1674 }); 1675 var extra_index = try reserveExtra(astgen, elements.len); 1676 1677 for (elements, 0..) |elem_init, i| { 1678 const elem_ptr_inst = try gz.addPlNode(.array_init_elem_ptr, elem_init, Zir.Inst.ElemPtrImm{ 1679 .ptr = array_ptr_inst, 1680 .index = @intCast(i), 1681 }); 1682 astgen.extra.items[extra_index] = @intFromEnum(elem_ptr_inst.toIndex().?); 1683 extra_index += 1; 1684 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = elem_ptr_inst } } }, elem_init); 1685 } 1686 1687 _ = try gz.addPlNodePayloadIndex(.validate_ptr_array_init, node, payload_index); 1688 } 1689 1690 fn structInitExpr( 1691 gz: *GenZir, 1692 scope: *Scope, 1693 ri: ResultInfo, 1694 node: Ast.Node.Index, 1695 struct_init: Ast.full.StructInit, 1696 ) InnerError!Zir.Inst.Ref { 1697 const astgen = gz.astgen; 1698 const tree = astgen.tree; 1699 1700 if (struct_init.ast.type_expr == 0) { 1701 if (struct_init.ast.fields.len == 0) { 1702 // Anonymous init with no fields. 1703 switch (ri.rl) { 1704 .discard => return .void_value, 1705 .ref_coerced_ty => |ptr_ty_inst| return gz.addUnNode(.struct_init_empty_ref_result, ptr_ty_inst, node), 1706 .ty, .coerced_ty => |ty_inst| return gz.addUnNode(.struct_init_empty_result, ty_inst, node), 1707 .ptr => { 1708 // TODO: should we modify this to use RLS for the field stores here? 1709 const ty_inst = (try ri.rl.resultType(gz, node)).?; 1710 const val = try gz.addUnNode(.struct_init_empty_result, ty_inst, node); 1711 return rvalue(gz, ri, val, node); 1712 }, 1713 .none, .ref, .inferred_ptr => { 1714 return rvalue(gz, ri, .empty_struct, node); 1715 }, 1716 .destructure => |destructure| { 1717 return astgen.failNodeNotes(node, "empty initializer cannot be destructured", .{}, &.{ 1718 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}), 1719 }); 1720 }, 1721 } 1722 } 1723 } else array: { 1724 const node_tags = tree.nodes.items(.tag); 1725 const main_tokens = tree.nodes.items(.main_token); 1726 const array_type: Ast.full.ArrayType = tree.fullArrayType(struct_init.ast.type_expr) orelse { 1727 if (struct_init.ast.fields.len == 0) { 1728 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); 1729 const result = try gz.addUnNode(.struct_init_empty, ty_inst, node); 1730 return rvalue(gz, ri, result, node); 1731 } 1732 break :array; 1733 }; 1734 const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and 1735 // This intentionally does not support `@"_"` syntax. 1736 mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_"); 1737 if (struct_init.ast.fields.len == 0) { 1738 if (is_inferred_array_len) { 1739 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type); 1740 const array_type_inst = if (array_type.ast.sentinel == 0) blk: { 1741 break :blk try gz.addPlNode(.array_type, struct_init.ast.type_expr, Zir.Inst.Bin{ 1742 .lhs = .zero_usize, 1743 .rhs = elem_type, 1744 }); 1745 } else blk: { 1746 const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel); 1747 break :blk try gz.addPlNode( 1748 .array_type_sentinel, 1749 struct_init.ast.type_expr, 1750 Zir.Inst.ArrayTypeSentinel{ 1751 .len = .zero_usize, 1752 .elem_type = elem_type, 1753 .sentinel = sentinel, 1754 }, 1755 ); 1756 }; 1757 const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node); 1758 return rvalue(gz, ri, result, node); 1759 } 1760 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); 1761 const result = try gz.addUnNode(.struct_init_empty, ty_inst, node); 1762 return rvalue(gz, ri, result, node); 1763 } else { 1764 return astgen.failNode( 1765 struct_init.ast.type_expr, 1766 "initializing array with struct syntax", 1767 .{}, 1768 ); 1769 } 1770 } 1771 1772 { 1773 var sfba = std.heap.stackFallback(256, astgen.arena); 1774 const sfba_allocator = sfba.get(); 1775 1776 var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator); 1777 try duplicate_names.ensureTotalCapacity(@intCast(struct_init.ast.fields.len)); 1778 1779 // When there aren't errors, use this to avoid a second iteration. 1780 var any_duplicate = false; 1781 1782 for (struct_init.ast.fields) |field| { 1783 const name_token = tree.firstToken(field) - 2; 1784 const name_index = try astgen.identAsString(name_token); 1785 1786 const gop = try duplicate_names.getOrPut(name_index); 1787 1788 if (gop.found_existing) { 1789 try gop.value_ptr.append(sfba_allocator, name_token); 1790 any_duplicate = true; 1791 } else { 1792 gop.value_ptr.* = .{}; 1793 try gop.value_ptr.append(sfba_allocator, name_token); 1794 } 1795 } 1796 1797 if (any_duplicate) { 1798 var it = duplicate_names.iterator(); 1799 1800 while (it.next()) |entry| { 1801 const record = entry.value_ptr.*; 1802 if (record.items.len > 1) { 1803 var error_notes = std.ArrayList(u32).init(astgen.arena); 1804 1805 for (record.items[1..]) |duplicate| { 1806 try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate name here", .{})); 1807 } 1808 1809 try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{})); 1810 1811 try astgen.appendErrorTokNotes( 1812 record.items[0], 1813 "duplicate struct field name", 1814 .{}, 1815 error_notes.items, 1816 ); 1817 } 1818 } 1819 1820 return error.AnalysisFail; 1821 } 1822 } 1823 1824 if (struct_init.ast.type_expr != 0) { 1825 // Typed inits do not use RLS for language simplicity. 1826 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); 1827 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); 1828 switch (ri.rl) { 1829 .ref => return structInitExprTyped(gz, scope, node, struct_init, ty_inst, true), 1830 else => { 1831 const struct_inst = try structInitExprTyped(gz, scope, node, struct_init, ty_inst, false); 1832 return rvalue(gz, ri, struct_inst, node); 1833 }, 1834 } 1835 } 1836 1837 switch (ri.rl) { 1838 .none => return structInitExprAnon(gz, scope, node, struct_init), 1839 .discard => { 1840 // Even if discarding we must perform side-effects. 1841 for (struct_init.ast.fields) |field_init| { 1842 _ = try expr(gz, scope, .{ .rl = .discard }, field_init); 1843 } 1844 return .void_value; 1845 }, 1846 .ref => { 1847 const result = try structInitExprAnon(gz, scope, node, struct_init); 1848 return gz.addUnTok(.ref, result, tree.firstToken(node)); 1849 }, 1850 .ref_coerced_ty => |ptr_ty_inst| { 1851 const result_ty_inst = try gz.addUnNode(.elem_type, ptr_ty_inst, node); 1852 _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node); 1853 return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, true); 1854 }, 1855 .ty, .coerced_ty => |result_ty_inst| { 1856 _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node); 1857 return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, false); 1858 }, 1859 .ptr => |ptr| { 1860 try structInitExprPtr(gz, scope, node, struct_init, ptr.inst); 1861 return .void_value; 1862 }, 1863 .inferred_ptr => { 1864 // We can't get field pointers of an untyped inferred alloc, so must perform a 1865 // standard anonymous initialization followed by an rvalue store. 1866 // See corresponding logic in arrayInitExpr. 1867 const struct_inst = try structInitExprAnon(gz, scope, node, struct_init); 1868 return rvalue(gz, ri, struct_inst, node); 1869 }, 1870 .destructure => |destructure| { 1871 // This is an untyped init, so is an actual struct, which does 1872 // not support destructuring. 1873 return astgen.failNodeNotes(node, "struct value cannot be destructured", .{}, &.{ 1874 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}), 1875 }); 1876 }, 1877 } 1878 } 1879 1880 /// A struct initialization expression using a `struct_init_anon` instruction. 1881 fn structInitExprAnon( 1882 gz: *GenZir, 1883 scope: *Scope, 1884 node: Ast.Node.Index, 1885 struct_init: Ast.full.StructInit, 1886 ) InnerError!Zir.Inst.Ref { 1887 const astgen = gz.astgen; 1888 const tree = astgen.tree; 1889 1890 const payload_index = try addExtra(astgen, Zir.Inst.StructInitAnon{ 1891 .fields_len = @intCast(struct_init.ast.fields.len), 1892 }); 1893 const field_size = @typeInfo(Zir.Inst.StructInitAnon.Item).@"struct".fields.len; 1894 var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size); 1895 1896 for (struct_init.ast.fields) |field_init| { 1897 const name_token = tree.firstToken(field_init) - 2; 1898 const str_index = try astgen.identAsString(name_token); 1899 setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{ 1900 .field_name = str_index, 1901 .init = try expr(gz, scope, .{ .rl = .none }, field_init), 1902 }); 1903 extra_index += field_size; 1904 } 1905 1906 return gz.addPlNodePayloadIndex(.struct_init_anon, node, payload_index); 1907 } 1908 1909 /// A struct initialization expression using a `struct_init` or `struct_init_ref` instruction. 1910 fn structInitExprTyped( 1911 gz: *GenZir, 1912 scope: *Scope, 1913 node: Ast.Node.Index, 1914 struct_init: Ast.full.StructInit, 1915 ty_inst: Zir.Inst.Ref, 1916 is_ref: bool, 1917 ) InnerError!Zir.Inst.Ref { 1918 const astgen = gz.astgen; 1919 const tree = astgen.tree; 1920 1921 const payload_index = try addExtra(astgen, Zir.Inst.StructInit{ 1922 .fields_len = @intCast(struct_init.ast.fields.len), 1923 }); 1924 const field_size = @typeInfo(Zir.Inst.StructInit.Item).@"struct".fields.len; 1925 var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size); 1926 1927 for (struct_init.ast.fields) |field_init| { 1928 const name_token = tree.firstToken(field_init) - 2; 1929 const str_index = try astgen.identAsString(name_token); 1930 const field_ty_inst = try gz.addPlNode(.struct_init_field_type, field_init, Zir.Inst.FieldType{ 1931 .container_type = ty_inst, 1932 .name_start = str_index, 1933 }); 1934 setExtra(astgen, extra_index, Zir.Inst.StructInit.Item{ 1935 .field_type = field_ty_inst.toIndex().?, 1936 .init = try expr(gz, scope, .{ .rl = .{ .coerced_ty = field_ty_inst } }, field_init), 1937 }); 1938 extra_index += field_size; 1939 } 1940 1941 const tag: Zir.Inst.Tag = if (is_ref) .struct_init_ref else .struct_init; 1942 return gz.addPlNodePayloadIndex(tag, node, payload_index); 1943 } 1944 1945 /// A struct initialization expression using field pointers. 1946 fn structInitExprPtr( 1947 gz: *GenZir, 1948 scope: *Scope, 1949 node: Ast.Node.Index, 1950 struct_init: Ast.full.StructInit, 1951 ptr_inst: Zir.Inst.Ref, 1952 ) InnerError!void { 1953 const astgen = gz.astgen; 1954 const tree = astgen.tree; 1955 1956 const struct_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node); 1957 1958 const payload_index = try addExtra(astgen, Zir.Inst.Block{ 1959 .body_len = @intCast(struct_init.ast.fields.len), 1960 }); 1961 var extra_index = try reserveExtra(astgen, struct_init.ast.fields.len); 1962 1963 for (struct_init.ast.fields) |field_init| { 1964 const name_token = tree.firstToken(field_init) - 2; 1965 const str_index = try astgen.identAsString(name_token); 1966 const field_ptr = try gz.addPlNode(.struct_init_field_ptr, field_init, Zir.Inst.Field{ 1967 .lhs = struct_ptr_inst, 1968 .field_name_start = str_index, 1969 }); 1970 astgen.extra.items[extra_index] = @intFromEnum(field_ptr.toIndex().?); 1971 extra_index += 1; 1972 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = field_ptr } } }, field_init); 1973 } 1974 1975 _ = try gz.addPlNodePayloadIndex(.validate_ptr_struct_init, node, payload_index); 1976 } 1977 1978 /// This explicitly calls expr in a comptime scope by wrapping it in a `block_comptime` if 1979 /// necessary. It should be used whenever we need to force compile-time evaluation of something, 1980 /// such as a type. 1981 /// The function corresponding to `comptime` expression syntax is `comptimeExprAst`. 1982 fn comptimeExpr( 1983 gz: *GenZir, 1984 scope: *Scope, 1985 ri: ResultInfo, 1986 node: Ast.Node.Index, 1987 ) InnerError!Zir.Inst.Ref { 1988 if (gz.is_comptime) { 1989 // No need to change anything! 1990 return expr(gz, scope, ri, node); 1991 } 1992 1993 // There's an optimization here: if the body will be evaluated at comptime regardless, there's 1994 // no need to wrap it in a block. This is hard to determine in general, but we can identify a 1995 // common subset of trivially comptime expressions to take down the size of the ZIR a bit. 1996 const tree = gz.astgen.tree; 1997 const main_tokens = tree.nodes.items(.main_token); 1998 const node_tags = tree.nodes.items(.tag); 1999 switch (node_tags[node]) { 2000 // Any identifier in `primitive_instrs` is trivially comptime. In particular, this includes 2001 // some common types, so we can elide `block_comptime` for a few common type annotations. 2002 .identifier => { 2003 const ident_token = main_tokens[node]; 2004 const ident_name_raw = tree.tokenSlice(ident_token); 2005 if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| { 2006 // No need to worry about result location here, we're not creating a comptime block! 2007 return rvalue(gz, ri, zir_const_ref, node); 2008 } 2009 }, 2010 2011 // We can also avoid the block for a few trivial AST tags which are always comptime-known. 2012 .number_literal, .string_literal, .multiline_string_literal, .enum_literal, .error_value => { 2013 // No need to worry about result location here, we're not creating a comptime block! 2014 return expr(gz, scope, ri, node); 2015 }, 2016 2017 // Lastly, for labelled blocks, avoid emitting a labelled block directly inside this 2018 // comptime block, because that would be silly! Note that we don't bother doing this for 2019 // unlabelled blocks, since they don't generate blocks at comptime anyway (see `blockExpr`). 2020 .block_two, .block_two_semicolon, .block, .block_semicolon => { 2021 const token_tags = tree.tokens.items(.tag); 2022 const lbrace = main_tokens[node]; 2023 // Careful! We can't pass in the real result location here, since it may 2024 // refer to runtime memory. A runtime-to-comptime boundary has to remove 2025 // result location information, compute the result, and copy it to the true 2026 // result location at runtime. We do this below as well. 2027 const ty_only_ri: ResultInfo = .{ 2028 .ctx = ri.ctx, 2029 .rl = if (try ri.rl.resultType(gz, node)) |res_ty| 2030 .{ .coerced_ty = res_ty } 2031 else 2032 .none, 2033 }; 2034 if (token_tags[lbrace - 1] == .colon and 2035 token_tags[lbrace - 2] == .identifier) 2036 { 2037 const node_datas = tree.nodes.items(.data); 2038 switch (node_tags[node]) { 2039 .block_two, .block_two_semicolon => { 2040 const stmts: [2]Ast.Node.Index = .{ node_datas[node].lhs, node_datas[node].rhs }; 2041 const stmt_slice = if (stmts[0] == 0) 2042 stmts[0..0] 2043 else if (stmts[1] == 0) 2044 stmts[0..1] 2045 else 2046 stmts[0..2]; 2047 2048 const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmt_slice, true, .normal); 2049 return rvalue(gz, ri, block_ref, node); 2050 }, 2051 .block, .block_semicolon => { 2052 const stmts = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; 2053 // Replace result location and copy back later - see above. 2054 const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmts, true, .normal); 2055 return rvalue(gz, ri, block_ref, node); 2056 }, 2057 else => unreachable, 2058 } 2059 } 2060 }, 2061 2062 // In other cases, we don't optimize anything - we need a wrapper comptime block. 2063 else => {}, 2064 } 2065 2066 var block_scope = gz.makeSubBlock(scope); 2067 block_scope.is_comptime = true; 2068 defer block_scope.unstack(); 2069 2070 const block_inst = try gz.makeBlockInst(.block_comptime, node); 2071 // Replace result location and copy back later - see above. 2072 const ty_only_ri: ResultInfo = .{ 2073 .ctx = ri.ctx, 2074 .rl = if (try ri.rl.resultType(gz, node)) |res_ty| 2075 .{ .coerced_ty = res_ty } 2076 else 2077 .none, 2078 }; 2079 const block_result = try fullBodyExpr(&block_scope, scope, ty_only_ri, node, .normal); 2080 if (!gz.refIsNoReturn(block_result)) { 2081 _ = try block_scope.addBreak(.@"break", block_inst, block_result); 2082 } 2083 try block_scope.setBlockBody(block_inst); 2084 try gz.instructions.append(gz.astgen.gpa, block_inst); 2085 2086 return rvalue(gz, ri, block_inst.toRef(), node); 2087 } 2088 2089 /// This one is for an actual `comptime` syntax, and will emit a compile error if 2090 /// the scope is already known to be comptime-evaluated. 2091 /// See `comptimeExpr` for the helper function for calling expr in a comptime scope. 2092 fn comptimeExprAst( 2093 gz: *GenZir, 2094 scope: *Scope, 2095 ri: ResultInfo, 2096 node: Ast.Node.Index, 2097 ) InnerError!Zir.Inst.Ref { 2098 const astgen = gz.astgen; 2099 if (gz.is_comptime) { 2100 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{}); 2101 } 2102 const tree = astgen.tree; 2103 const node_datas = tree.nodes.items(.data); 2104 const body_node = node_datas[node].lhs; 2105 return comptimeExpr(gz, scope, ri, body_node); 2106 } 2107 2108 /// Restore the error return trace index. Performs the restore only if the result is a non-error or 2109 /// if the result location is a non-error-handling expression. 2110 fn restoreErrRetIndex( 2111 gz: *GenZir, 2112 bt: GenZir.BranchTarget, 2113 ri: ResultInfo, 2114 node: Ast.Node.Index, 2115 result: Zir.Inst.Ref, 2116 ) !void { 2117 const op = switch (nodeMayEvalToError(gz.astgen.tree, node)) { 2118 .always => return, // never restore/pop 2119 .never => .none, // always restore/pop 2120 .maybe => switch (ri.ctx) { 2121 .error_handling_expr, .@"return", .fn_arg, .const_init => switch (ri.rl) { 2122 .ptr => |ptr_res| try gz.addUnNode(.load, ptr_res.inst, node), 2123 .inferred_ptr => blk: { 2124 // This is a terrible workaround for Sema's inability to load from a .alloc_inferred ptr 2125 // before its type has been resolved. There is no valid operand to use here, so error 2126 // traces will be popped prematurely. 2127 // TODO: Update this to do a proper load from the rl_ptr, once Sema can support it. 2128 break :blk .none; 2129 }, 2130 .destructure => return, // value must be a tuple or array, so never restore/pop 2131 else => result, 2132 }, 2133 else => .none, // always restore/pop 2134 }, 2135 }; 2136 _ = try gz.addRestoreErrRetIndex(bt, .{ .if_non_error = op }, node); 2137 } 2138 2139 fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 2140 const astgen = parent_gz.astgen; 2141 const tree = astgen.tree; 2142 const node_datas = tree.nodes.items(.data); 2143 const break_label = node_datas[node].lhs; 2144 const rhs = node_datas[node].rhs; 2145 2146 // Look for the label in the scope. 2147 var scope = parent_scope; 2148 while (true) { 2149 switch (scope.tag) { 2150 .gen_zir => { 2151 const block_gz = scope.cast(GenZir).?; 2152 2153 if (block_gz.cur_defer_node != 0) { 2154 // We are breaking out of a `defer` block. 2155 return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{ 2156 try astgen.errNoteNode( 2157 block_gz.cur_defer_node, 2158 "defer expression here", 2159 .{}, 2160 ), 2161 }); 2162 } 2163 2164 const block_inst = blk: { 2165 if (break_label != 0) { 2166 if (block_gz.label) |*label| { 2167 if (try astgen.tokenIdentEql(label.token, break_label)) { 2168 label.used = true; 2169 break :blk label.block_inst; 2170 } 2171 } 2172 } else if (block_gz.break_block.unwrap()) |i| { 2173 break :blk i; 2174 } 2175 // If not the target, start over with the parent 2176 scope = block_gz.parent; 2177 continue; 2178 }; 2179 // If we made it here, this block is the target of the break expr 2180 2181 const break_tag: Zir.Inst.Tag = if (block_gz.is_inline) 2182 .break_inline 2183 else 2184 .@"break"; 2185 2186 if (rhs == 0) { 2187 _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node); 2188 2189 try genDefers(parent_gz, scope, parent_scope, .normal_only); 2190 2191 // As our last action before the break, "pop" the error trace if needed 2192 if (!block_gz.is_comptime) 2193 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, node); 2194 2195 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); 2196 return Zir.Inst.Ref.unreachable_value; 2197 } 2198 2199 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node); 2200 2201 try genDefers(parent_gz, scope, parent_scope, .normal_only); 2202 2203 // As our last action before the break, "pop" the error trace if needed 2204 if (!block_gz.is_comptime) 2205 try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand); 2206 2207 switch (block_gz.break_result_info.rl) { 2208 .ptr => { 2209 // In this case we don't have any mechanism to intercept it; 2210 // we assume the result location is written, and we break with void. 2211 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); 2212 }, 2213 .discard => { 2214 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); 2215 }, 2216 else => { 2217 _ = try parent_gz.addBreakWithSrcNode(break_tag, block_inst, operand, rhs); 2218 }, 2219 } 2220 return Zir.Inst.Ref.unreachable_value; 2221 }, 2222 .local_val => scope = scope.cast(Scope.LocalVal).?.parent, 2223 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, 2224 .namespace => break, 2225 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, 2226 .top => unreachable, 2227 } 2228 } 2229 if (break_label != 0) { 2230 const label_name = try astgen.identifierTokenString(break_label); 2231 return astgen.failTok(break_label, "label not found: '{s}'", .{label_name}); 2232 } else { 2233 return astgen.failNode(node, "break expression outside loop", .{}); 2234 } 2235 } 2236 2237 fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 2238 const astgen = parent_gz.astgen; 2239 const tree = astgen.tree; 2240 const node_datas = tree.nodes.items(.data); 2241 const break_label = node_datas[node].lhs; 2242 const rhs = node_datas[node].rhs; 2243 2244 if (break_label == 0 and rhs != 0) { 2245 return astgen.failNode(node, "cannot continue with operand without label", .{}); 2246 } 2247 2248 // Look for the label in the scope. 2249 var scope = parent_scope; 2250 while (true) { 2251 switch (scope.tag) { 2252 .gen_zir => { 2253 const gen_zir = scope.cast(GenZir).?; 2254 2255 if (gen_zir.cur_defer_node != 0) { 2256 return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{ 2257 try astgen.errNoteNode( 2258 gen_zir.cur_defer_node, 2259 "defer expression here", 2260 .{}, 2261 ), 2262 }); 2263 } 2264 const continue_block = gen_zir.continue_block.unwrap() orelse { 2265 scope = gen_zir.parent; 2266 continue; 2267 }; 2268 if (break_label != 0) blk: { 2269 if (gen_zir.label) |*label| { 2270 if (try astgen.tokenIdentEql(label.token, break_label)) { 2271 const maybe_switch_tag = astgen.instructions.items(.tag)[@intFromEnum(label.block_inst)]; 2272 if (rhs != 0) switch (maybe_switch_tag) { 2273 .switch_block, .switch_block_ref => {}, 2274 else => return astgen.failNode(node, "cannot continue loop with operand", .{}), 2275 } else switch (maybe_switch_tag) { 2276 .switch_block, .switch_block_ref => return astgen.failNode(node, "cannot continue switch without operand", .{}), 2277 else => {}, 2278 } 2279 2280 label.used = true; 2281 label.used_for_continue = true; 2282 break :blk; 2283 } 2284 } 2285 // found continue but either it has a different label, or no label 2286 scope = gen_zir.parent; 2287 continue; 2288 } else if (gen_zir.label) |label| { 2289 // This `continue` is unlabeled. If the gz we've found corresponds to a labeled 2290 // `switch`, ignore it and continue to parent scopes. 2291 switch (astgen.instructions.items(.tag)[@intFromEnum(label.block_inst)]) { 2292 .switch_block, .switch_block_ref => { 2293 scope = gen_zir.parent; 2294 continue; 2295 }, 2296 else => {}, 2297 } 2298 } 2299 2300 if (rhs != 0) { 2301 // We need to figure out the result info to use. 2302 // The type should match 2303 const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.continue_result_info, rhs, node); 2304 2305 try genDefers(parent_gz, scope, parent_scope, .normal_only); 2306 2307 // As our last action before the continue, "pop" the error trace if needed 2308 if (!gen_zir.is_comptime) 2309 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node); 2310 2311 _ = try parent_gz.addBreakWithSrcNode(.switch_continue, continue_block, operand, rhs); 2312 return Zir.Inst.Ref.unreachable_value; 2313 } 2314 2315 try genDefers(parent_gz, scope, parent_scope, .normal_only); 2316 2317 const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline) 2318 .break_inline 2319 else 2320 .@"break"; 2321 if (break_tag == .break_inline) { 2322 _ = try parent_gz.addUnNode(.check_comptime_control_flow, continue_block.toRef(), node); 2323 } 2324 2325 // As our last action before the continue, "pop" the error trace if needed 2326 if (!gen_zir.is_comptime) 2327 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node); 2328 2329 _ = try parent_gz.addBreak(break_tag, continue_block, .void_value); 2330 return Zir.Inst.Ref.unreachable_value; 2331 }, 2332 .local_val => scope = scope.cast(Scope.LocalVal).?.parent, 2333 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, 2334 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, 2335 .namespace => break, 2336 .top => unreachable, 2337 } 2338 } 2339 if (break_label != 0) { 2340 const label_name = try astgen.identifierTokenString(break_label); 2341 return astgen.failTok(break_label, "label not found: '{s}'", .{label_name}); 2342 } else { 2343 return astgen.failNode(node, "continue expression outside loop", .{}); 2344 } 2345 } 2346 2347 /// Similar to `expr`, but intended for use when `gz` corresponds to a body 2348 /// which will contain only this node's code. Differs from `expr` in that if the 2349 /// root expression is an unlabeled block, does not emit an actual block. 2350 /// Instead, the block contents are emitted directly into `gz`. 2351 fn fullBodyExpr( 2352 gz: *GenZir, 2353 scope: *Scope, 2354 ri: ResultInfo, 2355 node: Ast.Node.Index, 2356 block_kind: BlockKind, 2357 ) InnerError!Zir.Inst.Ref { 2358 const tree = gz.astgen.tree; 2359 const node_tags = tree.nodes.items(.tag); 2360 const node_datas = tree.nodes.items(.data); 2361 const main_tokens = tree.nodes.items(.main_token); 2362 const token_tags = tree.tokens.items(.tag); 2363 var stmt_buf: [2]Ast.Node.Index = undefined; 2364 const statements: []const Ast.Node.Index = switch (node_tags[node]) { 2365 else => return expr(gz, scope, ri, node), 2366 .block_two, .block_two_semicolon => if (node_datas[node].lhs == 0) s: { 2367 break :s &.{}; 2368 } else if (node_datas[node].rhs == 0) s: { 2369 stmt_buf[0] = node_datas[node].lhs; 2370 break :s stmt_buf[0..1]; 2371 } else s: { 2372 stmt_buf[0] = node_datas[node].lhs; 2373 stmt_buf[1] = node_datas[node].rhs; 2374 break :s stmt_buf[0..2]; 2375 }, 2376 .block, .block_semicolon => tree.extra_data[node_datas[node].lhs..node_datas[node].rhs], 2377 }; 2378 2379 const lbrace = main_tokens[node]; 2380 if (token_tags[lbrace - 1] == .colon and 2381 token_tags[lbrace - 2] == .identifier) 2382 { 2383 // Labeled blocks are tricky - forwarding result location information properly is non-trivial, 2384 // plus if this block is exited with a `break_inline` we aren't allowed multiple breaks. This 2385 // case is rare, so just treat it as a normal expression and create a nested block. 2386 return blockExpr(gz, scope, ri, node, statements, block_kind); 2387 } 2388 2389 var sub_gz = gz.makeSubBlock(scope); 2390 try blockExprStmts(&sub_gz, &sub_gz.base, statements, block_kind); 2391 2392 return rvalue(gz, ri, .void_value, node); 2393 } 2394 2395 const BlockKind = enum { normal, allow_branch_hint }; 2396 2397 fn blockExpr( 2398 gz: *GenZir, 2399 scope: *Scope, 2400 ri: ResultInfo, 2401 block_node: Ast.Node.Index, 2402 statements: []const Ast.Node.Index, 2403 kind: BlockKind, 2404 ) InnerError!Zir.Inst.Ref { 2405 const astgen = gz.astgen; 2406 const tree = astgen.tree; 2407 const main_tokens = tree.nodes.items(.main_token); 2408 const token_tags = tree.tokens.items(.tag); 2409 2410 const lbrace = main_tokens[block_node]; 2411 if (token_tags[lbrace - 1] == .colon and 2412 token_tags[lbrace - 2] == .identifier) 2413 { 2414 return labeledBlockExpr(gz, scope, ri, block_node, statements, false, kind); 2415 } 2416 2417 if (!gz.is_comptime) { 2418 // Since this block is unlabeled, its control flow is effectively linear and we 2419 // can *almost* get away with inlining the block here. However, we actually need 2420 // to preserve the .block for Sema, to properly pop the error return trace. 2421 2422 const block_tag: Zir.Inst.Tag = .block; 2423 const block_inst = try gz.makeBlockInst(block_tag, block_node); 2424 try gz.instructions.append(astgen.gpa, block_inst); 2425 2426 var block_scope = gz.makeSubBlock(scope); 2427 defer block_scope.unstack(); 2428 2429 try blockExprStmts(&block_scope, &block_scope.base, statements, kind); 2430 2431 if (!block_scope.endsWithNoReturn()) { 2432 // As our last action before the break, "pop" the error trace if needed 2433 _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node); 2434 _ = try block_scope.addBreak(.@"break", block_inst, .void_value); 2435 } 2436 2437 try block_scope.setBlockBody(block_inst); 2438 } else { 2439 var sub_gz = gz.makeSubBlock(scope); 2440 try blockExprStmts(&sub_gz, &sub_gz.base, statements, kind); 2441 } 2442 2443 return rvalue(gz, ri, .void_value, block_node); 2444 } 2445 2446 fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.TokenIndex) !void { 2447 // Look for the label in the scope. 2448 var scope = parent_scope; 2449 while (true) { 2450 switch (scope.tag) { 2451 .gen_zir => { 2452 const gen_zir = scope.cast(GenZir).?; 2453 if (gen_zir.label) |prev_label| { 2454 if (try astgen.tokenIdentEql(label, prev_label.token)) { 2455 const label_name = try astgen.identifierTokenString(label); 2456 return astgen.failTokNotes(label, "redefinition of label '{s}'", .{ 2457 label_name, 2458 }, &[_]u32{ 2459 try astgen.errNoteTok( 2460 prev_label.token, 2461 "previous definition here", 2462 .{}, 2463 ), 2464 }); 2465 } 2466 } 2467 scope = gen_zir.parent; 2468 }, 2469 .local_val => scope = scope.cast(Scope.LocalVal).?.parent, 2470 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, 2471 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, 2472 .namespace => break, 2473 .top => unreachable, 2474 } 2475 } 2476 } 2477 2478 fn labeledBlockExpr( 2479 gz: *GenZir, 2480 parent_scope: *Scope, 2481 ri: ResultInfo, 2482 block_node: Ast.Node.Index, 2483 statements: []const Ast.Node.Index, 2484 force_comptime: bool, 2485 block_kind: BlockKind, 2486 ) InnerError!Zir.Inst.Ref { 2487 const astgen = gz.astgen; 2488 const tree = astgen.tree; 2489 const main_tokens = tree.nodes.items(.main_token); 2490 const token_tags = tree.tokens.items(.tag); 2491 2492 const lbrace = main_tokens[block_node]; 2493 const label_token = lbrace - 2; 2494 assert(token_tags[label_token] == .identifier); 2495 2496 try astgen.checkLabelRedefinition(parent_scope, label_token); 2497 2498 const need_rl = astgen.nodes_need_rl.contains(block_node); 2499 const block_ri: ResultInfo = if (need_rl) ri else .{ 2500 .rl = switch (ri.rl) { 2501 .ptr => .{ .ty = (try ri.rl.resultType(gz, block_node)).? }, 2502 .inferred_ptr => .none, 2503 else => ri.rl, 2504 }, 2505 .ctx = ri.ctx, 2506 }; 2507 // We need to call `rvalue` to write through to the pointer only if we had a 2508 // result pointer and aren't forwarding it. 2509 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 2510 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 2511 2512 // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct 2513 // so that break statements can reference it. 2514 const block_tag: Zir.Inst.Tag = if (force_comptime) .block_comptime else .block; 2515 const block_inst = try gz.makeBlockInst(block_tag, block_node); 2516 try gz.instructions.append(astgen.gpa, block_inst); 2517 var block_scope = gz.makeSubBlock(parent_scope); 2518 block_scope.label = GenZir.Label{ 2519 .token = label_token, 2520 .block_inst = block_inst, 2521 }; 2522 block_scope.setBreakResultInfo(block_ri); 2523 if (force_comptime) block_scope.is_comptime = true; 2524 defer block_scope.unstack(); 2525 2526 try blockExprStmts(&block_scope, &block_scope.base, statements, block_kind); 2527 if (!block_scope.endsWithNoReturn()) { 2528 // As our last action before the return, "pop" the error trace if needed 2529 _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node); 2530 _ = try block_scope.addBreak(.@"break", block_inst, .void_value); 2531 } 2532 2533 if (!block_scope.label.?.used) { 2534 try astgen.appendErrorTok(label_token, "unused block label", .{}); 2535 } 2536 2537 try block_scope.setBlockBody(block_inst); 2538 if (need_result_rvalue) { 2539 return rvalue(gz, ri, block_inst.toRef(), block_node); 2540 } else { 2541 return block_inst.toRef(); 2542 } 2543 } 2544 2545 fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Node.Index, block_kind: BlockKind) !void { 2546 const astgen = gz.astgen; 2547 const tree = astgen.tree; 2548 const node_tags = tree.nodes.items(.tag); 2549 const node_data = tree.nodes.items(.data); 2550 2551 if (statements.len == 0) return; 2552 2553 var block_arena = std.heap.ArenaAllocator.init(gz.astgen.gpa); 2554 defer block_arena.deinit(); 2555 const block_arena_allocator = block_arena.allocator(); 2556 2557 var noreturn_src_node: Ast.Node.Index = 0; 2558 var scope = parent_scope; 2559 for (statements, 0..) |statement, stmt_idx| { 2560 if (noreturn_src_node != 0) { 2561 try astgen.appendErrorNodeNotes( 2562 statement, 2563 "unreachable code", 2564 .{}, 2565 &[_]u32{ 2566 try astgen.errNoteNode( 2567 noreturn_src_node, 2568 "control flow is diverted here", 2569 .{}, 2570 ), 2571 }, 2572 ); 2573 } 2574 const allow_branch_hint = switch (block_kind) { 2575 .normal => false, 2576 .allow_branch_hint => stmt_idx == 0, 2577 }; 2578 var inner_node = statement; 2579 while (true) { 2580 switch (node_tags[inner_node]) { 2581 // zig fmt: off 2582 .global_var_decl, 2583 .local_var_decl, 2584 .simple_var_decl, 2585 .aligned_var_decl, => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.fullVarDecl(statement).?), 2586 2587 .assign_destructure => scope = try assignDestructureMaybeDecls(gz, scope, statement, block_arena_allocator), 2588 2589 .@"defer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal), 2590 .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error), 2591 2592 .assign => try assign(gz, scope, statement), 2593 2594 .assign_shl => try assignShift(gz, scope, statement, .shl), 2595 .assign_shr => try assignShift(gz, scope, statement, .shr), 2596 2597 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and), 2598 .assign_bit_or => try assignOp(gz, scope, statement, .bit_or), 2599 .assign_bit_xor => try assignOp(gz, scope, statement, .xor), 2600 .assign_div => try assignOp(gz, scope, statement, .div), 2601 .assign_sub => try assignOp(gz, scope, statement, .sub), 2602 .assign_sub_wrap => try assignOp(gz, scope, statement, .subwrap), 2603 .assign_mod => try assignOp(gz, scope, statement, .mod_rem), 2604 .assign_add => try assignOp(gz, scope, statement, .add), 2605 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap), 2606 .assign_mul => try assignOp(gz, scope, statement, .mul), 2607 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap), 2608 2609 .grouped_expression => { 2610 inner_node = node_data[statement].lhs; 2611 continue; 2612 }, 2613 2614 .while_simple, 2615 .while_cont, 2616 .@"while", => _ = try whileExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullWhile(inner_node).?, true), 2617 2618 .for_simple, 2619 .@"for", => _ = try forExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullFor(inner_node).?, true), 2620 2621 // These cases are here to allow branch hints. 2622 .builtin_call_two, .builtin_call_two_comma => { 2623 try emitDbgNode(gz, inner_node); 2624 const ri: ResultInfo = .{ .rl = .none }; 2625 const result = if (node_data[inner_node].lhs == 0) r: { 2626 break :r try builtinCall(gz, scope, ri, inner_node, &.{}, allow_branch_hint); 2627 } else if (node_data[inner_node].rhs == 0) r: { 2628 break :r try builtinCall(gz, scope, ri, inner_node, &.{node_data[inner_node].lhs}, allow_branch_hint); 2629 } else r: { 2630 break :r try builtinCall(gz, scope, ri, inner_node, &.{ 2631 node_data[inner_node].lhs, 2632 node_data[inner_node].rhs, 2633 }, allow_branch_hint); 2634 }; 2635 noreturn_src_node = try addEnsureResult(gz, result, inner_node); 2636 }, 2637 .builtin_call, .builtin_call_comma => { 2638 try emitDbgNode(gz, inner_node); 2639 const ri: ResultInfo = .{ .rl = .none }; 2640 const params = tree.extra_data[node_data[inner_node].lhs..node_data[inner_node].rhs]; 2641 const result = try builtinCall(gz, scope, ri, inner_node, params, allow_branch_hint); 2642 noreturn_src_node = try addEnsureResult(gz, result, inner_node); 2643 }, 2644 2645 else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node), 2646 // zig fmt: on 2647 } 2648 break; 2649 } 2650 } 2651 2652 if (noreturn_src_node == 0) { 2653 try genDefers(gz, parent_scope, scope, .normal_only); 2654 } 2655 try checkUsed(gz, parent_scope, scope); 2656 } 2657 2658 /// Returns AST source node of the thing that is noreturn if the statement is 2659 /// definitely `noreturn`. Otherwise returns 0. 2660 fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.Index { 2661 try emitDbgNode(gz, statement); 2662 // We need to emit an error if the result is not `noreturn` or `void`, but 2663 // we want to avoid adding the ZIR instruction if possible for performance. 2664 const maybe_unused_result = try expr(gz, scope, .{ .rl = .none }, statement); 2665 return addEnsureResult(gz, maybe_unused_result, statement); 2666 } 2667 2668 fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: Ast.Node.Index) InnerError!Ast.Node.Index { 2669 var noreturn_src_node: Ast.Node.Index = 0; 2670 const elide_check = if (maybe_unused_result.toIndex()) |inst| b: { 2671 // Note that this array becomes invalid after appending more items to it 2672 // in the above while loop. 2673 const zir_tags = gz.astgen.instructions.items(.tag); 2674 switch (zir_tags[@intFromEnum(inst)]) { 2675 // For some instructions, modify the zir data 2676 // so we can avoid a separate ensure_result_used instruction. 2677 .call, .field_call => { 2678 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index; 2679 comptime assert(std.meta.fieldIndex(Zir.Inst.Call, "flags") == 2680 std.meta.fieldIndex(Zir.Inst.FieldCall, "flags")); 2681 const flags: *Zir.Inst.Call.Flags = @ptrCast(&gz.astgen.extra.items[ 2682 break_extra + std.meta.fieldIndex(Zir.Inst.Call, "flags").? 2683 ]); 2684 flags.ensure_result_used = true; 2685 break :b true; 2686 }, 2687 .builtin_call => { 2688 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index; 2689 const flags: *Zir.Inst.BuiltinCall.Flags = @ptrCast(&gz.astgen.extra.items[ 2690 break_extra + std.meta.fieldIndex(Zir.Inst.BuiltinCall, "flags").? 2691 ]); 2692 flags.ensure_result_used = true; 2693 break :b true; 2694 }, 2695 2696 // ZIR instructions that might be a type other than `noreturn` or `void`. 2697 .add, 2698 .addwrap, 2699 .add_sat, 2700 .add_unsafe, 2701 .param, 2702 .param_comptime, 2703 .param_anytype, 2704 .param_anytype_comptime, 2705 .alloc, 2706 .alloc_mut, 2707 .alloc_comptime_mut, 2708 .alloc_inferred, 2709 .alloc_inferred_mut, 2710 .alloc_inferred_comptime, 2711 .alloc_inferred_comptime_mut, 2712 .make_ptr_const, 2713 .array_cat, 2714 .array_mul, 2715 .array_type, 2716 .array_type_sentinel, 2717 .elem_type, 2718 .indexable_ptr_elem_type, 2719 .vec_arr_elem_type, 2720 .vector_type, 2721 .indexable_ptr_len, 2722 .anyframe_type, 2723 .as_node, 2724 .as_shift_operand, 2725 .bit_and, 2726 .bitcast, 2727 .bit_or, 2728 .block, 2729 .block_comptime, 2730 .block_inline, 2731 .declaration, 2732 .suspend_block, 2733 .loop, 2734 .bool_br_and, 2735 .bool_br_or, 2736 .bool_not, 2737 .cmp_lt, 2738 .cmp_lte, 2739 .cmp_eq, 2740 .cmp_gte, 2741 .cmp_gt, 2742 .cmp_neq, 2743 .decl_ref, 2744 .decl_val, 2745 .load, 2746 .div, 2747 .elem_ptr, 2748 .elem_val, 2749 .elem_ptr_node, 2750 .elem_val_node, 2751 .elem_val_imm, 2752 .field_ptr, 2753 .field_val, 2754 .field_ptr_named, 2755 .field_val_named, 2756 .func, 2757 .func_inferred, 2758 .func_fancy, 2759 .int, 2760 .int_big, 2761 .float, 2762 .float128, 2763 .int_type, 2764 .is_non_null, 2765 .is_non_null_ptr, 2766 .is_non_err, 2767 .is_non_err_ptr, 2768 .ret_is_non_err, 2769 .mod_rem, 2770 .mul, 2771 .mulwrap, 2772 .mul_sat, 2773 .ref, 2774 .shl, 2775 .shl_sat, 2776 .shr, 2777 .str, 2778 .sub, 2779 .subwrap, 2780 .sub_sat, 2781 .negate, 2782 .negate_wrap, 2783 .typeof, 2784 .typeof_builtin, 2785 .xor, 2786 .optional_type, 2787 .optional_payload_safe, 2788 .optional_payload_unsafe, 2789 .optional_payload_safe_ptr, 2790 .optional_payload_unsafe_ptr, 2791 .err_union_payload_unsafe, 2792 .err_union_payload_unsafe_ptr, 2793 .err_union_code, 2794 .err_union_code_ptr, 2795 .ptr_type, 2796 .enum_literal, 2797 .decl_literal, 2798 .decl_literal_no_coerce, 2799 .merge_error_sets, 2800 .error_union_type, 2801 .bit_not, 2802 .error_value, 2803 .slice_start, 2804 .slice_end, 2805 .slice_sentinel, 2806 .slice_length, 2807 .import, 2808 .switch_block, 2809 .switch_block_ref, 2810 .switch_block_err_union, 2811 .union_init, 2812 .field_type_ref, 2813 .error_set_decl, 2814 .enum_from_int, 2815 .int_from_enum, 2816 .type_info, 2817 .size_of, 2818 .bit_size_of, 2819 .typeof_log2_int_type, 2820 .int_from_ptr, 2821 .align_of, 2822 .int_from_bool, 2823 .embed_file, 2824 .error_name, 2825 .sqrt, 2826 .sin, 2827 .cos, 2828 .tan, 2829 .exp, 2830 .exp2, 2831 .log, 2832 .log2, 2833 .log10, 2834 .abs, 2835 .floor, 2836 .ceil, 2837 .trunc, 2838 .round, 2839 .tag_name, 2840 .type_name, 2841 .frame_type, 2842 .frame_size, 2843 .int_from_float, 2844 .float_from_int, 2845 .ptr_from_int, 2846 .float_cast, 2847 .int_cast, 2848 .ptr_cast, 2849 .truncate, 2850 .has_decl, 2851 .has_field, 2852 .clz, 2853 .ctz, 2854 .pop_count, 2855 .byte_swap, 2856 .bit_reverse, 2857 .div_exact, 2858 .div_floor, 2859 .div_trunc, 2860 .mod, 2861 .rem, 2862 .shl_exact, 2863 .shr_exact, 2864 .bit_offset_of, 2865 .offset_of, 2866 .splat, 2867 .reduce, 2868 .shuffle, 2869 .atomic_load, 2870 .atomic_rmw, 2871 .mul_add, 2872 .max, 2873 .min, 2874 .c_import, 2875 .@"resume", 2876 .@"await", 2877 .ret_err_value_code, 2878 .ret_ptr, 2879 .ret_type, 2880 .for_len, 2881 .@"try", 2882 .try_ptr, 2883 .opt_eu_base_ptr_init, 2884 .coerce_ptr_elem_ty, 2885 .struct_init_empty, 2886 .struct_init_empty_result, 2887 .struct_init_empty_ref_result, 2888 .struct_init_anon, 2889 .struct_init, 2890 .struct_init_ref, 2891 .struct_init_field_type, 2892 .struct_init_field_ptr, 2893 .array_init_anon, 2894 .array_init, 2895 .array_init_ref, 2896 .validate_array_init_ref_ty, 2897 .array_init_elem_type, 2898 .array_init_elem_ptr, 2899 => break :b false, 2900 2901 .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) { 2902 .breakpoint, 2903 .disable_instrumentation, 2904 .set_float_mode, 2905 .set_align_stack, 2906 .branch_hint, 2907 => break :b true, 2908 else => break :b false, 2909 }, 2910 2911 // ZIR instructions that are always `noreturn`. 2912 .@"break", 2913 .break_inline, 2914 .condbr, 2915 .condbr_inline, 2916 .compile_error, 2917 .ret_node, 2918 .ret_load, 2919 .ret_implicit, 2920 .ret_err_value, 2921 .@"unreachable", 2922 .repeat, 2923 .repeat_inline, 2924 .panic, 2925 .trap, 2926 .check_comptime_control_flow, 2927 .switch_continue, 2928 => { 2929 noreturn_src_node = statement; 2930 break :b true; 2931 }, 2932 2933 // ZIR instructions that are always `void`. 2934 .dbg_stmt, 2935 .dbg_var_ptr, 2936 .dbg_var_val, 2937 .ensure_result_used, 2938 .ensure_result_non_error, 2939 .ensure_err_union_payload_void, 2940 .@"export", 2941 .set_eval_branch_quota, 2942 .atomic_store, 2943 .store_node, 2944 .store_to_inferred_ptr, 2945 .resolve_inferred_alloc, 2946 .set_runtime_safety, 2947 .memcpy, 2948 .memset, 2949 .validate_deref, 2950 .validate_destructure, 2951 .save_err_ret_index, 2952 .restore_err_ret_index_unconditional, 2953 .restore_err_ret_index_fn_entry, 2954 .validate_struct_init_ty, 2955 .validate_struct_init_result_ty, 2956 .validate_ptr_struct_init, 2957 .validate_array_init_ty, 2958 .validate_array_init_result_ty, 2959 .validate_ptr_array_init, 2960 .validate_ref_ty, 2961 .try_operand_ty, 2962 .try_ref_operand_ty, 2963 => break :b true, 2964 2965 .@"defer" => unreachable, 2966 .defer_err_code => unreachable, 2967 } 2968 } else switch (maybe_unused_result) { 2969 .none => unreachable, 2970 2971 .unreachable_value => b: { 2972 noreturn_src_node = statement; 2973 break :b true; 2974 }, 2975 2976 .void_value => true, 2977 2978 else => false, 2979 }; 2980 if (!elide_check) { 2981 _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement); 2982 } 2983 return noreturn_src_node; 2984 } 2985 2986 fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct { 2987 have_any: bool, 2988 have_normal: bool, 2989 have_err: bool, 2990 need_err_code: bool, 2991 } { 2992 var have_normal = false; 2993 var have_err = false; 2994 var need_err_code = false; 2995 var scope = inner_scope; 2996 while (scope != outer_scope) { 2997 switch (scope.tag) { 2998 .gen_zir => scope = scope.cast(GenZir).?.parent, 2999 .local_val => scope = scope.cast(Scope.LocalVal).?.parent, 3000 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, 3001 .defer_normal => { 3002 const defer_scope = scope.cast(Scope.Defer).?; 3003 scope = defer_scope.parent; 3004 3005 have_normal = true; 3006 }, 3007 .defer_error => { 3008 const defer_scope = scope.cast(Scope.Defer).?; 3009 scope = defer_scope.parent; 3010 3011 have_err = true; 3012 3013 const have_err_payload = defer_scope.remapped_err_code != .none; 3014 need_err_code = need_err_code or have_err_payload; 3015 }, 3016 .namespace => unreachable, 3017 .top => unreachable, 3018 } 3019 } 3020 return .{ 3021 .have_any = have_normal or have_err, 3022 .have_normal = have_normal, 3023 .have_err = have_err, 3024 .need_err_code = need_err_code, 3025 }; 3026 } 3027 3028 const DefersToEmit = union(enum) { 3029 both: Zir.Inst.Ref, // err code 3030 both_sans_err, 3031 normal_only, 3032 }; 3033 3034 fn genDefers( 3035 gz: *GenZir, 3036 outer_scope: *Scope, 3037 inner_scope: *Scope, 3038 which_ones: DefersToEmit, 3039 ) InnerError!void { 3040 const gpa = gz.astgen.gpa; 3041 3042 var scope = inner_scope; 3043 while (scope != outer_scope) { 3044 switch (scope.tag) { 3045 .gen_zir => scope = scope.cast(GenZir).?.parent, 3046 .local_val => scope = scope.cast(Scope.LocalVal).?.parent, 3047 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, 3048 .defer_normal => { 3049 const defer_scope = scope.cast(Scope.Defer).?; 3050 scope = defer_scope.parent; 3051 try gz.addDefer(defer_scope.index, defer_scope.len); 3052 }, 3053 .defer_error => { 3054 const defer_scope = scope.cast(Scope.Defer).?; 3055 scope = defer_scope.parent; 3056 switch (which_ones) { 3057 .both_sans_err => { 3058 try gz.addDefer(defer_scope.index, defer_scope.len); 3059 }, 3060 .both => |err_code| { 3061 if (defer_scope.remapped_err_code.unwrap()) |remapped_err_code| { 3062 try gz.instructions.ensureUnusedCapacity(gpa, 1); 3063 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 3064 3065 const payload_index = try gz.astgen.addExtra(Zir.Inst.DeferErrCode{ 3066 .remapped_err_code = remapped_err_code, 3067 .index = defer_scope.index, 3068 .len = defer_scope.len, 3069 }); 3070 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 3071 gz.astgen.instructions.appendAssumeCapacity(.{ 3072 .tag = .defer_err_code, 3073 .data = .{ .defer_err_code = .{ 3074 .err_code = err_code, 3075 .payload_index = payload_index, 3076 } }, 3077 }); 3078 gz.instructions.appendAssumeCapacity(new_index); 3079 } else { 3080 try gz.addDefer(defer_scope.index, defer_scope.len); 3081 } 3082 }, 3083 .normal_only => continue, 3084 } 3085 }, 3086 .namespace => unreachable, 3087 .top => unreachable, 3088 } 3089 } 3090 } 3091 3092 fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!void { 3093 const astgen = gz.astgen; 3094 3095 var scope = inner_scope; 3096 while (scope != outer_scope) { 3097 switch (scope.tag) { 3098 .gen_zir => scope = scope.cast(GenZir).?.parent, 3099 .local_val => { 3100 const s = scope.cast(Scope.LocalVal).?; 3101 if (s.used == 0 and s.discarded == 0) { 3102 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)}); 3103 } else if (s.used != 0 and s.discarded != 0) { 3104 try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{ 3105 try gz.astgen.errNoteTok(s.used, "used here", .{}), 3106 }); 3107 } 3108 scope = s.parent; 3109 }, 3110 .local_ptr => { 3111 const s = scope.cast(Scope.LocalPtr).?; 3112 if (s.used == 0 and s.discarded == 0) { 3113 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)}); 3114 } else { 3115 if (s.used != 0 and s.discarded != 0) { 3116 try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{ 3117 try astgen.errNoteTok(s.used, "used here", .{}), 3118 }); 3119 } 3120 if (s.id_cat == .@"local variable" and !s.used_as_lvalue) { 3121 try astgen.appendErrorTokNotes(s.token_src, "local variable is never mutated", .{}, &.{ 3122 try astgen.errNoteTok(s.token_src, "consider using 'const'", .{}), 3123 }); 3124 } 3125 } 3126 3127 scope = s.parent; 3128 }, 3129 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, 3130 .namespace => unreachable, 3131 .top => unreachable, 3132 } 3133 } 3134 } 3135 3136 fn deferStmt( 3137 gz: *GenZir, 3138 scope: *Scope, 3139 node: Ast.Node.Index, 3140 block_arena: Allocator, 3141 scope_tag: Scope.Tag, 3142 ) InnerError!*Scope { 3143 var defer_gen = gz.makeSubBlock(scope); 3144 defer_gen.cur_defer_node = node; 3145 defer_gen.any_defer_node = node; 3146 defer defer_gen.unstack(); 3147 3148 const tree = gz.astgen.tree; 3149 const node_datas = tree.nodes.items(.data); 3150 const expr_node = node_datas[node].rhs; 3151 3152 const payload_token = node_datas[node].lhs; 3153 var local_val_scope: Scope.LocalVal = undefined; 3154 var opt_remapped_err_code: Zir.Inst.OptionalIndex = .none; 3155 const have_err_code = scope_tag == .defer_error and payload_token != 0; 3156 const sub_scope = if (!have_err_code) &defer_gen.base else blk: { 3157 const ident_name = try gz.astgen.identAsString(payload_token); 3158 if (std.mem.eql(u8, tree.tokenSlice(payload_token), "_")) { 3159 return gz.astgen.failTok(payload_token, "discard of error capture; omit it instead", .{}); 3160 } 3161 const remapped_err_code: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 3162 opt_remapped_err_code = remapped_err_code.toOptional(); 3163 try gz.astgen.instructions.append(gz.astgen.gpa, .{ 3164 .tag = .extended, 3165 .data = .{ .extended = .{ 3166 .opcode = .value_placeholder, 3167 .small = undefined, 3168 .operand = undefined, 3169 } }, 3170 }); 3171 const remapped_err_code_ref = remapped_err_code.toRef(); 3172 local_val_scope = .{ 3173 .parent = &defer_gen.base, 3174 .gen_zir = gz, 3175 .name = ident_name, 3176 .inst = remapped_err_code_ref, 3177 .token_src = payload_token, 3178 .id_cat = .capture, 3179 }; 3180 try gz.addDbgVar(.dbg_var_val, ident_name, remapped_err_code_ref); 3181 break :blk &local_val_scope.base; 3182 }; 3183 _ = try unusedResultExpr(&defer_gen, sub_scope, expr_node); 3184 try checkUsed(gz, scope, sub_scope); 3185 _ = try defer_gen.addBreak(.break_inline, @enumFromInt(0), .void_value); 3186 3187 // We must handle ref_table for remapped_err_code manually. 3188 const body = defer_gen.instructionsSlice(); 3189 const body_len = blk: { 3190 var refs: u32 = 0; 3191 if (opt_remapped_err_code.unwrap()) |remapped_err_code| { 3192 var cur_inst = remapped_err_code; 3193 while (gz.astgen.ref_table.get(cur_inst)) |ref_inst| { 3194 refs += 1; 3195 cur_inst = ref_inst; 3196 } 3197 } 3198 break :blk gz.astgen.countBodyLenAfterFixups(body) + refs; 3199 }; 3200 3201 const index: u32 = @intCast(gz.astgen.extra.items.len); 3202 try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len); 3203 if (opt_remapped_err_code.unwrap()) |remapped_err_code| { 3204 if (gz.astgen.ref_table.fetchRemove(remapped_err_code)) |kv| { 3205 gz.astgen.appendPossiblyRefdBodyInst(&gz.astgen.extra, kv.value); 3206 } 3207 } 3208 gz.astgen.appendBodyWithFixups(body); 3209 3210 const defer_scope = try block_arena.create(Scope.Defer); 3211 3212 defer_scope.* = .{ 3213 .base = .{ .tag = scope_tag }, 3214 .parent = scope, 3215 .index = index, 3216 .len = body_len, 3217 .remapped_err_code = opt_remapped_err_code, 3218 }; 3219 return &defer_scope.base; 3220 } 3221 3222 fn varDecl( 3223 gz: *GenZir, 3224 scope: *Scope, 3225 node: Ast.Node.Index, 3226 block_arena: Allocator, 3227 var_decl: Ast.full.VarDecl, 3228 ) InnerError!*Scope { 3229 try emitDbgNode(gz, node); 3230 const astgen = gz.astgen; 3231 const tree = astgen.tree; 3232 const token_tags = tree.tokens.items(.tag); 3233 const main_tokens = tree.nodes.items(.main_token); 3234 3235 const name_token = var_decl.ast.mut_token + 1; 3236 const ident_name_raw = tree.tokenSlice(name_token); 3237 if (mem.eql(u8, ident_name_raw, "_")) { 3238 return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{}); 3239 } 3240 const ident_name = try astgen.identAsString(name_token); 3241 3242 try astgen.detectLocalShadowing( 3243 scope, 3244 ident_name, 3245 name_token, 3246 ident_name_raw, 3247 if (token_tags[var_decl.ast.mut_token] == .keyword_const) .@"local constant" else .@"local variable", 3248 ); 3249 3250 if (var_decl.ast.init_node == 0) { 3251 return astgen.failNode(node, "variables must be initialized", .{}); 3252 } 3253 3254 if (var_decl.ast.addrspace_node != 0) { 3255 return astgen.failTok(main_tokens[var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw}); 3256 } 3257 3258 if (var_decl.ast.section_node != 0) { 3259 return astgen.failTok(main_tokens[var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw}); 3260 } 3261 3262 const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0) 3263 try expr(gz, scope, coerced_align_ri, var_decl.ast.align_node) 3264 else 3265 .none; 3266 3267 switch (token_tags[var_decl.ast.mut_token]) { 3268 .keyword_const => { 3269 if (var_decl.comptime_token) |comptime_token| { 3270 try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{}); 3271 } 3272 3273 // Depending on the type of AST the initialization expression is, we may need an lvalue 3274 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as 3275 // the variable, no memory location needed. 3276 const type_node = var_decl.ast.type_node; 3277 if (align_inst == .none and 3278 !astgen.nodes_need_rl.contains(node)) 3279 { 3280 const result_info: ResultInfo = if (type_node != 0) .{ 3281 .rl = .{ .ty = try typeExpr(gz, scope, type_node) }, 3282 .ctx = .const_init, 3283 } else .{ .rl = .none, .ctx = .const_init }; 3284 const prev_anon_name_strategy = gz.anon_name_strategy; 3285 gz.anon_name_strategy = .dbg_var; 3286 const init_inst = try reachableExpr(gz, scope, result_info, var_decl.ast.init_node, node); 3287 gz.anon_name_strategy = prev_anon_name_strategy; 3288 3289 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst); 3290 3291 // The const init expression may have modified the error return trace, so signal 3292 // to Sema that it should save the new index for restoring later. 3293 if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node)) 3294 _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst }); 3295 3296 const sub_scope = try block_arena.create(Scope.LocalVal); 3297 sub_scope.* = .{ 3298 .parent = scope, 3299 .gen_zir = gz, 3300 .name = ident_name, 3301 .inst = init_inst, 3302 .token_src = name_token, 3303 .id_cat = .@"local constant", 3304 }; 3305 return &sub_scope.base; 3306 } 3307 3308 const is_comptime = gz.is_comptime or 3309 tree.nodes.items(.tag)[var_decl.ast.init_node] == .@"comptime"; 3310 3311 var resolve_inferred_alloc: Zir.Inst.Ref = .none; 3312 var opt_type_inst: Zir.Inst.Ref = .none; 3313 const init_rl: ResultInfo.Loc = if (type_node != 0) init_rl: { 3314 const type_inst = try typeExpr(gz, scope, type_node); 3315 opt_type_inst = type_inst; 3316 if (align_inst == .none) { 3317 break :init_rl .{ .ptr = .{ .inst = try gz.addUnNode(.alloc, type_inst, node) } }; 3318 } else { 3319 break :init_rl .{ .ptr = .{ .inst = try gz.addAllocExtended(.{ 3320 .node = node, 3321 .type_inst = type_inst, 3322 .align_inst = align_inst, 3323 .is_const = true, 3324 .is_comptime = is_comptime, 3325 }) } }; 3326 } 3327 } else init_rl: { 3328 const alloc_inst = if (align_inst == .none) ptr: { 3329 const tag: Zir.Inst.Tag = if (is_comptime) 3330 .alloc_inferred_comptime 3331 else 3332 .alloc_inferred; 3333 break :ptr try gz.addNode(tag, node); 3334 } else ptr: { 3335 break :ptr try gz.addAllocExtended(.{ 3336 .node = node, 3337 .type_inst = .none, 3338 .align_inst = align_inst, 3339 .is_const = true, 3340 .is_comptime = is_comptime, 3341 }); 3342 }; 3343 resolve_inferred_alloc = alloc_inst; 3344 break :init_rl .{ .inferred_ptr = alloc_inst }; 3345 }; 3346 const var_ptr = switch (init_rl) { 3347 .ptr => |ptr| ptr.inst, 3348 .inferred_ptr => |inst| inst, 3349 else => unreachable, 3350 }; 3351 const init_result_info: ResultInfo = .{ .rl = init_rl, .ctx = .const_init }; 3352 3353 const prev_anon_name_strategy = gz.anon_name_strategy; 3354 gz.anon_name_strategy = .dbg_var; 3355 defer gz.anon_name_strategy = prev_anon_name_strategy; 3356 const init_inst = try reachableExpr(gz, scope, init_result_info, var_decl.ast.init_node, node); 3357 3358 // The const init expression may have modified the error return trace, so signal 3359 // to Sema that it should save the new index for restoring later. 3360 if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node)) 3361 _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst }); 3362 3363 const const_ptr = if (resolve_inferred_alloc != .none) p: { 3364 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node); 3365 break :p var_ptr; 3366 } else try gz.addUnNode(.make_ptr_const, var_ptr, node); 3367 3368 try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr); 3369 3370 const sub_scope = try block_arena.create(Scope.LocalPtr); 3371 sub_scope.* = .{ 3372 .parent = scope, 3373 .gen_zir = gz, 3374 .name = ident_name, 3375 .ptr = const_ptr, 3376 .token_src = name_token, 3377 .maybe_comptime = true, 3378 .id_cat = .@"local constant", 3379 }; 3380 return &sub_scope.base; 3381 }, 3382 .keyword_var => { 3383 if (var_decl.comptime_token != null and gz.is_comptime) 3384 return astgen.failTok(var_decl.comptime_token.?, "'comptime var' is redundant in comptime scope", .{}); 3385 const is_comptime = var_decl.comptime_token != null or gz.is_comptime; 3386 var resolve_inferred_alloc: Zir.Inst.Ref = .none; 3387 const alloc: Zir.Inst.Ref, const result_info: ResultInfo = if (var_decl.ast.type_node != 0) a: { 3388 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node); 3389 const alloc = alloc: { 3390 if (align_inst == .none) { 3391 const tag: Zir.Inst.Tag = if (is_comptime) 3392 .alloc_comptime_mut 3393 else 3394 .alloc_mut; 3395 break :alloc try gz.addUnNode(tag, type_inst, node); 3396 } else { 3397 break :alloc try gz.addAllocExtended(.{ 3398 .node = node, 3399 .type_inst = type_inst, 3400 .align_inst = align_inst, 3401 .is_const = false, 3402 .is_comptime = is_comptime, 3403 }); 3404 } 3405 }; 3406 break :a .{ alloc, .{ .rl = .{ .ptr = .{ .inst = alloc } } } }; 3407 } else a: { 3408 const alloc = alloc: { 3409 if (align_inst == .none) { 3410 const tag: Zir.Inst.Tag = if (is_comptime) 3411 .alloc_inferred_comptime_mut 3412 else 3413 .alloc_inferred_mut; 3414 break :alloc try gz.addNode(tag, node); 3415 } else { 3416 break :alloc try gz.addAllocExtended(.{ 3417 .node = node, 3418 .type_inst = .none, 3419 .align_inst = align_inst, 3420 .is_const = false, 3421 .is_comptime = is_comptime, 3422 }); 3423 } 3424 }; 3425 resolve_inferred_alloc = alloc; 3426 break :a .{ alloc, .{ .rl = .{ .inferred_ptr = alloc } } }; 3427 }; 3428 const prev_anon_name_strategy = gz.anon_name_strategy; 3429 gz.anon_name_strategy = .dbg_var; 3430 _ = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, is_comptime); 3431 gz.anon_name_strategy = prev_anon_name_strategy; 3432 if (resolve_inferred_alloc != .none) { 3433 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node); 3434 } 3435 3436 try gz.addDbgVar(.dbg_var_ptr, ident_name, alloc); 3437 3438 const sub_scope = try block_arena.create(Scope.LocalPtr); 3439 sub_scope.* = .{ 3440 .parent = scope, 3441 .gen_zir = gz, 3442 .name = ident_name, 3443 .ptr = alloc, 3444 .token_src = name_token, 3445 .maybe_comptime = is_comptime, 3446 .id_cat = .@"local variable", 3447 }; 3448 return &sub_scope.base; 3449 }, 3450 else => unreachable, 3451 } 3452 } 3453 3454 fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void { 3455 // The instruction emitted here is for debugging runtime code. 3456 // If the current block will be evaluated only during semantic analysis 3457 // then no dbg_stmt ZIR instruction is needed. 3458 if (gz.is_comptime) return; 3459 const astgen = gz.astgen; 3460 astgen.advanceSourceCursorToNode(node); 3461 const line = astgen.source_line - gz.decl_line; 3462 const column = astgen.source_column; 3463 try emitDbgStmt(gz, .{ line, column }); 3464 } 3465 3466 fn assign(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void { 3467 try emitDbgNode(gz, infix_node); 3468 const astgen = gz.astgen; 3469 const tree = astgen.tree; 3470 const node_datas = tree.nodes.items(.data); 3471 const main_tokens = tree.nodes.items(.main_token); 3472 const node_tags = tree.nodes.items(.tag); 3473 3474 const lhs = node_datas[infix_node].lhs; 3475 const rhs = node_datas[infix_node].rhs; 3476 if (node_tags[lhs] == .identifier) { 3477 // This intentionally does not support `@"_"` syntax. 3478 const ident_name = tree.tokenSlice(main_tokens[lhs]); 3479 if (mem.eql(u8, ident_name, "_")) { 3480 _ = try expr(gz, scope, .{ .rl = .discard, .ctx = .assignment }, rhs); 3481 return; 3482 } 3483 } 3484 const lvalue = try lvalExpr(gz, scope, lhs); 3485 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ 3486 .inst = lvalue, 3487 .src_node = infix_node, 3488 } } }, rhs); 3489 } 3490 3491 /// Handles destructure assignments where no LHS is a `const` or `var` decl. 3492 fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!void { 3493 try emitDbgNode(gz, node); 3494 const astgen = gz.astgen; 3495 const tree = astgen.tree; 3496 const main_tokens = tree.nodes.items(.main_token); 3497 const node_tags = tree.nodes.items(.tag); 3498 3499 const full = tree.assignDestructure(node); 3500 if (full.comptime_token != null and gz.is_comptime) { 3501 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{}); 3502 } 3503 3504 // If this expression is marked comptime, we must wrap the whole thing in a comptime block. 3505 var gz_buf: GenZir = undefined; 3506 const inner_gz = if (full.comptime_token) |_| bs: { 3507 gz_buf = gz.makeSubBlock(scope); 3508 gz_buf.is_comptime = true; 3509 break :bs &gz_buf; 3510 } else gz; 3511 defer if (full.comptime_token) |_| inner_gz.unstack(); 3512 3513 const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len); 3514 for (rl_components, full.ast.variables) |*variable_rl, variable_node| { 3515 if (node_tags[variable_node] == .identifier) { 3516 // This intentionally does not support `@"_"` syntax. 3517 const ident_name = tree.tokenSlice(main_tokens[variable_node]); 3518 if (mem.eql(u8, ident_name, "_")) { 3519 variable_rl.* = .discard; 3520 continue; 3521 } 3522 } 3523 variable_rl.* = .{ .typed_ptr = .{ 3524 .inst = try lvalExpr(inner_gz, scope, variable_node), 3525 .src_node = variable_node, 3526 } }; 3527 } 3528 3529 const ri: ResultInfo = .{ .rl = .{ .destructure = .{ 3530 .src_node = node, 3531 .components = rl_components, 3532 } } }; 3533 3534 _ = try expr(inner_gz, scope, ri, full.ast.value_expr); 3535 3536 if (full.comptime_token) |_| { 3537 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node); 3538 _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value); 3539 try inner_gz.setBlockBody(comptime_block_inst); 3540 try gz.instructions.append(gz.astgen.gpa, comptime_block_inst); 3541 } 3542 } 3543 3544 /// Handles destructure assignments where the LHS may contain `const` or `var` decls. 3545 fn assignDestructureMaybeDecls( 3546 gz: *GenZir, 3547 scope: *Scope, 3548 node: Ast.Node.Index, 3549 block_arena: Allocator, 3550 ) InnerError!*Scope { 3551 try emitDbgNode(gz, node); 3552 const astgen = gz.astgen; 3553 const tree = astgen.tree; 3554 const token_tags = tree.tokens.items(.tag); 3555 const main_tokens = tree.nodes.items(.main_token); 3556 const node_tags = tree.nodes.items(.tag); 3557 3558 const full = tree.assignDestructure(node); 3559 if (full.comptime_token != null and gz.is_comptime) { 3560 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{}); 3561 } 3562 3563 const is_comptime = full.comptime_token != null or gz.is_comptime; 3564 const value_is_comptime = node_tags[full.ast.value_expr] == .@"comptime"; 3565 3566 // When declaring consts via a destructure, we always use a result pointer. 3567 // This avoids the need to create tuple types, and is also likely easier to 3568 // optimize, since it's a bit tricky for the optimizer to "split up" the 3569 // value into individual pointer writes down the line. 3570 3571 // We know this rl information won't live past the evaluation of this 3572 // expression, so it may as well go in the block arena. 3573 const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len); 3574 var any_non_const_variables = false; 3575 var any_lvalue_expr = false; 3576 for (rl_components, full.ast.variables) |*variable_rl, variable_node| { 3577 switch (node_tags[variable_node]) { 3578 .identifier => { 3579 // This intentionally does not support `@"_"` syntax. 3580 const ident_name = tree.tokenSlice(main_tokens[variable_node]); 3581 if (mem.eql(u8, ident_name, "_")) { 3582 any_non_const_variables = true; 3583 variable_rl.* = .discard; 3584 continue; 3585 } 3586 }, 3587 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => { 3588 const full_var_decl = tree.fullVarDecl(variable_node).?; 3589 3590 const name_token = full_var_decl.ast.mut_token + 1; 3591 const ident_name_raw = tree.tokenSlice(name_token); 3592 if (mem.eql(u8, ident_name_raw, "_")) { 3593 return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{}); 3594 } 3595 3596 // We detect shadowing in the second pass over these, while we're creating scopes. 3597 3598 if (full_var_decl.ast.addrspace_node != 0) { 3599 return astgen.failTok(main_tokens[full_var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw}); 3600 } 3601 if (full_var_decl.ast.section_node != 0) { 3602 return astgen.failTok(main_tokens[full_var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw}); 3603 } 3604 3605 const is_const = switch (token_tags[full_var_decl.ast.mut_token]) { 3606 .keyword_var => false, 3607 .keyword_const => true, 3608 else => unreachable, 3609 }; 3610 if (!is_const) any_non_const_variables = true; 3611 3612 // We also mark `const`s as comptime if the RHS is definitely comptime-known. 3613 const this_variable_comptime = is_comptime or (is_const and value_is_comptime); 3614 3615 const align_inst: Zir.Inst.Ref = if (full_var_decl.ast.align_node != 0) 3616 try expr(gz, scope, coerced_align_ri, full_var_decl.ast.align_node) 3617 else 3618 .none; 3619 3620 if (full_var_decl.ast.type_node != 0) { 3621 // Typed alloc 3622 const type_inst = try typeExpr(gz, scope, full_var_decl.ast.type_node); 3623 const ptr = if (align_inst == .none) ptr: { 3624 const tag: Zir.Inst.Tag = if (is_const) 3625 .alloc 3626 else if (this_variable_comptime) 3627 .alloc_comptime_mut 3628 else 3629 .alloc_mut; 3630 break :ptr try gz.addUnNode(tag, type_inst, node); 3631 } else try gz.addAllocExtended(.{ 3632 .node = node, 3633 .type_inst = type_inst, 3634 .align_inst = align_inst, 3635 .is_const = is_const, 3636 .is_comptime = this_variable_comptime, 3637 }); 3638 variable_rl.* = .{ .typed_ptr = .{ .inst = ptr } }; 3639 } else { 3640 // Inferred alloc 3641 const ptr = if (align_inst == .none) ptr: { 3642 const tag: Zir.Inst.Tag = if (is_const) tag: { 3643 break :tag if (this_variable_comptime) .alloc_inferred_comptime else .alloc_inferred; 3644 } else tag: { 3645 break :tag if (this_variable_comptime) .alloc_inferred_comptime_mut else .alloc_inferred_mut; 3646 }; 3647 break :ptr try gz.addNode(tag, node); 3648 } else try gz.addAllocExtended(.{ 3649 .node = node, 3650 .type_inst = .none, 3651 .align_inst = align_inst, 3652 .is_const = is_const, 3653 .is_comptime = this_variable_comptime, 3654 }); 3655 variable_rl.* = .{ .inferred_ptr = ptr }; 3656 } 3657 3658 continue; 3659 }, 3660 else => {}, 3661 } 3662 // This variable is just an lvalue expression. 3663 // We will fill in its result pointer later, inside a comptime block. 3664 any_non_const_variables = true; 3665 any_lvalue_expr = true; 3666 variable_rl.* = .{ .typed_ptr = .{ 3667 .inst = undefined, 3668 .src_node = variable_node, 3669 } }; 3670 } 3671 3672 if (full.comptime_token != null and !any_non_const_variables) { 3673 try astgen.appendErrorTok(full.comptime_token.?, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{}); 3674 } 3675 3676 // If this expression is marked comptime, we must wrap it in a comptime block. 3677 var gz_buf: GenZir = undefined; 3678 const inner_gz = if (full.comptime_token) |_| bs: { 3679 gz_buf = gz.makeSubBlock(scope); 3680 gz_buf.is_comptime = true; 3681 break :bs &gz_buf; 3682 } else gz; 3683 defer if (full.comptime_token) |_| inner_gz.unstack(); 3684 3685 if (any_lvalue_expr) { 3686 // At least one variable was an lvalue expr. Iterate again in order to 3687 // evaluate the lvalues from within the possible block_comptime. 3688 for (rl_components, full.ast.variables) |*variable_rl, variable_node| { 3689 if (variable_rl.* != .typed_ptr) continue; 3690 switch (node_tags[variable_node]) { 3691 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue, 3692 else => {}, 3693 } 3694 variable_rl.typed_ptr.inst = try lvalExpr(inner_gz, scope, variable_node); 3695 } 3696 } 3697 3698 // We can't give a reasonable anon name strategy for destructured inits, so 3699 // leave it at its default of `.anon`. 3700 _ = try reachableExpr(inner_gz, scope, .{ .rl = .{ .destructure = .{ 3701 .src_node = node, 3702 .components = rl_components, 3703 } } }, full.ast.value_expr, node); 3704 3705 if (full.comptime_token) |_| { 3706 // Finish the block_comptime. Inferred alloc resolution etc will occur 3707 // in the parent block. 3708 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node); 3709 _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value); 3710 try inner_gz.setBlockBody(comptime_block_inst); 3711 try gz.instructions.append(gz.astgen.gpa, comptime_block_inst); 3712 } 3713 3714 // Now, iterate over the variable exprs to construct any new scopes. 3715 // If there were any inferred allocations, resolve them. 3716 // If there were any `const` decls, make the pointer constant. 3717 var cur_scope = scope; 3718 for (rl_components, full.ast.variables) |variable_rl, variable_node| { 3719 switch (node_tags[variable_node]) { 3720 .local_var_decl, .simple_var_decl, .aligned_var_decl => {}, 3721 else => continue, // We were mutating an existing lvalue - nothing to do 3722 } 3723 const full_var_decl = tree.fullVarDecl(variable_node).?; 3724 const raw_ptr = switch (variable_rl) { 3725 .discard => unreachable, 3726 .typed_ptr => |typed_ptr| typed_ptr.inst, 3727 .inferred_ptr => |ptr_inst| ptr_inst, 3728 }; 3729 // If the alloc was inferred, resolve it. 3730 if (full_var_decl.ast.type_node == 0) { 3731 _ = try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, variable_node); 3732 } 3733 const is_const = switch (token_tags[full_var_decl.ast.mut_token]) { 3734 .keyword_var => false, 3735 .keyword_const => true, 3736 else => unreachable, 3737 }; 3738 // If the alloc was const, make it const. 3739 const var_ptr = if (is_const and full_var_decl.ast.type_node != 0) make_const: { 3740 // Note that we don't do this if type_node == 0 since `resolve_inferred_alloc` 3741 // handles it for us. 3742 break :make_const try gz.addUnNode(.make_ptr_const, raw_ptr, node); 3743 } else raw_ptr; 3744 const name_token = full_var_decl.ast.mut_token + 1; 3745 const ident_name_raw = tree.tokenSlice(name_token); 3746 const ident_name = try astgen.identAsString(name_token); 3747 try astgen.detectLocalShadowing( 3748 cur_scope, 3749 ident_name, 3750 name_token, 3751 ident_name_raw, 3752 if (is_const) .@"local constant" else .@"local variable", 3753 ); 3754 try gz.addDbgVar(.dbg_var_ptr, ident_name, var_ptr); 3755 // Finally, create the scope. 3756 const sub_scope = try block_arena.create(Scope.LocalPtr); 3757 sub_scope.* = .{ 3758 .parent = cur_scope, 3759 .gen_zir = gz, 3760 .name = ident_name, 3761 .ptr = var_ptr, 3762 .token_src = name_token, 3763 .maybe_comptime = is_const or is_comptime, 3764 .id_cat = if (is_const) .@"local constant" else .@"local variable", 3765 }; 3766 cur_scope = &sub_scope.base; 3767 } 3768 3769 return cur_scope; 3770 } 3771 3772 fn assignOp( 3773 gz: *GenZir, 3774 scope: *Scope, 3775 infix_node: Ast.Node.Index, 3776 op_inst_tag: Zir.Inst.Tag, 3777 ) InnerError!void { 3778 try emitDbgNode(gz, infix_node); 3779 const astgen = gz.astgen; 3780 const tree = astgen.tree; 3781 const node_datas = tree.nodes.items(.data); 3782 3783 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs); 3784 3785 const cursor = switch (op_inst_tag) { 3786 .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, infix_node), 3787 else => undefined, 3788 }; 3789 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); 3790 3791 const rhs_res_ty = switch (op_inst_tag) { 3792 .add, 3793 .sub, 3794 => try gz.add(.{ 3795 .tag = .extended, 3796 .data = .{ .extended = .{ 3797 .opcode = .inplace_arith_result_ty, 3798 .small = @intFromEnum(@as(Zir.Inst.InplaceOp, switch (op_inst_tag) { 3799 .add => .add_eq, 3800 .sub => .sub_eq, 3801 else => unreachable, 3802 })), 3803 .operand = @intFromEnum(lhs), 3804 } }, 3805 }), 3806 else => try gz.addUnNode(.typeof, lhs, infix_node), // same as LHS type 3807 }; 3808 // Not `coerced_ty` since `add`/etc won't coerce to this type. 3809 const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_res_ty } }, node_datas[infix_node].rhs); 3810 3811 switch (op_inst_tag) { 3812 .add, .sub, .mul, .div, .mod_rem => { 3813 try emitDbgStmt(gz, cursor); 3814 }, 3815 else => {}, 3816 } 3817 const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{ 3818 .lhs = lhs, 3819 .rhs = rhs, 3820 }); 3821 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{ 3822 .lhs = lhs_ptr, 3823 .rhs = result, 3824 }); 3825 } 3826 3827 fn assignShift( 3828 gz: *GenZir, 3829 scope: *Scope, 3830 infix_node: Ast.Node.Index, 3831 op_inst_tag: Zir.Inst.Tag, 3832 ) InnerError!void { 3833 try emitDbgNode(gz, infix_node); 3834 const astgen = gz.astgen; 3835 const tree = astgen.tree; 3836 const node_datas = tree.nodes.items(.data); 3837 3838 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs); 3839 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); 3840 const rhs_type = try gz.addUnNode(.typeof_log2_int_type, lhs, infix_node); 3841 const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_type } }, node_datas[infix_node].rhs); 3842 3843 const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{ 3844 .lhs = lhs, 3845 .rhs = rhs, 3846 }); 3847 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{ 3848 .lhs = lhs_ptr, 3849 .rhs = result, 3850 }); 3851 } 3852 3853 fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void { 3854 try emitDbgNode(gz, infix_node); 3855 const astgen = gz.astgen; 3856 const tree = astgen.tree; 3857 const node_datas = tree.nodes.items(.data); 3858 3859 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs); 3860 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); 3861 // Saturating shift-left allows any integer type for both the LHS and RHS. 3862 const rhs = try expr(gz, scope, .{ .rl = .none }, node_datas[infix_node].rhs); 3863 3864 const result = try gz.addPlNode(.shl_sat, infix_node, Zir.Inst.Bin{ 3865 .lhs = lhs, 3866 .rhs = rhs, 3867 }); 3868 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{ 3869 .lhs = lhs_ptr, 3870 .rhs = result, 3871 }); 3872 } 3873 3874 fn ptrType( 3875 gz: *GenZir, 3876 scope: *Scope, 3877 ri: ResultInfo, 3878 node: Ast.Node.Index, 3879 ptr_info: Ast.full.PtrType, 3880 ) InnerError!Zir.Inst.Ref { 3881 if (ptr_info.size == .C and ptr_info.allowzero_token != null) { 3882 return gz.astgen.failTok(ptr_info.allowzero_token.?, "C pointers always allow address zero", .{}); 3883 } 3884 3885 const source_offset = gz.astgen.source_offset; 3886 const source_line = gz.astgen.source_line; 3887 const source_column = gz.astgen.source_column; 3888 const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type); 3889 3890 var sentinel_ref: Zir.Inst.Ref = .none; 3891 var align_ref: Zir.Inst.Ref = .none; 3892 var addrspace_ref: Zir.Inst.Ref = .none; 3893 var bit_start_ref: Zir.Inst.Ref = .none; 3894 var bit_end_ref: Zir.Inst.Ref = .none; 3895 var trailing_count: u32 = 0; 3896 3897 if (ptr_info.ast.sentinel != 0) { 3898 // These attributes can appear in any order and they all come before the 3899 // element type so we need to reset the source cursor before generating them. 3900 gz.astgen.source_offset = source_offset; 3901 gz.astgen.source_line = source_line; 3902 gz.astgen.source_column = source_column; 3903 3904 sentinel_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, ptr_info.ast.sentinel); 3905 trailing_count += 1; 3906 } 3907 if (ptr_info.ast.addrspace_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 const addrspace_ty = try gz.addBuiltinValue(ptr_info.ast.addrspace_node, .address_space); 3913 addrspace_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = addrspace_ty } }, ptr_info.ast.addrspace_node); 3914 trailing_count += 1; 3915 } 3916 if (ptr_info.ast.align_node != 0) { 3917 gz.astgen.source_offset = source_offset; 3918 gz.astgen.source_line = source_line; 3919 gz.astgen.source_column = source_column; 3920 3921 align_ref = try expr(gz, scope, coerced_align_ri, ptr_info.ast.align_node); 3922 trailing_count += 1; 3923 } 3924 if (ptr_info.ast.bit_range_start != 0) { 3925 assert(ptr_info.ast.bit_range_end != 0); 3926 bit_start_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, ptr_info.ast.bit_range_start); 3927 bit_end_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, ptr_info.ast.bit_range_end); 3928 trailing_count += 2; 3929 } 3930 3931 const gpa = gz.astgen.gpa; 3932 try gz.instructions.ensureUnusedCapacity(gpa, 1); 3933 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 3934 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.PtrType).@"struct".fields.len + 3935 trailing_count); 3936 3937 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{ 3938 .elem_type = elem_type, 3939 .src_node = gz.nodeIndexToRelative(node), 3940 }); 3941 if (sentinel_ref != .none) { 3942 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(sentinel_ref)); 3943 } 3944 if (align_ref != .none) { 3945 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(align_ref)); 3946 } 3947 if (addrspace_ref != .none) { 3948 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(addrspace_ref)); 3949 } 3950 if (bit_start_ref != .none) { 3951 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_start_ref)); 3952 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_end_ref)); 3953 } 3954 3955 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 3956 const result = new_index.toRef(); 3957 gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{ 3958 .ptr_type = .{ 3959 .flags = .{ 3960 .is_allowzero = ptr_info.allowzero_token != null, 3961 .is_mutable = ptr_info.const_token == null, 3962 .is_volatile = ptr_info.volatile_token != null, 3963 .has_sentinel = sentinel_ref != .none, 3964 .has_align = align_ref != .none, 3965 .has_addrspace = addrspace_ref != .none, 3966 .has_bit_range = bit_start_ref != .none, 3967 }, 3968 .size = ptr_info.size, 3969 .payload_index = payload_index, 3970 }, 3971 } }); 3972 gz.instructions.appendAssumeCapacity(new_index); 3973 3974 return rvalue(gz, ri, result, node); 3975 } 3976 3977 fn arrayType(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref { 3978 const astgen = gz.astgen; 3979 const tree = astgen.tree; 3980 const node_datas = tree.nodes.items(.data); 3981 const node_tags = tree.nodes.items(.tag); 3982 const main_tokens = tree.nodes.items(.main_token); 3983 3984 const len_node = node_datas[node].lhs; 3985 if (node_tags[len_node] == .identifier and 3986 mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_")) 3987 { 3988 return astgen.failNode(len_node, "unable to infer array size", .{}); 3989 } 3990 const len = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node); 3991 const elem_type = try typeExpr(gz, scope, node_datas[node].rhs); 3992 3993 const result = try gz.addPlNode(.array_type, node, Zir.Inst.Bin{ 3994 .lhs = len, 3995 .rhs = elem_type, 3996 }); 3997 return rvalue(gz, ri, result, node); 3998 } 3999 4000 fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref { 4001 const astgen = gz.astgen; 4002 const tree = astgen.tree; 4003 const node_datas = tree.nodes.items(.data); 4004 const node_tags = tree.nodes.items(.tag); 4005 const main_tokens = tree.nodes.items(.main_token); 4006 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.ArrayTypeSentinel); 4007 4008 const len_node = node_datas[node].lhs; 4009 if (node_tags[len_node] == .identifier and 4010 mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_")) 4011 { 4012 return astgen.failNode(len_node, "unable to infer array size", .{}); 4013 } 4014 const len = try reachableExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node, node); 4015 const elem_type = try typeExpr(gz, scope, extra.elem_type); 4016 const sentinel = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = elem_type } }, extra.sentinel, node, true); 4017 4018 const result = try gz.addPlNode(.array_type_sentinel, node, Zir.Inst.ArrayTypeSentinel{ 4019 .len = len, 4020 .elem_type = elem_type, 4021 .sentinel = sentinel, 4022 }); 4023 return rvalue(gz, ri, result, node); 4024 } 4025 4026 const WipMembers = struct { 4027 payload: *ArrayListUnmanaged(u32), 4028 payload_top: usize, 4029 field_bits_start: u32, 4030 fields_start: u32, 4031 fields_end: u32, 4032 decl_index: u32 = 0, 4033 field_index: u32 = 0, 4034 4035 const Self = @This(); 4036 4037 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 { 4038 const payload_top: u32 = @intCast(payload.items.len); 4039 const field_bits_start = payload_top + decl_count; 4040 const fields_start = field_bits_start + if (bits_per_field > 0) blk: { 4041 const fields_per_u32 = 32 / bits_per_field; 4042 break :blk (field_count + fields_per_u32 - 1) / fields_per_u32; 4043 } else 0; 4044 const payload_end = fields_start + field_count * max_field_size; 4045 try payload.resize(gpa, payload_end); 4046 return .{ 4047 .payload = payload, 4048 .payload_top = payload_top, 4049 .field_bits_start = field_bits_start, 4050 .fields_start = fields_start, 4051 .fields_end = fields_start, 4052 }; 4053 } 4054 4055 fn nextDecl(self: *Self, decl_inst: Zir.Inst.Index) void { 4056 self.payload.items[self.payload_top + self.decl_index] = @intFromEnum(decl_inst); 4057 self.decl_index += 1; 4058 } 4059 4060 fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void { 4061 const fields_per_u32 = 32 / bits_per_field; 4062 const index = self.field_bits_start + self.field_index / fields_per_u32; 4063 assert(index < self.fields_start); 4064 var bit_bag: u32 = if (self.field_index % fields_per_u32 == 0) 0 else self.payload.items[index]; 4065 bit_bag >>= bits_per_field; 4066 comptime var i = 0; 4067 inline while (i < bits_per_field) : (i += 1) { 4068 bit_bag |= @as(u32, @intFromBool(bits[i])) << (32 - bits_per_field + i); 4069 } 4070 self.payload.items[index] = bit_bag; 4071 self.field_index += 1; 4072 } 4073 4074 fn appendToField(self: *Self, data: u32) void { 4075 assert(self.fields_end < self.payload.items.len); 4076 self.payload.items[self.fields_end] = data; 4077 self.fields_end += 1; 4078 } 4079 4080 fn finishBits(self: *Self, comptime bits_per_field: u32) void { 4081 if (bits_per_field > 0) { 4082 const fields_per_u32 = 32 / bits_per_field; 4083 const empty_field_slots = fields_per_u32 - (self.field_index % fields_per_u32); 4084 if (self.field_index > 0 and empty_field_slots < fields_per_u32) { 4085 const index = self.field_bits_start + self.field_index / fields_per_u32; 4086 self.payload.items[index] >>= @intCast(empty_field_slots * bits_per_field); 4087 } 4088 } 4089 } 4090 4091 fn declsSlice(self: *Self) []u32 { 4092 return self.payload.items[self.payload_top..][0..self.decl_index]; 4093 } 4094 4095 fn fieldsSlice(self: *Self) []u32 { 4096 return self.payload.items[self.field_bits_start..self.fields_end]; 4097 } 4098 4099 fn deinit(self: *Self) void { 4100 self.payload.items.len = self.payload_top; 4101 } 4102 }; 4103 4104 fn fnDecl( 4105 astgen: *AstGen, 4106 gz: *GenZir, 4107 scope: *Scope, 4108 wip_members: *WipMembers, 4109 decl_node: Ast.Node.Index, 4110 body_node: Ast.Node.Index, 4111 fn_proto: Ast.full.FnProto, 4112 ) InnerError!void { 4113 const tree = astgen.tree; 4114 const token_tags = tree.tokens.items(.tag); 4115 4116 const old_hasher = astgen.src_hasher; 4117 defer astgen.src_hasher = old_hasher; 4118 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 4119 // We don't add the full source yet, because we also need the prototype hash! 4120 // The source slice is added towards the *end* of this function. 4121 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); 4122 4123 // missing function name already happened in scanContainer() 4124 const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail; 4125 4126 // We insert this at the beginning so that its instruction index marks the 4127 // start of the top level declaration. 4128 const decl_inst = try gz.makeDeclaration(fn_proto.ast.proto_node); 4129 astgen.advanceSourceCursorToNode(decl_node); 4130 4131 var decl_gz: GenZir = .{ 4132 .is_comptime = true, 4133 .decl_node_index = fn_proto.ast.proto_node, 4134 .decl_line = astgen.source_line, 4135 .parent = scope, 4136 .astgen = astgen, 4137 .instructions = gz.instructions, 4138 .instructions_top = gz.instructions.items.len, 4139 }; 4140 defer decl_gz.unstack(); 4141 4142 var fn_gz: GenZir = .{ 4143 .is_comptime = false, 4144 .decl_node_index = fn_proto.ast.proto_node, 4145 .decl_line = decl_gz.decl_line, 4146 .parent = &decl_gz.base, 4147 .astgen = astgen, 4148 .instructions = gz.instructions, 4149 .instructions_top = GenZir.unstacked_top, 4150 }; 4151 defer fn_gz.unstack(); 4152 4153 // Set this now, since parameter types, return type, etc may be generic. 4154 const prev_within_fn = astgen.within_fn; 4155 defer astgen.within_fn = prev_within_fn; 4156 astgen.within_fn = true; 4157 4158 const is_pub = fn_proto.visib_token != null; 4159 const is_export = blk: { 4160 const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false; 4161 break :blk token_tags[maybe_export_token] == .keyword_export; 4162 }; 4163 const is_extern = blk: { 4164 const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false; 4165 break :blk token_tags[maybe_extern_token] == .keyword_extern; 4166 }; 4167 const has_inline_keyword = blk: { 4168 const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false; 4169 break :blk token_tags[maybe_inline_token] == .keyword_inline; 4170 }; 4171 const is_noinline = blk: { 4172 const maybe_noinline_token = fn_proto.extern_export_inline_token orelse break :blk false; 4173 break :blk token_tags[maybe_noinline_token] == .keyword_noinline; 4174 }; 4175 4176 const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken()); 4177 4178 wip_members.nextDecl(decl_inst); 4179 4180 var noalias_bits: u32 = 0; 4181 var params_scope = &fn_gz.base; 4182 const is_var_args = is_var_args: { 4183 var param_type_i: usize = 0; 4184 var it = fn_proto.iterate(tree); 4185 while (it.next()) |param| : (param_type_i += 1) { 4186 const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) { 4187 .keyword_noalias => is_comptime: { 4188 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse 4189 return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{})); 4190 break :is_comptime false; 4191 }, 4192 .keyword_comptime => true, 4193 else => false, 4194 } else false; 4195 4196 const is_anytype = if (param.anytype_ellipsis3) |token| blk: { 4197 switch (token_tags[token]) { 4198 .keyword_anytype => break :blk true, 4199 .ellipsis3 => break :is_var_args true, 4200 else => unreachable, 4201 } 4202 } else false; 4203 4204 const param_name: Zir.NullTerminatedString = if (param.name_token) |name_token| blk: { 4205 const name_bytes = tree.tokenSlice(name_token); 4206 if (mem.eql(u8, "_", name_bytes)) 4207 break :blk .empty; 4208 4209 const param_name = try astgen.identAsString(name_token); 4210 if (!is_extern) { 4211 try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes, .@"function parameter"); 4212 } 4213 break :blk param_name; 4214 } else if (!is_extern) { 4215 if (param.anytype_ellipsis3) |tok| { 4216 return astgen.failTok(tok, "missing parameter name", .{}); 4217 } else { 4218 ambiguous: { 4219 if (tree.nodes.items(.tag)[param.type_expr] != .identifier) break :ambiguous; 4220 const main_token = tree.nodes.items(.main_token)[param.type_expr]; 4221 const identifier_str = tree.tokenSlice(main_token); 4222 if (isPrimitive(identifier_str)) break :ambiguous; 4223 return astgen.failNodeNotes( 4224 param.type_expr, 4225 "missing parameter name or type", 4226 .{}, 4227 &[_]u32{ 4228 try astgen.errNoteNode( 4229 param.type_expr, 4230 "if this is a name, annotate its type '{s}: T'", 4231 .{identifier_str}, 4232 ), 4233 try astgen.errNoteNode( 4234 param.type_expr, 4235 "if this is a type, give it a name '<name>: {s}'", 4236 .{identifier_str}, 4237 ), 4238 }, 4239 ); 4240 } 4241 return astgen.failNode(param.type_expr, "missing parameter name", .{}); 4242 } 4243 } else .empty; 4244 4245 const param_inst = if (is_anytype) param: { 4246 const name_token = param.name_token orelse param.anytype_ellipsis3.?; 4247 const tag: Zir.Inst.Tag = if (is_comptime) 4248 .param_anytype_comptime 4249 else 4250 .param_anytype; 4251 break :param try decl_gz.addStrTok(tag, param_name, name_token); 4252 } else param: { 4253 const param_type_node = param.type_expr; 4254 assert(param_type_node != 0); 4255 var param_gz = decl_gz.makeSubBlock(scope); 4256 defer param_gz.unstack(); 4257 const param_type = try fullBodyExpr(¶m_gz, params_scope, coerced_type_ri, param_type_node, .normal); 4258 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1); 4259 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node); 4260 4261 const main_tokens = tree.nodes.items(.main_token); 4262 const name_token = param.name_token orelse main_tokens[param_type_node]; 4263 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param; 4264 const param_inst = try decl_gz.addParam(¶m_gz, tag, name_token, param_name, param.first_doc_comment); 4265 assert(param_inst_expected == param_inst); 4266 break :param param_inst.toRef(); 4267 }; 4268 4269 if (param_name == .empty or is_extern) continue; 4270 4271 const sub_scope = try astgen.arena.create(Scope.LocalVal); 4272 sub_scope.* = .{ 4273 .parent = params_scope, 4274 .gen_zir = &decl_gz, 4275 .name = param_name, 4276 .inst = param_inst, 4277 .token_src = param.name_token.?, 4278 .id_cat = .@"function parameter", 4279 }; 4280 params_scope = &sub_scope.base; 4281 } 4282 break :is_var_args false; 4283 }; 4284 4285 const lib_name = if (fn_proto.lib_name) |lib_name_token| blk: { 4286 const lib_name_str = try astgen.strLitAsString(lib_name_token); 4287 const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len]; 4288 if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) { 4289 return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{}); 4290 } else if (lib_name_str.len == 0) { 4291 return astgen.failTok(lib_name_token, "library name cannot be empty", .{}); 4292 } 4293 break :blk lib_name_str.index; 4294 } else .empty; 4295 4296 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; 4297 const is_inferred_error = token_tags[maybe_bang] == .bang; 4298 4299 // After creating the function ZIR instruction, it will need to update the break 4300 // instructions inside the expression blocks for align, addrspace, cc, and ret_ty 4301 // to use the function instruction as the "block" to break from. 4302 4303 var align_gz = decl_gz.makeSubBlock(params_scope); 4304 defer align_gz.unstack(); 4305 const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { 4306 const inst = try expr(&decl_gz, params_scope, coerced_align_ri, fn_proto.ast.align_expr); 4307 if (align_gz.instructionsSlice().len == 0) { 4308 // In this case we will send a len=0 body which can be encoded more efficiently. 4309 break :inst inst; 4310 } 4311 _ = try align_gz.addBreak(.break_inline, @enumFromInt(0), inst); 4312 break :inst inst; 4313 }; 4314 4315 var addrspace_gz = decl_gz.makeSubBlock(params_scope); 4316 defer addrspace_gz.unstack(); 4317 const addrspace_ref: Zir.Inst.Ref = if (fn_proto.ast.addrspace_expr == 0) .none else inst: { 4318 const addrspace_ty = try decl_gz.addBuiltinValue(fn_proto.ast.addrspace_expr, .address_space); 4319 const inst = try expr(&decl_gz, params_scope, .{ .rl = .{ .coerced_ty = addrspace_ty } }, fn_proto.ast.addrspace_expr); 4320 if (addrspace_gz.instructionsSlice().len == 0) { 4321 // In this case we will send a len=0 body which can be encoded more efficiently. 4322 break :inst inst; 4323 } 4324 _ = try addrspace_gz.addBreak(.break_inline, @enumFromInt(0), inst); 4325 break :inst inst; 4326 }; 4327 4328 var section_gz = decl_gz.makeSubBlock(params_scope); 4329 defer section_gz.unstack(); 4330 const section_ref: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: { 4331 const inst = try expr(&decl_gz, params_scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, fn_proto.ast.section_expr); 4332 if (section_gz.instructionsSlice().len == 0) { 4333 // In this case we will send a len=0 body which can be encoded more efficiently. 4334 break :inst inst; 4335 } 4336 _ = try section_gz.addBreak(.break_inline, @enumFromInt(0), inst); 4337 break :inst inst; 4338 }; 4339 4340 var cc_gz = decl_gz.makeSubBlock(params_scope); 4341 defer cc_gz.unstack(); 4342 const cc_ref: Zir.Inst.Ref = blk: { 4343 if (fn_proto.ast.callconv_expr != 0) { 4344 if (has_inline_keyword) { 4345 return astgen.failNode( 4346 fn_proto.ast.callconv_expr, 4347 "explicit callconv incompatible with inline keyword", 4348 .{}, 4349 ); 4350 } 4351 const inst = try expr( 4352 &cc_gz, 4353 params_scope, 4354 .{ .rl = .{ .coerced_ty = try cc_gz.addBuiltinValue(fn_proto.ast.callconv_expr, .calling_convention) } }, 4355 fn_proto.ast.callconv_expr, 4356 ); 4357 if (cc_gz.instructionsSlice().len == 0) { 4358 // In this case we will send a len=0 body which can be encoded more efficiently. 4359 break :blk inst; 4360 } 4361 _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst); 4362 break :blk inst; 4363 } else if (is_extern) { 4364 const inst = try cc_gz.addBuiltinValue(decl_node, .calling_convention_c); 4365 _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst); 4366 break :blk inst; 4367 } else if (has_inline_keyword) { 4368 const inst = try cc_gz.addBuiltinValue(decl_node, .calling_convention_inline); 4369 _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst); 4370 break :blk inst; 4371 } else { 4372 break :blk .none; 4373 } 4374 }; 4375 4376 var ret_gz = decl_gz.makeSubBlock(params_scope); 4377 defer ret_gz.unstack(); 4378 const ret_ref: Zir.Inst.Ref = inst: { 4379 const inst = try fullBodyExpr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type, .normal); 4380 if (ret_gz.instructionsSlice().len == 0) { 4381 // In this case we will send a len=0 body which can be encoded more efficiently. 4382 break :inst inst; 4383 } 4384 _ = try ret_gz.addBreak(.break_inline, @enumFromInt(0), inst); 4385 break :inst inst; 4386 }; 4387 4388 const func_inst: Zir.Inst.Ref = if (body_node == 0) func: { 4389 if (!is_extern) { 4390 return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{}); 4391 } 4392 if (is_inferred_error) { 4393 return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{}); 4394 } 4395 break :func try decl_gz.addFunc(.{ 4396 .src_node = decl_node, 4397 .cc_ref = cc_ref, 4398 .cc_gz = &cc_gz, 4399 .align_ref = align_ref, 4400 .align_gz = &align_gz, 4401 .ret_ref = ret_ref, 4402 .ret_gz = &ret_gz, 4403 .section_ref = section_ref, 4404 .section_gz = §ion_gz, 4405 .addrspace_ref = addrspace_ref, 4406 .addrspace_gz = &addrspace_gz, 4407 .param_block = decl_inst, 4408 .body_gz = null, 4409 .lib_name = lib_name, 4410 .is_var_args = is_var_args, 4411 .is_inferred_error = false, 4412 .is_test = false, 4413 .is_extern = true, 4414 .is_noinline = is_noinline, 4415 .noalias_bits = noalias_bits, 4416 .proto_hash = undefined, // ignored for `body_gz == null` 4417 }); 4418 } else func: { 4419 // as a scope, fn_gz encloses ret_gz, but for instruction list, fn_gz stacks on ret_gz 4420 fn_gz.instructions_top = ret_gz.instructions.items.len; 4421 4422 // Construct the prototype hash. 4423 // Leave `astgen.src_hasher` unmodified; this will be used for hashing 4424 // the *whole* function declaration, including its body. 4425 var proto_hasher = astgen.src_hasher; 4426 const proto_node = tree.nodes.items(.data)[decl_node].lhs; 4427 proto_hasher.update(tree.getNodeSource(proto_node)); 4428 var proto_hash: std.zig.SrcHash = undefined; 4429 proto_hasher.final(&proto_hash); 4430 4431 const prev_fn_block = astgen.fn_block; 4432 const prev_fn_ret_ty = astgen.fn_ret_ty; 4433 defer { 4434 astgen.fn_block = prev_fn_block; 4435 astgen.fn_ret_ty = prev_fn_ret_ty; 4436 } 4437 astgen.fn_block = &fn_gz; 4438 astgen.fn_ret_ty = if (is_inferred_error or ret_ref.toIndex() != null) r: { 4439 // We're essentially guaranteed to need the return type at some point, 4440 // since the return type is likely not `void` or `noreturn` so there 4441 // will probably be an explicit return requiring RLS. Fetch this 4442 // return type now so the rest of the function can use it. 4443 break :r try fn_gz.addNode(.ret_type, decl_node); 4444 } else ret_ref; 4445 4446 const prev_var_args = astgen.fn_var_args; 4447 astgen.fn_var_args = is_var_args; 4448 defer astgen.fn_var_args = prev_var_args; 4449 4450 astgen.advanceSourceCursorToNode(body_node); 4451 const lbrace_line = astgen.source_line - decl_gz.decl_line; 4452 const lbrace_column = astgen.source_column; 4453 4454 _ = try fullBodyExpr(&fn_gz, params_scope, .{ .rl = .none }, body_node, .allow_branch_hint); 4455 try checkUsed(gz, &fn_gz.base, params_scope); 4456 4457 if (!fn_gz.endsWithNoReturn()) { 4458 // As our last action before the return, "pop" the error trace if needed 4459 _ = try fn_gz.addRestoreErrRetIndex(.ret, .always, decl_node); 4460 4461 // Add implicit return at end of function. 4462 _ = try fn_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node)); 4463 } 4464 4465 break :func try decl_gz.addFunc(.{ 4466 .src_node = decl_node, 4467 .cc_ref = cc_ref, 4468 .cc_gz = &cc_gz, 4469 .align_ref = align_ref, 4470 .align_gz = &align_gz, 4471 .ret_ref = ret_ref, 4472 .ret_gz = &ret_gz, 4473 .section_ref = section_ref, 4474 .section_gz = §ion_gz, 4475 .addrspace_ref = addrspace_ref, 4476 .addrspace_gz = &addrspace_gz, 4477 .lbrace_line = lbrace_line, 4478 .lbrace_column = lbrace_column, 4479 .param_block = decl_inst, 4480 .body_gz = &fn_gz, 4481 .lib_name = lib_name, 4482 .is_var_args = is_var_args, 4483 .is_inferred_error = is_inferred_error, 4484 .is_test = false, 4485 .is_extern = false, 4486 .is_noinline = is_noinline, 4487 .noalias_bits = noalias_bits, 4488 .proto_hash = proto_hash, 4489 }); 4490 }; 4491 4492 // *Now* we can incorporate the full source code into the hasher. 4493 astgen.src_hasher.update(tree.getNodeSource(decl_node)); 4494 4495 // We add this at the end so that its instruction index marks the end range 4496 // of the top level declaration. addFunc already unstacked fn_gz and ret_gz. 4497 _ = try decl_gz.addBreak(.break_inline, decl_inst, func_inst); 4498 4499 var hash: std.zig.SrcHash = undefined; 4500 astgen.src_hasher.final(&hash); 4501 try setDeclaration( 4502 decl_inst, 4503 hash, 4504 .{ .named = fn_name_token }, 4505 decl_gz.decl_line, 4506 is_pub, 4507 is_export, 4508 doc_comment_index, 4509 &decl_gz, 4510 // align, linksection, and addrspace are passed in the func instruction in this case. 4511 // TODO: move them from the function instruction to the declaration instruction? 4512 null, 4513 ); 4514 } 4515 4516 fn globalVarDecl( 4517 astgen: *AstGen, 4518 gz: *GenZir, 4519 scope: *Scope, 4520 wip_members: *WipMembers, 4521 node: Ast.Node.Index, 4522 var_decl: Ast.full.VarDecl, 4523 ) InnerError!void { 4524 const tree = astgen.tree; 4525 const token_tags = tree.tokens.items(.tag); 4526 4527 const old_hasher = astgen.src_hasher; 4528 defer astgen.src_hasher = old_hasher; 4529 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 4530 astgen.src_hasher.update(tree.getNodeSource(node)); 4531 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); 4532 4533 const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var; 4534 // We do this at the beginning so that the instruction index marks the range start 4535 // of the top level declaration. 4536 const decl_inst = try gz.makeDeclaration(node); 4537 4538 const name_token = var_decl.ast.mut_token + 1; 4539 astgen.advanceSourceCursorToNode(node); 4540 4541 var block_scope: GenZir = .{ 4542 .parent = scope, 4543 .decl_node_index = node, 4544 .decl_line = astgen.source_line, 4545 .astgen = astgen, 4546 .is_comptime = true, 4547 .instructions = gz.instructions, 4548 .instructions_top = gz.instructions.items.len, 4549 }; 4550 defer block_scope.unstack(); 4551 4552 const is_pub = var_decl.visib_token != null; 4553 const is_export = blk: { 4554 const maybe_export_token = var_decl.extern_export_token orelse break :blk false; 4555 break :blk token_tags[maybe_export_token] == .keyword_export; 4556 }; 4557 const is_extern = blk: { 4558 const maybe_extern_token = var_decl.extern_export_token orelse break :blk false; 4559 break :blk token_tags[maybe_extern_token] == .keyword_extern; 4560 }; 4561 wip_members.nextDecl(decl_inst); 4562 4563 const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: { 4564 if (!is_mutable) { 4565 return astgen.failTok(tok, "threadlocal variable cannot be constant", .{}); 4566 } 4567 break :blk true; 4568 } else false; 4569 4570 const lib_name = if (var_decl.lib_name) |lib_name_token| blk: { 4571 const lib_name_str = try astgen.strLitAsString(lib_name_token); 4572 const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len]; 4573 if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) { 4574 return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{}); 4575 } else if (lib_name_str.len == 0) { 4576 return astgen.failTok(lib_name_token, "library name cannot be empty", .{}); 4577 } 4578 break :blk lib_name_str.index; 4579 } else .empty; 4580 4581 const doc_comment_index = try astgen.docCommentAsString(var_decl.firstToken()); 4582 4583 assert(var_decl.comptime_token == null); // handled by parser 4584 4585 const var_inst: Zir.Inst.Ref = if (var_decl.ast.init_node != 0) vi: { 4586 if (is_extern) { 4587 return astgen.failNode( 4588 var_decl.ast.init_node, 4589 "extern variables have no initializers", 4590 .{}, 4591 ); 4592 } 4593 4594 const type_inst: Zir.Inst.Ref = if (var_decl.ast.type_node != 0) 4595 try expr( 4596 &block_scope, 4597 &block_scope.base, 4598 coerced_type_ri, 4599 var_decl.ast.type_node, 4600 ) 4601 else 4602 .none; 4603 4604 block_scope.anon_name_strategy = .parent; 4605 4606 const init_inst = try expr( 4607 &block_scope, 4608 &block_scope.base, 4609 if (type_inst != .none) .{ .rl = .{ .ty = type_inst } } else .{ .rl = .none }, 4610 var_decl.ast.init_node, 4611 ); 4612 4613 if (is_mutable) { 4614 const var_inst = try block_scope.addVar(.{ 4615 .var_type = type_inst, 4616 .lib_name = .empty, 4617 .align_inst = .none, // passed via the decls data 4618 .init = init_inst, 4619 .is_extern = false, 4620 .is_const = !is_mutable, 4621 .is_threadlocal = is_threadlocal, 4622 }); 4623 break :vi var_inst; 4624 } else { 4625 break :vi init_inst; 4626 } 4627 } else if (!is_extern) { 4628 return astgen.failNode(node, "variables must be initialized", .{}); 4629 } else if (var_decl.ast.type_node != 0) vi: { 4630 // Extern variable which has an explicit type. 4631 const type_inst = try typeExpr(&block_scope, &block_scope.base, var_decl.ast.type_node); 4632 4633 block_scope.anon_name_strategy = .parent; 4634 4635 const var_inst = try block_scope.addVar(.{ 4636 .var_type = type_inst, 4637 .lib_name = lib_name, 4638 .align_inst = .none, // passed via the decls data 4639 .init = .none, 4640 .is_extern = true, 4641 .is_const = !is_mutable, 4642 .is_threadlocal = is_threadlocal, 4643 }); 4644 break :vi var_inst; 4645 } else { 4646 return astgen.failNode(node, "unable to infer variable type", .{}); 4647 }; 4648 4649 // We do this at the end so that the instruction index marks the end 4650 // range of a top level declaration. 4651 _ = try block_scope.addBreakWithSrcNode(.break_inline, decl_inst, var_inst, node); 4652 4653 var align_gz = block_scope.makeSubBlock(scope); 4654 if (var_decl.ast.align_node != 0) { 4655 const align_inst = try fullBodyExpr(&align_gz, &align_gz.base, coerced_align_ri, var_decl.ast.align_node, .normal); 4656 _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node); 4657 } 4658 4659 var linksection_gz = align_gz.makeSubBlock(scope); 4660 if (var_decl.ast.section_node != 0) { 4661 const linksection_inst = try fullBodyExpr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, var_decl.ast.section_node, .normal); 4662 _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node); 4663 } 4664 4665 var addrspace_gz = linksection_gz.makeSubBlock(scope); 4666 if (var_decl.ast.addrspace_node != 0) { 4667 const addrspace_ty = try addrspace_gz.addBuiltinValue(var_decl.ast.addrspace_node, .address_space); 4668 const addrspace_inst = try fullBodyExpr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, var_decl.ast.addrspace_node, .normal); 4669 _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node); 4670 } 4671 4672 var hash: std.zig.SrcHash = undefined; 4673 astgen.src_hasher.final(&hash); 4674 try setDeclaration( 4675 decl_inst, 4676 hash, 4677 .{ .named = name_token }, 4678 block_scope.decl_line, 4679 is_pub, 4680 is_export, 4681 doc_comment_index, 4682 &block_scope, 4683 .{ 4684 .align_gz = &align_gz, 4685 .linksection_gz = &linksection_gz, 4686 .addrspace_gz = &addrspace_gz, 4687 }, 4688 ); 4689 } 4690 4691 fn comptimeDecl( 4692 astgen: *AstGen, 4693 gz: *GenZir, 4694 scope: *Scope, 4695 wip_members: *WipMembers, 4696 node: Ast.Node.Index, 4697 ) InnerError!void { 4698 const tree = astgen.tree; 4699 const node_datas = tree.nodes.items(.data); 4700 const body_node = node_datas[node].lhs; 4701 4702 const old_hasher = astgen.src_hasher; 4703 defer astgen.src_hasher = old_hasher; 4704 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 4705 astgen.src_hasher.update(tree.getNodeSource(node)); 4706 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); 4707 4708 // Up top so the ZIR instruction index marks the start range of this 4709 // top-level declaration. 4710 const decl_inst = try gz.makeDeclaration(node); 4711 wip_members.nextDecl(decl_inst); 4712 astgen.advanceSourceCursorToNode(node); 4713 4714 var decl_block: GenZir = .{ 4715 .is_comptime = true, 4716 .decl_node_index = node, 4717 .decl_line = astgen.source_line, 4718 .parent = scope, 4719 .astgen = astgen, 4720 .instructions = gz.instructions, 4721 .instructions_top = gz.instructions.items.len, 4722 }; 4723 defer decl_block.unstack(); 4724 4725 const block_result = try fullBodyExpr(&decl_block, &decl_block.base, .{ .rl = .none }, body_node, .normal); 4726 if (decl_block.isEmpty() or !decl_block.refIsNoReturn(block_result)) { 4727 _ = try decl_block.addBreak(.break_inline, decl_inst, .void_value); 4728 } 4729 4730 var hash: std.zig.SrcHash = undefined; 4731 astgen.src_hasher.final(&hash); 4732 try setDeclaration( 4733 decl_inst, 4734 hash, 4735 .@"comptime", 4736 decl_block.decl_line, 4737 false, 4738 false, 4739 .empty, 4740 &decl_block, 4741 null, 4742 ); 4743 } 4744 4745 fn usingnamespaceDecl( 4746 astgen: *AstGen, 4747 gz: *GenZir, 4748 scope: *Scope, 4749 wip_members: *WipMembers, 4750 node: Ast.Node.Index, 4751 ) InnerError!void { 4752 const tree = astgen.tree; 4753 const node_datas = tree.nodes.items(.data); 4754 4755 const old_hasher = astgen.src_hasher; 4756 defer astgen.src_hasher = old_hasher; 4757 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 4758 astgen.src_hasher.update(tree.getNodeSource(node)); 4759 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); 4760 4761 const type_expr = node_datas[node].lhs; 4762 const is_pub = blk: { 4763 const main_tokens = tree.nodes.items(.main_token); 4764 const token_tags = tree.tokens.items(.tag); 4765 const main_token = main_tokens[node]; 4766 break :blk (main_token > 0 and token_tags[main_token - 1] == .keyword_pub); 4767 }; 4768 // Up top so the ZIR instruction index marks the start range of this 4769 // top-level declaration. 4770 const decl_inst = try gz.makeDeclaration(node); 4771 wip_members.nextDecl(decl_inst); 4772 astgen.advanceSourceCursorToNode(node); 4773 4774 var decl_block: GenZir = .{ 4775 .is_comptime = true, 4776 .decl_node_index = node, 4777 .decl_line = astgen.source_line, 4778 .parent = scope, 4779 .astgen = astgen, 4780 .instructions = gz.instructions, 4781 .instructions_top = gz.instructions.items.len, 4782 }; 4783 defer decl_block.unstack(); 4784 4785 const namespace_inst = try typeExpr(&decl_block, &decl_block.base, type_expr); 4786 _ = try decl_block.addBreak(.break_inline, decl_inst, namespace_inst); 4787 4788 var hash: std.zig.SrcHash = undefined; 4789 astgen.src_hasher.final(&hash); 4790 try setDeclaration( 4791 decl_inst, 4792 hash, 4793 .@"usingnamespace", 4794 decl_block.decl_line, 4795 is_pub, 4796 false, 4797 .empty, 4798 &decl_block, 4799 null, 4800 ); 4801 } 4802 4803 fn testDecl( 4804 astgen: *AstGen, 4805 gz: *GenZir, 4806 scope: *Scope, 4807 wip_members: *WipMembers, 4808 node: Ast.Node.Index, 4809 ) InnerError!void { 4810 const tree = astgen.tree; 4811 const node_datas = tree.nodes.items(.data); 4812 const body_node = node_datas[node].rhs; 4813 4814 const old_hasher = astgen.src_hasher; 4815 defer astgen.src_hasher = old_hasher; 4816 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 4817 astgen.src_hasher.update(tree.getNodeSource(node)); 4818 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); 4819 4820 // Up top so the ZIR instruction index marks the start range of this 4821 // top-level declaration. 4822 const decl_inst = try gz.makeDeclaration(node); 4823 4824 wip_members.nextDecl(decl_inst); 4825 astgen.advanceSourceCursorToNode(node); 4826 4827 var decl_block: GenZir = .{ 4828 .is_comptime = true, 4829 .decl_node_index = node, 4830 .decl_line = astgen.source_line, 4831 .parent = scope, 4832 .astgen = astgen, 4833 .instructions = gz.instructions, 4834 .instructions_top = gz.instructions.items.len, 4835 }; 4836 defer decl_block.unstack(); 4837 4838 const main_tokens = tree.nodes.items(.main_token); 4839 const token_tags = tree.tokens.items(.tag); 4840 const test_token = main_tokens[node]; 4841 const test_name_token = test_token + 1; 4842 const test_name: DeclarationName = switch (token_tags[test_name_token]) { 4843 else => .unnamed_test, 4844 .string_literal => .{ .named_test = test_name_token }, 4845 .identifier => blk: { 4846 const ident_name_raw = tree.tokenSlice(test_name_token); 4847 4848 if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{}); 4849 4850 // if not @"" syntax, just use raw token slice 4851 if (ident_name_raw[0] != '@') { 4852 if (isPrimitive(ident_name_raw)) return astgen.failTok(test_name_token, "cannot test a primitive", .{}); 4853 } 4854 4855 // Local variables, including function parameters. 4856 const name_str_index = try astgen.identAsString(test_name_token); 4857 var s = scope; 4858 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already 4859 var num_namespaces_out: u32 = 0; 4860 var capturing_namespace: ?*Scope.Namespace = null; 4861 while (true) switch (s.tag) { 4862 .local_val => { 4863 const local_val = s.cast(Scope.LocalVal).?; 4864 if (local_val.name == name_str_index) { 4865 local_val.used = test_name_token; 4866 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{ 4867 @tagName(local_val.id_cat), 4868 }, &[_]u32{ 4869 try astgen.errNoteTok(local_val.token_src, "{s} declared here", .{ 4870 @tagName(local_val.id_cat), 4871 }), 4872 }); 4873 } 4874 s = local_val.parent; 4875 }, 4876 .local_ptr => { 4877 const local_ptr = s.cast(Scope.LocalPtr).?; 4878 if (local_ptr.name == name_str_index) { 4879 local_ptr.used = test_name_token; 4880 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{ 4881 @tagName(local_ptr.id_cat), 4882 }, &[_]u32{ 4883 try astgen.errNoteTok(local_ptr.token_src, "{s} declared here", .{ 4884 @tagName(local_ptr.id_cat), 4885 }), 4886 }); 4887 } 4888 s = local_ptr.parent; 4889 }, 4890 .gen_zir => s = s.cast(GenZir).?.parent, 4891 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, 4892 .namespace => { 4893 const ns = s.cast(Scope.Namespace).?; 4894 if (ns.decls.get(name_str_index)) |i| { 4895 if (found_already) |f| { 4896 return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{ 4897 try astgen.errNoteNode(f, "declared here", .{}), 4898 try astgen.errNoteNode(i, "also declared here", .{}), 4899 }); 4900 } 4901 // We found a match but must continue looking for ambiguous references to decls. 4902 found_already = i; 4903 } 4904 num_namespaces_out += 1; 4905 capturing_namespace = ns; 4906 s = ns.parent; 4907 }, 4908 .top => break, 4909 }; 4910 if (found_already == null) { 4911 const ident_name = try astgen.identifierTokenString(test_name_token); 4912 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name}); 4913 } 4914 4915 break :blk .{ .decltest = name_str_index }; 4916 }, 4917 }; 4918 4919 var fn_block: GenZir = .{ 4920 .is_comptime = false, 4921 .decl_node_index = node, 4922 .decl_line = decl_block.decl_line, 4923 .parent = &decl_block.base, 4924 .astgen = astgen, 4925 .instructions = decl_block.instructions, 4926 .instructions_top = decl_block.instructions.items.len, 4927 }; 4928 defer fn_block.unstack(); 4929 4930 const prev_within_fn = astgen.within_fn; 4931 const prev_fn_block = astgen.fn_block; 4932 const prev_fn_ret_ty = astgen.fn_ret_ty; 4933 astgen.within_fn = true; 4934 astgen.fn_block = &fn_block; 4935 astgen.fn_ret_ty = .anyerror_void_error_union_type; 4936 defer { 4937 astgen.within_fn = prev_within_fn; 4938 astgen.fn_block = prev_fn_block; 4939 astgen.fn_ret_ty = prev_fn_ret_ty; 4940 } 4941 4942 astgen.advanceSourceCursorToNode(body_node); 4943 const lbrace_line = astgen.source_line - decl_block.decl_line; 4944 const lbrace_column = astgen.source_column; 4945 4946 const block_result = try fullBodyExpr(&fn_block, &fn_block.base, .{ .rl = .none }, body_node, .normal); 4947 if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) { 4948 4949 // As our last action before the return, "pop" the error trace if needed 4950 _ = try fn_block.addRestoreErrRetIndex(.ret, .always, node); 4951 4952 // Add implicit return at end of function. 4953 _ = try fn_block.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node)); 4954 } 4955 4956 const func_inst = try decl_block.addFunc(.{ 4957 .src_node = node, 4958 4959 .cc_ref = .none, 4960 .cc_gz = null, 4961 .align_ref = .none, 4962 .align_gz = null, 4963 .ret_ref = .anyerror_void_error_union_type, 4964 .ret_gz = null, 4965 .section_ref = .none, 4966 .section_gz = null, 4967 .addrspace_ref = .none, 4968 .addrspace_gz = null, 4969 4970 .lbrace_line = lbrace_line, 4971 .lbrace_column = lbrace_column, 4972 .param_block = decl_inst, 4973 .body_gz = &fn_block, 4974 .lib_name = .empty, 4975 .is_var_args = false, 4976 .is_inferred_error = false, 4977 .is_test = true, 4978 .is_extern = false, 4979 .is_noinline = false, 4980 .noalias_bits = 0, 4981 4982 // Tests don't have a prototype that needs hashing 4983 .proto_hash = .{0} ** 16, 4984 }); 4985 4986 _ = try decl_block.addBreak(.break_inline, decl_inst, func_inst); 4987 4988 var hash: std.zig.SrcHash = undefined; 4989 astgen.src_hasher.final(&hash); 4990 try setDeclaration( 4991 decl_inst, 4992 hash, 4993 test_name, 4994 decl_block.decl_line, 4995 false, 4996 false, 4997 .empty, 4998 &decl_block, 4999 null, 5000 ); 5001 } 5002 5003 fn structDeclInner( 5004 gz: *GenZir, 5005 scope: *Scope, 5006 node: Ast.Node.Index, 5007 container_decl: Ast.full.ContainerDecl, 5008 layout: std.builtin.Type.ContainerLayout, 5009 backing_int_node: Ast.Node.Index, 5010 ) InnerError!Zir.Inst.Ref { 5011 const decl_inst = try gz.reserveInstructionIndex(); 5012 5013 if (container_decl.ast.members.len == 0 and backing_int_node == 0) { 5014 try gz.setStruct(decl_inst, .{ 5015 .src_node = node, 5016 .layout = layout, 5017 .captures_len = 0, 5018 .fields_len = 0, 5019 .decls_len = 0, 5020 .has_backing_int = false, 5021 .known_non_opv = false, 5022 .known_comptime_only = false, 5023 .is_tuple = false, 5024 .any_comptime_fields = false, 5025 .any_default_inits = false, 5026 .any_aligned_fields = false, 5027 .fields_hash = std.zig.hashSrc(@tagName(layout)), 5028 }); 5029 return decl_inst.toRef(); 5030 } 5031 5032 const astgen = gz.astgen; 5033 const gpa = astgen.gpa; 5034 const tree = astgen.tree; 5035 5036 var namespace: Scope.Namespace = .{ 5037 .parent = scope, 5038 .node = node, 5039 .inst = decl_inst, 5040 .declaring_gz = gz, 5041 .maybe_generic = astgen.within_fn, 5042 }; 5043 defer namespace.deinit(gpa); 5044 5045 // The struct_decl instruction introduces a scope in which the decls of the struct 5046 // are in scope, so that field types, alignments, and default value expressions 5047 // can refer to decls within the struct itself. 5048 astgen.advanceSourceCursorToNode(node); 5049 var block_scope: GenZir = .{ 5050 .parent = &namespace.base, 5051 .decl_node_index = node, 5052 .decl_line = gz.decl_line, 5053 .astgen = astgen, 5054 .is_comptime = true, 5055 .instructions = gz.instructions, 5056 .instructions_top = gz.instructions.items.len, 5057 }; 5058 defer block_scope.unstack(); 5059 5060 const scratch_top = astgen.scratch.items.len; 5061 defer astgen.scratch.items.len = scratch_top; 5062 5063 var backing_int_body_len: usize = 0; 5064 const backing_int_ref: Zir.Inst.Ref = blk: { 5065 if (backing_int_node != 0) { 5066 if (layout != .@"packed") { 5067 return astgen.failNode(backing_int_node, "non-packed struct does not support backing integer type", .{}); 5068 } else { 5069 const backing_int_ref = try typeExpr(&block_scope, &namespace.base, backing_int_node); 5070 if (!block_scope.isEmpty()) { 5071 if (!block_scope.endsWithNoReturn()) { 5072 _ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref); 5073 } 5074 5075 const body = block_scope.instructionsSlice(); 5076 const old_scratch_len = astgen.scratch.items.len; 5077 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); 5078 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 5079 backing_int_body_len = astgen.scratch.items.len - old_scratch_len; 5080 block_scope.instructions.items.len = block_scope.instructions_top; 5081 } 5082 break :blk backing_int_ref; 5083 } 5084 } else { 5085 break :blk .none; 5086 } 5087 }; 5088 5089 const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"struct"); 5090 const field_count: u32 = @intCast(container_decl.ast.members.len - decl_count); 5091 5092 const bits_per_field = 4; 5093 const max_field_size = 5; 5094 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size); 5095 defer wip_members.deinit(); 5096 5097 // We will use the scratch buffer, starting here, for the bodies: 5098 // bodies: { // for every fields_len 5099 // field_type_body_inst: Inst, // for each field_type_body_len 5100 // align_body_inst: Inst, // for each align_body_len 5101 // init_body_inst: Inst, // for each init_body_len 5102 // } 5103 // Note that the scratch buffer is simultaneously being used by WipMembers, however 5104 // it will not access any elements beyond this point in the ArrayList. It also 5105 // accesses via the ArrayList items field so it can handle the scratch buffer being 5106 // reallocated. 5107 // No defer needed here because it is handled by `wip_members.deinit()` above. 5108 const bodies_start = astgen.scratch.items.len; 5109 5110 const node_tags = tree.nodes.items(.tag); 5111 const is_tuple = for (container_decl.ast.members) |member_node| { 5112 const container_field = tree.fullContainerField(member_node) orelse continue; 5113 if (container_field.ast.tuple_like) break true; 5114 } else false; 5115 5116 if (is_tuple) switch (layout) { 5117 .auto => {}, 5118 .@"extern" => return astgen.failNode(node, "extern tuples are not supported", .{}), 5119 .@"packed" => return astgen.failNode(node, "packed tuples are not supported", .{}), 5120 }; 5121 5122 if (is_tuple) for (container_decl.ast.members) |member_node| { 5123 switch (node_tags[member_node]) { 5124 .container_field_init, 5125 .container_field_align, 5126 .container_field, 5127 .@"comptime", 5128 .test_decl, 5129 => continue, 5130 else => { 5131 const tuple_member = for (container_decl.ast.members) |maybe_tuple| switch (node_tags[maybe_tuple]) { 5132 .container_field_init, 5133 .container_field_align, 5134 .container_field, 5135 => break maybe_tuple, 5136 else => {}, 5137 } else unreachable; 5138 return astgen.failNodeNotes( 5139 member_node, 5140 "tuple declarations cannot contain declarations", 5141 .{}, 5142 &[_]u32{ 5143 try astgen.errNoteNode(tuple_member, "tuple field here", .{}), 5144 }, 5145 ); 5146 }, 5147 } 5148 }; 5149 5150 const old_hasher = astgen.src_hasher; 5151 defer astgen.src_hasher = old_hasher; 5152 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 5153 astgen.src_hasher.update(@tagName(layout)); 5154 if (backing_int_node != 0) { 5155 astgen.src_hasher.update(tree.getNodeSource(backing_int_node)); 5156 } 5157 5158 var known_non_opv = false; 5159 var known_comptime_only = false; 5160 var any_comptime_fields = false; 5161 var any_aligned_fields = false; 5162 var any_default_inits = false; 5163 for (container_decl.ast.members) |member_node| { 5164 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { 5165 .decl => continue, 5166 .field => |field| field, 5167 }; 5168 5169 astgen.src_hasher.update(tree.getNodeSource(member_node)); 5170 5171 if (!is_tuple) { 5172 const field_name = try astgen.identAsString(member.ast.main_token); 5173 5174 member.convertToNonTupleLike(astgen.tree.nodes); 5175 assert(!member.ast.tuple_like); 5176 5177 wip_members.appendToField(@intFromEnum(field_name)); 5178 } else if (!member.ast.tuple_like) { 5179 return astgen.failTok(member.ast.main_token, "tuple field has a name", .{}); 5180 } 5181 5182 const doc_comment_index = try astgen.docCommentAsString(member.firstToken()); 5183 wip_members.appendToField(@intFromEnum(doc_comment_index)); 5184 5185 if (member.ast.type_expr == 0) { 5186 return astgen.failTok(member.ast.main_token, "struct field missing type", .{}); 5187 } 5188 5189 const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr); 5190 const have_type_body = !block_scope.isEmpty(); 5191 const have_align = member.ast.align_expr != 0; 5192 const have_value = member.ast.value_expr != 0; 5193 const is_comptime = member.comptime_token != null; 5194 5195 if (is_comptime) { 5196 switch (layout) { 5197 .@"packed" => return astgen.failTok(member.comptime_token.?, "packed struct fields cannot be marked comptime", .{}), 5198 .@"extern" => return astgen.failTok(member.comptime_token.?, "extern struct fields cannot be marked comptime", .{}), 5199 .auto => any_comptime_fields = true, 5200 } 5201 } else { 5202 known_non_opv = known_non_opv or 5203 nodeImpliesMoreThanOnePossibleValue(tree, member.ast.type_expr); 5204 known_comptime_only = known_comptime_only or 5205 nodeImpliesComptimeOnly(tree, member.ast.type_expr); 5206 } 5207 wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, have_type_body }); 5208 5209 if (have_type_body) { 5210 if (!block_scope.endsWithNoReturn()) { 5211 _ = try block_scope.addBreak(.break_inline, decl_inst, field_type); 5212 } 5213 const body = block_scope.instructionsSlice(); 5214 const old_scratch_len = astgen.scratch.items.len; 5215 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); 5216 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 5217 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len)); 5218 block_scope.instructions.items.len = block_scope.instructions_top; 5219 } else { 5220 wip_members.appendToField(@intFromEnum(field_type)); 5221 } 5222 5223 if (have_align) { 5224 if (layout == .@"packed") { 5225 try astgen.appendErrorNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{}); 5226 } 5227 any_aligned_fields = true; 5228 const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, member.ast.align_expr); 5229 if (!block_scope.endsWithNoReturn()) { 5230 _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref); 5231 } 5232 const body = block_scope.instructionsSlice(); 5233 const old_scratch_len = astgen.scratch.items.len; 5234 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); 5235 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 5236 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len)); 5237 block_scope.instructions.items.len = block_scope.instructions_top; 5238 } 5239 5240 if (have_value) { 5241 any_default_inits = true; 5242 5243 // The decl_inst is used as here so that we can easily reconstruct a mapping 5244 // between it and the field type when the fields inits are analyzed. 5245 const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = decl_inst.toRef() } }; 5246 5247 const default_inst = try expr(&block_scope, &namespace.base, ri, member.ast.value_expr); 5248 if (!block_scope.endsWithNoReturn()) { 5249 _ = try block_scope.addBreak(.break_inline, decl_inst, default_inst); 5250 } 5251 const body = block_scope.instructionsSlice(); 5252 const old_scratch_len = astgen.scratch.items.len; 5253 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); 5254 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 5255 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len)); 5256 block_scope.instructions.items.len = block_scope.instructions_top; 5257 } else if (member.comptime_token) |comptime_token| { 5258 return astgen.failTok(comptime_token, "comptime field without default initialization value", .{}); 5259 } 5260 } 5261 5262 var fields_hash: std.zig.SrcHash = undefined; 5263 astgen.src_hasher.final(&fields_hash); 5264 5265 try gz.setStruct(decl_inst, .{ 5266 .src_node = node, 5267 .layout = layout, 5268 .captures_len = @intCast(namespace.captures.count()), 5269 .fields_len = field_count, 5270 .decls_len = decl_count, 5271 .has_backing_int = backing_int_ref != .none, 5272 .known_non_opv = known_non_opv, 5273 .known_comptime_only = known_comptime_only, 5274 .is_tuple = is_tuple, 5275 .any_comptime_fields = any_comptime_fields, 5276 .any_default_inits = any_default_inits, 5277 .any_aligned_fields = any_aligned_fields, 5278 .fields_hash = fields_hash, 5279 }); 5280 5281 wip_members.finishBits(bits_per_field); 5282 const decls_slice = wip_members.declsSlice(); 5283 const fields_slice = wip_members.fieldsSlice(); 5284 const bodies_slice = astgen.scratch.items[bodies_start..]; 5285 try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len + 2 + 5286 decls_slice.len + namespace.captures.count() + fields_slice.len + bodies_slice.len); 5287 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); 5288 if (backing_int_ref != .none) { 5289 astgen.extra.appendAssumeCapacity(@intCast(backing_int_body_len)); 5290 if (backing_int_body_len == 0) { 5291 astgen.extra.appendAssumeCapacity(@intFromEnum(backing_int_ref)); 5292 } else { 5293 astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]); 5294 } 5295 } 5296 astgen.extra.appendSliceAssumeCapacity(decls_slice); 5297 astgen.extra.appendSliceAssumeCapacity(fields_slice); 5298 astgen.extra.appendSliceAssumeCapacity(bodies_slice); 5299 5300 block_scope.unstack(); 5301 return decl_inst.toRef(); 5302 } 5303 5304 fn unionDeclInner( 5305 gz: *GenZir, 5306 scope: *Scope, 5307 node: Ast.Node.Index, 5308 members: []const Ast.Node.Index, 5309 layout: std.builtin.Type.ContainerLayout, 5310 arg_node: Ast.Node.Index, 5311 auto_enum_tok: ?Ast.TokenIndex, 5312 ) InnerError!Zir.Inst.Ref { 5313 const decl_inst = try gz.reserveInstructionIndex(); 5314 5315 const astgen = gz.astgen; 5316 const gpa = astgen.gpa; 5317 5318 var namespace: Scope.Namespace = .{ 5319 .parent = scope, 5320 .node = node, 5321 .inst = decl_inst, 5322 .declaring_gz = gz, 5323 .maybe_generic = astgen.within_fn, 5324 }; 5325 defer namespace.deinit(gpa); 5326 5327 // The union_decl instruction introduces a scope in which the decls of the union 5328 // are in scope, so that field types, alignments, and default value expressions 5329 // can refer to decls within the union itself. 5330 astgen.advanceSourceCursorToNode(node); 5331 var block_scope: GenZir = .{ 5332 .parent = &namespace.base, 5333 .decl_node_index = node, 5334 .decl_line = gz.decl_line, 5335 .astgen = astgen, 5336 .is_comptime = true, 5337 .instructions = gz.instructions, 5338 .instructions_top = gz.instructions.items.len, 5339 }; 5340 defer block_scope.unstack(); 5341 5342 const decl_count = try astgen.scanContainer(&namespace, members, .@"union"); 5343 const field_count: u32 = @intCast(members.len - decl_count); 5344 5345 if (layout != .auto and (auto_enum_tok != null or arg_node != 0)) { 5346 if (arg_node != 0) { 5347 return astgen.failNode(arg_node, "{s} union does not support enum tag type", .{@tagName(layout)}); 5348 } else { 5349 return astgen.failTok(auto_enum_tok.?, "{s} union does not support enum tag type", .{@tagName(layout)}); 5350 } 5351 } 5352 5353 const arg_inst: Zir.Inst.Ref = if (arg_node != 0) 5354 try typeExpr(&block_scope, &namespace.base, arg_node) 5355 else 5356 .none; 5357 5358 const bits_per_field = 4; 5359 const max_field_size = 5; 5360 var any_aligned_fields = false; 5361 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size); 5362 defer wip_members.deinit(); 5363 5364 const old_hasher = astgen.src_hasher; 5365 defer astgen.src_hasher = old_hasher; 5366 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 5367 astgen.src_hasher.update(@tagName(layout)); 5368 astgen.src_hasher.update(&.{@intFromBool(auto_enum_tok != null)}); 5369 if (arg_node != 0) { 5370 astgen.src_hasher.update(astgen.tree.getNodeSource(arg_node)); 5371 } 5372 5373 for (members) |member_node| { 5374 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { 5375 .decl => continue, 5376 .field => |field| field, 5377 }; 5378 astgen.src_hasher.update(astgen.tree.getNodeSource(member_node)); 5379 member.convertToNonTupleLike(astgen.tree.nodes); 5380 if (member.ast.tuple_like) { 5381 return astgen.failTok(member.ast.main_token, "union field missing name", .{}); 5382 } 5383 if (member.comptime_token) |comptime_token| { 5384 return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{}); 5385 } 5386 5387 const field_name = try astgen.identAsString(member.ast.main_token); 5388 wip_members.appendToField(@intFromEnum(field_name)); 5389 5390 const doc_comment_index = try astgen.docCommentAsString(member.firstToken()); 5391 wip_members.appendToField(@intFromEnum(doc_comment_index)); 5392 5393 const have_type = member.ast.type_expr != 0; 5394 const have_align = member.ast.align_expr != 0; 5395 const have_value = member.ast.value_expr != 0; 5396 const unused = false; 5397 wip_members.nextField(bits_per_field, .{ have_type, have_align, have_value, unused }); 5398 5399 if (have_type) { 5400 const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr); 5401 wip_members.appendToField(@intFromEnum(field_type)); 5402 } else if (arg_inst == .none and auto_enum_tok == null) { 5403 return astgen.failNode(member_node, "union field missing type", .{}); 5404 } 5405 if (have_align) { 5406 const align_inst = try expr(&block_scope, &block_scope.base, coerced_align_ri, member.ast.align_expr); 5407 wip_members.appendToField(@intFromEnum(align_inst)); 5408 any_aligned_fields = true; 5409 } 5410 if (have_value) { 5411 if (arg_inst == .none) { 5412 return astgen.failNodeNotes( 5413 node, 5414 "explicitly valued tagged union missing integer tag type", 5415 .{}, 5416 &[_]u32{ 5417 try astgen.errNoteNode( 5418 member.ast.value_expr, 5419 "tag value specified here", 5420 .{}, 5421 ), 5422 }, 5423 ); 5424 } 5425 if (auto_enum_tok == null) { 5426 return astgen.failNodeNotes( 5427 node, 5428 "explicitly valued tagged union requires inferred enum tag type", 5429 .{}, 5430 &[_]u32{ 5431 try astgen.errNoteNode( 5432 member.ast.value_expr, 5433 "tag value specified here", 5434 .{}, 5435 ), 5436 }, 5437 ); 5438 } 5439 const tag_value = try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr); 5440 wip_members.appendToField(@intFromEnum(tag_value)); 5441 } 5442 } 5443 5444 var fields_hash: std.zig.SrcHash = undefined; 5445 astgen.src_hasher.final(&fields_hash); 5446 5447 if (!block_scope.isEmpty()) { 5448 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); 5449 } 5450 5451 const body = block_scope.instructionsSlice(); 5452 const body_len = astgen.countBodyLenAfterFixups(body); 5453 5454 try gz.setUnion(decl_inst, .{ 5455 .src_node = node, 5456 .layout = layout, 5457 .tag_type = arg_inst, 5458 .captures_len = @intCast(namespace.captures.count()), 5459 .body_len = body_len, 5460 .fields_len = field_count, 5461 .decls_len = decl_count, 5462 .auto_enum_tag = auto_enum_tok != null, 5463 .any_aligned_fields = any_aligned_fields, 5464 .fields_hash = fields_hash, 5465 }); 5466 5467 wip_members.finishBits(bits_per_field); 5468 const decls_slice = wip_members.declsSlice(); 5469 const fields_slice = wip_members.fieldsSlice(); 5470 try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len + body_len + fields_slice.len); 5471 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); 5472 astgen.extra.appendSliceAssumeCapacity(decls_slice); 5473 astgen.appendBodyWithFixups(body); 5474 astgen.extra.appendSliceAssumeCapacity(fields_slice); 5475 5476 block_scope.unstack(); 5477 return decl_inst.toRef(); 5478 } 5479 5480 fn containerDecl( 5481 gz: *GenZir, 5482 scope: *Scope, 5483 ri: ResultInfo, 5484 node: Ast.Node.Index, 5485 container_decl: Ast.full.ContainerDecl, 5486 ) InnerError!Zir.Inst.Ref { 5487 const astgen = gz.astgen; 5488 const gpa = astgen.gpa; 5489 const tree = astgen.tree; 5490 const token_tags = tree.tokens.items(.tag); 5491 5492 const prev_fn_block = astgen.fn_block; 5493 astgen.fn_block = null; 5494 defer astgen.fn_block = prev_fn_block; 5495 5496 // We must not create any types until Sema. Here the goal is only to generate 5497 // ZIR for all the field types, alignments, and default value expressions. 5498 5499 switch (token_tags[container_decl.ast.main_token]) { 5500 .keyword_struct => { 5501 const layout: std.builtin.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (token_tags[t]) { 5502 .keyword_packed => .@"packed", 5503 .keyword_extern => .@"extern", 5504 else => unreachable, 5505 } else .auto; 5506 5507 const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg); 5508 return rvalue(gz, ri, result, node); 5509 }, 5510 .keyword_union => { 5511 const layout: std.builtin.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (token_tags[t]) { 5512 .keyword_packed => .@"packed", 5513 .keyword_extern => .@"extern", 5514 else => unreachable, 5515 } else .auto; 5516 5517 const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, container_decl.ast.enum_token); 5518 return rvalue(gz, ri, result, node); 5519 }, 5520 .keyword_enum => { 5521 if (container_decl.layout_token) |t| { 5522 return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{}); 5523 } 5524 // Count total fields as well as how many have explicitly provided tag values. 5525 const counts = blk: { 5526 var values: usize = 0; 5527 var total_fields: usize = 0; 5528 var decls: usize = 0; 5529 var nonexhaustive_node: Ast.Node.Index = 0; 5530 var nonfinal_nonexhaustive = false; 5531 for (container_decl.ast.members) |member_node| { 5532 var member = tree.fullContainerField(member_node) orelse { 5533 decls += 1; 5534 continue; 5535 }; 5536 member.convertToNonTupleLike(astgen.tree.nodes); 5537 if (member.ast.tuple_like) { 5538 return astgen.failTok(member.ast.main_token, "enum field missing name", .{}); 5539 } 5540 if (member.comptime_token) |comptime_token| { 5541 return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{}); 5542 } 5543 if (member.ast.type_expr != 0) { 5544 return astgen.failNodeNotes( 5545 member.ast.type_expr, 5546 "enum fields do not have types", 5547 .{}, 5548 &[_]u32{ 5549 try astgen.errNoteNode( 5550 node, 5551 "consider 'union(enum)' here to make it a tagged union", 5552 .{}, 5553 ), 5554 }, 5555 ); 5556 } 5557 if (member.ast.align_expr != 0) { 5558 return astgen.failNode(member.ast.align_expr, "enum fields cannot be aligned", .{}); 5559 } 5560 5561 const name_token = member.ast.main_token; 5562 if (mem.eql(u8, tree.tokenSlice(name_token), "_")) { 5563 if (nonexhaustive_node != 0) { 5564 return astgen.failNodeNotes( 5565 member_node, 5566 "redundant non-exhaustive enum mark", 5567 .{}, 5568 &[_]u32{ 5569 try astgen.errNoteNode( 5570 nonexhaustive_node, 5571 "other mark here", 5572 .{}, 5573 ), 5574 }, 5575 ); 5576 } 5577 nonexhaustive_node = member_node; 5578 if (member.ast.value_expr != 0) { 5579 return astgen.failNode(member.ast.value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{}); 5580 } 5581 continue; 5582 } else if (nonexhaustive_node != 0) { 5583 nonfinal_nonexhaustive = true; 5584 } 5585 total_fields += 1; 5586 if (member.ast.value_expr != 0) { 5587 if (container_decl.ast.arg == 0) { 5588 return astgen.failNode(member.ast.value_expr, "value assigned to enum tag with inferred tag type", .{}); 5589 } 5590 values += 1; 5591 } 5592 } 5593 if (nonfinal_nonexhaustive) { 5594 return astgen.failNode(nonexhaustive_node, "'_' field of non-exhaustive enum must be last", .{}); 5595 } 5596 break :blk .{ 5597 .total_fields = total_fields, 5598 .values = values, 5599 .decls = decls, 5600 .nonexhaustive_node = nonexhaustive_node, 5601 }; 5602 }; 5603 if (counts.nonexhaustive_node != 0 and container_decl.ast.arg == 0) { 5604 try astgen.appendErrorNodeNotes( 5605 node, 5606 "non-exhaustive enum missing integer tag type", 5607 .{}, 5608 &[_]u32{ 5609 try astgen.errNoteNode( 5610 counts.nonexhaustive_node, 5611 "marked non-exhaustive here", 5612 .{}, 5613 ), 5614 }, 5615 ); 5616 } 5617 // In this case we must generate ZIR code for the tag values, similar to 5618 // how structs are handled above. 5619 const nonexhaustive = counts.nonexhaustive_node != 0; 5620 5621 const decl_inst = try gz.reserveInstructionIndex(); 5622 5623 var namespace: Scope.Namespace = .{ 5624 .parent = scope, 5625 .node = node, 5626 .inst = decl_inst, 5627 .declaring_gz = gz, 5628 .maybe_generic = astgen.within_fn, 5629 }; 5630 defer namespace.deinit(gpa); 5631 5632 // The enum_decl instruction introduces a scope in which the decls of the enum 5633 // are in scope, so that tag values can refer to decls within the enum itself. 5634 astgen.advanceSourceCursorToNode(node); 5635 var block_scope: GenZir = .{ 5636 .parent = &namespace.base, 5637 .decl_node_index = node, 5638 .decl_line = gz.decl_line, 5639 .astgen = astgen, 5640 .is_comptime = true, 5641 .instructions = gz.instructions, 5642 .instructions_top = gz.instructions.items.len, 5643 }; 5644 defer block_scope.unstack(); 5645 5646 _ = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"enum"); 5647 namespace.base.tag = .namespace; 5648 5649 const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0) 5650 try comptimeExpr(&block_scope, &namespace.base, coerced_type_ri, container_decl.ast.arg) 5651 else 5652 .none; 5653 5654 const bits_per_field = 1; 5655 const max_field_size = 3; 5656 var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(counts.decls), @intCast(counts.total_fields), bits_per_field, max_field_size); 5657 defer wip_members.deinit(); 5658 5659 const old_hasher = astgen.src_hasher; 5660 defer astgen.src_hasher = old_hasher; 5661 astgen.src_hasher = std.zig.SrcHasher.init(.{}); 5662 if (container_decl.ast.arg != 0) { 5663 astgen.src_hasher.update(tree.getNodeSource(container_decl.ast.arg)); 5664 } 5665 astgen.src_hasher.update(&.{@intFromBool(nonexhaustive)}); 5666 5667 for (container_decl.ast.members) |member_node| { 5668 if (member_node == counts.nonexhaustive_node) 5669 continue; 5670 astgen.src_hasher.update(tree.getNodeSource(member_node)); 5671 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { 5672 .decl => continue, 5673 .field => |field| field, 5674 }; 5675 member.convertToNonTupleLike(astgen.tree.nodes); 5676 assert(member.comptime_token == null); 5677 assert(member.ast.type_expr == 0); 5678 assert(member.ast.align_expr == 0); 5679 5680 const field_name = try astgen.identAsString(member.ast.main_token); 5681 wip_members.appendToField(@intFromEnum(field_name)); 5682 5683 const doc_comment_index = try astgen.docCommentAsString(member.firstToken()); 5684 wip_members.appendToField(@intFromEnum(doc_comment_index)); 5685 5686 const have_value = member.ast.value_expr != 0; 5687 wip_members.nextField(bits_per_field, .{have_value}); 5688 5689 if (have_value) { 5690 if (arg_inst == .none) { 5691 return astgen.failNodeNotes( 5692 node, 5693 "explicitly valued enum missing integer tag type", 5694 .{}, 5695 &[_]u32{ 5696 try astgen.errNoteNode( 5697 member.ast.value_expr, 5698 "tag value specified here", 5699 .{}, 5700 ), 5701 }, 5702 ); 5703 } 5704 const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr); 5705 wip_members.appendToField(@intFromEnum(tag_value_inst)); 5706 } 5707 } 5708 5709 if (!block_scope.isEmpty()) { 5710 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); 5711 } 5712 5713 var fields_hash: std.zig.SrcHash = undefined; 5714 astgen.src_hasher.final(&fields_hash); 5715 5716 const body = block_scope.instructionsSlice(); 5717 const body_len = astgen.countBodyLenAfterFixups(body); 5718 5719 try gz.setEnum(decl_inst, .{ 5720 .src_node = node, 5721 .nonexhaustive = nonexhaustive, 5722 .tag_type = arg_inst, 5723 .captures_len = @intCast(namespace.captures.count()), 5724 .body_len = body_len, 5725 .fields_len = @intCast(counts.total_fields), 5726 .decls_len = @intCast(counts.decls), 5727 .fields_hash = fields_hash, 5728 }); 5729 5730 wip_members.finishBits(bits_per_field); 5731 const decls_slice = wip_members.declsSlice(); 5732 const fields_slice = wip_members.fieldsSlice(); 5733 try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len + body_len + fields_slice.len); 5734 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); 5735 astgen.extra.appendSliceAssumeCapacity(decls_slice); 5736 astgen.appendBodyWithFixups(body); 5737 astgen.extra.appendSliceAssumeCapacity(fields_slice); 5738 5739 block_scope.unstack(); 5740 return rvalue(gz, ri, decl_inst.toRef(), node); 5741 }, 5742 .keyword_opaque => { 5743 assert(container_decl.ast.arg == 0); 5744 5745 const decl_inst = try gz.reserveInstructionIndex(); 5746 5747 var namespace: Scope.Namespace = .{ 5748 .parent = scope, 5749 .node = node, 5750 .inst = decl_inst, 5751 .declaring_gz = gz, 5752 .maybe_generic = astgen.within_fn, 5753 }; 5754 defer namespace.deinit(gpa); 5755 5756 astgen.advanceSourceCursorToNode(node); 5757 var block_scope: GenZir = .{ 5758 .parent = &namespace.base, 5759 .decl_node_index = node, 5760 .decl_line = gz.decl_line, 5761 .astgen = astgen, 5762 .is_comptime = true, 5763 .instructions = gz.instructions, 5764 .instructions_top = gz.instructions.items.len, 5765 }; 5766 defer block_scope.unstack(); 5767 5768 const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"opaque"); 5769 5770 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0); 5771 defer wip_members.deinit(); 5772 5773 for (container_decl.ast.members) |member_node| { 5774 const res = try containerMember(&block_scope, &namespace.base, &wip_members, member_node); 5775 if (res == .field) { 5776 return astgen.failNode(member_node, "opaque types cannot have fields", .{}); 5777 } 5778 } 5779 5780 try gz.setOpaque(decl_inst, .{ 5781 .src_node = node, 5782 .captures_len = @intCast(namespace.captures.count()), 5783 .decls_len = decl_count, 5784 }); 5785 5786 wip_members.finishBits(0); 5787 const decls_slice = wip_members.declsSlice(); 5788 try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len); 5789 astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys())); 5790 astgen.extra.appendSliceAssumeCapacity(decls_slice); 5791 5792 block_scope.unstack(); 5793 return rvalue(gz, ri, decl_inst.toRef(), node); 5794 }, 5795 else => unreachable, 5796 } 5797 } 5798 5799 const ContainerMemberResult = union(enum) { decl, field: Ast.full.ContainerField }; 5800 5801 fn containerMember( 5802 gz: *GenZir, 5803 scope: *Scope, 5804 wip_members: *WipMembers, 5805 member_node: Ast.Node.Index, 5806 ) InnerError!ContainerMemberResult { 5807 const astgen = gz.astgen; 5808 const tree = astgen.tree; 5809 const node_tags = tree.nodes.items(.tag); 5810 const node_datas = tree.nodes.items(.data); 5811 switch (node_tags[member_node]) { 5812 .container_field_init, 5813 .container_field_align, 5814 .container_field, 5815 => return ContainerMemberResult{ .field = tree.fullContainerField(member_node).? }, 5816 5817 .fn_proto, 5818 .fn_proto_multi, 5819 .fn_proto_one, 5820 .fn_proto_simple, 5821 .fn_decl, 5822 => { 5823 var buf: [1]Ast.Node.Index = undefined; 5824 const full = tree.fullFnProto(&buf, member_node).?; 5825 const body = if (node_tags[member_node] == .fn_decl) node_datas[member_node].rhs else 0; 5826 5827 astgen.fnDecl(gz, scope, wip_members, member_node, body, full) catch |err| switch (err) { 5828 error.OutOfMemory => return error.OutOfMemory, 5829 error.AnalysisFail => {}, 5830 }; 5831 }, 5832 5833 .global_var_decl, 5834 .local_var_decl, 5835 .simple_var_decl, 5836 .aligned_var_decl, 5837 => { 5838 astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.fullVarDecl(member_node).?) catch |err| switch (err) { 5839 error.OutOfMemory => return error.OutOfMemory, 5840 error.AnalysisFail => {}, 5841 }; 5842 }, 5843 5844 .@"comptime" => { 5845 astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) { 5846 error.OutOfMemory => return error.OutOfMemory, 5847 error.AnalysisFail => {}, 5848 }; 5849 }, 5850 .@"usingnamespace" => { 5851 astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) { 5852 error.OutOfMemory => return error.OutOfMemory, 5853 error.AnalysisFail => {}, 5854 }; 5855 }, 5856 .test_decl => { 5857 astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) { 5858 error.OutOfMemory => return error.OutOfMemory, 5859 error.AnalysisFail => {}, 5860 }; 5861 }, 5862 else => unreachable, 5863 } 5864 return .decl; 5865 } 5866 5867 fn errorSetDecl(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 5868 const astgen = gz.astgen; 5869 const gpa = astgen.gpa; 5870 const tree = astgen.tree; 5871 const main_tokens = tree.nodes.items(.main_token); 5872 const token_tags = tree.tokens.items(.tag); 5873 5874 const payload_index = try reserveExtra(astgen, @typeInfo(Zir.Inst.ErrorSetDecl).@"struct".fields.len); 5875 var fields_len: usize = 0; 5876 { 5877 var idents: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.TokenIndex) = .empty; 5878 defer idents.deinit(gpa); 5879 5880 const error_token = main_tokens[node]; 5881 var tok_i = error_token + 2; 5882 while (true) : (tok_i += 1) { 5883 switch (token_tags[tok_i]) { 5884 .doc_comment, .comma => {}, 5885 .identifier => { 5886 const str_index = try astgen.identAsString(tok_i); 5887 const gop = try idents.getOrPut(gpa, str_index); 5888 if (gop.found_existing) { 5889 const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(str_index))); 5890 defer gpa.free(name); 5891 return astgen.failTokNotes( 5892 tok_i, 5893 "duplicate error set field '{s}'", 5894 .{name}, 5895 &[_]u32{ 5896 try astgen.errNoteTok( 5897 gop.value_ptr.*, 5898 "previous declaration here", 5899 .{}, 5900 ), 5901 }, 5902 ); 5903 } 5904 gop.value_ptr.* = tok_i; 5905 5906 try astgen.extra.ensureUnusedCapacity(gpa, 2); 5907 astgen.extra.appendAssumeCapacity(@intFromEnum(str_index)); 5908 const doc_comment_index = try astgen.docCommentAsString(tok_i); 5909 astgen.extra.appendAssumeCapacity(@intFromEnum(doc_comment_index)); 5910 fields_len += 1; 5911 }, 5912 .r_brace => break, 5913 else => unreachable, 5914 } 5915 } 5916 } 5917 5918 setExtra(astgen, payload_index, Zir.Inst.ErrorSetDecl{ 5919 .fields_len = @intCast(fields_len), 5920 }); 5921 const result = try gz.addPlNodePayloadIndex(.error_set_decl, node, payload_index); 5922 return rvalue(gz, ri, result, node); 5923 } 5924 5925 fn tryExpr( 5926 parent_gz: *GenZir, 5927 scope: *Scope, 5928 ri: ResultInfo, 5929 node: Ast.Node.Index, 5930 operand_node: Ast.Node.Index, 5931 ) InnerError!Zir.Inst.Ref { 5932 const astgen = parent_gz.astgen; 5933 5934 const fn_block = astgen.fn_block orelse { 5935 return astgen.failNode(node, "'try' outside function scope", .{}); 5936 }; 5937 5938 if (parent_gz.any_defer_node != 0) { 5939 return astgen.failNodeNotes(node, "'try' not allowed inside defer expression", .{}, &.{ 5940 try astgen.errNoteNode( 5941 parent_gz.any_defer_node, 5942 "defer expression here", 5943 .{}, 5944 ), 5945 }); 5946 } 5947 5948 // Ensure debug line/column information is emitted for this try expression. 5949 // Then we will save the line/column so that we can emit another one that goes 5950 // "backwards" because we want to evaluate the operand, but then put the debug 5951 // info back at the try keyword for error return tracing. 5952 if (!parent_gz.is_comptime) { 5953 try emitDbgNode(parent_gz, node); 5954 } 5955 const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; 5956 5957 const operand_rl: ResultInfo.Loc, const block_tag: Zir.Inst.Tag = switch (ri.rl) { 5958 .ref => .{ .ref, .try_ptr }, 5959 .ref_coerced_ty => |payload_ptr_ty| .{ 5960 .{ .ref_coerced_ty = try parent_gz.addUnNode(.try_ref_operand_ty, payload_ptr_ty, node) }, 5961 .try_ptr, 5962 }, 5963 else => if (try ri.rl.resultType(parent_gz, node)) |payload_ty| .{ 5964 // `coerced_ty` is OK due to the `rvalue` call below 5965 .{ .coerced_ty = try parent_gz.addUnNode(.try_operand_ty, payload_ty, node) }, 5966 .@"try", 5967 } else .{ .none, .@"try" }, 5968 }; 5969 const operand_ri: ResultInfo = .{ .rl = operand_rl, .ctx = .error_handling_expr }; 5970 // This could be a pointer or value depending on the `ri` parameter. 5971 const operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, node); 5972 const try_inst = try parent_gz.makeBlockInst(block_tag, node); 5973 try parent_gz.instructions.append(astgen.gpa, try_inst); 5974 5975 var else_scope = parent_gz.makeSubBlock(scope); 5976 defer else_scope.unstack(); 5977 5978 const err_tag = switch (ri.rl) { 5979 .ref, .ref_coerced_ty => Zir.Inst.Tag.err_union_code_ptr, 5980 else => Zir.Inst.Tag.err_union_code, 5981 }; 5982 const err_code = try else_scope.addUnNode(err_tag, operand, node); 5983 try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code }); 5984 try emitDbgStmt(&else_scope, try_lc); 5985 _ = try else_scope.addUnNode(.ret_node, err_code, node); 5986 5987 try else_scope.setTryBody(try_inst, operand); 5988 const result = try_inst.toRef(); 5989 switch (ri.rl) { 5990 .ref, .ref_coerced_ty => return result, 5991 else => return rvalue(parent_gz, ri, result, node), 5992 } 5993 } 5994 5995 fn orelseCatchExpr( 5996 parent_gz: *GenZir, 5997 scope: *Scope, 5998 ri: ResultInfo, 5999 node: Ast.Node.Index, 6000 lhs: Ast.Node.Index, 6001 cond_op: Zir.Inst.Tag, 6002 unwrap_op: Zir.Inst.Tag, 6003 unwrap_code_op: Zir.Inst.Tag, 6004 rhs: Ast.Node.Index, 6005 payload_token: ?Ast.TokenIndex, 6006 ) InnerError!Zir.Inst.Ref { 6007 const astgen = parent_gz.astgen; 6008 const tree = astgen.tree; 6009 6010 const need_rl = astgen.nodes_need_rl.contains(node); 6011 const block_ri: ResultInfo = if (need_rl) ri else .{ 6012 .rl = switch (ri.rl) { 6013 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 6014 .inferred_ptr => .none, 6015 else => ri.rl, 6016 }, 6017 .ctx = ri.ctx, 6018 }; 6019 // We need to call `rvalue` to write through to the pointer only if we had a 6020 // result pointer and aren't forwarding it. 6021 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 6022 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 6023 6024 const do_err_trace = astgen.fn_block != null and (cond_op == .is_non_err or cond_op == .is_non_err_ptr); 6025 6026 var block_scope = parent_gz.makeSubBlock(scope); 6027 block_scope.setBreakResultInfo(block_ri); 6028 defer block_scope.unstack(); 6029 6030 const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) { 6031 .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none }, 6032 else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none }, 6033 }; 6034 // This could be a pointer or value depending on the `operand_ri` parameter. 6035 // We cannot use `block_scope.break_result_info` because that has the bare 6036 // type, whereas this expression has the optional type. Later we make 6037 // up for this fact by calling rvalue on the else branch. 6038 const operand = try reachableExpr(&block_scope, &block_scope.base, operand_ri, lhs, rhs); 6039 const cond = try block_scope.addUnNode(cond_op, operand, node); 6040 const condbr = try block_scope.addCondBr(.condbr, node); 6041 6042 const block = try parent_gz.makeBlockInst(.block, node); 6043 try block_scope.setBlockBody(block); 6044 // block_scope unstacked now, can add new instructions to parent_gz 6045 try parent_gz.instructions.append(astgen.gpa, block); 6046 6047 var then_scope = block_scope.makeSubBlock(scope); 6048 defer then_scope.unstack(); 6049 6050 // This could be a pointer or value depending on `unwrap_op`. 6051 const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node); 6052 const then_result = switch (ri.rl) { 6053 .ref, .ref_coerced_ty => unwrapped_payload, 6054 else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node), 6055 }; 6056 _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node); 6057 6058 var else_scope = block_scope.makeSubBlock(scope); 6059 defer else_scope.unstack(); 6060 6061 // We know that the operand (almost certainly) modified the error return trace, 6062 // so signal to Sema that it should save the new index for restoring later. 6063 if (do_err_trace and nodeMayAppendToErrorTrace(tree, lhs)) 6064 _ = try else_scope.addSaveErrRetIndex(.always); 6065 6066 var err_val_scope: Scope.LocalVal = undefined; 6067 const else_sub_scope = blk: { 6068 const payload = payload_token orelse break :blk &else_scope.base; 6069 const err_str = tree.tokenSlice(payload); 6070 if (mem.eql(u8, err_str, "_")) { 6071 return astgen.failTok(payload, "discard of error capture; omit it instead", .{}); 6072 } 6073 const err_name = try astgen.identAsString(payload); 6074 6075 try astgen.detectLocalShadowing(scope, err_name, payload, err_str, .capture); 6076 6077 err_val_scope = .{ 6078 .parent = &else_scope.base, 6079 .gen_zir = &else_scope, 6080 .name = err_name, 6081 .inst = try else_scope.addUnNode(unwrap_code_op, operand, node), 6082 .token_src = payload, 6083 .id_cat = .capture, 6084 }; 6085 break :blk &err_val_scope.base; 6086 }; 6087 6088 const else_result = try fullBodyExpr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs, .allow_branch_hint); 6089 if (!else_scope.endsWithNoReturn()) { 6090 // As our last action before the break, "pop" the error trace if needed 6091 if (do_err_trace) 6092 try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, rhs, else_result); 6093 6094 _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, rhs); 6095 } 6096 try checkUsed(parent_gz, &else_scope.base, else_sub_scope); 6097 6098 try setCondBrPayload(condbr, cond, &then_scope, &else_scope); 6099 6100 if (need_result_rvalue) { 6101 return rvalue(parent_gz, ri, block.toRef(), node); 6102 } else { 6103 return block.toRef(); 6104 } 6105 } 6106 6107 /// Return whether the identifier names of two tokens are equal. Resolves @"" 6108 /// tokens without allocating. 6109 /// OK in theory it could do it without allocating. This implementation 6110 /// allocates when the @"" form is used. 6111 fn tokenIdentEql(astgen: *AstGen, token1: Ast.TokenIndex, token2: Ast.TokenIndex) !bool { 6112 const ident_name_1 = try astgen.identifierTokenString(token1); 6113 const ident_name_2 = try astgen.identifierTokenString(token2); 6114 return mem.eql(u8, ident_name_1, ident_name_2); 6115 } 6116 6117 fn fieldAccess( 6118 gz: *GenZir, 6119 scope: *Scope, 6120 ri: ResultInfo, 6121 node: Ast.Node.Index, 6122 ) InnerError!Zir.Inst.Ref { 6123 switch (ri.rl) { 6124 .ref, .ref_coerced_ty => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node), 6125 else => { 6126 const access = try addFieldAccess(.field_val, gz, scope, .{ .rl = .none }, node); 6127 return rvalue(gz, ri, access, node); 6128 }, 6129 } 6130 } 6131 6132 fn addFieldAccess( 6133 tag: Zir.Inst.Tag, 6134 gz: *GenZir, 6135 scope: *Scope, 6136 lhs_ri: ResultInfo, 6137 node: Ast.Node.Index, 6138 ) InnerError!Zir.Inst.Ref { 6139 const astgen = gz.astgen; 6140 const tree = astgen.tree; 6141 const main_tokens = tree.nodes.items(.main_token); 6142 const node_datas = tree.nodes.items(.data); 6143 6144 const object_node = node_datas[node].lhs; 6145 const dot_token = main_tokens[node]; 6146 const field_ident = dot_token + 1; 6147 const str_index = try astgen.identAsString(field_ident); 6148 const lhs = try expr(gz, scope, lhs_ri, object_node); 6149 6150 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 6151 try emitDbgStmt(gz, cursor); 6152 6153 return gz.addPlNode(tag, node, Zir.Inst.Field{ 6154 .lhs = lhs, 6155 .field_name_start = str_index, 6156 }); 6157 } 6158 6159 fn arrayAccess( 6160 gz: *GenZir, 6161 scope: *Scope, 6162 ri: ResultInfo, 6163 node: Ast.Node.Index, 6164 ) InnerError!Zir.Inst.Ref { 6165 const tree = gz.astgen.tree; 6166 const node_datas = tree.nodes.items(.data); 6167 switch (ri.rl) { 6168 .ref, .ref_coerced_ty => { 6169 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); 6170 6171 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 6172 6173 const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs); 6174 try emitDbgStmt(gz, cursor); 6175 6176 return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); 6177 }, 6178 else => { 6179 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs); 6180 6181 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 6182 6183 const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs); 6184 try emitDbgStmt(gz, cursor); 6185 6186 return rvalue(gz, ri, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }), node); 6187 }, 6188 } 6189 } 6190 6191 fn simpleBinOp( 6192 gz: *GenZir, 6193 scope: *Scope, 6194 ri: ResultInfo, 6195 node: Ast.Node.Index, 6196 op_inst_tag: Zir.Inst.Tag, 6197 ) InnerError!Zir.Inst.Ref { 6198 const astgen = gz.astgen; 6199 const tree = astgen.tree; 6200 const node_datas = tree.nodes.items(.data); 6201 6202 if (op_inst_tag == .cmp_neq or op_inst_tag == .cmp_eq) { 6203 const node_tags = tree.nodes.items(.tag); 6204 const str = if (op_inst_tag == .cmp_eq) "==" else "!="; 6205 if (node_tags[node_datas[node].lhs] == .string_literal or 6206 node_tags[node_datas[node].rhs] == .string_literal) 6207 return astgen.failNode(node, "cannot compare strings with {s}", .{str}); 6208 } 6209 6210 const lhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].lhs, node); 6211 const cursor = switch (op_inst_tag) { 6212 .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, node), 6213 else => undefined, 6214 }; 6215 const rhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].rhs, node); 6216 6217 switch (op_inst_tag) { 6218 .add, .sub, .mul, .div, .mod_rem => { 6219 try emitDbgStmt(gz, cursor); 6220 }, 6221 else => {}, 6222 } 6223 const result = try gz.addPlNode(op_inst_tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); 6224 return rvalue(gz, ri, result, node); 6225 } 6226 6227 fn simpleStrTok( 6228 gz: *GenZir, 6229 ri: ResultInfo, 6230 ident_token: Ast.TokenIndex, 6231 node: Ast.Node.Index, 6232 op_inst_tag: Zir.Inst.Tag, 6233 ) InnerError!Zir.Inst.Ref { 6234 const astgen = gz.astgen; 6235 const str_index = try astgen.identAsString(ident_token); 6236 const result = try gz.addStrTok(op_inst_tag, str_index, ident_token); 6237 return rvalue(gz, ri, result, node); 6238 } 6239 6240 fn boolBinOp( 6241 gz: *GenZir, 6242 scope: *Scope, 6243 ri: ResultInfo, 6244 node: Ast.Node.Index, 6245 zir_tag: Zir.Inst.Tag, 6246 ) InnerError!Zir.Inst.Ref { 6247 const astgen = gz.astgen; 6248 const tree = astgen.tree; 6249 const node_datas = tree.nodes.items(.data); 6250 6251 const lhs = try expr(gz, scope, coerced_bool_ri, node_datas[node].lhs); 6252 const bool_br = (try gz.addPlNodePayloadIndex(zir_tag, node, undefined)).toIndex().?; 6253 6254 var rhs_scope = gz.makeSubBlock(scope); 6255 defer rhs_scope.unstack(); 6256 const rhs = try fullBodyExpr(&rhs_scope, &rhs_scope.base, coerced_bool_ri, node_datas[node].rhs, .allow_branch_hint); 6257 if (!gz.refIsNoReturn(rhs)) { 6258 _ = try rhs_scope.addBreakWithSrcNode(.break_inline, bool_br, rhs, node_datas[node].rhs); 6259 } 6260 try rhs_scope.setBoolBrBody(bool_br, lhs); 6261 6262 const block_ref = bool_br.toRef(); 6263 return rvalue(gz, ri, block_ref, node); 6264 } 6265 6266 fn ifExpr( 6267 parent_gz: *GenZir, 6268 scope: *Scope, 6269 ri: ResultInfo, 6270 node: Ast.Node.Index, 6271 if_full: Ast.full.If, 6272 ) InnerError!Zir.Inst.Ref { 6273 const astgen = parent_gz.astgen; 6274 const tree = astgen.tree; 6275 const token_tags = tree.tokens.items(.tag); 6276 6277 const do_err_trace = astgen.fn_block != null and if_full.error_token != null; 6278 6279 const need_rl = astgen.nodes_need_rl.contains(node); 6280 const block_ri: ResultInfo = if (need_rl) ri else .{ 6281 .rl = switch (ri.rl) { 6282 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 6283 .inferred_ptr => .none, 6284 else => ri.rl, 6285 }, 6286 .ctx = ri.ctx, 6287 }; 6288 // We need to call `rvalue` to write through to the pointer only if we had a 6289 // result pointer and aren't forwarding it. 6290 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 6291 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 6292 6293 var block_scope = parent_gz.makeSubBlock(scope); 6294 block_scope.setBreakResultInfo(block_ri); 6295 defer block_scope.unstack(); 6296 6297 const payload_is_ref = if (if_full.payload_token) |payload_token| 6298 token_tags[payload_token] == .asterisk 6299 else 6300 false; 6301 6302 try emitDbgNode(parent_gz, if_full.ast.cond_expr); 6303 const cond: struct { 6304 inst: Zir.Inst.Ref, 6305 bool_bit: Zir.Inst.Ref, 6306 } = c: { 6307 if (if_full.error_token) |_| { 6308 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none, .ctx = .error_handling_expr }; 6309 const err_union = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr); 6310 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err; 6311 break :c .{ 6312 .inst = err_union, 6313 .bool_bit = try block_scope.addUnNode(tag, err_union, if_full.ast.cond_expr), 6314 }; 6315 } else if (if_full.payload_token) |_| { 6316 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none }; 6317 const optional = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr); 6318 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null; 6319 break :c .{ 6320 .inst = optional, 6321 .bool_bit = try block_scope.addUnNode(tag, optional, if_full.ast.cond_expr), 6322 }; 6323 } else { 6324 const cond = try expr(&block_scope, &block_scope.base, coerced_bool_ri, if_full.ast.cond_expr); 6325 break :c .{ 6326 .inst = cond, 6327 .bool_bit = cond, 6328 }; 6329 } 6330 }; 6331 6332 const condbr = try block_scope.addCondBr(.condbr, node); 6333 6334 const block = try parent_gz.makeBlockInst(.block, node); 6335 try block_scope.setBlockBody(block); 6336 // block_scope unstacked now, can add new instructions to parent_gz 6337 try parent_gz.instructions.append(astgen.gpa, block); 6338 6339 var then_scope = parent_gz.makeSubBlock(scope); 6340 defer then_scope.unstack(); 6341 6342 var payload_val_scope: Scope.LocalVal = undefined; 6343 6344 const then_node = if_full.ast.then_expr; 6345 const then_sub_scope = s: { 6346 if (if_full.error_token != null) { 6347 if (if_full.payload_token) |payload_token| { 6348 const tag: Zir.Inst.Tag = if (payload_is_ref) 6349 .err_union_payload_unsafe_ptr 6350 else 6351 .err_union_payload_unsafe; 6352 const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node); 6353 const token_name_index = payload_token + @intFromBool(payload_is_ref); 6354 const ident_name = try astgen.identAsString(token_name_index); 6355 const token_name_str = tree.tokenSlice(token_name_index); 6356 if (mem.eql(u8, "_", token_name_str)) { 6357 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 6358 break :s &then_scope.base; 6359 } 6360 try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str, .capture); 6361 payload_val_scope = .{ 6362 .parent = &then_scope.base, 6363 .gen_zir = &then_scope, 6364 .name = ident_name, 6365 .inst = payload_inst, 6366 .token_src = token_name_index, 6367 .id_cat = .capture, 6368 }; 6369 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst); 6370 break :s &payload_val_scope.base; 6371 } else { 6372 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node); 6373 break :s &then_scope.base; 6374 } 6375 } else if (if_full.payload_token) |payload_token| { 6376 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token; 6377 const tag: Zir.Inst.Tag = if (payload_is_ref) 6378 .optional_payload_unsafe_ptr 6379 else 6380 .optional_payload_unsafe; 6381 const ident_bytes = tree.tokenSlice(ident_token); 6382 if (mem.eql(u8, "_", ident_bytes)) { 6383 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 6384 break :s &then_scope.base; 6385 } 6386 const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node); 6387 const ident_name = try astgen.identAsString(ident_token); 6388 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture); 6389 payload_val_scope = .{ 6390 .parent = &then_scope.base, 6391 .gen_zir = &then_scope, 6392 .name = ident_name, 6393 .inst = payload_inst, 6394 .token_src = ident_token, 6395 .id_cat = .capture, 6396 }; 6397 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst); 6398 break :s &payload_val_scope.base; 6399 } else { 6400 break :s &then_scope.base; 6401 } 6402 }; 6403 6404 const then_result = try fullBodyExpr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node, .allow_branch_hint); 6405 try checkUsed(parent_gz, &then_scope.base, then_sub_scope); 6406 if (!then_scope.endsWithNoReturn()) { 6407 _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, then_node); 6408 } 6409 6410 var else_scope = parent_gz.makeSubBlock(scope); 6411 defer else_scope.unstack(); 6412 6413 // We know that the operand (almost certainly) modified the error return trace, 6414 // so signal to Sema that it should save the new index for restoring later. 6415 if (do_err_trace and nodeMayAppendToErrorTrace(tree, if_full.ast.cond_expr)) 6416 _ = try else_scope.addSaveErrRetIndex(.always); 6417 6418 const else_node = if_full.ast.else_expr; 6419 if (else_node != 0) { 6420 const sub_scope = s: { 6421 if (if_full.error_token) |error_token| { 6422 const tag: Zir.Inst.Tag = if (payload_is_ref) 6423 .err_union_code_ptr 6424 else 6425 .err_union_code; 6426 const payload_inst = try else_scope.addUnNode(tag, cond.inst, if_full.ast.cond_expr); 6427 const ident_name = try astgen.identAsString(error_token); 6428 const error_token_str = tree.tokenSlice(error_token); 6429 if (mem.eql(u8, "_", error_token_str)) 6430 break :s &else_scope.base; 6431 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str, .capture); 6432 payload_val_scope = .{ 6433 .parent = &else_scope.base, 6434 .gen_zir = &else_scope, 6435 .name = ident_name, 6436 .inst = payload_inst, 6437 .token_src = error_token, 6438 .id_cat = .capture, 6439 }; 6440 try else_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst); 6441 break :s &payload_val_scope.base; 6442 } else { 6443 break :s &else_scope.base; 6444 } 6445 }; 6446 const else_result = try fullBodyExpr(&else_scope, sub_scope, block_scope.break_result_info, else_node, .allow_branch_hint); 6447 if (!else_scope.endsWithNoReturn()) { 6448 // As our last action before the break, "pop" the error trace if needed 6449 if (do_err_trace) 6450 try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, else_node, else_result); 6451 _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, else_node); 6452 } 6453 try checkUsed(parent_gz, &else_scope.base, sub_scope); 6454 } else { 6455 const result = try rvalue(&else_scope, ri, .void_value, node); 6456 _ = try else_scope.addBreak(.@"break", block, result); 6457 } 6458 6459 try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope); 6460 6461 if (need_result_rvalue) { 6462 return rvalue(parent_gz, ri, block.toRef(), node); 6463 } else { 6464 return block.toRef(); 6465 } 6466 } 6467 6468 /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`. 6469 fn setCondBrPayload( 6470 condbr: Zir.Inst.Index, 6471 cond: Zir.Inst.Ref, 6472 then_scope: *GenZir, 6473 else_scope: *GenZir, 6474 ) !void { 6475 defer then_scope.unstack(); 6476 defer else_scope.unstack(); 6477 const astgen = then_scope.astgen; 6478 const then_body = then_scope.instructionsSliceUpto(else_scope); 6479 const else_body = else_scope.instructionsSlice(); 6480 const then_body_len = astgen.countBodyLenAfterFixups(then_body); 6481 const else_body_len = astgen.countBodyLenAfterFixups(else_body); 6482 try astgen.extra.ensureUnusedCapacity( 6483 astgen.gpa, 6484 @typeInfo(Zir.Inst.CondBr).@"struct".fields.len + then_body_len + else_body_len, 6485 ); 6486 6487 const zir_datas = astgen.instructions.items(.data); 6488 zir_datas[@intFromEnum(condbr)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{ 6489 .condition = cond, 6490 .then_body_len = then_body_len, 6491 .else_body_len = else_body_len, 6492 }); 6493 astgen.appendBodyWithFixups(then_body); 6494 astgen.appendBodyWithFixups(else_body); 6495 } 6496 6497 fn whileExpr( 6498 parent_gz: *GenZir, 6499 scope: *Scope, 6500 ri: ResultInfo, 6501 node: Ast.Node.Index, 6502 while_full: Ast.full.While, 6503 is_statement: bool, 6504 ) InnerError!Zir.Inst.Ref { 6505 const astgen = parent_gz.astgen; 6506 const tree = astgen.tree; 6507 const token_tags = tree.tokens.items(.tag); 6508 6509 const need_rl = astgen.nodes_need_rl.contains(node); 6510 const block_ri: ResultInfo = if (need_rl) ri else .{ 6511 .rl = switch (ri.rl) { 6512 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 6513 .inferred_ptr => .none, 6514 else => ri.rl, 6515 }, 6516 .ctx = ri.ctx, 6517 }; 6518 // We need to call `rvalue` to write through to the pointer only if we had a 6519 // result pointer and aren't forwarding it. 6520 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 6521 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 6522 6523 if (while_full.label_token) |label_token| { 6524 try astgen.checkLabelRedefinition(scope, label_token); 6525 } 6526 6527 const is_inline = while_full.inline_token != null; 6528 if (parent_gz.is_comptime and is_inline) { 6529 return astgen.failTok(while_full.inline_token.?, "redundant inline keyword in comptime scope", .{}); 6530 } 6531 const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop; 6532 const loop_block = try parent_gz.makeBlockInst(loop_tag, node); 6533 try parent_gz.instructions.append(astgen.gpa, loop_block); 6534 6535 var loop_scope = parent_gz.makeSubBlock(scope); 6536 loop_scope.is_inline = is_inline; 6537 loop_scope.setBreakResultInfo(block_ri); 6538 defer loop_scope.unstack(); 6539 6540 var cond_scope = parent_gz.makeSubBlock(&loop_scope.base); 6541 defer cond_scope.unstack(); 6542 6543 const payload_is_ref = if (while_full.payload_token) |payload_token| 6544 token_tags[payload_token] == .asterisk 6545 else 6546 false; 6547 6548 try emitDbgNode(parent_gz, while_full.ast.cond_expr); 6549 const cond: struct { 6550 inst: Zir.Inst.Ref, 6551 bool_bit: Zir.Inst.Ref, 6552 } = c: { 6553 if (while_full.error_token) |_| { 6554 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none }; 6555 const err_union = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr, .normal); 6556 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err; 6557 break :c .{ 6558 .inst = err_union, 6559 .bool_bit = try cond_scope.addUnNode(tag, err_union, while_full.ast.cond_expr), 6560 }; 6561 } else if (while_full.payload_token) |_| { 6562 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none }; 6563 const optional = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr, .normal); 6564 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null; 6565 break :c .{ 6566 .inst = optional, 6567 .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.cond_expr), 6568 }; 6569 } else { 6570 const cond = try fullBodyExpr(&cond_scope, &cond_scope.base, coerced_bool_ri, while_full.ast.cond_expr, .normal); 6571 break :c .{ 6572 .inst = cond, 6573 .bool_bit = cond, 6574 }; 6575 } 6576 }; 6577 6578 const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; 6579 const condbr = try cond_scope.addCondBr(condbr_tag, node); 6580 const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block; 6581 const cond_block = try loop_scope.makeBlockInst(block_tag, node); 6582 try cond_scope.setBlockBody(cond_block); 6583 // cond_scope unstacked now, can add new instructions to loop_scope 6584 try loop_scope.instructions.append(astgen.gpa, cond_block); 6585 6586 // make scope now but don't stack on parent_gz until loop_scope 6587 // gets unstacked after cont_expr is emitted and added below 6588 var then_scope = parent_gz.makeSubBlock(&cond_scope.base); 6589 then_scope.instructions_top = GenZir.unstacked_top; 6590 defer then_scope.unstack(); 6591 6592 var dbg_var_name: Zir.NullTerminatedString = .empty; 6593 var dbg_var_inst: Zir.Inst.Ref = undefined; 6594 var opt_payload_inst: Zir.Inst.OptionalIndex = .none; 6595 var payload_val_scope: Scope.LocalVal = undefined; 6596 const then_sub_scope = s: { 6597 if (while_full.error_token != null) { 6598 if (while_full.payload_token) |payload_token| { 6599 const tag: Zir.Inst.Tag = if (payload_is_ref) 6600 .err_union_payload_unsafe_ptr 6601 else 6602 .err_union_payload_unsafe; 6603 // will add this instruction to then_scope.instructions below 6604 const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr); 6605 opt_payload_inst = payload_inst.toOptional(); 6606 const ident_token = payload_token + @intFromBool(payload_is_ref); 6607 const ident_bytes = tree.tokenSlice(ident_token); 6608 if (mem.eql(u8, "_", ident_bytes)) { 6609 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 6610 break :s &then_scope.base; 6611 } 6612 const ident_name = try astgen.identAsString(ident_token); 6613 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture); 6614 payload_val_scope = .{ 6615 .parent = &then_scope.base, 6616 .gen_zir = &then_scope, 6617 .name = ident_name, 6618 .inst = payload_inst.toRef(), 6619 .token_src = ident_token, 6620 .id_cat = .capture, 6621 }; 6622 dbg_var_name = ident_name; 6623 dbg_var_inst = payload_inst.toRef(); 6624 break :s &payload_val_scope.base; 6625 } else { 6626 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node); 6627 break :s &then_scope.base; 6628 } 6629 } else if (while_full.payload_token) |payload_token| { 6630 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token; 6631 const tag: Zir.Inst.Tag = if (payload_is_ref) 6632 .optional_payload_unsafe_ptr 6633 else 6634 .optional_payload_unsafe; 6635 // will add this instruction to then_scope.instructions below 6636 const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr); 6637 opt_payload_inst = payload_inst.toOptional(); 6638 const ident_name = try astgen.identAsString(ident_token); 6639 const ident_bytes = tree.tokenSlice(ident_token); 6640 if (mem.eql(u8, "_", ident_bytes)) { 6641 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 6642 break :s &then_scope.base; 6643 } 6644 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture); 6645 payload_val_scope = .{ 6646 .parent = &then_scope.base, 6647 .gen_zir = &then_scope, 6648 .name = ident_name, 6649 .inst = payload_inst.toRef(), 6650 .token_src = ident_token, 6651 .id_cat = .capture, 6652 }; 6653 dbg_var_name = ident_name; 6654 dbg_var_inst = payload_inst.toRef(); 6655 break :s &payload_val_scope.base; 6656 } else { 6657 break :s &then_scope.base; 6658 } 6659 }; 6660 6661 var continue_scope = parent_gz.makeSubBlock(then_sub_scope); 6662 continue_scope.instructions_top = GenZir.unstacked_top; 6663 defer continue_scope.unstack(); 6664 const continue_block = try then_scope.makeBlockInst(block_tag, node); 6665 6666 const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; 6667 _ = try loop_scope.addNode(repeat_tag, node); 6668 6669 try loop_scope.setBlockBody(loop_block); 6670 loop_scope.break_block = loop_block.toOptional(); 6671 loop_scope.continue_block = continue_block.toOptional(); 6672 if (while_full.label_token) |label_token| { 6673 loop_scope.label = .{ 6674 .token = label_token, 6675 .block_inst = loop_block, 6676 }; 6677 } 6678 6679 // done adding instructions to loop_scope, can now stack then_scope 6680 then_scope.instructions_top = then_scope.instructions.items.len; 6681 6682 const then_node = while_full.ast.then_expr; 6683 if (opt_payload_inst.unwrap()) |payload_inst| { 6684 try then_scope.instructions.append(astgen.gpa, payload_inst); 6685 } 6686 if (dbg_var_name != .empty) try then_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst); 6687 try then_scope.instructions.append(astgen.gpa, continue_block); 6688 // This code could be improved to avoid emitting the continue expr when there 6689 // are no jumps to it. This happens when the last statement of a while body is noreturn 6690 // and there are no `continue` statements. 6691 // Tracking issue: https://github.com/ziglang/zig/issues/9185 6692 if (while_full.ast.cont_expr != 0) { 6693 _ = try unusedResultExpr(&then_scope, then_sub_scope, while_full.ast.cont_expr); 6694 } 6695 6696 continue_scope.instructions_top = continue_scope.instructions.items.len; 6697 { 6698 try emitDbgNode(&continue_scope, then_node); 6699 const unused_result = try fullBodyExpr(&continue_scope, &continue_scope.base, .{ .rl = .none }, then_node, .allow_branch_hint); 6700 _ = try addEnsureResult(&continue_scope, unused_result, then_node); 6701 } 6702 try checkUsed(parent_gz, &then_scope.base, then_sub_scope); 6703 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; 6704 if (!continue_scope.endsWithNoReturn()) { 6705 _ = try continue_scope.addBreak(break_tag, continue_block, .void_value); 6706 } 6707 try continue_scope.setBlockBody(continue_block); 6708 _ = try then_scope.addBreak(break_tag, cond_block, .void_value); 6709 6710 var else_scope = parent_gz.makeSubBlock(&cond_scope.base); 6711 defer else_scope.unstack(); 6712 6713 const else_node = while_full.ast.else_expr; 6714 if (else_node != 0) { 6715 const sub_scope = s: { 6716 if (while_full.error_token) |error_token| { 6717 const tag: Zir.Inst.Tag = if (payload_is_ref) 6718 .err_union_code_ptr 6719 else 6720 .err_union_code; 6721 const else_payload_inst = try else_scope.addUnNode(tag, cond.inst, while_full.ast.cond_expr); 6722 const ident_name = try astgen.identAsString(error_token); 6723 const ident_bytes = tree.tokenSlice(error_token); 6724 if (mem.eql(u8, ident_bytes, "_")) 6725 break :s &else_scope.base; 6726 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, ident_bytes, .capture); 6727 payload_val_scope = .{ 6728 .parent = &else_scope.base, 6729 .gen_zir = &else_scope, 6730 .name = ident_name, 6731 .inst = else_payload_inst, 6732 .token_src = error_token, 6733 .id_cat = .capture, 6734 }; 6735 try else_scope.addDbgVar(.dbg_var_val, ident_name, else_payload_inst); 6736 break :s &payload_val_scope.base; 6737 } else { 6738 break :s &else_scope.base; 6739 } 6740 }; 6741 // Remove the continue block and break block so that `continue` and `break` 6742 // control flow apply to outer loops; not this one. 6743 loop_scope.continue_block = .none; 6744 loop_scope.break_block = .none; 6745 const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint); 6746 if (is_statement) { 6747 _ = try addEnsureResult(&else_scope, else_result, else_node); 6748 } 6749 6750 try checkUsed(parent_gz, &else_scope.base, sub_scope); 6751 if (!else_scope.endsWithNoReturn()) { 6752 _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node); 6753 } 6754 } else { 6755 const result = try rvalue(&else_scope, ri, .void_value, node); 6756 _ = try else_scope.addBreak(break_tag, loop_block, result); 6757 } 6758 6759 if (loop_scope.label) |some| { 6760 if (!some.used) { 6761 try astgen.appendErrorTok(some.token, "unused while loop label", .{}); 6762 } 6763 } 6764 6765 try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope); 6766 6767 const result = if (need_result_rvalue) 6768 try rvalue(parent_gz, ri, loop_block.toRef(), node) 6769 else 6770 loop_block.toRef(); 6771 6772 if (is_statement) { 6773 _ = try parent_gz.addUnNode(.ensure_result_used, result, node); 6774 } 6775 6776 return result; 6777 } 6778 6779 fn forExpr( 6780 parent_gz: *GenZir, 6781 scope: *Scope, 6782 ri: ResultInfo, 6783 node: Ast.Node.Index, 6784 for_full: Ast.full.For, 6785 is_statement: bool, 6786 ) InnerError!Zir.Inst.Ref { 6787 const astgen = parent_gz.astgen; 6788 6789 if (for_full.label_token) |label_token| { 6790 try astgen.checkLabelRedefinition(scope, label_token); 6791 } 6792 6793 const need_rl = astgen.nodes_need_rl.contains(node); 6794 const block_ri: ResultInfo = if (need_rl) ri else .{ 6795 .rl = switch (ri.rl) { 6796 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 6797 .inferred_ptr => .none, 6798 else => ri.rl, 6799 }, 6800 .ctx = ri.ctx, 6801 }; 6802 // We need to call `rvalue` to write through to the pointer only if we had a 6803 // result pointer and aren't forwarding it. 6804 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 6805 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 6806 6807 const is_inline = for_full.inline_token != null; 6808 if (parent_gz.is_comptime and is_inline) { 6809 return astgen.failTok(for_full.inline_token.?, "redundant inline keyword in comptime scope", .{}); 6810 } 6811 const tree = astgen.tree; 6812 const token_tags = tree.tokens.items(.tag); 6813 const node_tags = tree.nodes.items(.tag); 6814 const node_data = tree.nodes.items(.data); 6815 const gpa = astgen.gpa; 6816 6817 // For counters, this is the start value; for indexables, this is the base 6818 // pointer that can be used with elem_ptr and similar instructions. 6819 // Special value `none` means that this is a counter and its start value is 6820 // zero, indicating that the main index counter can be used directly. 6821 const indexables = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len); 6822 defer gpa.free(indexables); 6823 // elements of this array can be `none`, indicating no length check. 6824 const lens = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len); 6825 defer gpa.free(lens); 6826 6827 // We will use a single zero-based counter no matter how many indexables there are. 6828 const index_ptr = blk: { 6829 const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc; 6830 const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node); 6831 // initialize to zero 6832 _ = try parent_gz.addPlNode(.store_node, node, Zir.Inst.Bin{ 6833 .lhs = index_ptr, 6834 .rhs = .zero_usize, 6835 }); 6836 break :blk index_ptr; 6837 }; 6838 6839 var any_len_checks = false; 6840 6841 { 6842 var capture_token = for_full.payload_token; 6843 for (for_full.ast.inputs, indexables, lens) |input, *indexable_ref, *len_ref| { 6844 const capture_is_ref = token_tags[capture_token] == .asterisk; 6845 const ident_tok = capture_token + @intFromBool(capture_is_ref); 6846 const is_discard = mem.eql(u8, tree.tokenSlice(ident_tok), "_"); 6847 6848 if (is_discard and capture_is_ref) { 6849 return astgen.failTok(capture_token, "pointer modifier invalid on discard", .{}); 6850 } 6851 // Skip over the comma, and on to the next capture (or the ending pipe character). 6852 capture_token = ident_tok + 2; 6853 6854 try emitDbgNode(parent_gz, input); 6855 if (node_tags[input] == .for_range) { 6856 if (capture_is_ref) { 6857 return astgen.failTok(ident_tok, "cannot capture reference to range", .{}); 6858 } 6859 const start_node = node_data[input].lhs; 6860 const start_val = try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, start_node); 6861 6862 const end_node = node_data[input].rhs; 6863 const end_val = if (end_node != 0) 6864 try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_data[input].rhs) 6865 else 6866 .none; 6867 6868 if (end_val == .none and is_discard) { 6869 return astgen.failTok(ident_tok, "discard of unbounded counter", .{}); 6870 } 6871 6872 const start_is_zero = nodeIsTriviallyZero(tree, start_node); 6873 const range_len = if (end_val == .none or start_is_zero) 6874 end_val 6875 else 6876 try parent_gz.addPlNode(.sub, input, Zir.Inst.Bin{ 6877 .lhs = end_val, 6878 .rhs = start_val, 6879 }); 6880 6881 any_len_checks = any_len_checks or range_len != .none; 6882 indexable_ref.* = if (start_is_zero) .none else start_val; 6883 len_ref.* = range_len; 6884 } else { 6885 const indexable = try expr(parent_gz, scope, .{ .rl = .none }, input); 6886 6887 any_len_checks = true; 6888 indexable_ref.* = indexable; 6889 len_ref.* = indexable; 6890 } 6891 } 6892 } 6893 6894 if (!any_len_checks) { 6895 return astgen.failNode(node, "unbounded for loop", .{}); 6896 } 6897 6898 // We use a dedicated ZIR instruction to assert the lengths to assist with 6899 // nicer error reporting as well as fewer ZIR bytes emitted. 6900 const len: Zir.Inst.Ref = len: { 6901 const lens_len: u32 = @intCast(lens.len); 6902 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).@"struct".fields.len + lens_len); 6903 const len = try parent_gz.addPlNode(.for_len, node, Zir.Inst.MultiOp{ 6904 .operands_len = lens_len, 6905 }); 6906 appendRefsAssumeCapacity(astgen, lens); 6907 break :len len; 6908 }; 6909 6910 const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop; 6911 const loop_block = try parent_gz.makeBlockInst(loop_tag, node); 6912 try parent_gz.instructions.append(gpa, loop_block); 6913 6914 var loop_scope = parent_gz.makeSubBlock(scope); 6915 loop_scope.is_inline = is_inline; 6916 loop_scope.setBreakResultInfo(block_ri); 6917 defer loop_scope.unstack(); 6918 6919 // We need to finish loop_scope later once we have the deferred refs from then_scope. However, the 6920 // load must be removed from instructions in the meantime or it appears to be part of parent_gz. 6921 const index = try loop_scope.addUnNode(.load, index_ptr, node); 6922 _ = loop_scope.instructions.pop(); 6923 6924 var cond_scope = parent_gz.makeSubBlock(&loop_scope.base); 6925 defer cond_scope.unstack(); 6926 6927 // Check the condition. 6928 const cond = try cond_scope.addPlNode(.cmp_lt, node, Zir.Inst.Bin{ 6929 .lhs = index, 6930 .rhs = len, 6931 }); 6932 6933 const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; 6934 const condbr = try cond_scope.addCondBr(condbr_tag, node); 6935 const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block; 6936 const cond_block = try loop_scope.makeBlockInst(block_tag, node); 6937 try cond_scope.setBlockBody(cond_block); 6938 6939 loop_scope.break_block = loop_block.toOptional(); 6940 loop_scope.continue_block = cond_block.toOptional(); 6941 if (for_full.label_token) |label_token| { 6942 loop_scope.label = .{ 6943 .token = label_token, 6944 .block_inst = loop_block, 6945 }; 6946 } 6947 6948 const then_node = for_full.ast.then_expr; 6949 var then_scope = parent_gz.makeSubBlock(&cond_scope.base); 6950 defer then_scope.unstack(); 6951 6952 const capture_scopes = try gpa.alloc(Scope.LocalVal, for_full.ast.inputs.len); 6953 defer gpa.free(capture_scopes); 6954 6955 const then_sub_scope = blk: { 6956 var capture_token = for_full.payload_token; 6957 var capture_sub_scope: *Scope = &then_scope.base; 6958 for (for_full.ast.inputs, indexables, capture_scopes) |input, indexable_ref, *capture_scope| { 6959 const capture_is_ref = token_tags[capture_token] == .asterisk; 6960 const ident_tok = capture_token + @intFromBool(capture_is_ref); 6961 const capture_name = tree.tokenSlice(ident_tok); 6962 // Skip over the comma, and on to the next capture (or the ending pipe character). 6963 capture_token = ident_tok + 2; 6964 6965 if (mem.eql(u8, capture_name, "_")) continue; 6966 6967 const name_str_index = try astgen.identAsString(ident_tok); 6968 try astgen.detectLocalShadowing(capture_sub_scope, name_str_index, ident_tok, capture_name, .capture); 6969 6970 const capture_inst = inst: { 6971 const is_counter = node_tags[input] == .for_range; 6972 6973 if (indexable_ref == .none) { 6974 // Special case: the main index can be used directly. 6975 assert(is_counter); 6976 assert(!capture_is_ref); 6977 break :inst index; 6978 } 6979 6980 // For counters, we add the index variable to the start value; for 6981 // indexables, we use it as an element index. This is so similar 6982 // that they can share the same code paths, branching only on the 6983 // ZIR tag. 6984 const switch_cond = (@as(u2, @intFromBool(capture_is_ref)) << 1) | @intFromBool(is_counter); 6985 const tag: Zir.Inst.Tag = switch (switch_cond) { 6986 0b00 => .elem_val, 6987 0b01 => .add, 6988 0b10 => .elem_ptr, 6989 0b11 => unreachable, // compile error emitted already 6990 }; 6991 break :inst try then_scope.addPlNode(tag, input, Zir.Inst.Bin{ 6992 .lhs = indexable_ref, 6993 .rhs = index, 6994 }); 6995 }; 6996 6997 capture_scope.* = .{ 6998 .parent = capture_sub_scope, 6999 .gen_zir = &then_scope, 7000 .name = name_str_index, 7001 .inst = capture_inst, 7002 .token_src = ident_tok, 7003 .id_cat = .capture, 7004 }; 7005 7006 try then_scope.addDbgVar(.dbg_var_val, name_str_index, capture_inst); 7007 capture_sub_scope = &capture_scope.base; 7008 } 7009 7010 break :blk capture_sub_scope; 7011 }; 7012 7013 const then_result = try fullBodyExpr(&then_scope, then_sub_scope, .{ .rl = .none }, then_node, .allow_branch_hint); 7014 _ = try addEnsureResult(&then_scope, then_result, then_node); 7015 7016 try checkUsed(parent_gz, &then_scope.base, then_sub_scope); 7017 7018 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; 7019 7020 _ = try then_scope.addBreak(break_tag, cond_block, .void_value); 7021 7022 var else_scope = parent_gz.makeSubBlock(&cond_scope.base); 7023 defer else_scope.unstack(); 7024 7025 const else_node = for_full.ast.else_expr; 7026 if (else_node != 0) { 7027 const sub_scope = &else_scope.base; 7028 // Remove the continue block and break block so that `continue` and `break` 7029 // control flow apply to outer loops; not this one. 7030 loop_scope.continue_block = .none; 7031 loop_scope.break_block = .none; 7032 const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint); 7033 if (is_statement) { 7034 _ = try addEnsureResult(&else_scope, else_result, else_node); 7035 } 7036 if (!else_scope.endsWithNoReturn()) { 7037 _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node); 7038 } 7039 } else { 7040 const result = try rvalue(&else_scope, ri, .void_value, node); 7041 _ = try else_scope.addBreak(break_tag, loop_block, result); 7042 } 7043 7044 if (loop_scope.label) |some| { 7045 if (!some.used) { 7046 try astgen.appendErrorTok(some.token, "unused for loop label", .{}); 7047 } 7048 } 7049 7050 try setCondBrPayload(condbr, cond, &then_scope, &else_scope); 7051 7052 // then_block and else_block unstacked now, can resurrect loop_scope to finally finish it 7053 { 7054 loop_scope.instructions_top = loop_scope.instructions.items.len; 7055 try loop_scope.instructions.appendSlice(gpa, &.{ index.toIndex().?, cond_block }); 7056 7057 // Increment the index variable. 7058 const index_plus_one = try loop_scope.addPlNode(.add_unsafe, node, Zir.Inst.Bin{ 7059 .lhs = index, 7060 .rhs = .one_usize, 7061 }); 7062 _ = try loop_scope.addPlNode(.store_node, node, Zir.Inst.Bin{ 7063 .lhs = index_ptr, 7064 .rhs = index_plus_one, 7065 }); 7066 const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; 7067 _ = try loop_scope.addNode(repeat_tag, node); 7068 7069 try loop_scope.setBlockBody(loop_block); 7070 } 7071 7072 const result = if (need_result_rvalue) 7073 try rvalue(parent_gz, ri, loop_block.toRef(), node) 7074 else 7075 loop_block.toRef(); 7076 7077 if (is_statement) { 7078 _ = try parent_gz.addUnNode(.ensure_result_used, result, node); 7079 } 7080 return result; 7081 } 7082 7083 fn switchExprErrUnion( 7084 parent_gz: *GenZir, 7085 scope: *Scope, 7086 ri: ResultInfo, 7087 catch_or_if_node: Ast.Node.Index, 7088 node_ty: enum { @"catch", @"if" }, 7089 ) InnerError!Zir.Inst.Ref { 7090 const astgen = parent_gz.astgen; 7091 const gpa = astgen.gpa; 7092 const tree = astgen.tree; 7093 const node_datas = tree.nodes.items(.data); 7094 const node_tags = tree.nodes.items(.tag); 7095 const main_tokens = tree.nodes.items(.main_token); 7096 const token_tags = tree.tokens.items(.tag); 7097 7098 const if_full = switch (node_ty) { 7099 .@"catch" => undefined, 7100 .@"if" => tree.fullIf(catch_or_if_node).?, 7101 }; 7102 7103 const switch_node, const operand_node, const error_payload = switch (node_ty) { 7104 .@"catch" => .{ 7105 node_datas[catch_or_if_node].rhs, 7106 node_datas[catch_or_if_node].lhs, 7107 main_tokens[catch_or_if_node] + 2, 7108 }, 7109 .@"if" => .{ 7110 if_full.ast.else_expr, 7111 if_full.ast.cond_expr, 7112 if_full.error_token.?, 7113 }, 7114 }; 7115 assert(node_tags[switch_node] == .@"switch" or node_tags[switch_node] == .switch_comma); 7116 7117 const do_err_trace = astgen.fn_block != null; 7118 7119 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); 7120 const case_nodes = tree.extra_data[extra.start..extra.end]; 7121 7122 const need_rl = astgen.nodes_need_rl.contains(catch_or_if_node); 7123 const block_ri: ResultInfo = if (need_rl) ri else .{ 7124 .rl = switch (ri.rl) { 7125 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, catch_or_if_node)).? }, 7126 .inferred_ptr => .none, 7127 else => ri.rl, 7128 }, 7129 .ctx = ri.ctx, 7130 }; 7131 7132 const payload_is_ref = switch (node_ty) { 7133 .@"if" => if_full.payload_token != null and token_tags[if_full.payload_token.?] == .asterisk, 7134 .@"catch" => ri.rl == .ref or ri.rl == .ref_coerced_ty, 7135 }; 7136 7137 // We need to call `rvalue` to write through to the pointer only if we had a 7138 // result pointer and aren't forwarding it. 7139 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 7140 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 7141 var scalar_cases_len: u32 = 0; 7142 var multi_cases_len: u32 = 0; 7143 var inline_cases_len: u32 = 0; 7144 var has_else = false; 7145 var else_node: Ast.Node.Index = 0; 7146 var else_src: ?Ast.TokenIndex = null; 7147 for (case_nodes) |case_node| { 7148 const case = tree.fullSwitchCase(case_node).?; 7149 7150 if (case.ast.values.len == 0) { 7151 const case_src = case.ast.arrow_token - 1; 7152 if (else_src) |src| { 7153 return astgen.failTokNotes( 7154 case_src, 7155 "multiple else prongs in switch expression", 7156 .{}, 7157 &[_]u32{ 7158 try astgen.errNoteTok( 7159 src, 7160 "previous else prong here", 7161 .{}, 7162 ), 7163 }, 7164 ); 7165 } 7166 has_else = true; 7167 else_node = case_node; 7168 else_src = case_src; 7169 continue; 7170 } else if (case.ast.values.len == 1 and 7171 node_tags[case.ast.values[0]] == .identifier and 7172 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")) 7173 { 7174 const case_src = case.ast.arrow_token - 1; 7175 return astgen.failTokNotes( 7176 case_src, 7177 "'_' prong is not allowed when switching on errors", 7178 .{}, 7179 &[_]u32{ 7180 try astgen.errNoteTok( 7181 case_src, 7182 "consider using 'else'", 7183 .{}, 7184 ), 7185 }, 7186 ); 7187 } 7188 7189 for (case.ast.values) |val| { 7190 if (node_tags[val] == .string_literal) 7191 return astgen.failNode(val, "cannot switch on strings", .{}); 7192 } 7193 7194 if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) { 7195 scalar_cases_len += 1; 7196 } else { 7197 multi_cases_len += 1; 7198 } 7199 if (case.inline_token != null) { 7200 inline_cases_len += 1; 7201 } 7202 } 7203 7204 const operand_ri: ResultInfo = .{ 7205 .rl = if (payload_is_ref) .ref else .none, 7206 .ctx = .error_handling_expr, 7207 }; 7208 7209 astgen.advanceSourceCursorToNode(operand_node); 7210 const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; 7211 7212 const raw_operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, switch_node); 7213 const item_ri: ResultInfo = .{ .rl = .none }; 7214 7215 // This contains the data that goes into the `extra` array for the SwitchBlockErrUnion, except 7216 // the first cases_nodes.len slots are a table that indexes payloads later in the array, 7217 // with the non-error and else case indices coming first, then scalar_cases_len indexes, then 7218 // multi_cases_len indexes 7219 const payloads = &astgen.scratch; 7220 const scratch_top = astgen.scratch.items.len; 7221 const case_table_start = scratch_top; 7222 const scalar_case_table = case_table_start + 1 + @intFromBool(has_else); 7223 const multi_case_table = scalar_case_table + scalar_cases_len; 7224 const case_table_end = multi_case_table + multi_cases_len; 7225 7226 try astgen.scratch.resize(gpa, case_table_end); 7227 defer astgen.scratch.items.len = scratch_top; 7228 7229 var block_scope = parent_gz.makeSubBlock(scope); 7230 // block_scope not used for collecting instructions 7231 block_scope.instructions_top = GenZir.unstacked_top; 7232 block_scope.setBreakResultInfo(block_ri); 7233 7234 // Sema expects a dbg_stmt immediately before switch_block_err_union 7235 try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc); 7236 // This gets added to the parent block later, after the item expressions. 7237 const switch_block = try parent_gz.makeBlockInst(.switch_block_err_union, switch_node); 7238 7239 // We re-use this same scope for all cases, including the special prong, if any. 7240 var case_scope = parent_gz.makeSubBlock(&block_scope.base); 7241 case_scope.instructions_top = GenZir.unstacked_top; 7242 7243 { 7244 const body_len_index: u32 = @intCast(payloads.items.len); 7245 payloads.items[case_table_start] = body_len_index; 7246 try payloads.resize(gpa, body_len_index + 1); // body_len 7247 7248 case_scope.instructions_top = parent_gz.instructions.items.len; 7249 defer case_scope.unstack(); 7250 7251 const unwrap_payload_tag: Zir.Inst.Tag = if (payload_is_ref) 7252 .err_union_payload_unsafe_ptr 7253 else 7254 .err_union_payload_unsafe; 7255 7256 const unwrapped_payload = try case_scope.addUnNode( 7257 unwrap_payload_tag, 7258 raw_operand, 7259 catch_or_if_node, 7260 ); 7261 7262 switch (node_ty) { 7263 .@"catch" => { 7264 const case_result = switch (ri.rl) { 7265 .ref, .ref_coerced_ty => unwrapped_payload, 7266 else => try rvalue( 7267 &case_scope, 7268 block_scope.break_result_info, 7269 unwrapped_payload, 7270 catch_or_if_node, 7271 ), 7272 }; 7273 _ = try case_scope.addBreakWithSrcNode( 7274 .@"break", 7275 switch_block, 7276 case_result, 7277 catch_or_if_node, 7278 ); 7279 }, 7280 .@"if" => { 7281 var payload_val_scope: Scope.LocalVal = undefined; 7282 7283 const then_node = if_full.ast.then_expr; 7284 const then_sub_scope = s: { 7285 assert(if_full.error_token != null); 7286 if (if_full.payload_token) |payload_token| { 7287 const token_name_index = payload_token + @intFromBool(payload_is_ref); 7288 const ident_name = try astgen.identAsString(token_name_index); 7289 const token_name_str = tree.tokenSlice(token_name_index); 7290 if (mem.eql(u8, "_", token_name_str)) 7291 break :s &case_scope.base; 7292 try astgen.detectLocalShadowing( 7293 &case_scope.base, 7294 ident_name, 7295 token_name_index, 7296 token_name_str, 7297 .capture, 7298 ); 7299 payload_val_scope = .{ 7300 .parent = &case_scope.base, 7301 .gen_zir = &case_scope, 7302 .name = ident_name, 7303 .inst = unwrapped_payload, 7304 .token_src = token_name_index, 7305 .id_cat = .capture, 7306 }; 7307 try case_scope.addDbgVar(.dbg_var_val, ident_name, unwrapped_payload); 7308 break :s &payload_val_scope.base; 7309 } else { 7310 _ = try case_scope.addUnNode( 7311 .ensure_err_union_payload_void, 7312 raw_operand, 7313 catch_or_if_node, 7314 ); 7315 break :s &case_scope.base; 7316 } 7317 }; 7318 const then_result = try expr( 7319 &case_scope, 7320 then_sub_scope, 7321 block_scope.break_result_info, 7322 then_node, 7323 ); 7324 try checkUsed(parent_gz, &case_scope.base, then_sub_scope); 7325 if (!case_scope.endsWithNoReturn()) { 7326 _ = try case_scope.addBreakWithSrcNode( 7327 .@"break", 7328 switch_block, 7329 then_result, 7330 then_node, 7331 ); 7332 } 7333 }, 7334 } 7335 7336 const case_slice = case_scope.instructionsSlice(); 7337 // Since we use the switch_block_err_union instruction itself to refer 7338 // to the capture, which will not be added to the child block, we need 7339 // to handle ref_table manually. 7340 const refs_len = refs: { 7341 var n: usize = 0; 7342 var check_inst = switch_block; 7343 while (astgen.ref_table.get(check_inst)) |ref_inst| { 7344 n += 1; 7345 check_inst = ref_inst; 7346 } 7347 break :refs n; 7348 }; 7349 const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice); 7350 try payloads.ensureUnusedCapacity(gpa, body_len); 7351 const capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = switch (node_ty) { 7352 .@"catch" => .none, 7353 .@"if" => if (if_full.payload_token == null) 7354 .none 7355 else if (payload_is_ref) 7356 .by_ref 7357 else 7358 .by_val, 7359 }; 7360 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{ 7361 .body_len = @intCast(body_len), 7362 .capture = capture, 7363 .is_inline = false, 7364 .has_tag_capture = false, 7365 }); 7366 if (astgen.ref_table.fetchRemove(switch_block)) |kv| { 7367 appendPossiblyRefdBodyInst(astgen, payloads, kv.value); 7368 } 7369 appendBodyWithFixupsArrayList(astgen, payloads, case_slice); 7370 } 7371 7372 const err_name = blk: { 7373 const err_str = tree.tokenSlice(error_payload); 7374 if (mem.eql(u8, err_str, "_")) { 7375 return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{}); 7376 } 7377 const err_name = try astgen.identAsString(error_payload); 7378 try astgen.detectLocalShadowing(scope, err_name, error_payload, err_str, .capture); 7379 7380 break :blk err_name; 7381 }; 7382 7383 // allocate a shared dummy instruction for the error capture 7384 const err_inst = err_inst: { 7385 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 7386 try astgen.instructions.append(astgen.gpa, .{ 7387 .tag = .extended, 7388 .data = .{ .extended = .{ 7389 .opcode = .value_placeholder, 7390 .small = undefined, 7391 .operand = undefined, 7392 } }, 7393 }); 7394 break :err_inst inst; 7395 }; 7396 7397 // In this pass we generate all the item and prong expressions for error cases. 7398 var multi_case_index: u32 = 0; 7399 var scalar_case_index: u32 = 0; 7400 var any_uses_err_capture = false; 7401 for (case_nodes) |case_node| { 7402 const case = tree.fullSwitchCase(case_node).?; 7403 7404 const is_multi_case = case.ast.values.len > 1 or 7405 (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range); 7406 7407 var dbg_var_name: Zir.NullTerminatedString = .empty; 7408 var dbg_var_inst: Zir.Inst.Ref = undefined; 7409 var err_scope: Scope.LocalVal = undefined; 7410 var capture_scope: Scope.LocalVal = undefined; 7411 7412 const sub_scope = blk: { 7413 err_scope = .{ 7414 .parent = &case_scope.base, 7415 .gen_zir = &case_scope, 7416 .name = err_name, 7417 .inst = err_inst.toRef(), 7418 .token_src = error_payload, 7419 .id_cat = .capture, 7420 }; 7421 7422 const capture_token = case.payload_token orelse break :blk &err_scope.base; 7423 if (token_tags[capture_token] != .identifier) { 7424 return astgen.failTok(capture_token + 1, "error set cannot be captured by reference", .{}); 7425 } 7426 7427 const capture_slice = tree.tokenSlice(capture_token); 7428 if (mem.eql(u8, capture_slice, "_")) { 7429 return astgen.failTok(capture_token, "discard of error capture; omit it instead", .{}); 7430 } 7431 const tag_name = try astgen.identAsString(capture_token); 7432 try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture); 7433 7434 capture_scope = .{ 7435 .parent = &case_scope.base, 7436 .gen_zir = &case_scope, 7437 .name = tag_name, 7438 .inst = switch_block.toRef(), 7439 .token_src = capture_token, 7440 .id_cat = .capture, 7441 }; 7442 dbg_var_name = tag_name; 7443 dbg_var_inst = switch_block.toRef(); 7444 7445 err_scope.parent = &capture_scope.base; 7446 7447 break :blk &err_scope.base; 7448 }; 7449 7450 const header_index: u32 = @intCast(payloads.items.len); 7451 const body_len_index = if (is_multi_case) blk: { 7452 payloads.items[multi_case_table + multi_case_index] = header_index; 7453 multi_case_index += 1; 7454 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len 7455 7456 // items 7457 var items_len: u32 = 0; 7458 for (case.ast.values) |item_node| { 7459 if (node_tags[item_node] == .switch_range) continue; 7460 items_len += 1; 7461 7462 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node); 7463 try payloads.append(gpa, @intFromEnum(item_inst)); 7464 } 7465 7466 // ranges 7467 var ranges_len: u32 = 0; 7468 for (case.ast.values) |range| { 7469 if (node_tags[range] != .switch_range) continue; 7470 ranges_len += 1; 7471 7472 const first = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].lhs); 7473 const last = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].rhs); 7474 try payloads.appendSlice(gpa, &[_]u32{ 7475 @intFromEnum(first), @intFromEnum(last), 7476 }); 7477 } 7478 7479 payloads.items[header_index] = items_len; 7480 payloads.items[header_index + 1] = ranges_len; 7481 break :blk header_index + 2; 7482 } else if (case_node == else_node) blk: { 7483 payloads.items[case_table_start + 1] = header_index; 7484 try payloads.resize(gpa, header_index + 1); // body_len 7485 break :blk header_index; 7486 } else blk: { 7487 payloads.items[scalar_case_table + scalar_case_index] = header_index; 7488 scalar_case_index += 1; 7489 try payloads.resize(gpa, header_index + 2); // item, body_len 7490 const item_node = case.ast.values[0]; 7491 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node); 7492 payloads.items[header_index] = @intFromEnum(item_inst); 7493 break :blk header_index + 1; 7494 }; 7495 7496 { 7497 // temporarily stack case_scope on parent_gz 7498 case_scope.instructions_top = parent_gz.instructions.items.len; 7499 defer case_scope.unstack(); 7500 7501 if (do_err_trace and nodeMayAppendToErrorTrace(tree, operand_node)) 7502 _ = try case_scope.addSaveErrRetIndex(.always); 7503 7504 if (dbg_var_name != .empty) { 7505 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst); 7506 } 7507 7508 const target_expr_node = case.ast.target_expr; 7509 const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint); 7510 // check capture_scope, not err_scope to avoid false positive unused error capture 7511 try checkUsed(parent_gz, &case_scope.base, err_scope.parent); 7512 const uses_err = err_scope.used != 0 or err_scope.discarded != 0; 7513 if (uses_err) { 7514 try case_scope.addDbgVar(.dbg_var_val, err_name, err_inst.toRef()); 7515 any_uses_err_capture = true; 7516 } 7517 7518 if (!parent_gz.refIsNoReturn(case_result)) { 7519 if (do_err_trace) 7520 try restoreErrRetIndex( 7521 &case_scope, 7522 .{ .block = switch_block }, 7523 block_scope.break_result_info, 7524 target_expr_node, 7525 case_result, 7526 ); 7527 7528 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node); 7529 } 7530 7531 const case_slice = case_scope.instructionsSlice(); 7532 // Since we use the switch_block_err_union instruction itself to refer 7533 // to the capture, which will not be added to the child block, we need 7534 // to handle ref_table manually. 7535 const refs_len = refs: { 7536 var n: usize = 0; 7537 var check_inst = switch_block; 7538 while (astgen.ref_table.get(check_inst)) |ref_inst| { 7539 n += 1; 7540 check_inst = ref_inst; 7541 } 7542 if (uses_err) { 7543 check_inst = err_inst; 7544 while (astgen.ref_table.get(check_inst)) |ref_inst| { 7545 n += 1; 7546 check_inst = ref_inst; 7547 } 7548 } 7549 break :refs n; 7550 }; 7551 const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice); 7552 try payloads.ensureUnusedCapacity(gpa, body_len); 7553 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{ 7554 .body_len = @intCast(body_len), 7555 .capture = if (case.payload_token != null) .by_val else .none, 7556 .is_inline = case.inline_token != null, 7557 .has_tag_capture = false, 7558 }); 7559 if (astgen.ref_table.fetchRemove(switch_block)) |kv| { 7560 appendPossiblyRefdBodyInst(astgen, payloads, kv.value); 7561 } 7562 if (uses_err) { 7563 if (astgen.ref_table.fetchRemove(err_inst)) |kv| { 7564 appendPossiblyRefdBodyInst(astgen, payloads, kv.value); 7565 } 7566 } 7567 appendBodyWithFixupsArrayList(astgen, payloads, case_slice); 7568 } 7569 } 7570 // Now that the item expressions are generated we can add this. 7571 try parent_gz.instructions.append(gpa, switch_block); 7572 7573 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlockErrUnion).@"struct".fields.len + 7574 @intFromBool(multi_cases_len != 0) + 7575 payloads.items.len - case_table_end + 7576 (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len); 7577 7578 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlockErrUnion{ 7579 .operand = raw_operand, 7580 .bits = Zir.Inst.SwitchBlockErrUnion.Bits{ 7581 .has_multi_cases = multi_cases_len != 0, 7582 .has_else = has_else, 7583 .scalar_cases_len = @intCast(scalar_cases_len), 7584 .any_uses_err_capture = any_uses_err_capture, 7585 .payload_is_ref = payload_is_ref, 7586 }, 7587 .main_src_node_offset = parent_gz.nodeIndexToRelative(catch_or_if_node), 7588 }); 7589 7590 if (multi_cases_len != 0) { 7591 astgen.extra.appendAssumeCapacity(multi_cases_len); 7592 } 7593 7594 if (any_uses_err_capture) { 7595 astgen.extra.appendAssumeCapacity(@intFromEnum(err_inst)); 7596 } 7597 7598 const zir_datas = astgen.instructions.items(.data); 7599 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index; 7600 7601 for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| { 7602 var body_len_index = start_index; 7603 var end_index = start_index; 7604 const table_index = case_table_start + i; 7605 if (table_index < scalar_case_table) { 7606 end_index += 1; 7607 } else if (table_index < multi_case_table) { 7608 body_len_index += 1; 7609 end_index += 2; 7610 } else { 7611 body_len_index += 2; 7612 const items_len = payloads.items[start_index]; 7613 const ranges_len = payloads.items[start_index + 1]; 7614 end_index += 3 + items_len + 2 * ranges_len; 7615 } 7616 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]); 7617 end_index += prong_info.body_len; 7618 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]); 7619 } 7620 7621 if (need_result_rvalue) { 7622 return rvalue(parent_gz, ri, switch_block.toRef(), switch_node); 7623 } else { 7624 return switch_block.toRef(); 7625 } 7626 } 7627 7628 fn switchExpr( 7629 parent_gz: *GenZir, 7630 scope: *Scope, 7631 ri: ResultInfo, 7632 node: Ast.Node.Index, 7633 switch_full: Ast.full.Switch, 7634 ) InnerError!Zir.Inst.Ref { 7635 const astgen = parent_gz.astgen; 7636 const gpa = astgen.gpa; 7637 const tree = astgen.tree; 7638 const node_datas = tree.nodes.items(.data); 7639 const node_tags = tree.nodes.items(.tag); 7640 const main_tokens = tree.nodes.items(.main_token); 7641 const token_tags = tree.tokens.items(.tag); 7642 const operand_node = switch_full.ast.condition; 7643 const case_nodes = switch_full.ast.cases; 7644 7645 const need_rl = astgen.nodes_need_rl.contains(node); 7646 const block_ri: ResultInfo = if (need_rl) ri else .{ 7647 .rl = switch (ri.rl) { 7648 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, 7649 .inferred_ptr => .none, 7650 else => ri.rl, 7651 }, 7652 .ctx = ri.ctx, 7653 }; 7654 // We need to call `rvalue` to write through to the pointer only if we had a 7655 // result pointer and aren't forwarding it. 7656 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?; 7657 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); 7658 7659 if (switch_full.label_token) |label_token| { 7660 try astgen.checkLabelRedefinition(scope, label_token); 7661 } 7662 7663 // We perform two passes over the AST. This first pass is to collect information 7664 // for the following variables, make note of the special prong AST node index, 7665 // and bail out with a compile error if there are multiple special prongs present. 7666 var any_payload_is_ref = false; 7667 var any_has_tag_capture = false; 7668 var any_non_inline_capture = false; 7669 var scalar_cases_len: u32 = 0; 7670 var multi_cases_len: u32 = 0; 7671 var inline_cases_len: u32 = 0; 7672 var special_prong: Zir.SpecialProng = .none; 7673 var special_node: Ast.Node.Index = 0; 7674 var else_src: ?Ast.TokenIndex = null; 7675 var underscore_src: ?Ast.TokenIndex = null; 7676 for (case_nodes) |case_node| { 7677 const case = tree.fullSwitchCase(case_node).?; 7678 if (case.payload_token) |payload_token| { 7679 const ident = if (token_tags[payload_token] == .asterisk) blk: { 7680 any_payload_is_ref = true; 7681 break :blk payload_token + 1; 7682 } else payload_token; 7683 if (token_tags[ident + 1] == .comma) { 7684 any_has_tag_capture = true; 7685 } 7686 7687 // If the first capture is ignored, then there is no runtime-known 7688 // capture, as the tag capture must be for an inline prong. 7689 // This check isn't perfect, because for things like enums, the 7690 // first prong *is* comptime-known for inline prongs! But such 7691 // knowledge requires semantic analysis. 7692 if (!mem.eql(u8, tree.tokenSlice(ident), "_")) { 7693 any_non_inline_capture = true; 7694 } 7695 } 7696 // Check for else/`_` prong. 7697 if (case.ast.values.len == 0) { 7698 const case_src = case.ast.arrow_token - 1; 7699 if (else_src) |src| { 7700 return astgen.failTokNotes( 7701 case_src, 7702 "multiple else prongs in switch expression", 7703 .{}, 7704 &[_]u32{ 7705 try astgen.errNoteTok( 7706 src, 7707 "previous else prong here", 7708 .{}, 7709 ), 7710 }, 7711 ); 7712 } else if (underscore_src) |some_underscore| { 7713 return astgen.failNodeNotes( 7714 node, 7715 "else and '_' prong in switch expression", 7716 .{}, 7717 &[_]u32{ 7718 try astgen.errNoteTok( 7719 case_src, 7720 "else prong here", 7721 .{}, 7722 ), 7723 try astgen.errNoteTok( 7724 some_underscore, 7725 "'_' prong here", 7726 .{}, 7727 ), 7728 }, 7729 ); 7730 } 7731 special_node = case_node; 7732 special_prong = .@"else"; 7733 else_src = case_src; 7734 continue; 7735 } else if (case.ast.values.len == 1 and 7736 node_tags[case.ast.values[0]] == .identifier and 7737 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")) 7738 { 7739 const case_src = case.ast.arrow_token - 1; 7740 if (underscore_src) |src| { 7741 return astgen.failTokNotes( 7742 case_src, 7743 "multiple '_' prongs in switch expression", 7744 .{}, 7745 &[_]u32{ 7746 try astgen.errNoteTok( 7747 src, 7748 "previous '_' prong here", 7749 .{}, 7750 ), 7751 }, 7752 ); 7753 } else if (else_src) |some_else| { 7754 return astgen.failNodeNotes( 7755 node, 7756 "else and '_' prong in switch expression", 7757 .{}, 7758 &[_]u32{ 7759 try astgen.errNoteTok( 7760 some_else, 7761 "else prong here", 7762 .{}, 7763 ), 7764 try astgen.errNoteTok( 7765 case_src, 7766 "'_' prong here", 7767 .{}, 7768 ), 7769 }, 7770 ); 7771 } 7772 if (case.inline_token != null) { 7773 return astgen.failTok(case_src, "cannot inline '_' prong", .{}); 7774 } 7775 special_node = case_node; 7776 special_prong = .under; 7777 underscore_src = case_src; 7778 continue; 7779 } 7780 7781 for (case.ast.values) |val| { 7782 if (node_tags[val] == .string_literal) 7783 return astgen.failNode(val, "cannot switch on strings", .{}); 7784 } 7785 7786 if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) { 7787 scalar_cases_len += 1; 7788 } else { 7789 multi_cases_len += 1; 7790 } 7791 if (case.inline_token != null) { 7792 inline_cases_len += 1; 7793 } 7794 } 7795 7796 const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none }; 7797 7798 astgen.advanceSourceCursorToNode(operand_node); 7799 const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; 7800 7801 const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node); 7802 const item_ri: ResultInfo = .{ .rl = .none }; 7803 7804 // If this switch is labeled, it may have `continue`s targeting it, and thus we need the operand type 7805 // to provide a result type. 7806 const raw_operand_ty_ref = if (switch_full.label_token != null) t: { 7807 break :t try parent_gz.addUnNode(.typeof, raw_operand, operand_node); 7808 } else undefined; 7809 7810 // This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti, 7811 // except the first cases_nodes.len slots are a table that indexes payloads later in the array, with 7812 // the special case index coming first, then scalar_case_len indexes, then multi_cases_len indexes 7813 const payloads = &astgen.scratch; 7814 const scratch_top = astgen.scratch.items.len; 7815 const case_table_start = scratch_top; 7816 const scalar_case_table = case_table_start + @intFromBool(special_prong != .none); 7817 const multi_case_table = scalar_case_table + scalar_cases_len; 7818 const case_table_end = multi_case_table + multi_cases_len; 7819 try astgen.scratch.resize(gpa, case_table_end); 7820 defer astgen.scratch.items.len = scratch_top; 7821 7822 var block_scope = parent_gz.makeSubBlock(scope); 7823 // block_scope not used for collecting instructions 7824 block_scope.instructions_top = GenZir.unstacked_top; 7825 block_scope.setBreakResultInfo(block_ri); 7826 7827 // Sema expects a dbg_stmt immediately before switch_block(_ref) 7828 try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc); 7829 // This gets added to the parent block later, after the item expressions. 7830 const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block; 7831 const switch_block = try parent_gz.makeBlockInst(switch_tag, node); 7832 7833 if (switch_full.label_token) |label_token| { 7834 block_scope.continue_block = switch_block.toOptional(); 7835 block_scope.continue_result_info = .{ 7836 .rl = if (any_payload_is_ref) 7837 .{ .ref_coerced_ty = raw_operand_ty_ref } 7838 else 7839 .{ .coerced_ty = raw_operand_ty_ref }, 7840 }; 7841 7842 block_scope.label = .{ 7843 .token = label_token, 7844 .block_inst = switch_block, 7845 }; 7846 // `break` can target this via `label.block_inst` 7847 // `break_result_info` already set by `setBreakResultInfo` 7848 } 7849 7850 // We re-use this same scope for all cases, including the special prong, if any. 7851 var case_scope = parent_gz.makeSubBlock(&block_scope.base); 7852 case_scope.instructions_top = GenZir.unstacked_top; 7853 7854 // If any prong has an inline tag capture, allocate a shared dummy instruction for it 7855 const tag_inst = if (any_has_tag_capture) tag_inst: { 7856 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 7857 try astgen.instructions.append(astgen.gpa, .{ 7858 .tag = .extended, 7859 .data = .{ .extended = .{ 7860 .opcode = .value_placeholder, 7861 .small = undefined, 7862 .operand = undefined, 7863 } }, 7864 }); 7865 break :tag_inst inst; 7866 } else undefined; 7867 7868 // In this pass we generate all the item and prong expressions. 7869 var multi_case_index: u32 = 0; 7870 var scalar_case_index: u32 = 0; 7871 for (case_nodes) |case_node| { 7872 const case = tree.fullSwitchCase(case_node).?; 7873 7874 const is_multi_case = case.ast.values.len > 1 or 7875 (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range); 7876 7877 var dbg_var_name: Zir.NullTerminatedString = .empty; 7878 var dbg_var_inst: Zir.Inst.Ref = undefined; 7879 var dbg_var_tag_name: Zir.NullTerminatedString = .empty; 7880 var dbg_var_tag_inst: Zir.Inst.Ref = undefined; 7881 var has_tag_capture = false; 7882 var capture_val_scope: Scope.LocalVal = undefined; 7883 var tag_scope: Scope.LocalVal = undefined; 7884 7885 var capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = .none; 7886 7887 const sub_scope = blk: { 7888 const payload_token = case.payload_token orelse break :blk &case_scope.base; 7889 const ident = if (token_tags[payload_token] == .asterisk) 7890 payload_token + 1 7891 else 7892 payload_token; 7893 7894 const is_ptr = ident != payload_token; 7895 capture = if (is_ptr) .by_ref else .by_val; 7896 7897 const ident_slice = tree.tokenSlice(ident); 7898 var payload_sub_scope: *Scope = undefined; 7899 if (mem.eql(u8, ident_slice, "_")) { 7900 if (is_ptr) { 7901 return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{}); 7902 } 7903 payload_sub_scope = &case_scope.base; 7904 } else { 7905 const capture_name = try astgen.identAsString(ident); 7906 try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture); 7907 capture_val_scope = .{ 7908 .parent = &case_scope.base, 7909 .gen_zir = &case_scope, 7910 .name = capture_name, 7911 .inst = switch_block.toRef(), 7912 .token_src = ident, 7913 .id_cat = .capture, 7914 }; 7915 dbg_var_name = capture_name; 7916 dbg_var_inst = switch_block.toRef(); 7917 payload_sub_scope = &capture_val_scope.base; 7918 } 7919 7920 const tag_token = if (token_tags[ident + 1] == .comma) 7921 ident + 2 7922 else 7923 break :blk payload_sub_scope; 7924 const tag_slice = tree.tokenSlice(tag_token); 7925 if (mem.eql(u8, tag_slice, "_")) { 7926 return astgen.failTok(tag_token, "discard of tag capture; omit it instead", .{}); 7927 } else if (case.inline_token == null) { 7928 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{}); 7929 } 7930 const tag_name = try astgen.identAsString(tag_token); 7931 try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture"); 7932 7933 assert(any_has_tag_capture); 7934 has_tag_capture = true; 7935 7936 tag_scope = .{ 7937 .parent = payload_sub_scope, 7938 .gen_zir = &case_scope, 7939 .name = tag_name, 7940 .inst = tag_inst.toRef(), 7941 .token_src = tag_token, 7942 .id_cat = .@"switch tag capture", 7943 }; 7944 dbg_var_tag_name = tag_name; 7945 dbg_var_tag_inst = tag_inst.toRef(); 7946 break :blk &tag_scope.base; 7947 }; 7948 7949 const header_index: u32 = @intCast(payloads.items.len); 7950 const body_len_index = if (is_multi_case) blk: { 7951 payloads.items[multi_case_table + multi_case_index] = header_index; 7952 multi_case_index += 1; 7953 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len 7954 7955 // items 7956 var items_len: u32 = 0; 7957 for (case.ast.values) |item_node| { 7958 if (node_tags[item_node] == .switch_range) continue; 7959 items_len += 1; 7960 7961 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node); 7962 try payloads.append(gpa, @intFromEnum(item_inst)); 7963 } 7964 7965 // ranges 7966 var ranges_len: u32 = 0; 7967 for (case.ast.values) |range| { 7968 if (node_tags[range] != .switch_range) continue; 7969 ranges_len += 1; 7970 7971 const first = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].lhs); 7972 const last = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].rhs); 7973 try payloads.appendSlice(gpa, &[_]u32{ 7974 @intFromEnum(first), @intFromEnum(last), 7975 }); 7976 } 7977 7978 payloads.items[header_index] = items_len; 7979 payloads.items[header_index + 1] = ranges_len; 7980 break :blk header_index + 2; 7981 } else if (case_node == special_node) blk: { 7982 payloads.items[case_table_start] = header_index; 7983 try payloads.resize(gpa, header_index + 1); // body_len 7984 break :blk header_index; 7985 } else blk: { 7986 payloads.items[scalar_case_table + scalar_case_index] = header_index; 7987 scalar_case_index += 1; 7988 try payloads.resize(gpa, header_index + 2); // item, body_len 7989 const item_node = case.ast.values[0]; 7990 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node); 7991 payloads.items[header_index] = @intFromEnum(item_inst); 7992 break :blk header_index + 1; 7993 }; 7994 7995 { 7996 // temporarily stack case_scope on parent_gz 7997 case_scope.instructions_top = parent_gz.instructions.items.len; 7998 defer case_scope.unstack(); 7999 8000 if (dbg_var_name != .empty) { 8001 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst); 8002 } 8003 if (dbg_var_tag_name != .empty) { 8004 try case_scope.addDbgVar(.dbg_var_val, dbg_var_tag_name, dbg_var_tag_inst); 8005 } 8006 const target_expr_node = case.ast.target_expr; 8007 const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint); 8008 try checkUsed(parent_gz, &case_scope.base, sub_scope); 8009 if (!parent_gz.refIsNoReturn(case_result)) { 8010 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node); 8011 } 8012 8013 const case_slice = case_scope.instructionsSlice(); 8014 // Since we use the switch_block instruction itself to refer to the 8015 // capture, which will not be added to the child block, we need to 8016 // handle ref_table manually, and the same for the inline tag 8017 // capture instruction. 8018 const refs_len = refs: { 8019 var n: usize = 0; 8020 var check_inst = switch_block; 8021 while (astgen.ref_table.get(check_inst)) |ref_inst| { 8022 n += 1; 8023 check_inst = ref_inst; 8024 } 8025 if (has_tag_capture) { 8026 check_inst = tag_inst; 8027 while (astgen.ref_table.get(check_inst)) |ref_inst| { 8028 n += 1; 8029 check_inst = ref_inst; 8030 } 8031 } 8032 break :refs n; 8033 }; 8034 const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice); 8035 try payloads.ensureUnusedCapacity(gpa, body_len); 8036 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{ 8037 .body_len = @intCast(body_len), 8038 .capture = capture, 8039 .is_inline = case.inline_token != null, 8040 .has_tag_capture = has_tag_capture, 8041 }); 8042 if (astgen.ref_table.fetchRemove(switch_block)) |kv| { 8043 appendPossiblyRefdBodyInst(astgen, payloads, kv.value); 8044 } 8045 if (has_tag_capture) { 8046 if (astgen.ref_table.fetchRemove(tag_inst)) |kv| { 8047 appendPossiblyRefdBodyInst(astgen, payloads, kv.value); 8048 } 8049 } 8050 appendBodyWithFixupsArrayList(astgen, payloads, case_slice); 8051 } 8052 } 8053 8054 if (switch_full.label_token) |label_token| if (!block_scope.label.?.used) { 8055 try astgen.appendErrorTok(label_token, "unused switch label", .{}); 8056 }; 8057 8058 // Now that the item expressions are generated we can add this. 8059 try parent_gz.instructions.append(gpa, switch_block); 8060 8061 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).@"struct".fields.len + 8062 @intFromBool(multi_cases_len != 0) + 8063 @intFromBool(any_has_tag_capture) + 8064 payloads.items.len - case_table_end + 8065 (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len); 8066 8067 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{ 8068 .operand = raw_operand, 8069 .bits = Zir.Inst.SwitchBlock.Bits{ 8070 .has_multi_cases = multi_cases_len != 0, 8071 .has_else = special_prong == .@"else", 8072 .has_under = special_prong == .under, 8073 .any_has_tag_capture = any_has_tag_capture, 8074 .any_non_inline_capture = any_non_inline_capture, 8075 .has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue, 8076 .scalar_cases_len = @intCast(scalar_cases_len), 8077 }, 8078 }); 8079 8080 if (multi_cases_len != 0) { 8081 astgen.extra.appendAssumeCapacity(multi_cases_len); 8082 } 8083 8084 if (any_has_tag_capture) { 8085 astgen.extra.appendAssumeCapacity(@intFromEnum(tag_inst)); 8086 } 8087 8088 const zir_datas = astgen.instructions.items(.data); 8089 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index; 8090 8091 for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| { 8092 var body_len_index = start_index; 8093 var end_index = start_index; 8094 const table_index = case_table_start + i; 8095 if (table_index < scalar_case_table) { 8096 end_index += 1; 8097 } else if (table_index < multi_case_table) { 8098 body_len_index += 1; 8099 end_index += 2; 8100 } else { 8101 body_len_index += 2; 8102 const items_len = payloads.items[start_index]; 8103 const ranges_len = payloads.items[start_index + 1]; 8104 end_index += 3 + items_len + 2 * ranges_len; 8105 } 8106 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]); 8107 end_index += prong_info.body_len; 8108 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]); 8109 } 8110 8111 if (need_result_rvalue) { 8112 return rvalue(parent_gz, ri, switch_block.toRef(), node); 8113 } else { 8114 return switch_block.toRef(); 8115 } 8116 } 8117 8118 fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 8119 const astgen = gz.astgen; 8120 const tree = astgen.tree; 8121 const node_datas = tree.nodes.items(.data); 8122 const node_tags = tree.nodes.items(.tag); 8123 8124 if (astgen.fn_block == null) { 8125 return astgen.failNode(node, "'return' outside function scope", .{}); 8126 } 8127 8128 if (gz.any_defer_node != 0) { 8129 return astgen.failNodeNotes(node, "cannot return from defer expression", .{}, &.{ 8130 try astgen.errNoteNode( 8131 gz.any_defer_node, 8132 "defer expression here", 8133 .{}, 8134 ), 8135 }); 8136 } 8137 8138 // Ensure debug line/column information is emitted for this return expression. 8139 // Then we will save the line/column so that we can emit another one that goes 8140 // "backwards" because we want to evaluate the operand, but then put the debug 8141 // info back at the return keyword for error return tracing. 8142 if (!gz.is_comptime) { 8143 try emitDbgNode(gz, node); 8144 } 8145 const ret_lc = LineColumn{ astgen.source_line - gz.decl_line, astgen.source_column }; 8146 8147 const defer_outer = &astgen.fn_block.?.base; 8148 8149 const operand_node = node_datas[node].lhs; 8150 if (operand_node == 0) { 8151 // Returning a void value; skip error defers. 8152 try genDefers(gz, defer_outer, scope, .normal_only); 8153 8154 // As our last action before the return, "pop" the error trace if needed 8155 _ = try gz.addRestoreErrRetIndex(.ret, .always, node); 8156 8157 _ = try gz.addUnNode(.ret_node, .void_value, node); 8158 return Zir.Inst.Ref.unreachable_value; 8159 } 8160 8161 if (node_tags[operand_node] == .error_value) { 8162 // Hot path for `return error.Foo`. This bypasses result location logic as well as logic 8163 // for detecting whether to add something to the function's inferred error set. 8164 const ident_token = node_datas[operand_node].rhs; 8165 const err_name_str_index = try astgen.identAsString(ident_token); 8166 const defer_counts = countDefers(defer_outer, scope); 8167 if (!defer_counts.need_err_code) { 8168 try genDefers(gz, defer_outer, scope, .both_sans_err); 8169 try emitDbgStmt(gz, ret_lc); 8170 _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token); 8171 return Zir.Inst.Ref.unreachable_value; 8172 } 8173 const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token); 8174 try genDefers(gz, defer_outer, scope, .{ .both = err_code }); 8175 try emitDbgStmt(gz, ret_lc); 8176 _ = try gz.addUnNode(.ret_node, err_code, node); 8177 return Zir.Inst.Ref.unreachable_value; 8178 } 8179 8180 const ri: ResultInfo = if (astgen.nodes_need_rl.contains(node)) .{ 8181 .rl = .{ .ptr = .{ .inst = try gz.addNode(.ret_ptr, node) } }, 8182 .ctx = .@"return", 8183 } else .{ 8184 .rl = .{ .coerced_ty = astgen.fn_ret_ty }, 8185 .ctx = .@"return", 8186 }; 8187 const prev_anon_name_strategy = gz.anon_name_strategy; 8188 gz.anon_name_strategy = .func; 8189 const operand = try reachableExpr(gz, scope, ri, operand_node, node); 8190 gz.anon_name_strategy = prev_anon_name_strategy; 8191 8192 switch (nodeMayEvalToError(tree, operand_node)) { 8193 .never => { 8194 // Returning a value that cannot be an error; skip error defers. 8195 try genDefers(gz, defer_outer, scope, .normal_only); 8196 8197 // As our last action before the return, "pop" the error trace if needed 8198 _ = try gz.addRestoreErrRetIndex(.ret, .always, node); 8199 8200 try emitDbgStmt(gz, ret_lc); 8201 try gz.addRet(ri, operand, node); 8202 return Zir.Inst.Ref.unreachable_value; 8203 }, 8204 .always => { 8205 // Value is always an error. Emit both error defers and regular defers. 8206 const err_code = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand; 8207 try genDefers(gz, defer_outer, scope, .{ .both = err_code }); 8208 try emitDbgStmt(gz, ret_lc); 8209 try gz.addRet(ri, operand, node); 8210 return Zir.Inst.Ref.unreachable_value; 8211 }, 8212 .maybe => { 8213 const defer_counts = countDefers(defer_outer, scope); 8214 if (!defer_counts.have_err) { 8215 // Only regular defers; no branch needed. 8216 try genDefers(gz, defer_outer, scope, .normal_only); 8217 try emitDbgStmt(gz, ret_lc); 8218 8219 // As our last action before the return, "pop" the error trace if needed 8220 const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand; 8221 _ = try gz.addRestoreErrRetIndex(.ret, .{ .if_non_error = result }, node); 8222 8223 try gz.addRet(ri, operand, node); 8224 return Zir.Inst.Ref.unreachable_value; 8225 } 8226 8227 // Emit conditional branch for generating errdefers. 8228 const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand; 8229 const is_non_err = try gz.addUnNode(.ret_is_non_err, result, node); 8230 const condbr = try gz.addCondBr(.condbr, node); 8231 8232 var then_scope = gz.makeSubBlock(scope); 8233 defer then_scope.unstack(); 8234 8235 try genDefers(&then_scope, defer_outer, scope, .normal_only); 8236 8237 // As our last action before the return, "pop" the error trace if needed 8238 _ = try then_scope.addRestoreErrRetIndex(.ret, .always, node); 8239 8240 try emitDbgStmt(&then_scope, ret_lc); 8241 try then_scope.addRet(ri, operand, node); 8242 8243 var else_scope = gz.makeSubBlock(scope); 8244 defer else_scope.unstack(); 8245 8246 const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{ 8247 .both = try else_scope.addUnNode(.err_union_code, result, node), 8248 }; 8249 try genDefers(&else_scope, defer_outer, scope, which_ones); 8250 try emitDbgStmt(&else_scope, ret_lc); 8251 try else_scope.addRet(ri, operand, node); 8252 8253 try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope); 8254 8255 return Zir.Inst.Ref.unreachable_value; 8256 }, 8257 } 8258 } 8259 8260 /// Parses the string `buf` as a base 10 integer of type `u16`. 8261 /// 8262 /// Unlike std.fmt.parseInt, does not allow the '_' character in `buf`. 8263 fn parseBitCount(buf: []const u8) std.fmt.ParseIntError!u16 { 8264 if (buf.len == 0) return error.InvalidCharacter; 8265 8266 var x: u16 = 0; 8267 8268 for (buf) |c| { 8269 const digit = switch (c) { 8270 '0'...'9' => c - '0', 8271 else => return error.InvalidCharacter, 8272 }; 8273 8274 if (x != 0) x = try std.math.mul(u16, x, 10); 8275 x = try std.math.add(u16, x, digit); 8276 } 8277 8278 return x; 8279 } 8280 8281 fn identifier( 8282 gz: *GenZir, 8283 scope: *Scope, 8284 ri: ResultInfo, 8285 ident: Ast.Node.Index, 8286 ) InnerError!Zir.Inst.Ref { 8287 const astgen = gz.astgen; 8288 const tree = astgen.tree; 8289 const main_tokens = tree.nodes.items(.main_token); 8290 8291 const ident_token = main_tokens[ident]; 8292 const ident_name_raw = tree.tokenSlice(ident_token); 8293 if (mem.eql(u8, ident_name_raw, "_")) { 8294 return astgen.failNode(ident, "'_' used as an identifier without @\"_\" syntax", .{}); 8295 } 8296 8297 // if not @"" syntax, just use raw token slice 8298 if (ident_name_raw[0] != '@') { 8299 if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| { 8300 return rvalue(gz, ri, zir_const_ref, ident); 8301 } 8302 8303 if (ident_name_raw.len >= 2) integer: { 8304 const first_c = ident_name_raw[0]; 8305 if (first_c == 'i' or first_c == 'u') { 8306 const signedness: std.builtin.Signedness = switch (first_c == 'i') { 8307 true => .signed, 8308 false => .unsigned, 8309 }; 8310 if (ident_name_raw.len >= 3 and ident_name_raw[1] == '0') { 8311 return astgen.failNode( 8312 ident, 8313 "primitive integer type '{s}' has leading zero", 8314 .{ident_name_raw}, 8315 ); 8316 } 8317 const bit_count = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) { 8318 error.Overflow => return astgen.failNode( 8319 ident, 8320 "primitive integer type '{s}' exceeds maximum bit width of 65535", 8321 .{ident_name_raw}, 8322 ), 8323 error.InvalidCharacter => break :integer, 8324 }; 8325 const result = try gz.add(.{ 8326 .tag = .int_type, 8327 .data = .{ .int_type = .{ 8328 .src_node = gz.nodeIndexToRelative(ident), 8329 .signedness = signedness, 8330 .bit_count = bit_count, 8331 } }, 8332 }); 8333 return rvalue(gz, ri, result, ident); 8334 } 8335 } 8336 } 8337 8338 // Local variables, including function parameters. 8339 return localVarRef(gz, scope, ri, ident, ident_token); 8340 } 8341 8342 fn localVarRef( 8343 gz: *GenZir, 8344 scope: *Scope, 8345 ri: ResultInfo, 8346 ident: Ast.Node.Index, 8347 ident_token: Ast.TokenIndex, 8348 ) InnerError!Zir.Inst.Ref { 8349 const astgen = gz.astgen; 8350 const name_str_index = try astgen.identAsString(ident_token); 8351 var s = scope; 8352 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already 8353 var found_needs_tunnel: bool = undefined; // defined when `found_already != null` 8354 var found_namespaces_out: u32 = undefined; // defined when `found_already != null` 8355 8356 // The number of namespaces above `gz` we currently are 8357 var num_namespaces_out: u32 = 0; 8358 // defined by `num_namespaces_out != 0` 8359 var capturing_namespace: *Scope.Namespace = undefined; 8360 8361 while (true) switch (s.tag) { 8362 .local_val => { 8363 const local_val = s.cast(Scope.LocalVal).?; 8364 8365 if (local_val.name == name_str_index) { 8366 // Locals cannot shadow anything, so we do not need to look for ambiguous 8367 // references in this case. 8368 if (ri.rl == .discard and ri.ctx == .assignment) { 8369 local_val.discarded = ident_token; 8370 } else { 8371 local_val.used = ident_token; 8372 } 8373 8374 const value_inst = if (num_namespaces_out != 0) try tunnelThroughClosure( 8375 gz, 8376 ident, 8377 num_namespaces_out, 8378 .{ .ref = local_val.inst }, 8379 .{ .token = local_val.token_src }, 8380 ) else local_val.inst; 8381 8382 return rvalueNoCoercePreRef(gz, ri, value_inst, ident); 8383 } 8384 s = local_val.parent; 8385 }, 8386 .local_ptr => { 8387 const local_ptr = s.cast(Scope.LocalPtr).?; 8388 if (local_ptr.name == name_str_index) { 8389 if (ri.rl == .discard and ri.ctx == .assignment) { 8390 local_ptr.discarded = ident_token; 8391 } else { 8392 local_ptr.used = ident_token; 8393 } 8394 8395 // Can't close over a runtime variable 8396 if (num_namespaces_out != 0 and !local_ptr.maybe_comptime and !gz.is_typeof) { 8397 const ident_name = try astgen.identifierTokenString(ident_token); 8398 return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{ 8399 try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}), 8400 try astgen.errNoteNode(capturing_namespace.node, "crosses namespace boundary here", .{}), 8401 }); 8402 } 8403 8404 switch (ri.rl) { 8405 .ref, .ref_coerced_ty => { 8406 const ptr_inst = if (num_namespaces_out != 0) try tunnelThroughClosure( 8407 gz, 8408 ident, 8409 num_namespaces_out, 8410 .{ .ref = local_ptr.ptr }, 8411 .{ .token = local_ptr.token_src }, 8412 ) else local_ptr.ptr; 8413 local_ptr.used_as_lvalue = true; 8414 return ptr_inst; 8415 }, 8416 else => { 8417 const val_inst = if (num_namespaces_out != 0) try tunnelThroughClosure( 8418 gz, 8419 ident, 8420 num_namespaces_out, 8421 .{ .ref_load = local_ptr.ptr }, 8422 .{ .token = local_ptr.token_src }, 8423 ) else try gz.addUnNode(.load, local_ptr.ptr, ident); 8424 return rvalueNoCoercePreRef(gz, ri, val_inst, ident); 8425 }, 8426 } 8427 } 8428 s = local_ptr.parent; 8429 }, 8430 .gen_zir => s = s.cast(GenZir).?.parent, 8431 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, 8432 .namespace => { 8433 const ns = s.cast(Scope.Namespace).?; 8434 if (ns.decls.get(name_str_index)) |i| { 8435 if (found_already) |f| { 8436 return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{ 8437 try astgen.errNoteNode(f, "declared here", .{}), 8438 try astgen.errNoteNode(i, "also declared here", .{}), 8439 }); 8440 } 8441 // We found a match but must continue looking for ambiguous references to decls. 8442 found_already = i; 8443 found_needs_tunnel = ns.maybe_generic; 8444 found_namespaces_out = num_namespaces_out; 8445 } 8446 num_namespaces_out += 1; 8447 capturing_namespace = ns; 8448 s = ns.parent; 8449 }, 8450 .top => break, 8451 }; 8452 if (found_already == null) { 8453 const ident_name = try astgen.identifierTokenString(ident_token); 8454 return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name}); 8455 } 8456 8457 // Decl references happen by name rather than ZIR index so that when unrelated 8458 // decls are modified, ZIR code containing references to them can be unmodified. 8459 8460 if (found_namespaces_out > 0 and found_needs_tunnel) { 8461 switch (ri.rl) { 8462 .ref, .ref_coerced_ty => return tunnelThroughClosure( 8463 gz, 8464 ident, 8465 found_namespaces_out, 8466 .{ .decl_ref = name_str_index }, 8467 .{ .node = found_already.? }, 8468 ), 8469 else => { 8470 const result = try tunnelThroughClosure( 8471 gz, 8472 ident, 8473 found_namespaces_out, 8474 .{ .decl_val = name_str_index }, 8475 .{ .node = found_already.? }, 8476 ); 8477 return rvalueNoCoercePreRef(gz, ri, result, ident); 8478 }, 8479 } 8480 } 8481 8482 switch (ri.rl) { 8483 .ref, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token), 8484 else => { 8485 const result = try gz.addStrTok(.decl_val, name_str_index, ident_token); 8486 return rvalueNoCoercePreRef(gz, ri, result, ident); 8487 }, 8488 } 8489 } 8490 8491 /// Access a ZIR instruction through closure. May tunnel through arbitrarily 8492 /// many namespaces, adding closure captures as required. 8493 /// Returns the index of the `closure_get` instruction added to `gz`. 8494 fn tunnelThroughClosure( 8495 gz: *GenZir, 8496 /// The node which references the value to be captured. 8497 inner_ref_node: Ast.Node.Index, 8498 /// The number of namespaces being tunnelled through. At least 1. 8499 num_tunnels: u32, 8500 /// The value being captured. 8501 value: union(enum) { 8502 ref: Zir.Inst.Ref, 8503 ref_load: Zir.Inst.Ref, 8504 decl_val: Zir.NullTerminatedString, 8505 decl_ref: Zir.NullTerminatedString, 8506 }, 8507 /// The location of the value's declaration. 8508 decl_src: union(enum) { 8509 token: Ast.TokenIndex, 8510 node: Ast.Node.Index, 8511 }, 8512 ) !Zir.Inst.Ref { 8513 switch (value) { 8514 .ref => |v| if (v.toIndex() == null) return v, // trivial value; do not need tunnel 8515 .ref_load => |v| assert(v.toIndex() != null), // there are no constant pointer refs 8516 .decl_val, .decl_ref => {}, 8517 } 8518 8519 const astgen = gz.astgen; 8520 const gpa = astgen.gpa; 8521 8522 // Otherwise we need a tunnel. First, figure out the path of namespaces we 8523 // are tunneling through. This is usually only going to be one or two, so 8524 // use an SFBA to optimize for the common case. 8525 var sfba = std.heap.stackFallback(@sizeOf(usize) * 2, astgen.arena); 8526 var intermediate_tunnels = try sfba.get().alloc(*Scope.Namespace, num_tunnels - 1); 8527 8528 const root_ns = ns: { 8529 var i: usize = num_tunnels - 1; 8530 var scope: *Scope = gz.parent; 8531 while (i > 0) { 8532 if (scope.cast(Scope.Namespace)) |mid_ns| { 8533 i -= 1; 8534 intermediate_tunnels[i] = mid_ns; 8535 } 8536 scope = scope.parent().?; 8537 } 8538 while (true) { 8539 if (scope.cast(Scope.Namespace)) |ns| break :ns ns; 8540 scope = scope.parent().?; 8541 } 8542 }; 8543 8544 // Now that we know the scopes we're tunneling through, begin adding 8545 // captures as required, starting with the outermost namespace. 8546 const root_capture = Zir.Inst.Capture.wrap(switch (value) { 8547 .ref => |v| .{ .instruction = v.toIndex().? }, 8548 .ref_load => |v| .{ .instruction_load = v.toIndex().? }, 8549 .decl_val => |str| .{ .decl_val = str }, 8550 .decl_ref => |str| .{ .decl_ref = str }, 8551 }); 8552 var cur_capture_index = std.math.cast( 8553 u16, 8554 (try root_ns.captures.getOrPut(gpa, root_capture)).index, 8555 ) orelse return astgen.failNodeNotes(root_ns.node, "this compiler implementation only supports up to 65536 captures per namespace", .{}, &.{ 8556 switch (decl_src) { 8557 .token => |t| try astgen.errNoteTok(t, "captured value here", .{}), 8558 .node => |n| try astgen.errNoteNode(n, "captured value here", .{}), 8559 }, 8560 try astgen.errNoteNode(inner_ref_node, "value used here", .{}), 8561 }); 8562 8563 for (intermediate_tunnels) |tunnel_ns| { 8564 cur_capture_index = std.math.cast( 8565 u16, 8566 (try tunnel_ns.captures.getOrPut(gpa, Zir.Inst.Capture.wrap(.{ .nested = cur_capture_index }))).index, 8567 ) orelse return astgen.failNodeNotes(tunnel_ns.node, "this compiler implementation only supports up to 65536 captures per namespace", .{}, &.{ 8568 switch (decl_src) { 8569 .token => |t| try astgen.errNoteTok(t, "captured value here", .{}), 8570 .node => |n| try astgen.errNoteNode(n, "captured value here", .{}), 8571 }, 8572 try astgen.errNoteNode(inner_ref_node, "value used here", .{}), 8573 }); 8574 } 8575 8576 // Incorporate the capture index into the source hash, so that changes in 8577 // the order of captures cause suitable re-analysis. 8578 astgen.src_hasher.update(std.mem.asBytes(&cur_capture_index)); 8579 8580 // Add an instruction to get the value from the closure. 8581 return gz.addExtendedNodeSmall(.closure_get, inner_ref_node, cur_capture_index); 8582 } 8583 8584 fn stringLiteral( 8585 gz: *GenZir, 8586 ri: ResultInfo, 8587 node: Ast.Node.Index, 8588 ) InnerError!Zir.Inst.Ref { 8589 const astgen = gz.astgen; 8590 const tree = astgen.tree; 8591 const main_tokens = tree.nodes.items(.main_token); 8592 const str_lit_token = main_tokens[node]; 8593 const str = try astgen.strLitAsString(str_lit_token); 8594 const result = try gz.add(.{ 8595 .tag = .str, 8596 .data = .{ .str = .{ 8597 .start = str.index, 8598 .len = str.len, 8599 } }, 8600 }); 8601 return rvalue(gz, ri, result, node); 8602 } 8603 8604 fn multilineStringLiteral( 8605 gz: *GenZir, 8606 ri: ResultInfo, 8607 node: Ast.Node.Index, 8608 ) InnerError!Zir.Inst.Ref { 8609 const astgen = gz.astgen; 8610 const str = try astgen.strLitNodeAsString(node); 8611 const result = try gz.add(.{ 8612 .tag = .str, 8613 .data = .{ .str = .{ 8614 .start = str.index, 8615 .len = str.len, 8616 } }, 8617 }); 8618 return rvalue(gz, ri, result, node); 8619 } 8620 8621 fn charLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { 8622 const astgen = gz.astgen; 8623 const tree = astgen.tree; 8624 const main_tokens = tree.nodes.items(.main_token); 8625 const main_token = main_tokens[node]; 8626 const slice = tree.tokenSlice(main_token); 8627 8628 switch (std.zig.parseCharLiteral(slice)) { 8629 .success => |codepoint| { 8630 const result = try gz.addInt(codepoint); 8631 return rvalue(gz, ri, result, node); 8632 }, 8633 .failure => |err| return astgen.failWithStrLitError(err, main_token, slice, 0), 8634 } 8635 } 8636 8637 const Sign = enum { negative, positive }; 8638 8639 fn numberLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index, source_node: Ast.Node.Index, sign: Sign) InnerError!Zir.Inst.Ref { 8640 const astgen = gz.astgen; 8641 const tree = astgen.tree; 8642 const main_tokens = tree.nodes.items(.main_token); 8643 const num_token = main_tokens[node]; 8644 const bytes = tree.tokenSlice(num_token); 8645 8646 const result: Zir.Inst.Ref = switch (std.zig.parseNumberLiteral(bytes)) { 8647 .int => |num| switch (num) { 8648 0 => if (sign == .positive) .zero else return astgen.failTokNotes( 8649 num_token, 8650 "integer literal '-0' is ambiguous", 8651 .{}, 8652 &.{ 8653 try astgen.errNoteTok(num_token, "use '0' for an integer zero", .{}), 8654 try astgen.errNoteTok(num_token, "use '-0.0' for a floating-point signed zero", .{}), 8655 }, 8656 ), 8657 1 => { 8658 // Handle the negation here! 8659 const result: Zir.Inst.Ref = switch (sign) { 8660 .positive => .one, 8661 .negative => .negative_one, 8662 }; 8663 return rvalue(gz, ri, result, source_node); 8664 }, 8665 else => try gz.addInt(num), 8666 }, 8667 .big_int => |base| big: { 8668 const gpa = astgen.gpa; 8669 var big_int = try std.math.big.int.Managed.init(gpa); 8670 defer big_int.deinit(); 8671 const prefix_offset: usize = if (base == .decimal) 0 else 2; 8672 big_int.setString(@intFromEnum(base), bytes[prefix_offset..]) catch |err| switch (err) { 8673 error.InvalidCharacter => unreachable, // caught in `parseNumberLiteral` 8674 error.InvalidBase => unreachable, // we only pass 16, 8, 2, see above 8675 error.OutOfMemory => return error.OutOfMemory, 8676 }; 8677 8678 const limbs = big_int.limbs[0..big_int.len()]; 8679 assert(big_int.isPositive()); 8680 break :big try gz.addIntBig(limbs); 8681 }, 8682 .float => { 8683 const unsigned_float_number = std.fmt.parseFloat(f128, bytes) catch |err| switch (err) { 8684 error.InvalidCharacter => unreachable, // validated by tokenizer 8685 }; 8686 const float_number = switch (sign) { 8687 .negative => -unsigned_float_number, 8688 .positive => unsigned_float_number, 8689 }; 8690 // If the value fits into a f64 without losing any precision, store it that way. 8691 @setFloatMode(.strict); 8692 const smaller_float: f64 = @floatCast(float_number); 8693 const bigger_again: f128 = smaller_float; 8694 if (bigger_again == float_number) { 8695 const result = try gz.addFloat(smaller_float); 8696 return rvalue(gz, ri, result, source_node); 8697 } 8698 // We need to use 128 bits. Break the float into 4 u32 values so we can 8699 // put it into the `extra` array. 8700 const int_bits: u128 = @bitCast(float_number); 8701 const result = try gz.addPlNode(.float128, node, Zir.Inst.Float128{ 8702 .piece0 = @truncate(int_bits), 8703 .piece1 = @truncate(int_bits >> 32), 8704 .piece2 = @truncate(int_bits >> 64), 8705 .piece3 = @truncate(int_bits >> 96), 8706 }); 8707 return rvalue(gz, ri, result, source_node); 8708 }, 8709 .failure => |err| return astgen.failWithNumberError(err, num_token, bytes), 8710 }; 8711 8712 if (sign == .positive) { 8713 return rvalue(gz, ri, result, source_node); 8714 } else { 8715 const negated = try gz.addUnNode(.negate, result, source_node); 8716 return rvalue(gz, ri, negated, source_node); 8717 } 8718 } 8719 8720 fn failWithNumberError(astgen: *AstGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) InnerError { 8721 const is_float = std.mem.indexOfScalar(u8, bytes, '.') != null; 8722 switch (err) { 8723 .leading_zero => if (is_float) { 8724 return astgen.failTok(token, "number '{s}' has leading zero", .{bytes}); 8725 } else { 8726 return astgen.failTokNotes(token, "number '{s}' has leading zero", .{bytes}, &.{ 8727 try astgen.errNoteTok(token, "use '0o' prefix for octal literals", .{}), 8728 }); 8729 }, 8730 .digit_after_base => return astgen.failTok(token, "expected a digit after base prefix", .{}), 8731 .upper_case_base => |i| return astgen.failOff(token, @intCast(i), "base prefix must be lowercase", .{}), 8732 .invalid_float_base => |i| return astgen.failOff(token, @intCast(i), "invalid base for float literal", .{}), 8733 .repeated_underscore => |i| return astgen.failOff(token, @intCast(i), "repeated digit separator", .{}), 8734 .invalid_underscore_after_special => |i| return astgen.failOff(token, @intCast(i), "expected digit before digit separator", .{}), 8735 .invalid_digit => |info| return astgen.failOff(token, @intCast(info.i), "invalid digit '{c}' for {s} base", .{ bytes[info.i], @tagName(info.base) }), 8736 .invalid_digit_exponent => |i| return astgen.failOff(token, @intCast(i), "invalid digit '{c}' in exponent", .{bytes[i]}), 8737 .duplicate_exponent => |i| return astgen.failOff(token, @intCast(i), "duplicate exponent", .{}), 8738 .exponent_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before exponent", .{}), 8739 .special_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before '{c}'", .{bytes[i]}), 8740 .trailing_special => |i| return astgen.failOff(token, @intCast(i), "expected digit after '{c}'", .{bytes[i - 1]}), 8741 .trailing_underscore => |i| return astgen.failOff(token, @intCast(i), "trailing digit separator", .{}), 8742 .duplicate_period => unreachable, // Validated by tokenizer 8743 .invalid_character => unreachable, // Validated by tokenizer 8744 .invalid_exponent_sign => |i| { 8745 assert(bytes.len >= 2 and bytes[0] == '0' and bytes[1] == 'x'); // Validated by tokenizer 8746 return astgen.failOff(token, @intCast(i), "sign '{c}' cannot follow digit '{c}' in hex base", .{ bytes[i], bytes[i - 1] }); 8747 }, 8748 .period_after_exponent => |i| return astgen.failOff(token, @intCast(i), "unexpected period after exponent", .{}), 8749 } 8750 } 8751 8752 fn asmExpr( 8753 gz: *GenZir, 8754 scope: *Scope, 8755 ri: ResultInfo, 8756 node: Ast.Node.Index, 8757 full: Ast.full.Asm, 8758 ) InnerError!Zir.Inst.Ref { 8759 const astgen = gz.astgen; 8760 const tree = astgen.tree; 8761 const main_tokens = tree.nodes.items(.main_token); 8762 const node_datas = tree.nodes.items(.data); 8763 const node_tags = tree.nodes.items(.tag); 8764 const token_tags = tree.tokens.items(.tag); 8765 8766 const TagAndTmpl = struct { tag: Zir.Inst.Extended, tmpl: Zir.NullTerminatedString }; 8767 const tag_and_tmpl: TagAndTmpl = switch (node_tags[full.ast.template]) { 8768 .string_literal => .{ 8769 .tag = .@"asm", 8770 .tmpl = (try astgen.strLitAsString(main_tokens[full.ast.template])).index, 8771 }, 8772 .multiline_string_literal => .{ 8773 .tag = .@"asm", 8774 .tmpl = (try astgen.strLitNodeAsString(full.ast.template)).index, 8775 }, 8776 else => .{ 8777 .tag = .asm_expr, 8778 .tmpl = @enumFromInt(@intFromEnum(try comptimeExpr(gz, scope, .{ .rl = .none }, full.ast.template))), 8779 }, 8780 }; 8781 8782 // See https://github.com/ziglang/zig/issues/215 and related issues discussing 8783 // possible inline assembly improvements. Until then here is status quo AstGen 8784 // for assembly syntax. It's used by std lib crypto aesni.zig. 8785 const is_container_asm = astgen.fn_block == null; 8786 if (is_container_asm) { 8787 if (full.volatile_token) |t| 8788 return astgen.failTok(t, "volatile is meaningless on global assembly", .{}); 8789 if (full.outputs.len != 0 or full.inputs.len != 0 or full.first_clobber != null) 8790 return astgen.failNode(node, "global assembly cannot have inputs, outputs, or clobbers", .{}); 8791 } else { 8792 if (full.outputs.len == 0 and full.volatile_token == null) { 8793 return astgen.failNode(node, "assembly expression with no output must be marked volatile", .{}); 8794 } 8795 } 8796 if (full.outputs.len > 32) { 8797 return astgen.failNode(full.outputs[32], "too many asm outputs", .{}); 8798 } 8799 var outputs_buffer: [32]Zir.Inst.Asm.Output = undefined; 8800 const outputs = outputs_buffer[0..full.outputs.len]; 8801 8802 var output_type_bits: u32 = 0; 8803 8804 for (full.outputs, 0..) |output_node, i| { 8805 const symbolic_name = main_tokens[output_node]; 8806 const name = try astgen.identAsString(symbolic_name); 8807 const constraint_token = symbolic_name + 2; 8808 const constraint = (try astgen.strLitAsString(constraint_token)).index; 8809 const has_arrow = token_tags[symbolic_name + 4] == .arrow; 8810 if (has_arrow) { 8811 if (output_type_bits != 0) { 8812 return astgen.failNode(output_node, "inline assembly allows up to one output value", .{}); 8813 } 8814 output_type_bits |= @as(u32, 1) << @intCast(i); 8815 const out_type_node = node_datas[output_node].lhs; 8816 const out_type_inst = try typeExpr(gz, scope, out_type_node); 8817 outputs[i] = .{ 8818 .name = name, 8819 .constraint = constraint, 8820 .operand = out_type_inst, 8821 }; 8822 } else { 8823 const ident_token = symbolic_name + 4; 8824 // TODO have a look at #215 and related issues and decide how to 8825 // handle outputs. Do we want this to be identifiers? 8826 // Or maybe we want to force this to be expressions with a pointer type. 8827 outputs[i] = .{ 8828 .name = name, 8829 .constraint = constraint, 8830 .operand = try localVarRef(gz, scope, .{ .rl = .ref }, node, ident_token), 8831 }; 8832 } 8833 } 8834 8835 if (full.inputs.len > 32) { 8836 return astgen.failNode(full.inputs[32], "too many asm inputs", .{}); 8837 } 8838 var inputs_buffer: [32]Zir.Inst.Asm.Input = undefined; 8839 const inputs = inputs_buffer[0..full.inputs.len]; 8840 8841 for (full.inputs, 0..) |input_node, i| { 8842 const symbolic_name = main_tokens[input_node]; 8843 const name = try astgen.identAsString(symbolic_name); 8844 const constraint_token = symbolic_name + 2; 8845 const constraint = (try astgen.strLitAsString(constraint_token)).index; 8846 const operand = try expr(gz, scope, .{ .rl = .none }, node_datas[input_node].lhs); 8847 inputs[i] = .{ 8848 .name = name, 8849 .constraint = constraint, 8850 .operand = operand, 8851 }; 8852 } 8853 8854 var clobbers_buffer: [32]u32 = undefined; 8855 var clobber_i: usize = 0; 8856 if (full.first_clobber) |first_clobber| clobbers: { 8857 // asm ("foo" ::: "a", "b") 8858 // asm ("foo" ::: "a", "b",) 8859 var tok_i = first_clobber; 8860 while (true) : (tok_i += 1) { 8861 if (clobber_i >= clobbers_buffer.len) { 8862 return astgen.failTok(tok_i, "too many asm clobbers", .{}); 8863 } 8864 clobbers_buffer[clobber_i] = @intFromEnum((try astgen.strLitAsString(tok_i)).index); 8865 clobber_i += 1; 8866 tok_i += 1; 8867 switch (token_tags[tok_i]) { 8868 .r_paren => break :clobbers, 8869 .comma => { 8870 if (token_tags[tok_i + 1] == .r_paren) { 8871 break :clobbers; 8872 } else { 8873 continue; 8874 } 8875 }, 8876 else => unreachable, 8877 } 8878 } 8879 } 8880 8881 const result = try gz.addAsm(.{ 8882 .tag = tag_and_tmpl.tag, 8883 .node = node, 8884 .asm_source = tag_and_tmpl.tmpl, 8885 .is_volatile = full.volatile_token != null, 8886 .output_type_bits = output_type_bits, 8887 .outputs = outputs, 8888 .inputs = inputs, 8889 .clobbers = clobbers_buffer[0..clobber_i], 8890 }); 8891 return rvalue(gz, ri, result, node); 8892 } 8893 8894 fn as( 8895 gz: *GenZir, 8896 scope: *Scope, 8897 ri: ResultInfo, 8898 node: Ast.Node.Index, 8899 lhs: Ast.Node.Index, 8900 rhs: Ast.Node.Index, 8901 ) InnerError!Zir.Inst.Ref { 8902 const dest_type = try typeExpr(gz, scope, lhs); 8903 const result = try reachableExpr(gz, scope, .{ .rl = .{ .ty = dest_type } }, rhs, node); 8904 return rvalue(gz, ri, result, node); 8905 } 8906 8907 fn unionInit( 8908 gz: *GenZir, 8909 scope: *Scope, 8910 ri: ResultInfo, 8911 node: Ast.Node.Index, 8912 params: []const Ast.Node.Index, 8913 ) InnerError!Zir.Inst.Ref { 8914 const union_type = try typeExpr(gz, scope, params[0]); 8915 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]); 8916 const field_type = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{ 8917 .container_type = union_type, 8918 .field_name = field_name, 8919 }); 8920 const init = try reachableExpr(gz, scope, .{ .rl = .{ .ty = field_type } }, params[2], node); 8921 const result = try gz.addPlNode(.union_init, node, Zir.Inst.UnionInit{ 8922 .union_type = union_type, 8923 .init = init, 8924 .field_name = field_name, 8925 }); 8926 return rvalue(gz, ri, result, node); 8927 } 8928 8929 fn bitCast( 8930 gz: *GenZir, 8931 scope: *Scope, 8932 ri: ResultInfo, 8933 node: Ast.Node.Index, 8934 operand_node: Ast.Node.Index, 8935 ) InnerError!Zir.Inst.Ref { 8936 const dest_type = try ri.rl.resultTypeForCast(gz, node, "@bitCast"); 8937 const operand = try reachableExpr(gz, scope, .{ .rl = .none }, operand_node, node); 8938 const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{ 8939 .lhs = dest_type, 8940 .rhs = operand, 8941 }); 8942 return rvalue(gz, ri, result, node); 8943 } 8944 8945 /// Handle one or more nested pointer cast builtins: 8946 /// * @ptrCast 8947 /// * @alignCast 8948 /// * @addrSpaceCast 8949 /// * @constCast 8950 /// * @volatileCast 8951 /// Any sequence of such builtins is treated as a single operation. This allowed 8952 /// for sequences like `@ptrCast(@alignCast(ptr))` to work correctly despite the 8953 /// intermediate result type being unknown. 8954 fn ptrCast( 8955 gz: *GenZir, 8956 scope: *Scope, 8957 ri: ResultInfo, 8958 root_node: Ast.Node.Index, 8959 ) InnerError!Zir.Inst.Ref { 8960 const astgen = gz.astgen; 8961 const tree = astgen.tree; 8962 const main_tokens = tree.nodes.items(.main_token); 8963 const node_datas = tree.nodes.items(.data); 8964 const node_tags = tree.nodes.items(.tag); 8965 8966 const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?; 8967 var flags: Zir.Inst.FullPtrCastFlags = .{}; 8968 8969 // Note that all pointer cast builtins have one parameter, so we only need 8970 // to handle `builtin_call_two`. 8971 var node = root_node; 8972 while (true) { 8973 switch (node_tags[node]) { 8974 .builtin_call_two, .builtin_call_two_comma => {}, 8975 .grouped_expression => { 8976 // Handle the chaining even with redundant parentheses 8977 node = node_datas[node].lhs; 8978 continue; 8979 }, 8980 else => break, 8981 } 8982 8983 if (node_datas[node].lhs == 0) break; // 0 args 8984 8985 const builtin_token = main_tokens[node]; 8986 const builtin_name = tree.tokenSlice(builtin_token); 8987 const info = BuiltinFn.list.get(builtin_name) orelse break; 8988 if (node_datas[node].rhs == 0) { 8989 // 1 arg 8990 if (info.param_count != 1) break; 8991 8992 switch (info.tag) { 8993 else => break, 8994 inline .ptr_cast, 8995 .align_cast, 8996 .addrspace_cast, 8997 .const_cast, 8998 .volatile_cast, 8999 => |tag| { 9000 if (@field(flags, @tagName(tag))) { 9001 return astgen.failNode(node, "redundant {s}", .{builtin_name}); 9002 } 9003 @field(flags, @tagName(tag)) = true; 9004 }, 9005 } 9006 9007 node = node_datas[node].lhs; 9008 } else { 9009 // 2 args 9010 if (info.param_count != 2) break; 9011 9012 switch (info.tag) { 9013 else => break, 9014 .field_parent_ptr => { 9015 if (flags.ptr_cast) break; 9016 9017 const flags_int: FlagsInt = @bitCast(flags); 9018 const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node); 9019 const parent_ptr_type = try ri.rl.resultTypeForCast(gz, root_node, "@alignCast"); 9020 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, node_datas[node].lhs); 9021 const field_ptr = try expr(gz, scope, .{ .rl = .none }, node_datas[node].rhs); 9022 try emitDbgStmt(gz, cursor); 9023 const result = try gz.addExtendedPayloadSmall(.field_parent_ptr, flags_int, Zir.Inst.FieldParentPtr{ 9024 .src_node = gz.nodeIndexToRelative(node), 9025 .parent_ptr_type = parent_ptr_type, 9026 .field_name = field_name, 9027 .field_ptr = field_ptr, 9028 }); 9029 return rvalue(gz, ri, result, root_node); 9030 }, 9031 } 9032 } 9033 } 9034 9035 const flags_int: FlagsInt = @bitCast(flags); 9036 assert(flags_int != 0); 9037 9038 const ptr_only: Zir.Inst.FullPtrCastFlags = .{ .ptr_cast = true }; 9039 if (flags_int == @as(FlagsInt, @bitCast(ptr_only))) { 9040 // Special case: simpler representation 9041 return typeCast(gz, scope, ri, root_node, node, .ptr_cast, "@ptrCast"); 9042 } 9043 9044 const no_result_ty_flags: Zir.Inst.FullPtrCastFlags = .{ 9045 .const_cast = true, 9046 .volatile_cast = true, 9047 }; 9048 if ((flags_int & ~@as(FlagsInt, @bitCast(no_result_ty_flags))) == 0) { 9049 // Result type not needed 9050 const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node); 9051 const operand = try expr(gz, scope, .{ .rl = .none }, node); 9052 try emitDbgStmt(gz, cursor); 9053 const result = try gz.addExtendedPayloadSmall(.ptr_cast_no_dest, flags_int, Zir.Inst.UnNode{ 9054 .node = gz.nodeIndexToRelative(root_node), 9055 .operand = operand, 9056 }); 9057 return rvalue(gz, ri, result, root_node); 9058 } 9059 9060 // Full cast including result type 9061 9062 const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node); 9063 const result_type = try ri.rl.resultTypeForCast(gz, root_node, flags.needResultTypeBuiltinName()); 9064 const operand = try expr(gz, scope, .{ .rl = .none }, node); 9065 try emitDbgStmt(gz, cursor); 9066 const result = try gz.addExtendedPayloadSmall(.ptr_cast_full, flags_int, Zir.Inst.BinNode{ 9067 .node = gz.nodeIndexToRelative(root_node), 9068 .lhs = result_type, 9069 .rhs = operand, 9070 }); 9071 return rvalue(gz, ri, result, root_node); 9072 } 9073 9074 fn typeOf( 9075 gz: *GenZir, 9076 scope: *Scope, 9077 ri: ResultInfo, 9078 node: Ast.Node.Index, 9079 args: []const Ast.Node.Index, 9080 ) InnerError!Zir.Inst.Ref { 9081 const astgen = gz.astgen; 9082 if (args.len < 1) { 9083 return astgen.failNode(node, "expected at least 1 argument, found 0", .{}); 9084 } 9085 const gpa = astgen.gpa; 9086 if (args.len == 1) { 9087 const typeof_inst = try gz.makeBlockInst(.typeof_builtin, node); 9088 9089 var typeof_scope = gz.makeSubBlock(scope); 9090 typeof_scope.is_comptime = false; 9091 typeof_scope.is_typeof = true; 9092 typeof_scope.c_import = false; 9093 defer typeof_scope.unstack(); 9094 9095 const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, args[0], node); 9096 if (!gz.refIsNoReturn(ty_expr)) { 9097 _ = try typeof_scope.addBreak(.break_inline, typeof_inst, ty_expr); 9098 } 9099 try typeof_scope.setBlockBody(typeof_inst); 9100 9101 // typeof_scope unstacked now, can add new instructions to gz 9102 try gz.instructions.append(gpa, typeof_inst); 9103 return rvalue(gz, ri, typeof_inst.toRef(), node); 9104 } 9105 const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len; 9106 const payload_index = try reserveExtra(astgen, payload_size + args.len); 9107 const args_index = payload_index + payload_size; 9108 9109 const typeof_inst = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, args.len); 9110 9111 var typeof_scope = gz.makeSubBlock(scope); 9112 typeof_scope.is_comptime = false; 9113 9114 for (args, 0..) |arg, i| { 9115 const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, arg, node); 9116 astgen.extra.items[args_index + i] = @intFromEnum(param_ref); 9117 } 9118 _ = try typeof_scope.addBreak(.break_inline, typeof_inst.toIndex().?, .void_value); 9119 9120 const body = typeof_scope.instructionsSlice(); 9121 const body_len = astgen.countBodyLenAfterFixups(body); 9122 astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{ 9123 .body_len = @intCast(body_len), 9124 .body_index = @intCast(astgen.extra.items.len), 9125 .src_node = gz.nodeIndexToRelative(node), 9126 }); 9127 try astgen.extra.ensureUnusedCapacity(gpa, body_len); 9128 astgen.appendBodyWithFixups(body); 9129 typeof_scope.unstack(); 9130 9131 return rvalue(gz, ri, typeof_inst, node); 9132 } 9133 9134 fn minMax( 9135 gz: *GenZir, 9136 scope: *Scope, 9137 ri: ResultInfo, 9138 node: Ast.Node.Index, 9139 args: []const Ast.Node.Index, 9140 comptime op: enum { min, max }, 9141 ) InnerError!Zir.Inst.Ref { 9142 const astgen = gz.astgen; 9143 if (args.len < 2) { 9144 return astgen.failNode(node, "expected at least 2 arguments, found {}", .{args.len}); 9145 } 9146 if (args.len == 2) { 9147 const tag: Zir.Inst.Tag = switch (op) { 9148 .min => .min, 9149 .max => .max, 9150 }; 9151 const a = try expr(gz, scope, .{ .rl = .none }, args[0]); 9152 const b = try expr(gz, scope, .{ .rl = .none }, args[1]); 9153 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 9154 .lhs = a, 9155 .rhs = b, 9156 }); 9157 return rvalue(gz, ri, result, node); 9158 } 9159 const payload_index = try addExtra(astgen, Zir.Inst.NodeMultiOp{ 9160 .src_node = gz.nodeIndexToRelative(node), 9161 }); 9162 var extra_index = try reserveExtra(gz.astgen, args.len); 9163 for (args) |arg| { 9164 const arg_ref = try expr(gz, scope, .{ .rl = .none }, arg); 9165 astgen.extra.items[extra_index] = @intFromEnum(arg_ref); 9166 extra_index += 1; 9167 } 9168 const tag: Zir.Inst.Extended = switch (op) { 9169 .min => .min_multi, 9170 .max => .max_multi, 9171 }; 9172 const result = try gz.addExtendedMultiOpPayloadIndex(tag, payload_index, args.len); 9173 return rvalue(gz, ri, result, node); 9174 } 9175 9176 fn builtinCall( 9177 gz: *GenZir, 9178 scope: *Scope, 9179 ri: ResultInfo, 9180 node: Ast.Node.Index, 9181 params: []const Ast.Node.Index, 9182 allow_branch_hint: bool, 9183 ) InnerError!Zir.Inst.Ref { 9184 const astgen = gz.astgen; 9185 const tree = astgen.tree; 9186 const main_tokens = tree.nodes.items(.main_token); 9187 9188 const builtin_token = main_tokens[node]; 9189 const builtin_name = tree.tokenSlice(builtin_token); 9190 9191 // We handle the different builtins manually because they have different semantics depending 9192 // on the function. For example, `@as` and others participate in result location semantics, 9193 // and `@cImport` creates a special scope that collects a .c source code text buffer. 9194 // Also, some builtins have a variable number of parameters. 9195 9196 const info = BuiltinFn.list.get(builtin_name) orelse { 9197 return astgen.failNode(node, "invalid builtin function: '{s}'", .{ 9198 builtin_name, 9199 }); 9200 }; 9201 if (info.param_count) |expected| { 9202 if (expected != params.len) { 9203 const s = if (expected == 1) "" else "s"; 9204 return astgen.failNode(node, "expected {d} argument{s}, found {d}", .{ 9205 expected, s, params.len, 9206 }); 9207 } 9208 } 9209 9210 // Check function scope-only builtins 9211 9212 if (astgen.fn_block == null and info.illegal_outside_function) 9213 return astgen.failNode(node, "'{s}' outside function scope", .{builtin_name}); 9214 9215 switch (info.tag) { 9216 .branch_hint => { 9217 if (!allow_branch_hint) { 9218 return astgen.failNode(node, "'@branchHint' must appear as the first statement in a function or conditional branch", .{}); 9219 } 9220 const hint_ty = try gz.addBuiltinValue(node, .branch_hint); 9221 const hint_val = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = hint_ty } }, params[0]); 9222 _ = try gz.addExtendedPayload(.branch_hint, Zir.Inst.UnNode{ 9223 .node = gz.nodeIndexToRelative(node), 9224 .operand = hint_val, 9225 }); 9226 return rvalue(gz, ri, .void_value, node); 9227 }, 9228 .import => { 9229 const node_tags = tree.nodes.items(.tag); 9230 const operand_node = params[0]; 9231 9232 if (node_tags[operand_node] != .string_literal) { 9233 // Spec reference: https://github.com/ziglang/zig/issues/2206 9234 return astgen.failNode(operand_node, "@import operand must be a string literal", .{}); 9235 } 9236 const str_lit_token = main_tokens[operand_node]; 9237 const str = try astgen.strLitAsString(str_lit_token); 9238 const str_slice = astgen.string_bytes.items[@intFromEnum(str.index)..][0..str.len]; 9239 if (mem.indexOfScalar(u8, str_slice, 0) != null) { 9240 return astgen.failTok(str_lit_token, "import path cannot contain null bytes", .{}); 9241 } else if (str.len == 0) { 9242 return astgen.failTok(str_lit_token, "import path cannot be empty", .{}); 9243 } 9244 const result = try gz.addStrTok(.import, str.index, str_lit_token); 9245 const gop = try astgen.imports.getOrPut(astgen.gpa, str.index); 9246 if (!gop.found_existing) { 9247 gop.value_ptr.* = str_lit_token; 9248 } 9249 return rvalue(gz, ri, result, node); 9250 }, 9251 .compile_log => { 9252 const payload_index = try addExtra(gz.astgen, Zir.Inst.NodeMultiOp{ 9253 .src_node = gz.nodeIndexToRelative(node), 9254 }); 9255 var extra_index = try reserveExtra(gz.astgen, params.len); 9256 for (params) |param| { 9257 const param_ref = try expr(gz, scope, .{ .rl = .none }, param); 9258 astgen.extra.items[extra_index] = @intFromEnum(param_ref); 9259 extra_index += 1; 9260 } 9261 const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log, payload_index, params.len); 9262 return rvalue(gz, ri, result, node); 9263 }, 9264 .field => { 9265 if (ri.rl == .ref or ri.rl == .ref_coerced_ty) { 9266 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{ 9267 .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]), 9268 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]), 9269 }); 9270 } 9271 const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{ 9272 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), 9273 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]), 9274 }); 9275 return rvalue(gz, ri, result, node); 9276 }, 9277 9278 // zig fmt: off 9279 .as => return as( gz, scope, ri, node, params[0], params[1]), 9280 .bit_cast => return bitCast( gz, scope, ri, node, params[0]), 9281 .TypeOf => return typeOf( gz, scope, ri, node, params), 9282 .union_init => return unionInit(gz, scope, ri, node, params), 9283 .c_import => return cImport( gz, scope, node, params[0]), 9284 .min => return minMax( gz, scope, ri, node, params, .min), 9285 .max => return minMax( gz, scope, ri, node, params, .max), 9286 // zig fmt: on 9287 9288 .@"export" => { 9289 const exported = try expr(gz, scope, .{ .rl = .none }, params[0]); 9290 const export_options_ty = try gz.addBuiltinValue(node, .export_options); 9291 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = export_options_ty } }, params[1]); 9292 _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{ 9293 .exported = exported, 9294 .options = options, 9295 }); 9296 return rvalue(gz, ri, .void_value, node); 9297 }, 9298 .@"extern" => { 9299 const type_inst = try typeExpr(gz, scope, params[0]); 9300 const extern_options_ty = try gz.addBuiltinValue(node, .extern_options); 9301 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = extern_options_ty } }, params[1]); 9302 const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{ 9303 .node = gz.nodeIndexToRelative(node), 9304 .lhs = type_inst, 9305 .rhs = options, 9306 }); 9307 return rvalue(gz, ri, result, node); 9308 }, 9309 .set_float_mode => { 9310 const float_mode_ty = try gz.addBuiltinValue(node, .float_mode); 9311 const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_mode_ty } }, params[0]); 9312 _ = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{ 9313 .node = gz.nodeIndexToRelative(node), 9314 .operand = order, 9315 }); 9316 return rvalue(gz, ri, .void_value, node); 9317 }, 9318 .set_align_stack => { 9319 const order = try expr(gz, scope, coerced_align_ri, params[0]); 9320 _ = try gz.addExtendedPayload(.set_align_stack, Zir.Inst.UnNode{ 9321 .node = gz.nodeIndexToRelative(node), 9322 .operand = order, 9323 }); 9324 return rvalue(gz, ri, .void_value, node); 9325 }, 9326 9327 .src => { 9328 // Incorporate the source location into the source hash, so that 9329 // changes in the source location of `@src()` result in re-analysis. 9330 astgen.src_hasher.update( 9331 std.mem.asBytes(&astgen.source_line) ++ 9332 std.mem.asBytes(&astgen.source_column), 9333 ); 9334 9335 const token_starts = tree.tokens.items(.start); 9336 const node_start = token_starts[tree.firstToken(node)]; 9337 astgen.advanceSourceCursor(node_start); 9338 const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.Src{ 9339 .node = gz.nodeIndexToRelative(node), 9340 .line = astgen.source_line, 9341 .column = astgen.source_column, 9342 }); 9343 return rvalue(gz, ri, result, node); 9344 }, 9345 9346 // zig fmt: off 9347 .This => return rvalue(gz, ri, try gz.addNodeExtended(.this, node), node), 9348 .return_address => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr, node), node), 9349 .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node), 9350 .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node), 9351 .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node), 9352 .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node), 9353 .disable_instrumentation => return rvalue(gz, ri, try gz.addNodeExtended(.disable_instrumentation, node), node), 9354 9355 .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info), 9356 .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of), 9357 .bit_size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .bit_size_of), 9358 .align_of => return simpleUnOpType(gz, scope, ri, node, params[0], .align_of), 9359 9360 .int_from_ptr => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_ptr), 9361 .compile_error => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .compile_error), 9362 .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota), 9363 .int_from_enum => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_enum), 9364 .int_from_bool => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_bool), 9365 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file), 9366 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name), 9367 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, params[0], .set_runtime_safety), 9368 .sqrt => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sqrt), 9369 .sin => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sin), 9370 .cos => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .cos), 9371 .tan => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tan), 9372 .exp => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .exp), 9373 .exp2 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .exp2), 9374 .log => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log), 9375 .log2 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log2), 9376 .log10 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log10), 9377 .abs => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .abs), 9378 .floor => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .floor), 9379 .ceil => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .ceil), 9380 .trunc => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .trunc), 9381 .round => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .round), 9382 .tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name), 9383 .type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name), 9384 .Frame => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_type), 9385 .frame_size => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_size), 9386 9387 .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name), 9388 .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name), 9389 .ptr_from_int => return typeCast(gz, scope, ri, node, params[0], .ptr_from_int, builtin_name), 9390 .enum_from_int => return typeCast(gz, scope, ri, node, params[0], .enum_from_int, builtin_name), 9391 .float_cast => return typeCast(gz, scope, ri, node, params[0], .float_cast, builtin_name), 9392 .int_cast => return typeCast(gz, scope, ri, node, params[0], .int_cast, builtin_name), 9393 .truncate => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name), 9394 // zig fmt: on 9395 9396 .in_comptime => if (gz.is_comptime) { 9397 return astgen.failNode(node, "redundant '@inComptime' in comptime scope", .{}); 9398 } else { 9399 return rvalue(gz, ri, try gz.addNodeExtended(.in_comptime, node), node); 9400 }, 9401 9402 .Type => { 9403 const type_info_ty = try gz.addBuiltinValue(node, .type_info); 9404 const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = type_info_ty } }, params[0]); 9405 9406 const gpa = gz.astgen.gpa; 9407 9408 try gz.instructions.ensureUnusedCapacity(gpa, 1); 9409 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 9410 9411 const payload_index = try gz.astgen.addExtra(Zir.Inst.Reify{ 9412 .node = node, // Absolute node index -- see the definition of `Reify`. 9413 .operand = operand, 9414 .src_line = astgen.source_line, 9415 }); 9416 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 9417 gz.astgen.instructions.appendAssumeCapacity(.{ 9418 .tag = .extended, 9419 .data = .{ .extended = .{ 9420 .opcode = .reify, 9421 .small = @intFromEnum(gz.anon_name_strategy), 9422 .operand = payload_index, 9423 } }, 9424 }); 9425 gz.instructions.appendAssumeCapacity(new_index); 9426 const result = new_index.toRef(); 9427 return rvalue(gz, ri, result, node); 9428 }, 9429 .panic => { 9430 try emitDbgNode(gz, node); 9431 return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .panic); 9432 }, 9433 .trap => { 9434 try emitDbgNode(gz, node); 9435 _ = try gz.addNode(.trap, node); 9436 return rvalue(gz, ri, .unreachable_value, node); 9437 }, 9438 .int_from_error => { 9439 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]); 9440 const result = try gz.addExtendedPayload(.int_from_error, Zir.Inst.UnNode{ 9441 .node = gz.nodeIndexToRelative(node), 9442 .operand = operand, 9443 }); 9444 return rvalue(gz, ri, result, node); 9445 }, 9446 .error_from_int => { 9447 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]); 9448 const result = try gz.addExtendedPayload(.error_from_int, Zir.Inst.UnNode{ 9449 .node = gz.nodeIndexToRelative(node), 9450 .operand = operand, 9451 }); 9452 return rvalue(gz, ri, result, node); 9453 }, 9454 .error_cast => { 9455 try emitDbgNode(gz, node); 9456 9457 const result = try gz.addExtendedPayload(.error_cast, Zir.Inst.BinNode{ 9458 .lhs = try ri.rl.resultTypeForCast(gz, node, builtin_name), 9459 .rhs = try expr(gz, scope, .{ .rl = .none }, params[0]), 9460 .node = gz.nodeIndexToRelative(node), 9461 }); 9462 return rvalue(gz, ri, result, node); 9463 }, 9464 .ptr_cast, 9465 .align_cast, 9466 .addrspace_cast, 9467 .const_cast, 9468 .volatile_cast, 9469 => return ptrCast(gz, scope, ri, node), 9470 9471 // zig fmt: off 9472 .has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl), 9473 .has_field => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_field), 9474 9475 .clz => return bitBuiltin(gz, scope, ri, node, params[0], .clz), 9476 .ctz => return bitBuiltin(gz, scope, ri, node, params[0], .ctz), 9477 .pop_count => return bitBuiltin(gz, scope, ri, node, params[0], .pop_count), 9478 .byte_swap => return bitBuiltin(gz, scope, ri, node, params[0], .byte_swap), 9479 .bit_reverse => return bitBuiltin(gz, scope, ri, node, params[0], .bit_reverse), 9480 9481 .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact), 9482 .div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor), 9483 .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc), 9484 .mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod), 9485 .rem => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem), 9486 9487 .shl_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shl_exact), 9488 .shr_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shr_exact), 9489 9490 .bit_offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .bit_offset_of), 9491 .offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .offset_of), 9492 9493 .c_undef => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_undef), 9494 .c_include => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_include), 9495 9496 .cmpxchg_strong => return cmpxchg(gz, scope, ri, node, params, 1), 9497 .cmpxchg_weak => return cmpxchg(gz, scope, ri, node, params, 0), 9498 // zig fmt: on 9499 9500 .wasm_memory_size => { 9501 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]); 9502 const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{ 9503 .node = gz.nodeIndexToRelative(node), 9504 .operand = operand, 9505 }); 9506 return rvalue(gz, ri, result, node); 9507 }, 9508 .wasm_memory_grow => { 9509 const index_arg = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]); 9510 const delta_arg = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[1]); 9511 const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{ 9512 .node = gz.nodeIndexToRelative(node), 9513 .lhs = index_arg, 9514 .rhs = delta_arg, 9515 }); 9516 return rvalue(gz, ri, result, node); 9517 }, 9518 .c_define => { 9519 if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{}); 9520 const name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0]); 9521 const value = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]); 9522 const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{ 9523 .node = gz.nodeIndexToRelative(node), 9524 .lhs = name, 9525 .rhs = value, 9526 }); 9527 return rvalue(gz, ri, result, node); 9528 }, 9529 9530 .splat => { 9531 const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name); 9532 const elem_type = try gz.addUnNode(.vec_arr_elem_type, result_type, node); 9533 const scalar = try expr(gz, scope, .{ .rl = .{ .ty = elem_type } }, params[0]); 9534 const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{ 9535 .lhs = result_type, 9536 .rhs = scalar, 9537 }); 9538 return rvalue(gz, ri, result, node); 9539 }, 9540 .reduce => { 9541 const reduce_op_ty = try gz.addBuiltinValue(node, .reduce_op); 9542 const op = try expr(gz, scope, .{ .rl = .{ .coerced_ty = reduce_op_ty } }, params[0]); 9543 const scalar = try expr(gz, scope, .{ .rl = .none }, params[1]); 9544 const result = try gz.addPlNode(.reduce, node, Zir.Inst.Bin{ 9545 .lhs = op, 9546 .rhs = scalar, 9547 }); 9548 return rvalue(gz, ri, result, node); 9549 }, 9550 9551 .add_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .add_with_overflow), 9552 .sub_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .sub_with_overflow), 9553 .mul_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .mul_with_overflow), 9554 .shl_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .shl_with_overflow), 9555 9556 .atomic_load => { 9557 const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order); 9558 const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{ 9559 // zig fmt: off 9560 .elem_type = try typeExpr(gz, scope, params[0]), 9561 .ptr = try expr (gz, scope, .{ .rl = .none }, params[1]), 9562 .ordering = try expr (gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[2]), 9563 // zig fmt: on 9564 }); 9565 return rvalue(gz, ri, result, node); 9566 }, 9567 .atomic_rmw => { 9568 const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order); 9569 const atomic_rmw_op_type = try gz.addBuiltinValue(node, .atomic_rmw_op); 9570 const int_type = try typeExpr(gz, scope, params[0]); 9571 const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{ 9572 // zig fmt: off 9573 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), 9574 .operation = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_rmw_op_type } }, params[2]), 9575 .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[3]), 9576 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[4]), 9577 // zig fmt: on 9578 }); 9579 return rvalue(gz, ri, result, node); 9580 }, 9581 .atomic_store => { 9582 const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order); 9583 const int_type = try typeExpr(gz, scope, params[0]); 9584 _ = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{ 9585 // zig fmt: off 9586 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), 9587 .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]), 9588 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[3]), 9589 // zig fmt: on 9590 }); 9591 return rvalue(gz, ri, .void_value, node); 9592 }, 9593 .mul_add => { 9594 const float_type = try typeExpr(gz, scope, params[0]); 9595 const mulend1 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[1]); 9596 const mulend2 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[2]); 9597 const addend = try expr(gz, scope, .{ .rl = .{ .ty = float_type } }, params[3]); 9598 const result = try gz.addPlNode(.mul_add, node, Zir.Inst.MulAdd{ 9599 .mulend1 = mulend1, 9600 .mulend2 = mulend2, 9601 .addend = addend, 9602 }); 9603 return rvalue(gz, ri, result, node); 9604 }, 9605 .call => { 9606 const call_modifier_ty = try gz.addBuiltinValue(node, .call_modifier); 9607 const modifier = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = call_modifier_ty } }, params[0]); 9608 const callee = try expr(gz, scope, .{ .rl = .none }, params[1]); 9609 const args = try expr(gz, scope, .{ .rl = .none }, params[2]); 9610 const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{ 9611 .modifier = modifier, 9612 .callee = callee, 9613 .args = args, 9614 .flags = .{ 9615 .is_nosuspend = gz.nosuspend_node != 0, 9616 .ensure_result_used = false, 9617 }, 9618 }); 9619 return rvalue(gz, ri, result, node); 9620 }, 9621 .field_parent_ptr => { 9622 const parent_ptr_type = try ri.rl.resultTypeForCast(gz, node, builtin_name); 9623 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0]); 9624 const result = try gz.addExtendedPayloadSmall(.field_parent_ptr, 0, Zir.Inst.FieldParentPtr{ 9625 .src_node = gz.nodeIndexToRelative(node), 9626 .parent_ptr_type = parent_ptr_type, 9627 .field_name = field_name, 9628 .field_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), 9629 }); 9630 return rvalue(gz, ri, result, node); 9631 }, 9632 .memcpy => { 9633 _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{ 9634 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), 9635 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]), 9636 }); 9637 return rvalue(gz, ri, .void_value, node); 9638 }, 9639 .memset => { 9640 const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]); 9641 const lhs_ty = try gz.addUnNode(.typeof, lhs, params[0]); 9642 const elem_ty = try gz.addUnNode(.indexable_ptr_elem_type, lhs_ty, params[0]); 9643 _ = try gz.addPlNode(.memset, node, Zir.Inst.Bin{ 9644 .lhs = lhs, 9645 .rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = elem_ty } }, params[1]), 9646 }); 9647 return rvalue(gz, ri, .void_value, node); 9648 }, 9649 .shuffle => { 9650 const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{ 9651 .elem_type = try typeExpr(gz, scope, params[0]), 9652 .a = try expr(gz, scope, .{ .rl = .none }, params[1]), 9653 .b = try expr(gz, scope, .{ .rl = .none }, params[2]), 9654 .mask = try comptimeExpr(gz, scope, .{ .rl = .none }, params[3]), 9655 }); 9656 return rvalue(gz, ri, result, node); 9657 }, 9658 .select => { 9659 const result = try gz.addExtendedPayload(.select, Zir.Inst.Select{ 9660 .node = gz.nodeIndexToRelative(node), 9661 .elem_type = try typeExpr(gz, scope, params[0]), 9662 .pred = try expr(gz, scope, .{ .rl = .none }, params[1]), 9663 .a = try expr(gz, scope, .{ .rl = .none }, params[2]), 9664 .b = try expr(gz, scope, .{ .rl = .none }, params[3]), 9665 }); 9666 return rvalue(gz, ri, result, node); 9667 }, 9668 .async_call => { 9669 const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{ 9670 .node = gz.nodeIndexToRelative(node), 9671 .frame_buffer = try expr(gz, scope, .{ .rl = .none }, params[0]), 9672 .result_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), 9673 .fn_ptr = try expr(gz, scope, .{ .rl = .none }, params[2]), 9674 .args = try expr(gz, scope, .{ .rl = .none }, params[3]), 9675 }); 9676 return rvalue(gz, ri, result, node); 9677 }, 9678 .Vector => { 9679 const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{ 9680 .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]), 9681 .rhs = try typeExpr(gz, scope, params[1]), 9682 }); 9683 return rvalue(gz, ri, result, node); 9684 }, 9685 .prefetch => { 9686 const prefetch_options_ty = try gz.addBuiltinValue(node, .prefetch_options); 9687 const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]); 9688 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = prefetch_options_ty } }, params[1]); 9689 _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{ 9690 .node = gz.nodeIndexToRelative(node), 9691 .lhs = ptr, 9692 .rhs = options, 9693 }); 9694 return rvalue(gz, ri, .void_value, node); 9695 }, 9696 .c_va_arg => { 9697 const result = try gz.addExtendedPayload(.c_va_arg, Zir.Inst.BinNode{ 9698 .node = gz.nodeIndexToRelative(node), 9699 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), 9700 .rhs = try typeExpr(gz, scope, params[1]), 9701 }); 9702 return rvalue(gz, ri, result, node); 9703 }, 9704 .c_va_copy => { 9705 const result = try gz.addExtendedPayload(.c_va_copy, Zir.Inst.UnNode{ 9706 .node = gz.nodeIndexToRelative(node), 9707 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]), 9708 }); 9709 return rvalue(gz, ri, result, node); 9710 }, 9711 .c_va_end => { 9712 const result = try gz.addExtendedPayload(.c_va_end, Zir.Inst.UnNode{ 9713 .node = gz.nodeIndexToRelative(node), 9714 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]), 9715 }); 9716 return rvalue(gz, ri, result, node); 9717 }, 9718 .c_va_start => { 9719 if (!astgen.fn_var_args) { 9720 return astgen.failNode(node, "'@cVaStart' in a non-variadic function", .{}); 9721 } 9722 return rvalue(gz, ri, try gz.addNodeExtended(.c_va_start, node), node); 9723 }, 9724 9725 .work_item_id => { 9726 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]); 9727 const result = try gz.addExtendedPayload(.work_item_id, Zir.Inst.UnNode{ 9728 .node = gz.nodeIndexToRelative(node), 9729 .operand = operand, 9730 }); 9731 return rvalue(gz, ri, result, node); 9732 }, 9733 .work_group_size => { 9734 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]); 9735 const result = try gz.addExtendedPayload(.work_group_size, Zir.Inst.UnNode{ 9736 .node = gz.nodeIndexToRelative(node), 9737 .operand = operand, 9738 }); 9739 return rvalue(gz, ri, result, node); 9740 }, 9741 .work_group_id => { 9742 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]); 9743 const result = try gz.addExtendedPayload(.work_group_id, Zir.Inst.UnNode{ 9744 .node = gz.nodeIndexToRelative(node), 9745 .operand = operand, 9746 }); 9747 return rvalue(gz, ri, result, node); 9748 }, 9749 } 9750 } 9751 9752 fn hasDeclOrField( 9753 gz: *GenZir, 9754 scope: *Scope, 9755 ri: ResultInfo, 9756 node: Ast.Node.Index, 9757 lhs_node: Ast.Node.Index, 9758 rhs_node: Ast.Node.Index, 9759 tag: Zir.Inst.Tag, 9760 ) InnerError!Zir.Inst.Ref { 9761 const container_type = try typeExpr(gz, scope, lhs_node); 9762 const name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, rhs_node); 9763 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 9764 .lhs = container_type, 9765 .rhs = name, 9766 }); 9767 return rvalue(gz, ri, result, node); 9768 } 9769 9770 fn typeCast( 9771 gz: *GenZir, 9772 scope: *Scope, 9773 ri: ResultInfo, 9774 node: Ast.Node.Index, 9775 operand_node: Ast.Node.Index, 9776 tag: Zir.Inst.Tag, 9777 builtin_name: []const u8, 9778 ) InnerError!Zir.Inst.Ref { 9779 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 9780 const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name); 9781 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node); 9782 9783 try emitDbgStmt(gz, cursor); 9784 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 9785 .lhs = result_type, 9786 .rhs = operand, 9787 }); 9788 return rvalue(gz, ri, result, node); 9789 } 9790 9791 fn simpleUnOpType( 9792 gz: *GenZir, 9793 scope: *Scope, 9794 ri: ResultInfo, 9795 node: Ast.Node.Index, 9796 operand_node: Ast.Node.Index, 9797 tag: Zir.Inst.Tag, 9798 ) InnerError!Zir.Inst.Ref { 9799 const operand = try typeExpr(gz, scope, operand_node); 9800 const result = try gz.addUnNode(tag, operand, node); 9801 return rvalue(gz, ri, result, node); 9802 } 9803 9804 fn simpleUnOp( 9805 gz: *GenZir, 9806 scope: *Scope, 9807 ri: ResultInfo, 9808 node: Ast.Node.Index, 9809 operand_ri: ResultInfo, 9810 operand_node: Ast.Node.Index, 9811 tag: Zir.Inst.Tag, 9812 ) InnerError!Zir.Inst.Ref { 9813 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 9814 const operand = if (tag == .compile_error) 9815 try comptimeExpr(gz, scope, operand_ri, operand_node) 9816 else 9817 try expr(gz, scope, operand_ri, operand_node); 9818 switch (tag) { 9819 .tag_name, .error_name, .int_from_ptr => try emitDbgStmt(gz, cursor), 9820 else => {}, 9821 } 9822 const result = try gz.addUnNode(tag, operand, node); 9823 return rvalue(gz, ri, result, node); 9824 } 9825 9826 fn negation( 9827 gz: *GenZir, 9828 scope: *Scope, 9829 ri: ResultInfo, 9830 node: Ast.Node.Index, 9831 ) InnerError!Zir.Inst.Ref { 9832 const astgen = gz.astgen; 9833 const tree = astgen.tree; 9834 const node_tags = tree.nodes.items(.tag); 9835 const node_datas = tree.nodes.items(.data); 9836 9837 // Check for float literal as the sub-expression because we want to preserve 9838 // its negativity rather than having it go through comptime subtraction. 9839 const operand_node = node_datas[node].lhs; 9840 if (node_tags[operand_node] == .number_literal) { 9841 return numberLiteral(gz, ri, operand_node, node, .negative); 9842 } 9843 9844 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node); 9845 const result = try gz.addUnNode(.negate, operand, node); 9846 return rvalue(gz, ri, result, node); 9847 } 9848 9849 fn cmpxchg( 9850 gz: *GenZir, 9851 scope: *Scope, 9852 ri: ResultInfo, 9853 node: Ast.Node.Index, 9854 params: []const Ast.Node.Index, 9855 small: u16, 9856 ) InnerError!Zir.Inst.Ref { 9857 const int_type = try typeExpr(gz, scope, params[0]); 9858 const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order); 9859 const result = try gz.addExtendedPayloadSmall(.cmpxchg, small, Zir.Inst.Cmpxchg{ 9860 // zig fmt: off 9861 .node = gz.nodeIndexToRelative(node), 9862 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), 9863 .expected_value = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]), 9864 .new_value = try expr(gz, scope, .{ .rl = .{ .coerced_ty = int_type } }, params[3]), 9865 .success_order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[4]), 9866 .failure_order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[5]), 9867 // zig fmt: on 9868 }); 9869 return rvalue(gz, ri, result, node); 9870 } 9871 9872 fn bitBuiltin( 9873 gz: *GenZir, 9874 scope: *Scope, 9875 ri: ResultInfo, 9876 node: Ast.Node.Index, 9877 operand_node: Ast.Node.Index, 9878 tag: Zir.Inst.Tag, 9879 ) InnerError!Zir.Inst.Ref { 9880 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node); 9881 const result = try gz.addUnNode(tag, operand, node); 9882 return rvalue(gz, ri, result, node); 9883 } 9884 9885 fn divBuiltin( 9886 gz: *GenZir, 9887 scope: *Scope, 9888 ri: ResultInfo, 9889 node: Ast.Node.Index, 9890 lhs_node: Ast.Node.Index, 9891 rhs_node: Ast.Node.Index, 9892 tag: Zir.Inst.Tag, 9893 ) InnerError!Zir.Inst.Ref { 9894 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 9895 const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node); 9896 const rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node); 9897 9898 try emitDbgStmt(gz, cursor); 9899 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); 9900 return rvalue(gz, ri, result, node); 9901 } 9902 9903 fn simpleCBuiltin( 9904 gz: *GenZir, 9905 scope: *Scope, 9906 ri: ResultInfo, 9907 node: Ast.Node.Index, 9908 operand_node: Ast.Node.Index, 9909 tag: Zir.Inst.Extended, 9910 ) InnerError!Zir.Inst.Ref { 9911 const name: []const u8 = if (tag == .c_undef) "C undef" else "C include"; 9912 if (!gz.c_import) return gz.astgen.failNode(node, "{s} valid only inside C import block", .{name}); 9913 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, operand_node); 9914 _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{ 9915 .node = gz.nodeIndexToRelative(node), 9916 .operand = operand, 9917 }); 9918 return rvalue(gz, ri, .void_value, node); 9919 } 9920 9921 fn offsetOf( 9922 gz: *GenZir, 9923 scope: *Scope, 9924 ri: ResultInfo, 9925 node: Ast.Node.Index, 9926 lhs_node: Ast.Node.Index, 9927 rhs_node: Ast.Node.Index, 9928 tag: Zir.Inst.Tag, 9929 ) InnerError!Zir.Inst.Ref { 9930 const type_inst = try typeExpr(gz, scope, lhs_node); 9931 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, rhs_node); 9932 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 9933 .lhs = type_inst, 9934 .rhs = field_name, 9935 }); 9936 return rvalue(gz, ri, result, node); 9937 } 9938 9939 fn shiftOp( 9940 gz: *GenZir, 9941 scope: *Scope, 9942 ri: ResultInfo, 9943 node: Ast.Node.Index, 9944 lhs_node: Ast.Node.Index, 9945 rhs_node: Ast.Node.Index, 9946 tag: Zir.Inst.Tag, 9947 ) InnerError!Zir.Inst.Ref { 9948 const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node); 9949 9950 const cursor = switch (gz.astgen.tree.nodes.items(.tag)[node]) { 9951 .shl, .shr => maybeAdvanceSourceCursorToMainToken(gz, node), 9952 else => undefined, 9953 }; 9954 9955 const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node); 9956 const rhs = try expr(gz, scope, .{ .rl = .{ .ty = log2_int_type }, .ctx = .shift_op }, rhs_node); 9957 9958 switch (gz.astgen.tree.nodes.items(.tag)[node]) { 9959 .shl, .shr => try emitDbgStmt(gz, cursor), 9960 else => undefined, 9961 } 9962 9963 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ 9964 .lhs = lhs, 9965 .rhs = rhs, 9966 }); 9967 return rvalue(gz, ri, result, node); 9968 } 9969 9970 fn cImport( 9971 gz: *GenZir, 9972 scope: *Scope, 9973 node: Ast.Node.Index, 9974 body_node: Ast.Node.Index, 9975 ) InnerError!Zir.Inst.Ref { 9976 const astgen = gz.astgen; 9977 const gpa = astgen.gpa; 9978 9979 if (gz.c_import) return gz.astgen.failNode(node, "cannot nest @cImport", .{}); 9980 9981 var block_scope = gz.makeSubBlock(scope); 9982 block_scope.is_comptime = true; 9983 block_scope.c_import = true; 9984 defer block_scope.unstack(); 9985 9986 const block_inst = try gz.makeBlockInst(.c_import, node); 9987 const block_result = try fullBodyExpr(&block_scope, &block_scope.base, .{ .rl = .none }, body_node, .normal); 9988 _ = try gz.addUnNode(.ensure_result_used, block_result, node); 9989 if (!gz.refIsNoReturn(block_result)) { 9990 _ = try block_scope.addBreak(.break_inline, block_inst, .void_value); 9991 } 9992 try block_scope.setBlockBody(block_inst); 9993 // block_scope unstacked now, can add new instructions to gz 9994 try gz.instructions.append(gpa, block_inst); 9995 9996 return block_inst.toRef(); 9997 } 9998 9999 fn overflowArithmetic( 10000 gz: *GenZir, 10001 scope: *Scope, 10002 ri: ResultInfo, 10003 node: Ast.Node.Index, 10004 params: []const Ast.Node.Index, 10005 tag: Zir.Inst.Extended, 10006 ) InnerError!Zir.Inst.Ref { 10007 const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]); 10008 const rhs = try expr(gz, scope, .{ .rl = .none }, params[1]); 10009 const result = try gz.addExtendedPayload(tag, Zir.Inst.BinNode{ 10010 .node = gz.nodeIndexToRelative(node), 10011 .lhs = lhs, 10012 .rhs = rhs, 10013 }); 10014 return rvalue(gz, ri, result, node); 10015 } 10016 10017 fn callExpr( 10018 gz: *GenZir, 10019 scope: *Scope, 10020 ri: ResultInfo, 10021 node: Ast.Node.Index, 10022 call: Ast.full.Call, 10023 ) InnerError!Zir.Inst.Ref { 10024 const astgen = gz.astgen; 10025 10026 const callee = try calleeExpr(gz, scope, ri.rl, call.ast.fn_expr); 10027 const modifier: std.builtin.CallModifier = blk: { 10028 if (gz.is_comptime) { 10029 break :blk .compile_time; 10030 } 10031 if (call.async_token != null) { 10032 break :blk .async_kw; 10033 } 10034 if (gz.nosuspend_node != 0) { 10035 break :blk .no_async; 10036 } 10037 break :blk .auto; 10038 }; 10039 10040 { 10041 astgen.advanceSourceCursor(astgen.tree.tokens.items(.start)[call.ast.lparen]); 10042 const line = astgen.source_line - gz.decl_line; 10043 const column = astgen.source_column; 10044 // Sema expects a dbg_stmt immediately before call, 10045 try emitDbgStmtForceCurrentIndex(gz, .{ line, column }); 10046 } 10047 10048 switch (callee) { 10049 .direct => |obj| assert(obj != .none), 10050 .field => |field| assert(field.obj_ptr != .none), 10051 } 10052 assert(node != 0); 10053 10054 const call_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 10055 const call_inst = call_index.toRef(); 10056 try gz.astgen.instructions.append(astgen.gpa, undefined); 10057 try gz.instructions.append(astgen.gpa, call_index); 10058 10059 const scratch_top = astgen.scratch.items.len; 10060 defer astgen.scratch.items.len = scratch_top; 10061 10062 var scratch_index = scratch_top; 10063 try astgen.scratch.resize(astgen.gpa, scratch_top + call.ast.params.len); 10064 10065 for (call.ast.params) |param_node| { 10066 var arg_block = gz.makeSubBlock(scope); 10067 defer arg_block.unstack(); 10068 10069 // `call_inst` is reused to provide the param type. 10070 const arg_ref = try fullBodyExpr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node, .normal); 10071 _ = try arg_block.addBreakWithSrcNode(.break_inline, call_index, arg_ref, param_node); 10072 10073 const body = arg_block.instructionsSlice(); 10074 try astgen.scratch.ensureUnusedCapacity(astgen.gpa, countBodyLenAfterFixups(astgen, body)); 10075 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); 10076 10077 astgen.scratch.items[scratch_index] = @intCast(astgen.scratch.items.len - scratch_top); 10078 scratch_index += 1; 10079 } 10080 10081 // If our result location is a try/catch/error-union-if/return, a function argument, 10082 // or an initializer for a `const` variable, the error trace propagates. 10083 // Otherwise, it should always be popped (handled in Sema). 10084 const propagate_error_trace = switch (ri.ctx) { 10085 .error_handling_expr, .@"return", .fn_arg, .const_init => true, 10086 else => false, 10087 }; 10088 10089 switch (callee) { 10090 .direct => |callee_obj| { 10091 const payload_index = try addExtra(astgen, Zir.Inst.Call{ 10092 .callee = callee_obj, 10093 .flags = .{ 10094 .pop_error_return_trace = !propagate_error_trace, 10095 .packed_modifier = @intCast(@intFromEnum(modifier)), 10096 .args_len = @intCast(call.ast.params.len), 10097 }, 10098 }); 10099 if (call.ast.params.len != 0) { 10100 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]); 10101 } 10102 gz.astgen.instructions.set(@intFromEnum(call_index), .{ 10103 .tag = .call, 10104 .data = .{ .pl_node = .{ 10105 .src_node = gz.nodeIndexToRelative(node), 10106 .payload_index = payload_index, 10107 } }, 10108 }); 10109 }, 10110 .field => |callee_field| { 10111 const payload_index = try addExtra(astgen, Zir.Inst.FieldCall{ 10112 .obj_ptr = callee_field.obj_ptr, 10113 .field_name_start = callee_field.field_name_start, 10114 .flags = .{ 10115 .pop_error_return_trace = !propagate_error_trace, 10116 .packed_modifier = @intCast(@intFromEnum(modifier)), 10117 .args_len = @intCast(call.ast.params.len), 10118 }, 10119 }); 10120 if (call.ast.params.len != 0) { 10121 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]); 10122 } 10123 gz.astgen.instructions.set(@intFromEnum(call_index), .{ 10124 .tag = .field_call, 10125 .data = .{ .pl_node = .{ 10126 .src_node = gz.nodeIndexToRelative(node), 10127 .payload_index = payload_index, 10128 } }, 10129 }); 10130 }, 10131 } 10132 return rvalue(gz, ri, call_inst, node); // TODO function call with result location 10133 } 10134 10135 const Callee = union(enum) { 10136 field: struct { 10137 /// A *pointer* to the object the field is fetched on, so that we can 10138 /// promote the lvalue to an address if the first parameter requires it. 10139 obj_ptr: Zir.Inst.Ref, 10140 /// Offset into `string_bytes`. 10141 field_name_start: Zir.NullTerminatedString, 10142 }, 10143 direct: Zir.Inst.Ref, 10144 }; 10145 10146 /// calleeExpr generates the function part of a call expression (f in f(x)), but 10147 /// *not* the callee argument to the @call() builtin. Its purpose is to 10148 /// distinguish between standard calls and method call syntax `a.b()`. Thus, if 10149 /// the lhs is a field access, we return using the `field` union field; 10150 /// otherwise, we use the `direct` union field. 10151 fn calleeExpr( 10152 gz: *GenZir, 10153 scope: *Scope, 10154 call_rl: ResultInfo.Loc, 10155 node: Ast.Node.Index, 10156 ) InnerError!Callee { 10157 const astgen = gz.astgen; 10158 const tree = astgen.tree; 10159 10160 const tag = tree.nodes.items(.tag)[node]; 10161 switch (tag) { 10162 .field_access => { 10163 const main_tokens = tree.nodes.items(.main_token); 10164 const node_datas = tree.nodes.items(.data); 10165 const object_node = node_datas[node].lhs; 10166 const dot_token = main_tokens[node]; 10167 const field_ident = dot_token + 1; 10168 const str_index = try astgen.identAsString(field_ident); 10169 // Capture the object by reference so we can promote it to an 10170 // address in Sema if needed. 10171 const lhs = try expr(gz, scope, .{ .rl = .ref }, object_node); 10172 10173 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); 10174 try emitDbgStmt(gz, cursor); 10175 10176 return .{ .field = .{ 10177 .obj_ptr = lhs, 10178 .field_name_start = str_index, 10179 } }; 10180 }, 10181 .enum_literal => if (try call_rl.resultType(gz, node)) |res_ty| { 10182 // Decl literal call syntax, e.g. 10183 // `const foo: T = .init();` 10184 // Look up `init` in `T`, but don't try and coerce it. 10185 const str_index = try astgen.identAsString(tree.nodes.items(.main_token)[node]); 10186 const callee = try gz.addPlNode(.decl_literal_no_coerce, node, Zir.Inst.Field{ 10187 .lhs = res_ty, 10188 .field_name_start = str_index, 10189 }); 10190 return .{ .direct = callee }; 10191 } else { 10192 return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) }; 10193 }, 10194 else => return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) }, 10195 } 10196 } 10197 10198 const primitive_instrs = std.StaticStringMap(Zir.Inst.Ref).initComptime(.{ 10199 .{ "anyerror", .anyerror_type }, 10200 .{ "anyframe", .anyframe_type }, 10201 .{ "anyopaque", .anyopaque_type }, 10202 .{ "bool", .bool_type }, 10203 .{ "c_int", .c_int_type }, 10204 .{ "c_long", .c_long_type }, 10205 .{ "c_longdouble", .c_longdouble_type }, 10206 .{ "c_longlong", .c_longlong_type }, 10207 .{ "c_char", .c_char_type }, 10208 .{ "c_short", .c_short_type }, 10209 .{ "c_uint", .c_uint_type }, 10210 .{ "c_ulong", .c_ulong_type }, 10211 .{ "c_ulonglong", .c_ulonglong_type }, 10212 .{ "c_ushort", .c_ushort_type }, 10213 .{ "comptime_float", .comptime_float_type }, 10214 .{ "comptime_int", .comptime_int_type }, 10215 .{ "f128", .f128_type }, 10216 .{ "f16", .f16_type }, 10217 .{ "f32", .f32_type }, 10218 .{ "f64", .f64_type }, 10219 .{ "f80", .f80_type }, 10220 .{ "false", .bool_false }, 10221 .{ "i16", .i16_type }, 10222 .{ "i32", .i32_type }, 10223 .{ "i64", .i64_type }, 10224 .{ "i128", .i128_type }, 10225 .{ "i8", .i8_type }, 10226 .{ "isize", .isize_type }, 10227 .{ "noreturn", .noreturn_type }, 10228 .{ "null", .null_value }, 10229 .{ "true", .bool_true }, 10230 .{ "type", .type_type }, 10231 .{ "u16", .u16_type }, 10232 .{ "u29", .u29_type }, 10233 .{ "u32", .u32_type }, 10234 .{ "u64", .u64_type }, 10235 .{ "u128", .u128_type }, 10236 .{ "u1", .u1_type }, 10237 .{ "u8", .u8_type }, 10238 .{ "undefined", .undef }, 10239 .{ "usize", .usize_type }, 10240 .{ "void", .void_type }, 10241 }); 10242 10243 comptime { 10244 // These checks ensure that std.zig.primitives stays in sync with the primitive->Zir map. 10245 const primitives = std.zig.primitives; 10246 for (primitive_instrs.keys(), primitive_instrs.values()) |key, value| { 10247 if (!primitives.isPrimitive(key)) { 10248 @compileError("std.zig.isPrimitive() is not aware of Zir instr '" ++ @tagName(value) ++ "'"); 10249 } 10250 } 10251 for (primitives.names.keys()) |key| { 10252 if (primitive_instrs.get(key) == null) { 10253 @compileError("std.zig.primitives entry '" ++ key ++ "' does not have a corresponding Zir instr"); 10254 } 10255 } 10256 } 10257 10258 fn nodeIsTriviallyZero(tree: *const Ast, node: Ast.Node.Index) bool { 10259 const node_tags = tree.nodes.items(.tag); 10260 const main_tokens = tree.nodes.items(.main_token); 10261 10262 switch (node_tags[node]) { 10263 .number_literal => { 10264 const ident = main_tokens[node]; 10265 return switch (std.zig.parseNumberLiteral(tree.tokenSlice(ident))) { 10266 .int => |number| switch (number) { 10267 0 => true, 10268 else => false, 10269 }, 10270 else => false, 10271 }; 10272 }, 10273 else => return false, 10274 } 10275 } 10276 10277 fn nodeMayAppendToErrorTrace(tree: *const Ast, start_node: Ast.Node.Index) bool { 10278 const node_tags = tree.nodes.items(.tag); 10279 const node_datas = tree.nodes.items(.data); 10280 10281 var node = start_node; 10282 while (true) { 10283 switch (node_tags[node]) { 10284 // These don't have the opportunity to call any runtime functions. 10285 .error_value, 10286 .identifier, 10287 .@"comptime", 10288 => return false, 10289 10290 // Forward the question to the LHS sub-expression. 10291 .grouped_expression, 10292 .@"try", 10293 .@"nosuspend", 10294 .unwrap_optional, 10295 => node = node_datas[node].lhs, 10296 10297 // Anything that does not eval to an error is guaranteed to pop any 10298 // additions to the error trace, so it effectively does not append. 10299 else => return nodeMayEvalToError(tree, start_node) != .never, 10300 } 10301 } 10302 } 10303 10304 fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.EvalToError { 10305 const node_tags = tree.nodes.items(.tag); 10306 const node_datas = tree.nodes.items(.data); 10307 const main_tokens = tree.nodes.items(.main_token); 10308 const token_tags = tree.tokens.items(.tag); 10309 10310 var node = start_node; 10311 while (true) { 10312 switch (node_tags[node]) { 10313 .root, 10314 .@"usingnamespace", 10315 .test_decl, 10316 .switch_case, 10317 .switch_case_inline, 10318 .switch_case_one, 10319 .switch_case_inline_one, 10320 .container_field_init, 10321 .container_field_align, 10322 .container_field, 10323 .asm_output, 10324 .asm_input, 10325 => unreachable, 10326 10327 .error_value => return .always, 10328 10329 .@"asm", 10330 .asm_simple, 10331 .identifier, 10332 .field_access, 10333 .deref, 10334 .array_access, 10335 .while_simple, 10336 .while_cont, 10337 .for_simple, 10338 .if_simple, 10339 .@"while", 10340 .@"if", 10341 .@"for", 10342 .@"switch", 10343 .switch_comma, 10344 .call_one, 10345 .call_one_comma, 10346 .async_call_one, 10347 .async_call_one_comma, 10348 .call, 10349 .call_comma, 10350 .async_call, 10351 .async_call_comma, 10352 => return .maybe, 10353 10354 .@"return", 10355 .@"break", 10356 .@"continue", 10357 .bit_not, 10358 .bool_not, 10359 .global_var_decl, 10360 .local_var_decl, 10361 .simple_var_decl, 10362 .aligned_var_decl, 10363 .@"defer", 10364 .@"errdefer", 10365 .address_of, 10366 .optional_type, 10367 .negation, 10368 .negation_wrap, 10369 .@"resume", 10370 .array_type, 10371 .array_type_sentinel, 10372 .ptr_type_aligned, 10373 .ptr_type_sentinel, 10374 .ptr_type, 10375 .ptr_type_bit_range, 10376 .@"suspend", 10377 .fn_proto_simple, 10378 .fn_proto_multi, 10379 .fn_proto_one, 10380 .fn_proto, 10381 .fn_decl, 10382 .anyframe_type, 10383 .anyframe_literal, 10384 .number_literal, 10385 .enum_literal, 10386 .string_literal, 10387 .multiline_string_literal, 10388 .char_literal, 10389 .unreachable_literal, 10390 .error_set_decl, 10391 .container_decl, 10392 .container_decl_trailing, 10393 .container_decl_two, 10394 .container_decl_two_trailing, 10395 .container_decl_arg, 10396 .container_decl_arg_trailing, 10397 .tagged_union, 10398 .tagged_union_trailing, 10399 .tagged_union_two, 10400 .tagged_union_two_trailing, 10401 .tagged_union_enum_tag, 10402 .tagged_union_enum_tag_trailing, 10403 .add, 10404 .add_wrap, 10405 .add_sat, 10406 .array_cat, 10407 .array_mult, 10408 .assign, 10409 .assign_destructure, 10410 .assign_bit_and, 10411 .assign_bit_or, 10412 .assign_shl, 10413 .assign_shl_sat, 10414 .assign_shr, 10415 .assign_bit_xor, 10416 .assign_div, 10417 .assign_sub, 10418 .assign_sub_wrap, 10419 .assign_sub_sat, 10420 .assign_mod, 10421 .assign_add, 10422 .assign_add_wrap, 10423 .assign_add_sat, 10424 .assign_mul, 10425 .assign_mul_wrap, 10426 .assign_mul_sat, 10427 .bang_equal, 10428 .bit_and, 10429 .bit_or, 10430 .shl, 10431 .shl_sat, 10432 .shr, 10433 .bit_xor, 10434 .bool_and, 10435 .bool_or, 10436 .div, 10437 .equal_equal, 10438 .error_union, 10439 .greater_or_equal, 10440 .greater_than, 10441 .less_or_equal, 10442 .less_than, 10443 .merge_error_sets, 10444 .mod, 10445 .mul, 10446 .mul_wrap, 10447 .mul_sat, 10448 .switch_range, 10449 .for_range, 10450 .sub, 10451 .sub_wrap, 10452 .sub_sat, 10453 .slice, 10454 .slice_open, 10455 .slice_sentinel, 10456 .array_init_one, 10457 .array_init_one_comma, 10458 .array_init_dot_two, 10459 .array_init_dot_two_comma, 10460 .array_init_dot, 10461 .array_init_dot_comma, 10462 .array_init, 10463 .array_init_comma, 10464 .struct_init_one, 10465 .struct_init_one_comma, 10466 .struct_init_dot_two, 10467 .struct_init_dot_two_comma, 10468 .struct_init_dot, 10469 .struct_init_dot_comma, 10470 .struct_init, 10471 .struct_init_comma, 10472 => return .never, 10473 10474 // Forward the question to the LHS sub-expression. 10475 .grouped_expression, 10476 .@"try", 10477 .@"await", 10478 .@"comptime", 10479 .@"nosuspend", 10480 .unwrap_optional, 10481 => node = node_datas[node].lhs, 10482 10483 // LHS sub-expression may still be an error under the outer optional or error union 10484 .@"catch", 10485 .@"orelse", 10486 => return .maybe, 10487 10488 .block_two, 10489 .block_two_semicolon, 10490 .block, 10491 .block_semicolon, 10492 => { 10493 const lbrace = main_tokens[node]; 10494 if (token_tags[lbrace - 1] == .colon) { 10495 // Labeled blocks may need a memory location to forward 10496 // to their break statements. 10497 return .maybe; 10498 } else { 10499 return .never; 10500 } 10501 }, 10502 10503 .builtin_call, 10504 .builtin_call_comma, 10505 .builtin_call_two, 10506 .builtin_call_two_comma, 10507 => { 10508 const builtin_token = main_tokens[node]; 10509 const builtin_name = tree.tokenSlice(builtin_token); 10510 // If the builtin is an invalid name, we don't cause an error here; instead 10511 // let it pass, and the error will be "invalid builtin function" later. 10512 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return .maybe; 10513 return builtin_info.eval_to_error; 10514 }, 10515 } 10516 } 10517 } 10518 10519 /// Returns `true` if it is known the type expression has more than one possible value; 10520 /// `false` otherwise. 10521 fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.Index) bool { 10522 const node_tags = tree.nodes.items(.tag); 10523 const node_datas = tree.nodes.items(.data); 10524 10525 var node = start_node; 10526 while (true) { 10527 switch (node_tags[node]) { 10528 .root, 10529 .@"usingnamespace", 10530 .test_decl, 10531 .switch_case, 10532 .switch_case_inline, 10533 .switch_case_one, 10534 .switch_case_inline_one, 10535 .container_field_init, 10536 .container_field_align, 10537 .container_field, 10538 .asm_output, 10539 .asm_input, 10540 .global_var_decl, 10541 .local_var_decl, 10542 .simple_var_decl, 10543 .aligned_var_decl, 10544 => unreachable, 10545 10546 .@"return", 10547 .@"break", 10548 .@"continue", 10549 .bit_not, 10550 .bool_not, 10551 .@"defer", 10552 .@"errdefer", 10553 .address_of, 10554 .negation, 10555 .negation_wrap, 10556 .@"resume", 10557 .array_type, 10558 .@"suspend", 10559 .fn_decl, 10560 .anyframe_literal, 10561 .number_literal, 10562 .enum_literal, 10563 .string_literal, 10564 .multiline_string_literal, 10565 .char_literal, 10566 .unreachable_literal, 10567 .error_set_decl, 10568 .container_decl, 10569 .container_decl_trailing, 10570 .container_decl_two, 10571 .container_decl_two_trailing, 10572 .container_decl_arg, 10573 .container_decl_arg_trailing, 10574 .tagged_union, 10575 .tagged_union_trailing, 10576 .tagged_union_two, 10577 .tagged_union_two_trailing, 10578 .tagged_union_enum_tag, 10579 .tagged_union_enum_tag_trailing, 10580 .@"asm", 10581 .asm_simple, 10582 .add, 10583 .add_wrap, 10584 .add_sat, 10585 .array_cat, 10586 .array_mult, 10587 .assign, 10588 .assign_destructure, 10589 .assign_bit_and, 10590 .assign_bit_or, 10591 .assign_shl, 10592 .assign_shl_sat, 10593 .assign_shr, 10594 .assign_bit_xor, 10595 .assign_div, 10596 .assign_sub, 10597 .assign_sub_wrap, 10598 .assign_sub_sat, 10599 .assign_mod, 10600 .assign_add, 10601 .assign_add_wrap, 10602 .assign_add_sat, 10603 .assign_mul, 10604 .assign_mul_wrap, 10605 .assign_mul_sat, 10606 .bang_equal, 10607 .bit_and, 10608 .bit_or, 10609 .shl, 10610 .shl_sat, 10611 .shr, 10612 .bit_xor, 10613 .bool_and, 10614 .bool_or, 10615 .div, 10616 .equal_equal, 10617 .error_union, 10618 .greater_or_equal, 10619 .greater_than, 10620 .less_or_equal, 10621 .less_than, 10622 .merge_error_sets, 10623 .mod, 10624 .mul, 10625 .mul_wrap, 10626 .mul_sat, 10627 .switch_range, 10628 .for_range, 10629 .field_access, 10630 .sub, 10631 .sub_wrap, 10632 .sub_sat, 10633 .slice, 10634 .slice_open, 10635 .slice_sentinel, 10636 .deref, 10637 .array_access, 10638 .error_value, 10639 .while_simple, 10640 .while_cont, 10641 .for_simple, 10642 .if_simple, 10643 .@"catch", 10644 .@"orelse", 10645 .array_init_one, 10646 .array_init_one_comma, 10647 .array_init_dot_two, 10648 .array_init_dot_two_comma, 10649 .array_init_dot, 10650 .array_init_dot_comma, 10651 .array_init, 10652 .array_init_comma, 10653 .struct_init_one, 10654 .struct_init_one_comma, 10655 .struct_init_dot_two, 10656 .struct_init_dot_two_comma, 10657 .struct_init_dot, 10658 .struct_init_dot_comma, 10659 .struct_init, 10660 .struct_init_comma, 10661 .@"while", 10662 .@"if", 10663 .@"for", 10664 .@"switch", 10665 .switch_comma, 10666 .call_one, 10667 .call_one_comma, 10668 .async_call_one, 10669 .async_call_one_comma, 10670 .call, 10671 .call_comma, 10672 .async_call, 10673 .async_call_comma, 10674 .block_two, 10675 .block_two_semicolon, 10676 .block, 10677 .block_semicolon, 10678 .builtin_call, 10679 .builtin_call_comma, 10680 .builtin_call_two, 10681 .builtin_call_two_comma, 10682 // these are function bodies, not pointers 10683 .fn_proto_simple, 10684 .fn_proto_multi, 10685 .fn_proto_one, 10686 .fn_proto, 10687 => return false, 10688 10689 // Forward the question to the LHS sub-expression. 10690 .grouped_expression, 10691 .@"try", 10692 .@"await", 10693 .@"comptime", 10694 .@"nosuspend", 10695 .unwrap_optional, 10696 => node = node_datas[node].lhs, 10697 10698 .ptr_type_aligned, 10699 .ptr_type_sentinel, 10700 .ptr_type, 10701 .ptr_type_bit_range, 10702 .optional_type, 10703 .anyframe_type, 10704 .array_type_sentinel, 10705 => return true, 10706 10707 .identifier => { 10708 const main_tokens = tree.nodes.items(.main_token); 10709 const ident_bytes = tree.tokenSlice(main_tokens[node]); 10710 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) { 10711 .anyerror_type, 10712 .anyframe_type, 10713 .anyopaque_type, 10714 .bool_type, 10715 .c_int_type, 10716 .c_long_type, 10717 .c_longdouble_type, 10718 .c_longlong_type, 10719 .c_char_type, 10720 .c_short_type, 10721 .c_uint_type, 10722 .c_ulong_type, 10723 .c_ulonglong_type, 10724 .c_ushort_type, 10725 .comptime_float_type, 10726 .comptime_int_type, 10727 .f16_type, 10728 .f32_type, 10729 .f64_type, 10730 .f80_type, 10731 .f128_type, 10732 .i16_type, 10733 .i32_type, 10734 .i64_type, 10735 .i128_type, 10736 .i8_type, 10737 .isize_type, 10738 .type_type, 10739 .u16_type, 10740 .u29_type, 10741 .u32_type, 10742 .u64_type, 10743 .u128_type, 10744 .u1_type, 10745 .u8_type, 10746 .usize_type, 10747 => return true, 10748 10749 .void_type, 10750 .bool_false, 10751 .bool_true, 10752 .null_value, 10753 .undef, 10754 .noreturn_type, 10755 => return false, 10756 10757 else => unreachable, // that's all the values from `primitives`. 10758 } else { 10759 return false; 10760 } 10761 }, 10762 } 10763 } 10764 } 10765 10766 /// Returns `true` if it is known the expression is a type that cannot be used at runtime; 10767 /// `false` otherwise. 10768 fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { 10769 const node_tags = tree.nodes.items(.tag); 10770 const node_datas = tree.nodes.items(.data); 10771 10772 var node = start_node; 10773 while (true) { 10774 switch (node_tags[node]) { 10775 .root, 10776 .@"usingnamespace", 10777 .test_decl, 10778 .switch_case, 10779 .switch_case_inline, 10780 .switch_case_one, 10781 .switch_case_inline_one, 10782 .container_field_init, 10783 .container_field_align, 10784 .container_field, 10785 .asm_output, 10786 .asm_input, 10787 .global_var_decl, 10788 .local_var_decl, 10789 .simple_var_decl, 10790 .aligned_var_decl, 10791 => unreachable, 10792 10793 .@"return", 10794 .@"break", 10795 .@"continue", 10796 .bit_not, 10797 .bool_not, 10798 .@"defer", 10799 .@"errdefer", 10800 .address_of, 10801 .negation, 10802 .negation_wrap, 10803 .@"resume", 10804 .array_type, 10805 .@"suspend", 10806 .fn_decl, 10807 .anyframe_literal, 10808 .number_literal, 10809 .enum_literal, 10810 .string_literal, 10811 .multiline_string_literal, 10812 .char_literal, 10813 .unreachable_literal, 10814 .error_set_decl, 10815 .container_decl, 10816 .container_decl_trailing, 10817 .container_decl_two, 10818 .container_decl_two_trailing, 10819 .container_decl_arg, 10820 .container_decl_arg_trailing, 10821 .tagged_union, 10822 .tagged_union_trailing, 10823 .tagged_union_two, 10824 .tagged_union_two_trailing, 10825 .tagged_union_enum_tag, 10826 .tagged_union_enum_tag_trailing, 10827 .@"asm", 10828 .asm_simple, 10829 .add, 10830 .add_wrap, 10831 .add_sat, 10832 .array_cat, 10833 .array_mult, 10834 .assign, 10835 .assign_destructure, 10836 .assign_bit_and, 10837 .assign_bit_or, 10838 .assign_shl, 10839 .assign_shl_sat, 10840 .assign_shr, 10841 .assign_bit_xor, 10842 .assign_div, 10843 .assign_sub, 10844 .assign_sub_wrap, 10845 .assign_sub_sat, 10846 .assign_mod, 10847 .assign_add, 10848 .assign_add_wrap, 10849 .assign_add_sat, 10850 .assign_mul, 10851 .assign_mul_wrap, 10852 .assign_mul_sat, 10853 .bang_equal, 10854 .bit_and, 10855 .bit_or, 10856 .shl, 10857 .shl_sat, 10858 .shr, 10859 .bit_xor, 10860 .bool_and, 10861 .bool_or, 10862 .div, 10863 .equal_equal, 10864 .error_union, 10865 .greater_or_equal, 10866 .greater_than, 10867 .less_or_equal, 10868 .less_than, 10869 .merge_error_sets, 10870 .mod, 10871 .mul, 10872 .mul_wrap, 10873 .mul_sat, 10874 .switch_range, 10875 .for_range, 10876 .field_access, 10877 .sub, 10878 .sub_wrap, 10879 .sub_sat, 10880 .slice, 10881 .slice_open, 10882 .slice_sentinel, 10883 .deref, 10884 .array_access, 10885 .error_value, 10886 .while_simple, 10887 .while_cont, 10888 .for_simple, 10889 .if_simple, 10890 .@"catch", 10891 .@"orelse", 10892 .array_init_one, 10893 .array_init_one_comma, 10894 .array_init_dot_two, 10895 .array_init_dot_two_comma, 10896 .array_init_dot, 10897 .array_init_dot_comma, 10898 .array_init, 10899 .array_init_comma, 10900 .struct_init_one, 10901 .struct_init_one_comma, 10902 .struct_init_dot_two, 10903 .struct_init_dot_two_comma, 10904 .struct_init_dot, 10905 .struct_init_dot_comma, 10906 .struct_init, 10907 .struct_init_comma, 10908 .@"while", 10909 .@"if", 10910 .@"for", 10911 .@"switch", 10912 .switch_comma, 10913 .call_one, 10914 .call_one_comma, 10915 .async_call_one, 10916 .async_call_one_comma, 10917 .call, 10918 .call_comma, 10919 .async_call, 10920 .async_call_comma, 10921 .block_two, 10922 .block_two_semicolon, 10923 .block, 10924 .block_semicolon, 10925 .builtin_call, 10926 .builtin_call_comma, 10927 .builtin_call_two, 10928 .builtin_call_two_comma, 10929 .ptr_type_aligned, 10930 .ptr_type_sentinel, 10931 .ptr_type, 10932 .ptr_type_bit_range, 10933 .optional_type, 10934 .anyframe_type, 10935 .array_type_sentinel, 10936 => return false, 10937 10938 // these are function bodies, not pointers 10939 .fn_proto_simple, 10940 .fn_proto_multi, 10941 .fn_proto_one, 10942 .fn_proto, 10943 => return true, 10944 10945 // Forward the question to the LHS sub-expression. 10946 .grouped_expression, 10947 .@"try", 10948 .@"await", 10949 .@"comptime", 10950 .@"nosuspend", 10951 .unwrap_optional, 10952 => node = node_datas[node].lhs, 10953 10954 .identifier => { 10955 const main_tokens = tree.nodes.items(.main_token); 10956 const ident_bytes = tree.tokenSlice(main_tokens[node]); 10957 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) { 10958 .anyerror_type, 10959 .anyframe_type, 10960 .anyopaque_type, 10961 .bool_type, 10962 .c_int_type, 10963 .c_long_type, 10964 .c_longdouble_type, 10965 .c_longlong_type, 10966 .c_char_type, 10967 .c_short_type, 10968 .c_uint_type, 10969 .c_ulong_type, 10970 .c_ulonglong_type, 10971 .c_ushort_type, 10972 .f16_type, 10973 .f32_type, 10974 .f64_type, 10975 .f80_type, 10976 .f128_type, 10977 .i16_type, 10978 .i32_type, 10979 .i64_type, 10980 .i128_type, 10981 .i8_type, 10982 .isize_type, 10983 .u16_type, 10984 .u29_type, 10985 .u32_type, 10986 .u64_type, 10987 .u128_type, 10988 .u1_type, 10989 .u8_type, 10990 .usize_type, 10991 .void_type, 10992 .bool_false, 10993 .bool_true, 10994 .null_value, 10995 .undef, 10996 .noreturn_type, 10997 => return false, 10998 10999 .comptime_float_type, 11000 .comptime_int_type, 11001 .type_type, 11002 => return true, 11003 11004 else => unreachable, // that's all the values from `primitives`. 11005 } else { 11006 return false; 11007 } 11008 }, 11009 } 11010 } 11011 } 11012 11013 /// Returns `true` if the node uses `gz.anon_name_strategy`. 11014 fn nodeUsesAnonNameStrategy(tree: *const Ast, node: Ast.Node.Index) bool { 11015 const node_tags = tree.nodes.items(.tag); 11016 switch (node_tags[node]) { 11017 .container_decl, 11018 .container_decl_trailing, 11019 .container_decl_two, 11020 .container_decl_two_trailing, 11021 .container_decl_arg, 11022 .container_decl_arg_trailing, 11023 .tagged_union, 11024 .tagged_union_trailing, 11025 .tagged_union_two, 11026 .tagged_union_two_trailing, 11027 .tagged_union_enum_tag, 11028 .tagged_union_enum_tag_trailing, 11029 => return true, 11030 .builtin_call_two, .builtin_call_two_comma, .builtin_call, .builtin_call_comma => { 11031 const builtin_token = tree.nodes.items(.main_token)[node]; 11032 const builtin_name = tree.tokenSlice(builtin_token); 11033 return std.mem.eql(u8, builtin_name, "@Type"); 11034 }, 11035 else => return false, 11036 } 11037 } 11038 11039 /// Applies `rl` semantics to `result`. Expressions which do not do their own handling of 11040 /// result locations must call this function on their result. 11041 /// As an example, if `ri.rl` is `.ptr`, it will write the result to the pointer. 11042 /// If `ri.rl` is `.ty`, it will coerce the result to the type. 11043 /// Assumes nothing stacked on `gz`. 11044 fn rvalue( 11045 gz: *GenZir, 11046 ri: ResultInfo, 11047 raw_result: Zir.Inst.Ref, 11048 src_node: Ast.Node.Index, 11049 ) InnerError!Zir.Inst.Ref { 11050 return rvalueInner(gz, ri, raw_result, src_node, true); 11051 } 11052 11053 /// Like `rvalue`, but refuses to perform coercions before taking references for 11054 /// the `ref_coerced_ty` result type. This is used for local variables which do 11055 /// not have `alloc`s, because we want variables to have consistent addresses, 11056 /// i.e. we want them to act like lvalues. 11057 fn rvalueNoCoercePreRef( 11058 gz: *GenZir, 11059 ri: ResultInfo, 11060 raw_result: Zir.Inst.Ref, 11061 src_node: Ast.Node.Index, 11062 ) InnerError!Zir.Inst.Ref { 11063 return rvalueInner(gz, ri, raw_result, src_node, false); 11064 } 11065 11066 fn rvalueInner( 11067 gz: *GenZir, 11068 ri: ResultInfo, 11069 raw_result: Zir.Inst.Ref, 11070 src_node: Ast.Node.Index, 11071 allow_coerce_pre_ref: bool, 11072 ) InnerError!Zir.Inst.Ref { 11073 const result = r: { 11074 if (raw_result.toIndex()) |result_index| { 11075 const zir_tags = gz.astgen.instructions.items(.tag); 11076 const data = gz.astgen.instructions.items(.data)[@intFromEnum(result_index)]; 11077 if (zir_tags[@intFromEnum(result_index)].isAlwaysVoid(data)) { 11078 break :r Zir.Inst.Ref.void_value; 11079 } 11080 } 11081 break :r raw_result; 11082 }; 11083 if (gz.endsWithNoReturn()) return result; 11084 switch (ri.rl) { 11085 .none, .coerced_ty => return result, 11086 .discard => { 11087 // Emit a compile error for discarding error values. 11088 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); 11089 return .void_value; 11090 }, 11091 .ref, .ref_coerced_ty => { 11092 const coerced_result = if (allow_coerce_pre_ref and ri.rl == .ref_coerced_ty) res: { 11093 const ptr_ty = ri.rl.ref_coerced_ty; 11094 break :res try gz.addPlNode(.coerce_ptr_elem_ty, src_node, Zir.Inst.Bin{ 11095 .lhs = ptr_ty, 11096 .rhs = result, 11097 }); 11098 } else result; 11099 // We need a pointer but we have a value. 11100 // Unfortunately it's not quite as simple as directly emitting a ref 11101 // instruction here because we need subsequent address-of operator on 11102 // const locals to return the same address. 11103 const astgen = gz.astgen; 11104 const tree = astgen.tree; 11105 const src_token = tree.firstToken(src_node); 11106 const result_index = coerced_result.toIndex() orelse 11107 return gz.addUnTok(.ref, coerced_result, src_token); 11108 const zir_tags = gz.astgen.instructions.items(.tag); 11109 if (zir_tags[@intFromEnum(result_index)].isParam() or astgen.isInferred(coerced_result)) 11110 return gz.addUnTok(.ref, coerced_result, src_token); 11111 const gop = try astgen.ref_table.getOrPut(astgen.gpa, result_index); 11112 if (!gop.found_existing) { 11113 gop.value_ptr.* = try gz.makeUnTok(.ref, coerced_result, src_token); 11114 } 11115 return gop.value_ptr.*.toRef(); 11116 }, 11117 .ty => |ty_inst| { 11118 // Quickly eliminate some common, unnecessary type coercion. 11119 const as_ty = @as(u64, @intFromEnum(Zir.Inst.Ref.type_type)) << 32; 11120 const as_bool = @as(u64, @intFromEnum(Zir.Inst.Ref.bool_type)) << 32; 11121 const as_void = @as(u64, @intFromEnum(Zir.Inst.Ref.void_type)) << 32; 11122 const as_comptime_int = @as(u64, @intFromEnum(Zir.Inst.Ref.comptime_int_type)) << 32; 11123 const as_usize = @as(u64, @intFromEnum(Zir.Inst.Ref.usize_type)) << 32; 11124 const as_u8 = @as(u64, @intFromEnum(Zir.Inst.Ref.u8_type)) << 32; 11125 switch ((@as(u64, @intFromEnum(ty_inst)) << 32) | @as(u64, @intFromEnum(result))) { 11126 as_ty | @intFromEnum(Zir.Inst.Ref.u1_type), 11127 as_ty | @intFromEnum(Zir.Inst.Ref.u8_type), 11128 as_ty | @intFromEnum(Zir.Inst.Ref.i8_type), 11129 as_ty | @intFromEnum(Zir.Inst.Ref.u16_type), 11130 as_ty | @intFromEnum(Zir.Inst.Ref.u29_type), 11131 as_ty | @intFromEnum(Zir.Inst.Ref.i16_type), 11132 as_ty | @intFromEnum(Zir.Inst.Ref.u32_type), 11133 as_ty | @intFromEnum(Zir.Inst.Ref.i32_type), 11134 as_ty | @intFromEnum(Zir.Inst.Ref.u64_type), 11135 as_ty | @intFromEnum(Zir.Inst.Ref.i64_type), 11136 as_ty | @intFromEnum(Zir.Inst.Ref.u128_type), 11137 as_ty | @intFromEnum(Zir.Inst.Ref.i128_type), 11138 as_ty | @intFromEnum(Zir.Inst.Ref.usize_type), 11139 as_ty | @intFromEnum(Zir.Inst.Ref.isize_type), 11140 as_ty | @intFromEnum(Zir.Inst.Ref.c_char_type), 11141 as_ty | @intFromEnum(Zir.Inst.Ref.c_short_type), 11142 as_ty | @intFromEnum(Zir.Inst.Ref.c_ushort_type), 11143 as_ty | @intFromEnum(Zir.Inst.Ref.c_int_type), 11144 as_ty | @intFromEnum(Zir.Inst.Ref.c_uint_type), 11145 as_ty | @intFromEnum(Zir.Inst.Ref.c_long_type), 11146 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulong_type), 11147 as_ty | @intFromEnum(Zir.Inst.Ref.c_longlong_type), 11148 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulonglong_type), 11149 as_ty | @intFromEnum(Zir.Inst.Ref.c_longdouble_type), 11150 as_ty | @intFromEnum(Zir.Inst.Ref.f16_type), 11151 as_ty | @intFromEnum(Zir.Inst.Ref.f32_type), 11152 as_ty | @intFromEnum(Zir.Inst.Ref.f64_type), 11153 as_ty | @intFromEnum(Zir.Inst.Ref.f80_type), 11154 as_ty | @intFromEnum(Zir.Inst.Ref.f128_type), 11155 as_ty | @intFromEnum(Zir.Inst.Ref.anyopaque_type), 11156 as_ty | @intFromEnum(Zir.Inst.Ref.bool_type), 11157 as_ty | @intFromEnum(Zir.Inst.Ref.void_type), 11158 as_ty | @intFromEnum(Zir.Inst.Ref.type_type), 11159 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_type), 11160 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_int_type), 11161 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_float_type), 11162 as_ty | @intFromEnum(Zir.Inst.Ref.noreturn_type), 11163 as_ty | @intFromEnum(Zir.Inst.Ref.anyframe_type), 11164 as_ty | @intFromEnum(Zir.Inst.Ref.null_type), 11165 as_ty | @intFromEnum(Zir.Inst.Ref.undefined_type), 11166 as_ty | @intFromEnum(Zir.Inst.Ref.enum_literal_type), 11167 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_u8_type), 11168 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_type), 11169 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_sentinel_0_type), 11170 as_ty | @intFromEnum(Zir.Inst.Ref.single_const_pointer_to_comptime_int_type), 11171 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_type), 11172 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_sentinel_0_type), 11173 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_void_error_union_type), 11174 as_ty | @intFromEnum(Zir.Inst.Ref.generic_poison_type), 11175 as_ty | @intFromEnum(Zir.Inst.Ref.empty_struct_type), 11176 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero), 11177 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one), 11178 as_comptime_int | @intFromEnum(Zir.Inst.Ref.negative_one), 11179 as_usize | @intFromEnum(Zir.Inst.Ref.zero_usize), 11180 as_usize | @intFromEnum(Zir.Inst.Ref.one_usize), 11181 as_u8 | @intFromEnum(Zir.Inst.Ref.zero_u8), 11182 as_u8 | @intFromEnum(Zir.Inst.Ref.one_u8), 11183 as_u8 | @intFromEnum(Zir.Inst.Ref.four_u8), 11184 as_bool | @intFromEnum(Zir.Inst.Ref.bool_true), 11185 as_bool | @intFromEnum(Zir.Inst.Ref.bool_false), 11186 as_void | @intFromEnum(Zir.Inst.Ref.void_value), 11187 => return result, // type of result is already correct 11188 11189 as_usize | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_usize, 11190 as_u8 | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_u8, 11191 as_usize | @intFromEnum(Zir.Inst.Ref.one) => return .one_usize, 11192 as_u8 | @intFromEnum(Zir.Inst.Ref.one) => return .one_u8, 11193 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero, 11194 as_u8 | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero_u8, 11195 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one, 11196 as_u8 | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one_u8, 11197 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_u8) => return .zero, 11198 as_usize | @intFromEnum(Zir.Inst.Ref.zero_u8) => return .zero_usize, 11199 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_u8) => return .one, 11200 as_usize | @intFromEnum(Zir.Inst.Ref.one_u8) => return .one_usize, 11201 11202 // Need an explicit type coercion instruction. 11203 else => return gz.addPlNode(ri.zirTag(), src_node, Zir.Inst.As{ 11204 .dest_type = ty_inst, 11205 .operand = result, 11206 }), 11207 } 11208 }, 11209 .ptr => |ptr_res| { 11210 _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{ 11211 .lhs = ptr_res.inst, 11212 .rhs = result, 11213 }); 11214 return .void_value; 11215 }, 11216 .inferred_ptr => |alloc| { 11217 _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{ 11218 .lhs = alloc, 11219 .rhs = result, 11220 }); 11221 return .void_value; 11222 }, 11223 .destructure => |destructure| { 11224 const components = destructure.components; 11225 _ = try gz.addPlNode(.validate_destructure, src_node, Zir.Inst.ValidateDestructure{ 11226 .operand = result, 11227 .destructure_node = gz.nodeIndexToRelative(destructure.src_node), 11228 .expect_len = @intCast(components.len), 11229 }); 11230 for (components, 0..) |component, i| { 11231 if (component == .discard) continue; 11232 const elem_val = try gz.add(.{ 11233 .tag = .elem_val_imm, 11234 .data = .{ .elem_val_imm = .{ 11235 .operand = result, 11236 .idx = @intCast(i), 11237 } }, 11238 }); 11239 switch (component) { 11240 .typed_ptr => |ptr_res| { 11241 _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{ 11242 .lhs = ptr_res.inst, 11243 .rhs = elem_val, 11244 }); 11245 }, 11246 .inferred_ptr => |ptr_inst| { 11247 _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{ 11248 .lhs = ptr_inst, 11249 .rhs = elem_val, 11250 }); 11251 }, 11252 .discard => unreachable, 11253 } 11254 } 11255 return .void_value; 11256 }, 11257 } 11258 } 11259 11260 /// Given an identifier token, obtain the string for it. 11261 /// If the token uses @"" syntax, parses as a string, reports errors if applicable, 11262 /// and allocates the result within `astgen.arena`. 11263 /// Otherwise, returns a reference to the source code bytes directly. 11264 /// See also `appendIdentStr` and `parseStrLit`. 11265 fn identifierTokenString(astgen: *AstGen, token: Ast.TokenIndex) InnerError![]const u8 { 11266 const tree = astgen.tree; 11267 const token_tags = tree.tokens.items(.tag); 11268 assert(token_tags[token] == .identifier); 11269 const ident_name = tree.tokenSlice(token); 11270 if (!mem.startsWith(u8, ident_name, "@")) { 11271 return ident_name; 11272 } 11273 var buf: ArrayListUnmanaged(u8) = .empty; 11274 defer buf.deinit(astgen.gpa); 11275 try astgen.parseStrLit(token, &buf, ident_name, 1); 11276 if (mem.indexOfScalar(u8, buf.items, 0) != null) { 11277 return astgen.failTok(token, "identifier cannot contain null bytes", .{}); 11278 } else if (buf.items.len == 0) { 11279 return astgen.failTok(token, "identifier cannot be empty", .{}); 11280 } 11281 const duped = try astgen.arena.dupe(u8, buf.items); 11282 return duped; 11283 } 11284 11285 /// Given an identifier token, obtain the string for it (possibly parsing as a string 11286 /// literal if it is @"" syntax), and append the string to `buf`. 11287 /// See also `identifierTokenString` and `parseStrLit`. 11288 fn appendIdentStr( 11289 astgen: *AstGen, 11290 token: Ast.TokenIndex, 11291 buf: *ArrayListUnmanaged(u8), 11292 ) InnerError!void { 11293 const tree = astgen.tree; 11294 const token_tags = tree.tokens.items(.tag); 11295 assert(token_tags[token] == .identifier); 11296 const ident_name = tree.tokenSlice(token); 11297 if (!mem.startsWith(u8, ident_name, "@")) { 11298 return buf.appendSlice(astgen.gpa, ident_name); 11299 } else { 11300 const start = buf.items.len; 11301 try astgen.parseStrLit(token, buf, ident_name, 1); 11302 const slice = buf.items[start..]; 11303 if (mem.indexOfScalar(u8, slice, 0) != null) { 11304 return astgen.failTok(token, "identifier cannot contain null bytes", .{}); 11305 } else if (slice.len == 0) { 11306 return astgen.failTok(token, "identifier cannot be empty", .{}); 11307 } 11308 } 11309 } 11310 11311 /// Appends the result to `buf`. 11312 fn parseStrLit( 11313 astgen: *AstGen, 11314 token: Ast.TokenIndex, 11315 buf: *ArrayListUnmanaged(u8), 11316 bytes: []const u8, 11317 offset: u32, 11318 ) InnerError!void { 11319 const raw_string = bytes[offset..]; 11320 var buf_managed = buf.toManaged(astgen.gpa); 11321 const result = std.zig.string_literal.parseWrite(buf_managed.writer(), raw_string); 11322 buf.* = buf_managed.moveToUnmanaged(); 11323 switch (try result) { 11324 .success => return, 11325 .failure => |err| return astgen.failWithStrLitError(err, token, bytes, offset), 11326 } 11327 } 11328 11329 fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError { 11330 const raw_string = bytes[offset..]; 11331 switch (err) { 11332 .invalid_escape_character => |bad_index| { 11333 return astgen.failOff( 11334 token, 11335 offset + @as(u32, @intCast(bad_index)), 11336 "invalid escape character: '{c}'", 11337 .{raw_string[bad_index]}, 11338 ); 11339 }, 11340 .expected_hex_digit => |bad_index| { 11341 return astgen.failOff( 11342 token, 11343 offset + @as(u32, @intCast(bad_index)), 11344 "expected hex digit, found '{c}'", 11345 .{raw_string[bad_index]}, 11346 ); 11347 }, 11348 .empty_unicode_escape_sequence => |bad_index| { 11349 return astgen.failOff( 11350 token, 11351 offset + @as(u32, @intCast(bad_index)), 11352 "empty unicode escape sequence", 11353 .{}, 11354 ); 11355 }, 11356 .expected_hex_digit_or_rbrace => |bad_index| { 11357 return astgen.failOff( 11358 token, 11359 offset + @as(u32, @intCast(bad_index)), 11360 "expected hex digit or '}}', found '{c}'", 11361 .{raw_string[bad_index]}, 11362 ); 11363 }, 11364 .invalid_unicode_codepoint => |bad_index| { 11365 return astgen.failOff( 11366 token, 11367 offset + @as(u32, @intCast(bad_index)), 11368 "unicode escape does not correspond to a valid unicode scalar value", 11369 .{}, 11370 ); 11371 }, 11372 .expected_lbrace => |bad_index| { 11373 return astgen.failOff( 11374 token, 11375 offset + @as(u32, @intCast(bad_index)), 11376 "expected '{{', found '{c}", 11377 .{raw_string[bad_index]}, 11378 ); 11379 }, 11380 .expected_rbrace => |bad_index| { 11381 return astgen.failOff( 11382 token, 11383 offset + @as(u32, @intCast(bad_index)), 11384 "expected '}}', found '{c}", 11385 .{raw_string[bad_index]}, 11386 ); 11387 }, 11388 .expected_single_quote => |bad_index| { 11389 return astgen.failOff( 11390 token, 11391 offset + @as(u32, @intCast(bad_index)), 11392 "expected single quote ('), found '{c}", 11393 .{raw_string[bad_index]}, 11394 ); 11395 }, 11396 .invalid_character => |bad_index| { 11397 return astgen.failOff( 11398 token, 11399 offset + @as(u32, @intCast(bad_index)), 11400 "invalid byte in string or character literal: '{c}'", 11401 .{raw_string[bad_index]}, 11402 ); 11403 }, 11404 .empty_char_literal => { 11405 return astgen.failOff(token, offset, "empty character literal", .{}); 11406 }, 11407 } 11408 } 11409 11410 fn failNode( 11411 astgen: *AstGen, 11412 node: Ast.Node.Index, 11413 comptime format: []const u8, 11414 args: anytype, 11415 ) InnerError { 11416 return astgen.failNodeNotes(node, format, args, &[0]u32{}); 11417 } 11418 11419 fn appendErrorNode( 11420 astgen: *AstGen, 11421 node: Ast.Node.Index, 11422 comptime format: []const u8, 11423 args: anytype, 11424 ) Allocator.Error!void { 11425 try astgen.appendErrorNodeNotes(node, format, args, &[0]u32{}); 11426 } 11427 11428 fn appendErrorNodeNotes( 11429 astgen: *AstGen, 11430 node: Ast.Node.Index, 11431 comptime format: []const u8, 11432 args: anytype, 11433 notes: []const u32, 11434 ) Allocator.Error!void { 11435 @branchHint(.cold); 11436 const string_bytes = &astgen.string_bytes; 11437 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len); 11438 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args); 11439 const notes_index: u32 = if (notes.len != 0) blk: { 11440 const notes_start = astgen.extra.items.len; 11441 try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len); 11442 astgen.extra.appendAssumeCapacity(@intCast(notes.len)); 11443 astgen.extra.appendSliceAssumeCapacity(notes); 11444 break :blk @intCast(notes_start); 11445 } else 0; 11446 try astgen.compile_errors.append(astgen.gpa, .{ 11447 .msg = msg, 11448 .node = node, 11449 .token = 0, 11450 .byte_offset = 0, 11451 .notes = notes_index, 11452 }); 11453 } 11454 11455 fn failNodeNotes( 11456 astgen: *AstGen, 11457 node: Ast.Node.Index, 11458 comptime format: []const u8, 11459 args: anytype, 11460 notes: []const u32, 11461 ) InnerError { 11462 try appendErrorNodeNotes(astgen, node, format, args, notes); 11463 return error.AnalysisFail; 11464 } 11465 11466 fn failTok( 11467 astgen: *AstGen, 11468 token: Ast.TokenIndex, 11469 comptime format: []const u8, 11470 args: anytype, 11471 ) InnerError { 11472 return astgen.failTokNotes(token, format, args, &[0]u32{}); 11473 } 11474 11475 fn appendErrorTok( 11476 astgen: *AstGen, 11477 token: Ast.TokenIndex, 11478 comptime format: []const u8, 11479 args: anytype, 11480 ) !void { 11481 try astgen.appendErrorTokNotesOff(token, 0, format, args, &[0]u32{}); 11482 } 11483 11484 fn failTokNotes( 11485 astgen: *AstGen, 11486 token: Ast.TokenIndex, 11487 comptime format: []const u8, 11488 args: anytype, 11489 notes: []const u32, 11490 ) InnerError { 11491 try appendErrorTokNotesOff(astgen, token, 0, format, args, notes); 11492 return error.AnalysisFail; 11493 } 11494 11495 fn appendErrorTokNotes( 11496 astgen: *AstGen, 11497 token: Ast.TokenIndex, 11498 comptime format: []const u8, 11499 args: anytype, 11500 notes: []const u32, 11501 ) !void { 11502 return appendErrorTokNotesOff(astgen, token, 0, format, args, notes); 11503 } 11504 11505 /// Same as `fail`, except given a token plus an offset from its starting byte 11506 /// offset. 11507 fn failOff( 11508 astgen: *AstGen, 11509 token: Ast.TokenIndex, 11510 byte_offset: u32, 11511 comptime format: []const u8, 11512 args: anytype, 11513 ) InnerError { 11514 try appendErrorTokNotesOff(astgen, token, byte_offset, format, args, &.{}); 11515 return error.AnalysisFail; 11516 } 11517 11518 fn appendErrorTokNotesOff( 11519 astgen: *AstGen, 11520 token: Ast.TokenIndex, 11521 byte_offset: u32, 11522 comptime format: []const u8, 11523 args: anytype, 11524 notes: []const u32, 11525 ) !void { 11526 @branchHint(.cold); 11527 const gpa = astgen.gpa; 11528 const string_bytes = &astgen.string_bytes; 11529 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len); 11530 try string_bytes.writer(gpa).print(format ++ "\x00", args); 11531 const notes_index: u32 = if (notes.len != 0) blk: { 11532 const notes_start = astgen.extra.items.len; 11533 try astgen.extra.ensureTotalCapacity(gpa, notes_start + 1 + notes.len); 11534 astgen.extra.appendAssumeCapacity(@intCast(notes.len)); 11535 astgen.extra.appendSliceAssumeCapacity(notes); 11536 break :blk @intCast(notes_start); 11537 } else 0; 11538 try astgen.compile_errors.append(gpa, .{ 11539 .msg = msg, 11540 .node = 0, 11541 .token = token, 11542 .byte_offset = byte_offset, 11543 .notes = notes_index, 11544 }); 11545 } 11546 11547 fn errNoteTok( 11548 astgen: *AstGen, 11549 token: Ast.TokenIndex, 11550 comptime format: []const u8, 11551 args: anytype, 11552 ) Allocator.Error!u32 { 11553 return errNoteTokOff(astgen, token, 0, format, args); 11554 } 11555 11556 fn errNoteTokOff( 11557 astgen: *AstGen, 11558 token: Ast.TokenIndex, 11559 byte_offset: u32, 11560 comptime format: []const u8, 11561 args: anytype, 11562 ) Allocator.Error!u32 { 11563 @branchHint(.cold); 11564 const string_bytes = &astgen.string_bytes; 11565 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len); 11566 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args); 11567 return astgen.addExtra(Zir.Inst.CompileErrors.Item{ 11568 .msg = msg, 11569 .node = 0, 11570 .token = token, 11571 .byte_offset = byte_offset, 11572 .notes = 0, 11573 }); 11574 } 11575 11576 fn errNoteNode( 11577 astgen: *AstGen, 11578 node: Ast.Node.Index, 11579 comptime format: []const u8, 11580 args: anytype, 11581 ) Allocator.Error!u32 { 11582 @branchHint(.cold); 11583 const string_bytes = &astgen.string_bytes; 11584 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len); 11585 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args); 11586 return astgen.addExtra(Zir.Inst.CompileErrors.Item{ 11587 .msg = msg, 11588 .node = node, 11589 .token = 0, 11590 .byte_offset = 0, 11591 .notes = 0, 11592 }); 11593 } 11594 11595 fn identAsString(astgen: *AstGen, ident_token: Ast.TokenIndex) !Zir.NullTerminatedString { 11596 const gpa = astgen.gpa; 11597 const string_bytes = &astgen.string_bytes; 11598 const str_index: u32 = @intCast(string_bytes.items.len); 11599 try astgen.appendIdentStr(ident_token, string_bytes); 11600 const key: []const u8 = string_bytes.items[str_index..]; 11601 const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{ 11602 .bytes = string_bytes, 11603 }, StringIndexContext{ 11604 .bytes = string_bytes, 11605 }); 11606 if (gop.found_existing) { 11607 string_bytes.shrinkRetainingCapacity(str_index); 11608 return @enumFromInt(gop.key_ptr.*); 11609 } else { 11610 gop.key_ptr.* = str_index; 11611 try string_bytes.append(gpa, 0); 11612 return @enumFromInt(str_index); 11613 } 11614 } 11615 11616 /// Adds a doc comment block to `string_bytes` by walking backwards from `end_token`. 11617 /// `end_token` must point at the first token after the last doc comment line. 11618 /// Returns 0 if no doc comment is present. 11619 fn docCommentAsString(astgen: *AstGen, end_token: Ast.TokenIndex) !Zir.NullTerminatedString { 11620 if (end_token == 0) return .empty; 11621 11622 const token_tags = astgen.tree.tokens.items(.tag); 11623 11624 var tok = end_token - 1; 11625 while (token_tags[tok] == .doc_comment) { 11626 if (tok == 0) break; 11627 tok -= 1; 11628 } else { 11629 tok += 1; 11630 } 11631 11632 return docCommentAsStringFromFirst(astgen, end_token, tok); 11633 } 11634 11635 /// end_token must be > the index of the last doc comment. 11636 fn docCommentAsStringFromFirst( 11637 astgen: *AstGen, 11638 end_token: Ast.TokenIndex, 11639 start_token: Ast.TokenIndex, 11640 ) !Zir.NullTerminatedString { 11641 if (start_token == end_token) return .empty; 11642 11643 const gpa = astgen.gpa; 11644 const string_bytes = &astgen.string_bytes; 11645 const str_index: u32 = @intCast(string_bytes.items.len); 11646 const token_starts = astgen.tree.tokens.items(.start); 11647 const token_tags = astgen.tree.tokens.items(.tag); 11648 11649 const total_bytes = token_starts[end_token] - token_starts[start_token]; 11650 try string_bytes.ensureUnusedCapacity(gpa, total_bytes); 11651 11652 var current_token = start_token; 11653 while (current_token < end_token) : (current_token += 1) { 11654 switch (token_tags[current_token]) { 11655 .doc_comment => { 11656 const tok_bytes = astgen.tree.tokenSlice(current_token)[3..]; 11657 string_bytes.appendSliceAssumeCapacity(tok_bytes); 11658 if (current_token != end_token - 1) { 11659 string_bytes.appendAssumeCapacity('\n'); 11660 } 11661 }, 11662 else => break, 11663 } 11664 } 11665 11666 const key: []const u8 = string_bytes.items[str_index..]; 11667 const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{ 11668 .bytes = string_bytes, 11669 }, StringIndexContext{ 11670 .bytes = string_bytes, 11671 }); 11672 11673 if (gop.found_existing) { 11674 string_bytes.shrinkRetainingCapacity(str_index); 11675 return @enumFromInt(gop.key_ptr.*); 11676 } else { 11677 gop.key_ptr.* = str_index; 11678 try string_bytes.append(gpa, 0); 11679 return @enumFromInt(str_index); 11680 } 11681 } 11682 11683 const IndexSlice = struct { index: Zir.NullTerminatedString, len: u32 }; 11684 11685 fn strLitAsString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !IndexSlice { 11686 const gpa = astgen.gpa; 11687 const string_bytes = &astgen.string_bytes; 11688 const str_index: u32 = @intCast(string_bytes.items.len); 11689 const token_bytes = astgen.tree.tokenSlice(str_lit_token); 11690 try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0); 11691 const key: []const u8 = string_bytes.items[str_index..]; 11692 if (std.mem.indexOfScalar(u8, key, 0)) |_| return .{ 11693 .index = @enumFromInt(str_index), 11694 .len = @intCast(key.len), 11695 }; 11696 const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{ 11697 .bytes = string_bytes, 11698 }, StringIndexContext{ 11699 .bytes = string_bytes, 11700 }); 11701 if (gop.found_existing) { 11702 string_bytes.shrinkRetainingCapacity(str_index); 11703 return .{ 11704 .index = @enumFromInt(gop.key_ptr.*), 11705 .len = @intCast(key.len), 11706 }; 11707 } else { 11708 gop.key_ptr.* = str_index; 11709 // Still need a null byte because we are using the same table 11710 // to lookup null terminated strings, so if we get a match, it has to 11711 // be null terminated for that to work. 11712 try string_bytes.append(gpa, 0); 11713 return .{ 11714 .index = @enumFromInt(str_index), 11715 .len = @intCast(key.len), 11716 }; 11717 } 11718 } 11719 11720 fn strLitNodeAsString(astgen: *AstGen, node: Ast.Node.Index) !IndexSlice { 11721 const tree = astgen.tree; 11722 const node_datas = tree.nodes.items(.data); 11723 11724 const start = node_datas[node].lhs; 11725 const end = node_datas[node].rhs; 11726 11727 const gpa = astgen.gpa; 11728 const string_bytes = &astgen.string_bytes; 11729 const str_index = string_bytes.items.len; 11730 11731 // First line: do not append a newline. 11732 var tok_i = start; 11733 { 11734 const slice = tree.tokenSlice(tok_i); 11735 const line_bytes = slice[2..]; 11736 try string_bytes.appendSlice(gpa, line_bytes); 11737 tok_i += 1; 11738 } 11739 // Following lines: each line prepends a newline. 11740 while (tok_i <= end) : (tok_i += 1) { 11741 const slice = tree.tokenSlice(tok_i); 11742 const line_bytes = slice[2..]; 11743 try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1); 11744 string_bytes.appendAssumeCapacity('\n'); 11745 string_bytes.appendSliceAssumeCapacity(line_bytes); 11746 } 11747 const len = string_bytes.items.len - str_index; 11748 try string_bytes.append(gpa, 0); 11749 return IndexSlice{ 11750 .index = @enumFromInt(str_index), 11751 .len = @intCast(len), 11752 }; 11753 } 11754 11755 fn testNameString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !Zir.NullTerminatedString { 11756 const gpa = astgen.gpa; 11757 const string_bytes = &astgen.string_bytes; 11758 const str_index: u32 = @intCast(string_bytes.items.len); 11759 const token_bytes = astgen.tree.tokenSlice(str_lit_token); 11760 try string_bytes.append(gpa, 0); // Indicates this is a test. 11761 try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0); 11762 const slice = string_bytes.items[str_index + 1 ..]; 11763 if (mem.indexOfScalar(u8, slice, 0) != null) { 11764 return astgen.failTok(str_lit_token, "test name cannot contain null bytes", .{}); 11765 } else if (slice.len == 0) { 11766 return astgen.failTok(str_lit_token, "empty test name must be omitted", .{}); 11767 } 11768 try string_bytes.append(gpa, 0); 11769 return @enumFromInt(str_index); 11770 } 11771 11772 const Scope = struct { 11773 tag: Tag, 11774 11775 fn cast(base: *Scope, comptime T: type) ?*T { 11776 if (T == Defer) { 11777 switch (base.tag) { 11778 .defer_normal, .defer_error => return @alignCast(@fieldParentPtr("base", base)), 11779 else => return null, 11780 } 11781 } 11782 if (T == Namespace) { 11783 switch (base.tag) { 11784 .namespace => return @alignCast(@fieldParentPtr("base", base)), 11785 else => return null, 11786 } 11787 } 11788 if (base.tag != T.base_tag) 11789 return null; 11790 11791 return @alignCast(@fieldParentPtr("base", base)); 11792 } 11793 11794 fn parent(base: *Scope) ?*Scope { 11795 return switch (base.tag) { 11796 .gen_zir => base.cast(GenZir).?.parent, 11797 .local_val => base.cast(LocalVal).?.parent, 11798 .local_ptr => base.cast(LocalPtr).?.parent, 11799 .defer_normal, .defer_error => base.cast(Defer).?.parent, 11800 .namespace => base.cast(Namespace).?.parent, 11801 .top => null, 11802 }; 11803 } 11804 11805 const Tag = enum { 11806 gen_zir, 11807 local_val, 11808 local_ptr, 11809 defer_normal, 11810 defer_error, 11811 namespace, 11812 top, 11813 }; 11814 11815 /// The category of identifier. These tag names are user-visible in compile errors. 11816 const IdCat = enum { 11817 @"function parameter", 11818 @"local constant", 11819 @"local variable", 11820 @"switch tag capture", 11821 capture, 11822 }; 11823 11824 /// This is always a `const` local and importantly the `inst` is a value type, not a pointer. 11825 /// This structure lives as long as the AST generation of the Block 11826 /// node that contains the variable. 11827 const LocalVal = struct { 11828 const base_tag: Tag = .local_val; 11829 base: Scope = Scope{ .tag = base_tag }, 11830 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11831 parent: *Scope, 11832 gen_zir: *GenZir, 11833 inst: Zir.Inst.Ref, 11834 /// Source location of the corresponding variable declaration. 11835 token_src: Ast.TokenIndex, 11836 /// Track the first identifier where it is referenced. 11837 /// 0 means never referenced. 11838 used: Ast.TokenIndex = 0, 11839 /// Track the identifier where it is discarded, like this `_ = foo;`. 11840 /// 0 means never discarded. 11841 discarded: Ast.TokenIndex = 0, 11842 /// String table index. 11843 name: Zir.NullTerminatedString, 11844 id_cat: IdCat, 11845 }; 11846 11847 /// This could be a `const` or `var` local. It has a pointer instead of a value. 11848 /// This structure lives as long as the AST generation of the Block 11849 /// node that contains the variable. 11850 const LocalPtr = struct { 11851 const base_tag: Tag = .local_ptr; 11852 base: Scope = Scope{ .tag = base_tag }, 11853 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11854 parent: *Scope, 11855 gen_zir: *GenZir, 11856 ptr: Zir.Inst.Ref, 11857 /// Source location of the corresponding variable declaration. 11858 token_src: Ast.TokenIndex, 11859 /// Track the first identifier where it is referenced. 11860 /// 0 means never referenced. 11861 used: Ast.TokenIndex = 0, 11862 /// Track the identifier where it is discarded, like this `_ = foo;`. 11863 /// 0 means never discarded. 11864 discarded: Ast.TokenIndex = 0, 11865 /// Whether this value is used as an lvalue after initialization. 11866 /// If not, we know it can be `const`, so will emit a compile error if it is `var`. 11867 used_as_lvalue: bool = false, 11868 /// String table index. 11869 name: Zir.NullTerminatedString, 11870 id_cat: IdCat, 11871 /// true means we find out during Sema whether the value is comptime. 11872 /// false means it is already known at AstGen the value is runtime-known. 11873 maybe_comptime: bool, 11874 }; 11875 11876 const Defer = struct { 11877 base: Scope, 11878 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11879 parent: *Scope, 11880 index: u32, 11881 len: u32, 11882 remapped_err_code: Zir.Inst.OptionalIndex = .none, 11883 }; 11884 11885 /// Represents a global scope that has any number of declarations in it. 11886 /// Each declaration has this as the parent scope. 11887 const Namespace = struct { 11888 const base_tag: Tag = .namespace; 11889 base: Scope = Scope{ .tag = base_tag }, 11890 11891 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11892 parent: *Scope, 11893 /// Maps string table index to the source location of declaration, 11894 /// for the purposes of reporting name shadowing compile errors. 11895 decls: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .empty, 11896 node: Ast.Node.Index, 11897 inst: Zir.Inst.Index, 11898 maybe_generic: bool, 11899 11900 /// The astgen scope containing this namespace. 11901 /// Only valid during astgen. 11902 declaring_gz: ?*GenZir, 11903 11904 /// Set of captures used by this namespace. 11905 captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Capture, void) = .empty, 11906 11907 fn deinit(self: *Namespace, gpa: Allocator) void { 11908 self.decls.deinit(gpa); 11909 self.captures.deinit(gpa); 11910 self.* = undefined; 11911 } 11912 }; 11913 11914 const Top = struct { 11915 const base_tag: Scope.Tag = .top; 11916 base: Scope = Scope{ .tag = base_tag }, 11917 }; 11918 }; 11919 11920 /// This is a temporary structure; references to it are valid only 11921 /// while constructing a `Zir`. 11922 const GenZir = struct { 11923 const base_tag: Scope.Tag = .gen_zir; 11924 base: Scope = Scope{ .tag = base_tag }, 11925 /// Whether we're already in a scope known to be comptime. This is set 11926 /// whenever we know Sema will analyze the current block with `is_comptime`, 11927 /// for instance when we're within a `struct_decl` or a `block_comptime`. 11928 is_comptime: bool, 11929 /// Whether we're in an expression within a `@TypeOf` operand. In this case, closure of runtime 11930 /// variables is permitted where it is usually not. 11931 is_typeof: bool = false, 11932 /// This is set to true for a `GenZir` of a `block_inline`, indicating that 11933 /// exits from this block should use `break_inline` rather than `break`. 11934 is_inline: bool = false, 11935 c_import: bool = false, 11936 /// How decls created in this scope should be named. 11937 anon_name_strategy: Zir.Inst.NameStrategy = .anon, 11938 /// The containing decl AST node. 11939 decl_node_index: Ast.Node.Index, 11940 /// The containing decl line index, absolute. 11941 decl_line: u32, 11942 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. 11943 parent: *Scope, 11944 /// All `GenZir` scopes for the same ZIR share this. 11945 astgen: *AstGen, 11946 /// Keeps track of the list of instructions in this scope. Possibly shared. 11947 /// Indexes to instructions in `astgen`. 11948 instructions: *ArrayListUnmanaged(Zir.Inst.Index), 11949 /// A sub-block may share its instructions ArrayList with containing GenZir, 11950 /// if use is strictly nested. This saves prior size of list for unstacking. 11951 instructions_top: usize, 11952 label: ?Label = null, 11953 break_block: Zir.Inst.OptionalIndex = .none, 11954 continue_block: Zir.Inst.OptionalIndex = .none, 11955 /// Only valid when setBreakResultInfo is called. 11956 break_result_info: AstGen.ResultInfo = undefined, 11957 continue_result_info: AstGen.ResultInfo = undefined, 11958 11959 suspend_node: Ast.Node.Index = 0, 11960 nosuspend_node: Ast.Node.Index = 0, 11961 /// Set if this GenZir is a defer. 11962 cur_defer_node: Ast.Node.Index = 0, 11963 // Set if this GenZir is a defer or it is inside a defer. 11964 any_defer_node: Ast.Node.Index = 0, 11965 11966 const unstacked_top = std.math.maxInt(usize); 11967 /// Call unstack before adding any new instructions to containing GenZir. 11968 fn unstack(self: *GenZir) void { 11969 if (self.instructions_top != unstacked_top) { 11970 self.instructions.items.len = self.instructions_top; 11971 self.instructions_top = unstacked_top; 11972 } 11973 } 11974 11975 fn isEmpty(self: *const GenZir) bool { 11976 return (self.instructions_top == unstacked_top) or 11977 (self.instructions.items.len == self.instructions_top); 11978 } 11979 11980 fn instructionsSlice(self: *const GenZir) []Zir.Inst.Index { 11981 return if (self.instructions_top == unstacked_top) 11982 &[0]Zir.Inst.Index{} 11983 else 11984 self.instructions.items[self.instructions_top..]; 11985 } 11986 11987 fn instructionsSliceUpto(self: *const GenZir, stacked_gz: *GenZir) []Zir.Inst.Index { 11988 return if (self.instructions_top == unstacked_top) 11989 &[0]Zir.Inst.Index{} 11990 else if (self.instructions == stacked_gz.instructions and stacked_gz.instructions_top != unstacked_top) 11991 self.instructions.items[self.instructions_top..stacked_gz.instructions_top] 11992 else 11993 self.instructions.items[self.instructions_top..]; 11994 } 11995 11996 fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir { 11997 return .{ 11998 .is_comptime = gz.is_comptime, 11999 .is_typeof = gz.is_typeof, 12000 .c_import = gz.c_import, 12001 .decl_node_index = gz.decl_node_index, 12002 .decl_line = gz.decl_line, 12003 .parent = scope, 12004 .astgen = gz.astgen, 12005 .suspend_node = gz.suspend_node, 12006 .nosuspend_node = gz.nosuspend_node, 12007 .any_defer_node = gz.any_defer_node, 12008 .instructions = gz.instructions, 12009 .instructions_top = gz.instructions.items.len, 12010 }; 12011 } 12012 12013 const Label = struct { 12014 token: Ast.TokenIndex, 12015 block_inst: Zir.Inst.Index, 12016 used: bool = false, 12017 used_for_continue: bool = false, 12018 }; 12019 12020 /// Assumes nothing stacked on `gz`. 12021 fn endsWithNoReturn(gz: GenZir) bool { 12022 if (gz.isEmpty()) return false; 12023 const tags = gz.astgen.instructions.items(.tag); 12024 const last_inst = gz.instructions.items[gz.instructions.items.len - 1]; 12025 return tags[@intFromEnum(last_inst)].isNoReturn(); 12026 } 12027 12028 /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`. 12029 fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool { 12030 if (inst_ref == .unreachable_value) return true; 12031 if (inst_ref.toIndex()) |inst_index| { 12032 return gz.astgen.instructions.items(.tag)[@intFromEnum(inst_index)].isNoReturn(); 12033 } 12034 return false; 12035 } 12036 12037 fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) i32 { 12038 return @as(i32, @bitCast(node_index)) - @as(i32, @bitCast(gz.decl_node_index)); 12039 } 12040 12041 fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) u32 { 12042 return token - gz.srcToken(); 12043 } 12044 12045 fn srcToken(gz: GenZir) Ast.TokenIndex { 12046 return gz.astgen.tree.firstToken(gz.decl_node_index); 12047 } 12048 12049 fn setBreakResultInfo(gz: *GenZir, parent_ri: AstGen.ResultInfo) void { 12050 // Depending on whether the result location is a pointer or value, different 12051 // ZIR needs to be generated. In the former case we rely on storing to the 12052 // pointer to communicate the result, and use breakvoid; in the latter case 12053 // the block break instructions will have the result values. 12054 switch (parent_ri.rl) { 12055 .coerced_ty => |ty_inst| { 12056 // Type coercion needs to happen before breaks. 12057 gz.break_result_info = .{ .rl = .{ .ty = ty_inst }, .ctx = parent_ri.ctx }; 12058 }, 12059 .discard => { 12060 // We don't forward the result context here. This prevents 12061 // "unnecessary discard" errors from being caused by expressions 12062 // far from the actual discard, such as a `break` from a 12063 // discarded block. 12064 gz.break_result_info = .{ .rl = .discard }; 12065 }, 12066 else => { 12067 gz.break_result_info = parent_ri; 12068 }, 12069 } 12070 } 12071 12072 /// Assumes nothing stacked on `gz`. Unstacks `gz`. 12073 fn setBoolBrBody(gz: *GenZir, bool_br: Zir.Inst.Index, bool_br_lhs: Zir.Inst.Ref) !void { 12074 const astgen = gz.astgen; 12075 const gpa = astgen.gpa; 12076 const body = gz.instructionsSlice(); 12077 const body_len = astgen.countBodyLenAfterFixups(body); 12078 try astgen.extra.ensureUnusedCapacity( 12079 gpa, 12080 @typeInfo(Zir.Inst.BoolBr).@"struct".fields.len + body_len, 12081 ); 12082 const zir_datas = astgen.instructions.items(.data); 12083 zir_datas[@intFromEnum(bool_br)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.BoolBr{ 12084 .lhs = bool_br_lhs, 12085 .body_len = body_len, 12086 }); 12087 astgen.appendBodyWithFixups(body); 12088 gz.unstack(); 12089 } 12090 12091 /// Assumes nothing stacked on `gz`. Unstacks `gz`. 12092 fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void { 12093 const astgen = gz.astgen; 12094 const gpa = astgen.gpa; 12095 const body = gz.instructionsSlice(); 12096 const body_len = astgen.countBodyLenAfterFixups(body); 12097 try astgen.extra.ensureUnusedCapacity( 12098 gpa, 12099 @typeInfo(Zir.Inst.Block).@"struct".fields.len + body_len, 12100 ); 12101 const zir_datas = astgen.instructions.items(.data); 12102 zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity( 12103 Zir.Inst.Block{ .body_len = body_len }, 12104 ); 12105 astgen.appendBodyWithFixups(body); 12106 gz.unstack(); 12107 } 12108 12109 /// Assumes nothing stacked on `gz`. Unstacks `gz`. 12110 fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void { 12111 const astgen = gz.astgen; 12112 const gpa = astgen.gpa; 12113 const body = gz.instructionsSlice(); 12114 const body_len = astgen.countBodyLenAfterFixups(body); 12115 try astgen.extra.ensureUnusedCapacity( 12116 gpa, 12117 @typeInfo(Zir.Inst.Try).@"struct".fields.len + body_len, 12118 ); 12119 const zir_datas = astgen.instructions.items(.data); 12120 zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity( 12121 Zir.Inst.Try{ 12122 .operand = operand, 12123 .body_len = body_len, 12124 }, 12125 ); 12126 astgen.appendBodyWithFixups(body); 12127 gz.unstack(); 12128 } 12129 12130 /// Must be called with the following stack set up: 12131 /// * gz (bottom) 12132 /// * align_gz 12133 /// * addrspace_gz 12134 /// * section_gz 12135 /// * cc_gz 12136 /// * ret_gz 12137 /// * body_gz (top) 12138 /// Unstacks all of those except for `gz`. 12139 fn addFunc(gz: *GenZir, args: struct { 12140 src_node: Ast.Node.Index, 12141 lbrace_line: u32 = 0, 12142 lbrace_column: u32 = 0, 12143 param_block: Zir.Inst.Index, 12144 12145 align_gz: ?*GenZir, 12146 addrspace_gz: ?*GenZir, 12147 section_gz: ?*GenZir, 12148 cc_gz: ?*GenZir, 12149 ret_gz: ?*GenZir, 12150 body_gz: ?*GenZir, 12151 12152 align_ref: Zir.Inst.Ref, 12153 addrspace_ref: Zir.Inst.Ref, 12154 section_ref: Zir.Inst.Ref, 12155 cc_ref: Zir.Inst.Ref, 12156 ret_ref: Zir.Inst.Ref, 12157 12158 lib_name: Zir.NullTerminatedString, 12159 noalias_bits: u32, 12160 is_var_args: bool, 12161 is_inferred_error: bool, 12162 is_test: bool, 12163 is_extern: bool, 12164 is_noinline: bool, 12165 12166 /// Ignored if `body_gz == null`. 12167 proto_hash: std.zig.SrcHash, 12168 }) !Zir.Inst.Ref { 12169 assert(args.src_node != 0); 12170 const astgen = gz.astgen; 12171 const gpa = astgen.gpa; 12172 const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref; 12173 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12174 12175 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12176 12177 var body: []Zir.Inst.Index = &[0]Zir.Inst.Index{}; 12178 var ret_body: []Zir.Inst.Index = &[0]Zir.Inst.Index{}; 12179 var src_locs_and_hash_buffer: [7]u32 = undefined; 12180 var src_locs_and_hash: []u32 = src_locs_and_hash_buffer[0..0]; 12181 if (args.body_gz) |body_gz| { 12182 const tree = astgen.tree; 12183 const node_tags = tree.nodes.items(.tag); 12184 const node_datas = tree.nodes.items(.data); 12185 const token_starts = tree.tokens.items(.start); 12186 const fn_decl = args.src_node; 12187 assert(node_tags[fn_decl] == .fn_decl or node_tags[fn_decl] == .test_decl); 12188 const block = node_datas[fn_decl].rhs; 12189 const rbrace_start = token_starts[tree.lastToken(block)]; 12190 astgen.advanceSourceCursor(rbrace_start); 12191 const rbrace_line: u32 = @intCast(astgen.source_line - gz.decl_line); 12192 const rbrace_column: u32 = @intCast(astgen.source_column); 12193 12194 const columns = args.lbrace_column | (rbrace_column << 16); 12195 12196 const proto_hash_arr: [4]u32 = @bitCast(args.proto_hash); 12197 12198 src_locs_and_hash_buffer = .{ 12199 args.lbrace_line, 12200 rbrace_line, 12201 columns, 12202 proto_hash_arr[0], 12203 proto_hash_arr[1], 12204 proto_hash_arr[2], 12205 proto_hash_arr[3], 12206 }; 12207 src_locs_and_hash = &src_locs_and_hash_buffer; 12208 12209 body = body_gz.instructionsSlice(); 12210 if (args.ret_gz) |ret_gz| 12211 ret_body = ret_gz.instructionsSliceUpto(body_gz); 12212 } else { 12213 if (args.ret_gz) |ret_gz| 12214 ret_body = ret_gz.instructionsSlice(); 12215 } 12216 const body_len = astgen.countBodyLenAfterFixups(body); 12217 12218 if (args.cc_ref != .none or args.lib_name != .empty or args.is_var_args or args.is_test or 12219 args.is_extern or args.align_ref != .none or args.section_ref != .none or 12220 args.addrspace_ref != .none or args.noalias_bits != 0 or args.is_noinline) 12221 { 12222 var align_body: []Zir.Inst.Index = &.{}; 12223 var addrspace_body: []Zir.Inst.Index = &.{}; 12224 var section_body: []Zir.Inst.Index = &.{}; 12225 var cc_body: []Zir.Inst.Index = &.{}; 12226 if (args.ret_gz != null) { 12227 align_body = args.align_gz.?.instructionsSliceUpto(args.addrspace_gz.?); 12228 addrspace_body = args.addrspace_gz.?.instructionsSliceUpto(args.section_gz.?); 12229 section_body = args.section_gz.?.instructionsSliceUpto(args.cc_gz.?); 12230 cc_body = args.cc_gz.?.instructionsSliceUpto(args.ret_gz.?); 12231 } 12232 12233 try astgen.extra.ensureUnusedCapacity( 12234 gpa, 12235 @typeInfo(Zir.Inst.FuncFancy).@"struct".fields.len + 12236 fancyFnExprExtraLen(astgen, align_body, args.align_ref) + 12237 fancyFnExprExtraLen(astgen, addrspace_body, args.addrspace_ref) + 12238 fancyFnExprExtraLen(astgen, section_body, args.section_ref) + 12239 fancyFnExprExtraLen(astgen, cc_body, args.cc_ref) + 12240 fancyFnExprExtraLen(astgen, ret_body, ret_ref) + 12241 body_len + src_locs_and_hash.len + 12242 @intFromBool(args.lib_name != .empty) + 12243 @intFromBool(args.noalias_bits != 0), 12244 ); 12245 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{ 12246 .param_block = args.param_block, 12247 .body_len = body_len, 12248 .bits = .{ 12249 .is_var_args = args.is_var_args, 12250 .is_inferred_error = args.is_inferred_error, 12251 .is_test = args.is_test, 12252 .is_extern = args.is_extern, 12253 .is_noinline = args.is_noinline, 12254 .has_lib_name = args.lib_name != .empty, 12255 .has_any_noalias = args.noalias_bits != 0, 12256 12257 .has_align_ref = args.align_ref != .none, 12258 .has_addrspace_ref = args.addrspace_ref != .none, 12259 .has_section_ref = args.section_ref != .none, 12260 .has_cc_ref = args.cc_ref != .none, 12261 .has_ret_ty_ref = ret_ref != .none, 12262 12263 .has_align_body = align_body.len != 0, 12264 .has_addrspace_body = addrspace_body.len != 0, 12265 .has_section_body = section_body.len != 0, 12266 .has_cc_body = cc_body.len != 0, 12267 .has_ret_ty_body = ret_body.len != 0, 12268 }, 12269 }); 12270 if (args.lib_name != .empty) { 12271 astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name)); 12272 } 12273 12274 const zir_datas = astgen.instructions.items(.data); 12275 if (align_body.len != 0) { 12276 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, align_body)); 12277 astgen.appendBodyWithFixups(align_body); 12278 const break_extra = zir_datas[@intFromEnum(align_body[align_body.len - 1])].@"break".payload_index; 12279 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] = 12280 @intFromEnum(new_index); 12281 } else if (args.align_ref != .none) { 12282 astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_ref)); 12283 } 12284 if (addrspace_body.len != 0) { 12285 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, addrspace_body)); 12286 astgen.appendBodyWithFixups(addrspace_body); 12287 const break_extra = 12288 zir_datas[@intFromEnum(addrspace_body[addrspace_body.len - 1])].@"break".payload_index; 12289 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] = 12290 @intFromEnum(new_index); 12291 } else if (args.addrspace_ref != .none) { 12292 astgen.extra.appendAssumeCapacity(@intFromEnum(args.addrspace_ref)); 12293 } 12294 if (section_body.len != 0) { 12295 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, section_body)); 12296 astgen.appendBodyWithFixups(section_body); 12297 const break_extra = 12298 zir_datas[@intFromEnum(section_body[section_body.len - 1])].@"break".payload_index; 12299 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] = 12300 @intFromEnum(new_index); 12301 } else if (args.section_ref != .none) { 12302 astgen.extra.appendAssumeCapacity(@intFromEnum(args.section_ref)); 12303 } 12304 if (cc_body.len != 0) { 12305 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, cc_body)); 12306 astgen.appendBodyWithFixups(cc_body); 12307 const break_extra = zir_datas[@intFromEnum(cc_body[cc_body.len - 1])].@"break".payload_index; 12308 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] = 12309 @intFromEnum(new_index); 12310 } else if (args.cc_ref != .none) { 12311 astgen.extra.appendAssumeCapacity(@intFromEnum(args.cc_ref)); 12312 } 12313 if (ret_body.len != 0) { 12314 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, ret_body)); 12315 astgen.appendBodyWithFixups(ret_body); 12316 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index; 12317 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] = 12318 @intFromEnum(new_index); 12319 } else if (ret_ref != .none) { 12320 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref)); 12321 } 12322 12323 if (args.noalias_bits != 0) { 12324 astgen.extra.appendAssumeCapacity(args.noalias_bits); 12325 } 12326 12327 astgen.appendBodyWithFixups(body); 12328 astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash); 12329 12330 // Order is important when unstacking. 12331 if (args.body_gz) |body_gz| body_gz.unstack(); 12332 if (args.ret_gz != null) { 12333 args.ret_gz.?.unstack(); 12334 args.cc_gz.?.unstack(); 12335 args.section_gz.?.unstack(); 12336 args.addrspace_gz.?.unstack(); 12337 args.align_gz.?.unstack(); 12338 } 12339 12340 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12341 12342 astgen.instructions.appendAssumeCapacity(.{ 12343 .tag = .func_fancy, 12344 .data = .{ .pl_node = .{ 12345 .src_node = gz.nodeIndexToRelative(args.src_node), 12346 .payload_index = payload_index, 12347 } }, 12348 }); 12349 gz.instructions.appendAssumeCapacity(new_index); 12350 return new_index.toRef(); 12351 } else { 12352 try astgen.extra.ensureUnusedCapacity( 12353 gpa, 12354 @typeInfo(Zir.Inst.Func).@"struct".fields.len + 1 + 12355 fancyFnExprExtraLen(astgen, ret_body, ret_ref) + 12356 body_len + src_locs_and_hash.len, 12357 ); 12358 12359 const ret_body_len = if (ret_body.len != 0) 12360 countBodyLenAfterFixups(astgen, ret_body) 12361 else 12362 @intFromBool(ret_ref != .none); 12363 12364 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{ 12365 .param_block = args.param_block, 12366 .ret_body_len = ret_body_len, 12367 .body_len = body_len, 12368 }); 12369 const zir_datas = astgen.instructions.items(.data); 12370 if (ret_body.len != 0) { 12371 astgen.appendBodyWithFixups(ret_body); 12372 12373 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index; 12374 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] = 12375 @intFromEnum(new_index); 12376 } else if (ret_ref != .none) { 12377 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref)); 12378 } 12379 astgen.appendBodyWithFixups(body); 12380 astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash); 12381 12382 // Order is important when unstacking. 12383 if (args.body_gz) |body_gz| body_gz.unstack(); 12384 if (args.ret_gz) |ret_gz| ret_gz.unstack(); 12385 if (args.cc_gz) |cc_gz| cc_gz.unstack(); 12386 if (args.section_gz) |section_gz| section_gz.unstack(); 12387 if (args.addrspace_gz) |addrspace_gz| addrspace_gz.unstack(); 12388 if (args.align_gz) |align_gz| align_gz.unstack(); 12389 12390 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12391 12392 const tag: Zir.Inst.Tag = if (args.is_inferred_error) .func_inferred else .func; 12393 astgen.instructions.appendAssumeCapacity(.{ 12394 .tag = tag, 12395 .data = .{ .pl_node = .{ 12396 .src_node = gz.nodeIndexToRelative(args.src_node), 12397 .payload_index = payload_index, 12398 } }, 12399 }); 12400 gz.instructions.appendAssumeCapacity(new_index); 12401 return new_index.toRef(); 12402 } 12403 } 12404 12405 fn fancyFnExprExtraLen(astgen: *AstGen, body: []Zir.Inst.Index, ref: Zir.Inst.Ref) u32 { 12406 // In the case of non-empty body, there is one for the body length, 12407 // and then one for each instruction. 12408 return countBodyLenAfterFixups(astgen, body) + @intFromBool(ref != .none); 12409 } 12410 12411 fn addVar(gz: *GenZir, args: struct { 12412 align_inst: Zir.Inst.Ref, 12413 lib_name: Zir.NullTerminatedString, 12414 var_type: Zir.Inst.Ref, 12415 init: Zir.Inst.Ref, 12416 is_extern: bool, 12417 is_const: bool, 12418 is_threadlocal: bool, 12419 }) !Zir.Inst.Ref { 12420 const astgen = gz.astgen; 12421 const gpa = astgen.gpa; 12422 12423 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12424 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12425 12426 try astgen.extra.ensureUnusedCapacity( 12427 gpa, 12428 @typeInfo(Zir.Inst.ExtendedVar).@"struct".fields.len + 12429 @intFromBool(args.lib_name != .empty) + 12430 @intFromBool(args.align_inst != .none) + 12431 @intFromBool(args.init != .none), 12432 ); 12433 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedVar{ 12434 .var_type = args.var_type, 12435 }); 12436 if (args.lib_name != .empty) { 12437 astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name)); 12438 } 12439 if (args.align_inst != .none) { 12440 astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst)); 12441 } 12442 if (args.init != .none) { 12443 astgen.extra.appendAssumeCapacity(@intFromEnum(args.init)); 12444 } 12445 12446 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12447 astgen.instructions.appendAssumeCapacity(.{ 12448 .tag = .extended, 12449 .data = .{ .extended = .{ 12450 .opcode = .variable, 12451 .small = @bitCast(Zir.Inst.ExtendedVar.Small{ 12452 .has_lib_name = args.lib_name != .empty, 12453 .has_align = args.align_inst != .none, 12454 .has_init = args.init != .none, 12455 .is_extern = args.is_extern, 12456 .is_const = args.is_const, 12457 .is_threadlocal = args.is_threadlocal, 12458 }), 12459 .operand = payload_index, 12460 } }, 12461 }); 12462 gz.instructions.appendAssumeCapacity(new_index); 12463 return new_index.toRef(); 12464 } 12465 12466 fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref { 12467 return gz.add(.{ 12468 .tag = .int, 12469 .data = .{ .int = integer }, 12470 }); 12471 } 12472 12473 fn addIntBig(gz: *GenZir, limbs: []const std.math.big.Limb) !Zir.Inst.Ref { 12474 const astgen = gz.astgen; 12475 const gpa = astgen.gpa; 12476 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12477 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12478 try astgen.string_bytes.ensureUnusedCapacity(gpa, @sizeOf(std.math.big.Limb) * limbs.len); 12479 12480 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12481 astgen.instructions.appendAssumeCapacity(.{ 12482 .tag = .int_big, 12483 .data = .{ .str = .{ 12484 .start = @enumFromInt(astgen.string_bytes.items.len), 12485 .len = @intCast(limbs.len), 12486 } }, 12487 }); 12488 gz.instructions.appendAssumeCapacity(new_index); 12489 astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs)); 12490 return new_index.toRef(); 12491 } 12492 12493 fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref { 12494 return gz.add(.{ 12495 .tag = .float, 12496 .data = .{ .float = number }, 12497 }); 12498 } 12499 12500 fn addUnNode( 12501 gz: *GenZir, 12502 tag: Zir.Inst.Tag, 12503 operand: Zir.Inst.Ref, 12504 /// Absolute node index. This function does the conversion to offset from Decl. 12505 src_node: Ast.Node.Index, 12506 ) !Zir.Inst.Ref { 12507 assert(operand != .none); 12508 return gz.add(.{ 12509 .tag = tag, 12510 .data = .{ .un_node = .{ 12511 .operand = operand, 12512 .src_node = gz.nodeIndexToRelative(src_node), 12513 } }, 12514 }); 12515 } 12516 12517 fn makeUnNode( 12518 gz: *GenZir, 12519 tag: Zir.Inst.Tag, 12520 operand: Zir.Inst.Ref, 12521 /// Absolute node index. This function does the conversion to offset from Decl. 12522 src_node: Ast.Node.Index, 12523 ) !Zir.Inst.Index { 12524 assert(operand != .none); 12525 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12526 try gz.astgen.instructions.append(gz.astgen.gpa, .{ 12527 .tag = tag, 12528 .data = .{ .un_node = .{ 12529 .operand = operand, 12530 .src_node = gz.nodeIndexToRelative(src_node), 12531 } }, 12532 }); 12533 return new_index; 12534 } 12535 12536 fn addPlNode( 12537 gz: *GenZir, 12538 tag: Zir.Inst.Tag, 12539 /// Absolute node index. This function does the conversion to offset from Decl. 12540 src_node: Ast.Node.Index, 12541 extra: anytype, 12542 ) !Zir.Inst.Ref { 12543 const gpa = gz.astgen.gpa; 12544 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12545 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 12546 12547 const payload_index = try gz.astgen.addExtra(extra); 12548 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12549 gz.astgen.instructions.appendAssumeCapacity(.{ 12550 .tag = tag, 12551 .data = .{ .pl_node = .{ 12552 .src_node = gz.nodeIndexToRelative(src_node), 12553 .payload_index = payload_index, 12554 } }, 12555 }); 12556 gz.instructions.appendAssumeCapacity(new_index); 12557 return new_index.toRef(); 12558 } 12559 12560 fn addPlNodePayloadIndex( 12561 gz: *GenZir, 12562 tag: Zir.Inst.Tag, 12563 /// Absolute node index. This function does the conversion to offset from Decl. 12564 src_node: Ast.Node.Index, 12565 payload_index: u32, 12566 ) !Zir.Inst.Ref { 12567 return try gz.add(.{ 12568 .tag = tag, 12569 .data = .{ .pl_node = .{ 12570 .src_node = gz.nodeIndexToRelative(src_node), 12571 .payload_index = payload_index, 12572 } }, 12573 }); 12574 } 12575 12576 /// Supports `param_gz` stacked on `gz`. Assumes nothing stacked on `param_gz`. Unstacks `param_gz`. 12577 fn addParam( 12578 gz: *GenZir, 12579 param_gz: *GenZir, 12580 tag: Zir.Inst.Tag, 12581 /// Absolute token index. This function does the conversion to Decl offset. 12582 abs_tok_index: Ast.TokenIndex, 12583 name: Zir.NullTerminatedString, 12584 first_doc_comment: ?Ast.TokenIndex, 12585 ) !Zir.Inst.Index { 12586 const gpa = gz.astgen.gpa; 12587 const param_body = param_gz.instructionsSlice(); 12588 const body_len = gz.astgen.countBodyLenAfterFixups(param_body); 12589 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 12590 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).@"struct".fields.len + body_len); 12591 12592 const doc_comment_index = if (first_doc_comment) |first| 12593 try gz.astgen.docCommentAsStringFromFirst(abs_tok_index, first) 12594 else 12595 .empty; 12596 12597 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{ 12598 .name = name, 12599 .doc_comment = doc_comment_index, 12600 .body_len = @intCast(body_len), 12601 }); 12602 gz.astgen.appendBodyWithFixups(param_body); 12603 param_gz.unstack(); 12604 12605 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12606 gz.astgen.instructions.appendAssumeCapacity(.{ 12607 .tag = tag, 12608 .data = .{ .pl_tok = .{ 12609 .src_tok = gz.tokenIndexToRelative(abs_tok_index), 12610 .payload_index = payload_index, 12611 } }, 12612 }); 12613 gz.instructions.appendAssumeCapacity(new_index); 12614 return new_index; 12615 } 12616 12617 fn addBuiltinValue(gz: *GenZir, src_node: Ast.Node.Index, val: Zir.Inst.BuiltinValue) !Zir.Inst.Ref { 12618 return addExtendedNodeSmall(gz, .builtin_value, src_node, @intFromEnum(val)); 12619 } 12620 12621 fn addExtendedPayload(gz: *GenZir, opcode: Zir.Inst.Extended, extra: anytype) !Zir.Inst.Ref { 12622 return addExtendedPayloadSmall(gz, opcode, undefined, extra); 12623 } 12624 12625 fn addExtendedPayloadSmall( 12626 gz: *GenZir, 12627 opcode: Zir.Inst.Extended, 12628 small: u16, 12629 extra: anytype, 12630 ) !Zir.Inst.Ref { 12631 const gpa = gz.astgen.gpa; 12632 12633 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12634 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 12635 12636 const payload_index = try gz.astgen.addExtra(extra); 12637 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12638 gz.astgen.instructions.appendAssumeCapacity(.{ 12639 .tag = .extended, 12640 .data = .{ .extended = .{ 12641 .opcode = opcode, 12642 .small = small, 12643 .operand = payload_index, 12644 } }, 12645 }); 12646 gz.instructions.appendAssumeCapacity(new_index); 12647 return new_index.toRef(); 12648 } 12649 12650 fn addExtendedMultiOp( 12651 gz: *GenZir, 12652 opcode: Zir.Inst.Extended, 12653 node: Ast.Node.Index, 12654 operands: []const Zir.Inst.Ref, 12655 ) !Zir.Inst.Ref { 12656 const astgen = gz.astgen; 12657 const gpa = astgen.gpa; 12658 12659 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12660 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12661 try astgen.extra.ensureUnusedCapacity( 12662 gpa, 12663 @typeInfo(Zir.Inst.NodeMultiOp).@"struct".fields.len + operands.len, 12664 ); 12665 12666 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{ 12667 .src_node = gz.nodeIndexToRelative(node), 12668 }); 12669 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12670 astgen.instructions.appendAssumeCapacity(.{ 12671 .tag = .extended, 12672 .data = .{ .extended = .{ 12673 .opcode = opcode, 12674 .small = @intCast(operands.len), 12675 .operand = payload_index, 12676 } }, 12677 }); 12678 gz.instructions.appendAssumeCapacity(new_index); 12679 astgen.appendRefsAssumeCapacity(operands); 12680 return new_index.toRef(); 12681 } 12682 12683 fn addExtendedMultiOpPayloadIndex( 12684 gz: *GenZir, 12685 opcode: Zir.Inst.Extended, 12686 payload_index: u32, 12687 trailing_len: usize, 12688 ) !Zir.Inst.Ref { 12689 const astgen = gz.astgen; 12690 const gpa = astgen.gpa; 12691 12692 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12693 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12694 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12695 astgen.instructions.appendAssumeCapacity(.{ 12696 .tag = .extended, 12697 .data = .{ .extended = .{ 12698 .opcode = opcode, 12699 .small = @intCast(trailing_len), 12700 .operand = payload_index, 12701 } }, 12702 }); 12703 gz.instructions.appendAssumeCapacity(new_index); 12704 return new_index.toRef(); 12705 } 12706 12707 fn addExtendedNodeSmall( 12708 gz: *GenZir, 12709 opcode: Zir.Inst.Extended, 12710 src_node: Ast.Node.Index, 12711 small: u16, 12712 ) !Zir.Inst.Ref { 12713 const astgen = gz.astgen; 12714 const gpa = astgen.gpa; 12715 12716 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12717 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 12718 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12719 astgen.instructions.appendAssumeCapacity(.{ 12720 .tag = .extended, 12721 .data = .{ .extended = .{ 12722 .opcode = opcode, 12723 .small = small, 12724 .operand = @bitCast(gz.nodeIndexToRelative(src_node)), 12725 } }, 12726 }); 12727 gz.instructions.appendAssumeCapacity(new_index); 12728 return new_index.toRef(); 12729 } 12730 12731 fn addUnTok( 12732 gz: *GenZir, 12733 tag: Zir.Inst.Tag, 12734 operand: Zir.Inst.Ref, 12735 /// Absolute token index. This function does the conversion to Decl offset. 12736 abs_tok_index: Ast.TokenIndex, 12737 ) !Zir.Inst.Ref { 12738 assert(operand != .none); 12739 return gz.add(.{ 12740 .tag = tag, 12741 .data = .{ .un_tok = .{ 12742 .operand = operand, 12743 .src_tok = gz.tokenIndexToRelative(abs_tok_index), 12744 } }, 12745 }); 12746 } 12747 12748 fn makeUnTok( 12749 gz: *GenZir, 12750 tag: Zir.Inst.Tag, 12751 operand: Zir.Inst.Ref, 12752 /// Absolute token index. This function does the conversion to Decl offset. 12753 abs_tok_index: Ast.TokenIndex, 12754 ) !Zir.Inst.Index { 12755 const astgen = gz.astgen; 12756 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 12757 assert(operand != .none); 12758 try astgen.instructions.append(astgen.gpa, .{ 12759 .tag = tag, 12760 .data = .{ .un_tok = .{ 12761 .operand = operand, 12762 .src_tok = gz.tokenIndexToRelative(abs_tok_index), 12763 } }, 12764 }); 12765 return new_index; 12766 } 12767 12768 fn addStrTok( 12769 gz: *GenZir, 12770 tag: Zir.Inst.Tag, 12771 str_index: Zir.NullTerminatedString, 12772 /// Absolute token index. This function does the conversion to Decl offset. 12773 abs_tok_index: Ast.TokenIndex, 12774 ) !Zir.Inst.Ref { 12775 return gz.add(.{ 12776 .tag = tag, 12777 .data = .{ .str_tok = .{ 12778 .start = str_index, 12779 .src_tok = gz.tokenIndexToRelative(abs_tok_index), 12780 } }, 12781 }); 12782 } 12783 12784 fn addSaveErrRetIndex( 12785 gz: *GenZir, 12786 cond: union(enum) { 12787 always: void, 12788 if_of_error_type: Zir.Inst.Ref, 12789 }, 12790 ) !Zir.Inst.Index { 12791 return gz.addAsIndex(.{ 12792 .tag = .save_err_ret_index, 12793 .data = .{ .save_err_ret_index = .{ 12794 .operand = switch (cond) { 12795 .if_of_error_type => |x| x, 12796 else => .none, 12797 }, 12798 } }, 12799 }); 12800 } 12801 12802 const BranchTarget = union(enum) { 12803 ret, 12804 block: Zir.Inst.Index, 12805 }; 12806 12807 fn addRestoreErrRetIndex( 12808 gz: *GenZir, 12809 bt: BranchTarget, 12810 cond: union(enum) { 12811 always: void, 12812 if_non_error: Zir.Inst.Ref, 12813 }, 12814 src_node: Ast.Node.Index, 12815 ) !Zir.Inst.Index { 12816 switch (cond) { 12817 .always => return gz.addAsIndex(.{ 12818 .tag = .restore_err_ret_index_unconditional, 12819 .data = .{ .un_node = .{ 12820 .operand = switch (bt) { 12821 .ret => .none, 12822 .block => |b| b.toRef(), 12823 }, 12824 .src_node = gz.nodeIndexToRelative(src_node), 12825 } }, 12826 }), 12827 .if_non_error => |operand| switch (bt) { 12828 .ret => return gz.addAsIndex(.{ 12829 .tag = .restore_err_ret_index_fn_entry, 12830 .data = .{ .un_node = .{ 12831 .operand = operand, 12832 .src_node = gz.nodeIndexToRelative(src_node), 12833 } }, 12834 }), 12835 .block => |block| return (try gz.addExtendedPayload( 12836 .restore_err_ret_index, 12837 Zir.Inst.RestoreErrRetIndex{ 12838 .src_node = gz.nodeIndexToRelative(src_node), 12839 .block = block.toRef(), 12840 .operand = operand, 12841 }, 12842 )).toIndex().?, 12843 }, 12844 } 12845 } 12846 12847 fn addBreak( 12848 gz: *GenZir, 12849 tag: Zir.Inst.Tag, 12850 block_inst: Zir.Inst.Index, 12851 operand: Zir.Inst.Ref, 12852 ) !Zir.Inst.Index { 12853 const gpa = gz.astgen.gpa; 12854 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12855 12856 const new_index = try gz.makeBreak(tag, block_inst, operand); 12857 gz.instructions.appendAssumeCapacity(new_index); 12858 return new_index; 12859 } 12860 12861 fn makeBreak( 12862 gz: *GenZir, 12863 tag: Zir.Inst.Tag, 12864 block_inst: Zir.Inst.Index, 12865 operand: Zir.Inst.Ref, 12866 ) !Zir.Inst.Index { 12867 return gz.makeBreakCommon(tag, block_inst, operand, null); 12868 } 12869 12870 fn addBreakWithSrcNode( 12871 gz: *GenZir, 12872 tag: Zir.Inst.Tag, 12873 block_inst: Zir.Inst.Index, 12874 operand: Zir.Inst.Ref, 12875 operand_src_node: Ast.Node.Index, 12876 ) !Zir.Inst.Index { 12877 const gpa = gz.astgen.gpa; 12878 try gz.instructions.ensureUnusedCapacity(gpa, 1); 12879 12880 const new_index = try gz.makeBreakWithSrcNode(tag, block_inst, operand, operand_src_node); 12881 gz.instructions.appendAssumeCapacity(new_index); 12882 return new_index; 12883 } 12884 12885 fn makeBreakWithSrcNode( 12886 gz: *GenZir, 12887 tag: Zir.Inst.Tag, 12888 block_inst: Zir.Inst.Index, 12889 operand: Zir.Inst.Ref, 12890 operand_src_node: Ast.Node.Index, 12891 ) !Zir.Inst.Index { 12892 return gz.makeBreakCommon(tag, block_inst, operand, operand_src_node); 12893 } 12894 12895 fn makeBreakCommon( 12896 gz: *GenZir, 12897 tag: Zir.Inst.Tag, 12898 block_inst: Zir.Inst.Index, 12899 operand: Zir.Inst.Ref, 12900 operand_src_node: ?Ast.Node.Index, 12901 ) !Zir.Inst.Index { 12902 const gpa = gz.astgen.gpa; 12903 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 12904 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Break).@"struct".fields.len); 12905 12906 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 12907 gz.astgen.instructions.appendAssumeCapacity(.{ 12908 .tag = tag, 12909 .data = .{ .@"break" = .{ 12910 .operand = operand, 12911 .payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Break{ 12912 .operand_src_node = if (operand_src_node) |src_node| 12913 gz.nodeIndexToRelative(src_node) 12914 else 12915 Zir.Inst.Break.no_src_node, 12916 .block_inst = block_inst, 12917 }), 12918 } }, 12919 }); 12920 return new_index; 12921 } 12922 12923 fn addBin( 12924 gz: *GenZir, 12925 tag: Zir.Inst.Tag, 12926 lhs: Zir.Inst.Ref, 12927 rhs: Zir.Inst.Ref, 12928 ) !Zir.Inst.Ref { 12929 assert(lhs != .none); 12930 assert(rhs != .none); 12931 return gz.add(.{ 12932 .tag = tag, 12933 .data = .{ .bin = .{ 12934 .lhs = lhs, 12935 .rhs = rhs, 12936 } }, 12937 }); 12938 } 12939 12940 fn addDefer(gz: *GenZir, index: u32, len: u32) !void { 12941 _ = try gz.add(.{ 12942 .tag = .@"defer", 12943 .data = .{ .@"defer" = .{ 12944 .index = index, 12945 .len = len, 12946 } }, 12947 }); 12948 } 12949 12950 fn addDecl( 12951 gz: *GenZir, 12952 tag: Zir.Inst.Tag, 12953 decl_index: u32, 12954 src_node: Ast.Node.Index, 12955 ) !Zir.Inst.Ref { 12956 return gz.add(.{ 12957 .tag = tag, 12958 .data = .{ .pl_node = .{ 12959 .src_node = gz.nodeIndexToRelative(src_node), 12960 .payload_index = decl_index, 12961 } }, 12962 }); 12963 } 12964 12965 fn addNode( 12966 gz: *GenZir, 12967 tag: Zir.Inst.Tag, 12968 /// Absolute node index. This function does the conversion to offset from Decl. 12969 src_node: Ast.Node.Index, 12970 ) !Zir.Inst.Ref { 12971 return gz.add(.{ 12972 .tag = tag, 12973 .data = .{ .node = gz.nodeIndexToRelative(src_node) }, 12974 }); 12975 } 12976 12977 fn addInstNode( 12978 gz: *GenZir, 12979 tag: Zir.Inst.Tag, 12980 inst: Zir.Inst.Index, 12981 /// Absolute node index. This function does the conversion to offset from Decl. 12982 src_node: Ast.Node.Index, 12983 ) !Zir.Inst.Ref { 12984 return gz.add(.{ 12985 .tag = tag, 12986 .data = .{ .inst_node = .{ 12987 .inst = inst, 12988 .src_node = gz.nodeIndexToRelative(src_node), 12989 } }, 12990 }); 12991 } 12992 12993 fn addNodeExtended( 12994 gz: *GenZir, 12995 opcode: Zir.Inst.Extended, 12996 /// Absolute node index. This function does the conversion to offset from Decl. 12997 src_node: Ast.Node.Index, 12998 ) !Zir.Inst.Ref { 12999 return gz.add(.{ 13000 .tag = .extended, 13001 .data = .{ .extended = .{ 13002 .opcode = opcode, 13003 .small = undefined, 13004 .operand = @bitCast(gz.nodeIndexToRelative(src_node)), 13005 } }, 13006 }); 13007 } 13008 13009 fn addAllocExtended( 13010 gz: *GenZir, 13011 args: struct { 13012 /// Absolute node index. This function does the conversion to offset from Decl. 13013 node: Ast.Node.Index, 13014 type_inst: Zir.Inst.Ref, 13015 align_inst: Zir.Inst.Ref, 13016 is_const: bool, 13017 is_comptime: bool, 13018 }, 13019 ) !Zir.Inst.Ref { 13020 const astgen = gz.astgen; 13021 const gpa = astgen.gpa; 13022 13023 try gz.instructions.ensureUnusedCapacity(gpa, 1); 13024 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 13025 try astgen.extra.ensureUnusedCapacity( 13026 gpa, 13027 @typeInfo(Zir.Inst.AllocExtended).@"struct".fields.len + 13028 @intFromBool(args.type_inst != .none) + 13029 @intFromBool(args.align_inst != .none), 13030 ); 13031 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.AllocExtended{ 13032 .src_node = gz.nodeIndexToRelative(args.node), 13033 }); 13034 if (args.type_inst != .none) { 13035 astgen.extra.appendAssumeCapacity(@intFromEnum(args.type_inst)); 13036 } 13037 if (args.align_inst != .none) { 13038 astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst)); 13039 } 13040 13041 const has_type: u4 = @intFromBool(args.type_inst != .none); 13042 const has_align: u4 = @intFromBool(args.align_inst != .none); 13043 const is_const: u4 = @intFromBool(args.is_const); 13044 const is_comptime: u4 = @intFromBool(args.is_comptime); 13045 const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3); 13046 13047 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 13048 astgen.instructions.appendAssumeCapacity(.{ 13049 .tag = .extended, 13050 .data = .{ .extended = .{ 13051 .opcode = .alloc, 13052 .small = small, 13053 .operand = payload_index, 13054 } }, 13055 }); 13056 gz.instructions.appendAssumeCapacity(new_index); 13057 return new_index.toRef(); 13058 } 13059 13060 fn addAsm( 13061 gz: *GenZir, 13062 args: struct { 13063 tag: Zir.Inst.Extended, 13064 /// Absolute node index. This function does the conversion to offset from Decl. 13065 node: Ast.Node.Index, 13066 asm_source: Zir.NullTerminatedString, 13067 output_type_bits: u32, 13068 is_volatile: bool, 13069 outputs: []const Zir.Inst.Asm.Output, 13070 inputs: []const Zir.Inst.Asm.Input, 13071 clobbers: []const u32, 13072 }, 13073 ) !Zir.Inst.Ref { 13074 const astgen = gz.astgen; 13075 const gpa = astgen.gpa; 13076 13077 try gz.instructions.ensureUnusedCapacity(gpa, 1); 13078 try astgen.instructions.ensureUnusedCapacity(gpa, 1); 13079 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Asm).@"struct".fields.len + 13080 args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).@"struct".fields.len + 13081 args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).@"struct".fields.len + 13082 args.clobbers.len); 13083 13084 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{ 13085 .src_node = gz.nodeIndexToRelative(args.node), 13086 .asm_source = args.asm_source, 13087 .output_type_bits = args.output_type_bits, 13088 }); 13089 for (args.outputs) |output| { 13090 _ = gz.astgen.addExtraAssumeCapacity(output); 13091 } 13092 for (args.inputs) |input| { 13093 _ = gz.astgen.addExtraAssumeCapacity(input); 13094 } 13095 gz.astgen.extra.appendSliceAssumeCapacity(args.clobbers); 13096 13097 // * 0b00000000_000XXXXX - `outputs_len`. 13098 // * 0b000000XX_XXX00000 - `inputs_len`. 13099 // * 0b0XXXXX00_00000000 - `clobbers_len`. 13100 // * 0bX0000000_00000000 - is volatile 13101 const small: u16 = @as(u16, @intCast(args.outputs.len)) | 13102 @as(u16, @intCast(args.inputs.len << 5)) | 13103 @as(u16, @intCast(args.clobbers.len << 10)) | 13104 (@as(u16, @intFromBool(args.is_volatile)) << 15); 13105 13106 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); 13107 astgen.instructions.appendAssumeCapacity(.{ 13108 .tag = .extended, 13109 .data = .{ .extended = .{ 13110 .opcode = args.tag, 13111 .small = small, 13112 .operand = payload_index, 13113 } }, 13114 }); 13115 gz.instructions.appendAssumeCapacity(new_index); 13116 return new_index.toRef(); 13117 } 13118 13119 /// Note that this returns a `Zir.Inst.Index` not a ref. 13120 /// Does *not* append the block instruction to the scope. 13121 /// Leaves the `payload_index` field undefined. 13122 fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index { 13123 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 13124 const gpa = gz.astgen.gpa; 13125 try gz.astgen.instructions.append(gpa, .{ 13126 .tag = tag, 13127 .data = .{ .pl_node = .{ 13128 .src_node = gz.nodeIndexToRelative(node), 13129 .payload_index = undefined, 13130 } }, 13131 }); 13132 return new_index; 13133 } 13134 13135 /// Note that this returns a `Zir.Inst.Index` not a ref. 13136 /// Does *not* append the block instruction to the scope. 13137 /// Leaves the `payload_index` field undefined. Use `setDeclaration` to finalize. 13138 fn makeDeclaration(gz: *GenZir, node: Ast.Node.Index) !Zir.Inst.Index { 13139 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 13140 try gz.astgen.instructions.append(gz.astgen.gpa, .{ 13141 .tag = .declaration, 13142 .data = .{ .declaration = .{ 13143 .src_node = node, 13144 .payload_index = undefined, 13145 } }, 13146 }); 13147 return new_index; 13148 } 13149 13150 /// Note that this returns a `Zir.Inst.Index` not a ref. 13151 /// Leaves the `payload_index` field undefined. 13152 fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index { 13153 const gpa = gz.astgen.gpa; 13154 try gz.instructions.ensureUnusedCapacity(gpa, 1); 13155 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 13156 try gz.astgen.instructions.append(gpa, .{ 13157 .tag = tag, 13158 .data = .{ .pl_node = .{ 13159 .src_node = gz.nodeIndexToRelative(node), 13160 .payload_index = undefined, 13161 } }, 13162 }); 13163 gz.instructions.appendAssumeCapacity(new_index); 13164 return new_index; 13165 } 13166 13167 fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct { 13168 src_node: Ast.Node.Index, 13169 captures_len: u32, 13170 fields_len: u32, 13171 decls_len: u32, 13172 has_backing_int: bool, 13173 layout: std.builtin.Type.ContainerLayout, 13174 known_non_opv: bool, 13175 known_comptime_only: bool, 13176 is_tuple: bool, 13177 any_comptime_fields: bool, 13178 any_default_inits: bool, 13179 any_aligned_fields: bool, 13180 fields_hash: std.zig.SrcHash, 13181 }) !void { 13182 const astgen = gz.astgen; 13183 const gpa = astgen.gpa; 13184 13185 // Node 0 is valid for the root `struct_decl` of a file! 13186 assert(args.src_node != 0 or gz.parent.tag == .top); 13187 13188 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); 13189 13190 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len + 3); 13191 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.StructDecl{ 13192 .fields_hash_0 = fields_hash_arr[0], 13193 .fields_hash_1 = fields_hash_arr[1], 13194 .fields_hash_2 = fields_hash_arr[2], 13195 .fields_hash_3 = fields_hash_arr[3], 13196 .src_line = astgen.source_line, 13197 .src_node = args.src_node, 13198 }); 13199 13200 if (args.captures_len != 0) { 13201 astgen.extra.appendAssumeCapacity(args.captures_len); 13202 } 13203 if (args.fields_len != 0) { 13204 astgen.extra.appendAssumeCapacity(args.fields_len); 13205 } 13206 if (args.decls_len != 0) { 13207 astgen.extra.appendAssumeCapacity(args.decls_len); 13208 } 13209 astgen.instructions.set(@intFromEnum(inst), .{ 13210 .tag = .extended, 13211 .data = .{ .extended = .{ 13212 .opcode = .struct_decl, 13213 .small = @bitCast(Zir.Inst.StructDecl.Small{ 13214 .has_captures_len = args.captures_len != 0, 13215 .has_fields_len = args.fields_len != 0, 13216 .has_decls_len = args.decls_len != 0, 13217 .has_backing_int = args.has_backing_int, 13218 .known_non_opv = args.known_non_opv, 13219 .known_comptime_only = args.known_comptime_only, 13220 .is_tuple = args.is_tuple, 13221 .name_strategy = gz.anon_name_strategy, 13222 .layout = args.layout, 13223 .any_comptime_fields = args.any_comptime_fields, 13224 .any_default_inits = args.any_default_inits, 13225 .any_aligned_fields = args.any_aligned_fields, 13226 }), 13227 .operand = payload_index, 13228 } }, 13229 }); 13230 } 13231 13232 fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct { 13233 src_node: Ast.Node.Index, 13234 tag_type: Zir.Inst.Ref, 13235 captures_len: u32, 13236 body_len: u32, 13237 fields_len: u32, 13238 decls_len: u32, 13239 layout: std.builtin.Type.ContainerLayout, 13240 auto_enum_tag: bool, 13241 any_aligned_fields: bool, 13242 fields_hash: std.zig.SrcHash, 13243 }) !void { 13244 const astgen = gz.astgen; 13245 const gpa = astgen.gpa; 13246 13247 assert(args.src_node != 0); 13248 13249 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); 13250 13251 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).@"struct".fields.len + 5); 13252 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.UnionDecl{ 13253 .fields_hash_0 = fields_hash_arr[0], 13254 .fields_hash_1 = fields_hash_arr[1], 13255 .fields_hash_2 = fields_hash_arr[2], 13256 .fields_hash_3 = fields_hash_arr[3], 13257 .src_line = astgen.source_line, 13258 .src_node = args.src_node, 13259 }); 13260 13261 if (args.tag_type != .none) { 13262 astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type)); 13263 } 13264 if (args.captures_len != 0) { 13265 astgen.extra.appendAssumeCapacity(args.captures_len); 13266 } 13267 if (args.body_len != 0) { 13268 astgen.extra.appendAssumeCapacity(args.body_len); 13269 } 13270 if (args.fields_len != 0) { 13271 astgen.extra.appendAssumeCapacity(args.fields_len); 13272 } 13273 if (args.decls_len != 0) { 13274 astgen.extra.appendAssumeCapacity(args.decls_len); 13275 } 13276 astgen.instructions.set(@intFromEnum(inst), .{ 13277 .tag = .extended, 13278 .data = .{ .extended = .{ 13279 .opcode = .union_decl, 13280 .small = @bitCast(Zir.Inst.UnionDecl.Small{ 13281 .has_tag_type = args.tag_type != .none, 13282 .has_captures_len = args.captures_len != 0, 13283 .has_body_len = args.body_len != 0, 13284 .has_fields_len = args.fields_len != 0, 13285 .has_decls_len = args.decls_len != 0, 13286 .name_strategy = gz.anon_name_strategy, 13287 .layout = args.layout, 13288 .auto_enum_tag = args.auto_enum_tag, 13289 .any_aligned_fields = args.any_aligned_fields, 13290 }), 13291 .operand = payload_index, 13292 } }, 13293 }); 13294 } 13295 13296 fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct { 13297 src_node: Ast.Node.Index, 13298 tag_type: Zir.Inst.Ref, 13299 captures_len: u32, 13300 body_len: u32, 13301 fields_len: u32, 13302 decls_len: u32, 13303 nonexhaustive: bool, 13304 fields_hash: std.zig.SrcHash, 13305 }) !void { 13306 const astgen = gz.astgen; 13307 const gpa = astgen.gpa; 13308 13309 assert(args.src_node != 0); 13310 13311 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); 13312 13313 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.EnumDecl).@"struct".fields.len + 5); 13314 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.EnumDecl{ 13315 .fields_hash_0 = fields_hash_arr[0], 13316 .fields_hash_1 = fields_hash_arr[1], 13317 .fields_hash_2 = fields_hash_arr[2], 13318 .fields_hash_3 = fields_hash_arr[3], 13319 .src_line = astgen.source_line, 13320 .src_node = args.src_node, 13321 }); 13322 13323 if (args.tag_type != .none) { 13324 astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type)); 13325 } 13326 if (args.captures_len != 0) { 13327 astgen.extra.appendAssumeCapacity(args.captures_len); 13328 } 13329 if (args.body_len != 0) { 13330 astgen.extra.appendAssumeCapacity(args.body_len); 13331 } 13332 if (args.fields_len != 0) { 13333 astgen.extra.appendAssumeCapacity(args.fields_len); 13334 } 13335 if (args.decls_len != 0) { 13336 astgen.extra.appendAssumeCapacity(args.decls_len); 13337 } 13338 astgen.instructions.set(@intFromEnum(inst), .{ 13339 .tag = .extended, 13340 .data = .{ .extended = .{ 13341 .opcode = .enum_decl, 13342 .small = @bitCast(Zir.Inst.EnumDecl.Small{ 13343 .has_tag_type = args.tag_type != .none, 13344 .has_captures_len = args.captures_len != 0, 13345 .has_body_len = args.body_len != 0, 13346 .has_fields_len = args.fields_len != 0, 13347 .has_decls_len = args.decls_len != 0, 13348 .name_strategy = gz.anon_name_strategy, 13349 .nonexhaustive = args.nonexhaustive, 13350 }), 13351 .operand = payload_index, 13352 } }, 13353 }); 13354 } 13355 13356 fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct { 13357 src_node: Ast.Node.Index, 13358 captures_len: u32, 13359 decls_len: u32, 13360 }) !void { 13361 const astgen = gz.astgen; 13362 const gpa = astgen.gpa; 13363 13364 assert(args.src_node != 0); 13365 13366 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).@"struct".fields.len + 2); 13367 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.OpaqueDecl{ 13368 .src_line = astgen.source_line, 13369 .src_node = args.src_node, 13370 }); 13371 13372 if (args.captures_len != 0) { 13373 astgen.extra.appendAssumeCapacity(args.captures_len); 13374 } 13375 if (args.decls_len != 0) { 13376 astgen.extra.appendAssumeCapacity(args.decls_len); 13377 } 13378 astgen.instructions.set(@intFromEnum(inst), .{ 13379 .tag = .extended, 13380 .data = .{ .extended = .{ 13381 .opcode = .opaque_decl, 13382 .small = @bitCast(Zir.Inst.OpaqueDecl.Small{ 13383 .has_captures_len = args.captures_len != 0, 13384 .has_decls_len = args.decls_len != 0, 13385 .name_strategy = gz.anon_name_strategy, 13386 }), 13387 .operand = payload_index, 13388 } }, 13389 }); 13390 } 13391 13392 fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref { 13393 return (try gz.addAsIndex(inst)).toRef(); 13394 } 13395 13396 fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index { 13397 const gpa = gz.astgen.gpa; 13398 try gz.instructions.ensureUnusedCapacity(gpa, 1); 13399 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 13400 13401 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 13402 gz.astgen.instructions.appendAssumeCapacity(inst); 13403 gz.instructions.appendAssumeCapacity(new_index); 13404 return new_index; 13405 } 13406 13407 fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index { 13408 const gpa = gz.astgen.gpa; 13409 try gz.instructions.ensureUnusedCapacity(gpa, 1); 13410 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); 13411 13412 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len); 13413 gz.astgen.instructions.len += 1; 13414 gz.instructions.appendAssumeCapacity(new_index); 13415 return new_index; 13416 } 13417 13418 fn addRet(gz: *GenZir, ri: ResultInfo, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void { 13419 switch (ri.rl) { 13420 .ptr => |ptr_res| _ = try gz.addUnNode(.ret_load, ptr_res.inst, node), 13421 .coerced_ty => _ = try gz.addUnNode(.ret_node, operand, node), 13422 else => unreachable, 13423 } 13424 } 13425 13426 fn addDbgVar(gz: *GenZir, tag: Zir.Inst.Tag, name: Zir.NullTerminatedString, inst: Zir.Inst.Ref) !void { 13427 if (gz.is_comptime) return; 13428 13429 _ = try gz.add(.{ .tag = tag, .data = .{ 13430 .str_op = .{ 13431 .str = name, 13432 .operand = inst, 13433 }, 13434 } }); 13435 } 13436 }; 13437 13438 /// This can only be for short-lived references; the memory becomes invalidated 13439 /// when another string is added. 13440 fn nullTerminatedString(astgen: AstGen, index: Zir.NullTerminatedString) [*:0]const u8 { 13441 return @ptrCast(astgen.string_bytes.items[@intFromEnum(index)..]); 13442 } 13443 13444 /// Local variables shadowing detection, including function parameters. 13445 fn detectLocalShadowing( 13446 astgen: *AstGen, 13447 scope: *Scope, 13448 ident_name: Zir.NullTerminatedString, 13449 name_token: Ast.TokenIndex, 13450 token_bytes: []const u8, 13451 id_cat: Scope.IdCat, 13452 ) !void { 13453 const gpa = astgen.gpa; 13454 if (token_bytes[0] != '@' and isPrimitive(token_bytes)) { 13455 return astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{ 13456 token_bytes, 13457 }, &[_]u32{ 13458 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{ 13459 token_bytes, 13460 }), 13461 }); 13462 } 13463 13464 var s = scope; 13465 var outer_scope = false; 13466 while (true) switch (s.tag) { 13467 .local_val => { 13468 const local_val = s.cast(Scope.LocalVal).?; 13469 if (local_val.name == ident_name) { 13470 const name_slice = mem.span(astgen.nullTerminatedString(ident_name)); 13471 const name = try gpa.dupe(u8, name_slice); 13472 defer gpa.free(name); 13473 if (outer_scope) { 13474 return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{ 13475 @tagName(id_cat), name, @tagName(local_val.id_cat), 13476 }, &[_]u32{ 13477 try astgen.errNoteTok( 13478 local_val.token_src, 13479 "previous declaration here", 13480 .{}, 13481 ), 13482 }); 13483 } 13484 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{ 13485 @tagName(local_val.id_cat), name, 13486 }, &[_]u32{ 13487 try astgen.errNoteTok( 13488 local_val.token_src, 13489 "previous declaration here", 13490 .{}, 13491 ), 13492 }); 13493 } 13494 s = local_val.parent; 13495 }, 13496 .local_ptr => { 13497 const local_ptr = s.cast(Scope.LocalPtr).?; 13498 if (local_ptr.name == ident_name) { 13499 const name_slice = mem.span(astgen.nullTerminatedString(ident_name)); 13500 const name = try gpa.dupe(u8, name_slice); 13501 defer gpa.free(name); 13502 if (outer_scope) { 13503 return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{ 13504 @tagName(id_cat), name, @tagName(local_ptr.id_cat), 13505 }, &[_]u32{ 13506 try astgen.errNoteTok( 13507 local_ptr.token_src, 13508 "previous declaration here", 13509 .{}, 13510 ), 13511 }); 13512 } 13513 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{ 13514 @tagName(local_ptr.id_cat), name, 13515 }, &[_]u32{ 13516 try astgen.errNoteTok( 13517 local_ptr.token_src, 13518 "previous declaration here", 13519 .{}, 13520 ), 13521 }); 13522 } 13523 s = local_ptr.parent; 13524 }, 13525 .namespace => { 13526 outer_scope = true; 13527 const ns = s.cast(Scope.Namespace).?; 13528 const decl_node = ns.decls.get(ident_name) orelse { 13529 s = ns.parent; 13530 continue; 13531 }; 13532 const name_slice = mem.span(astgen.nullTerminatedString(ident_name)); 13533 const name = try gpa.dupe(u8, name_slice); 13534 defer gpa.free(name); 13535 return astgen.failTokNotes(name_token, "{s} shadows declaration of '{s}'", .{ 13536 @tagName(id_cat), name, 13537 }, &[_]u32{ 13538 try astgen.errNoteNode(decl_node, "declared here", .{}), 13539 }); 13540 }, 13541 .gen_zir => { 13542 s = s.cast(GenZir).?.parent; 13543 outer_scope = true; 13544 }, 13545 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, 13546 .top => break, 13547 }; 13548 } 13549 13550 const LineColumn = struct { u32, u32 }; 13551 13552 /// Advances the source cursor to the main token of `node` if not in comptime scope. 13553 /// Usually paired with `emitDbgStmt`. 13554 fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) LineColumn { 13555 if (gz.is_comptime) return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column }; 13556 13557 const tree = gz.astgen.tree; 13558 const token_starts = tree.tokens.items(.start); 13559 const main_tokens = tree.nodes.items(.main_token); 13560 const node_start = token_starts[main_tokens[node]]; 13561 gz.astgen.advanceSourceCursor(node_start); 13562 13563 return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column }; 13564 } 13565 13566 /// Advances the source cursor to the beginning of `node`. 13567 fn advanceSourceCursorToNode(astgen: *AstGen, node: Ast.Node.Index) void { 13568 const tree = astgen.tree; 13569 const token_starts = tree.tokens.items(.start); 13570 const node_start = token_starts[tree.firstToken(node)]; 13571 astgen.advanceSourceCursor(node_start); 13572 } 13573 13574 /// Advances the source cursor to an absolute byte offset `end` in the file. 13575 fn advanceSourceCursor(astgen: *AstGen, end: usize) void { 13576 const source = astgen.tree.source; 13577 var i = astgen.source_offset; 13578 var line = astgen.source_line; 13579 var column = astgen.source_column; 13580 assert(i <= end); 13581 while (i < end) : (i += 1) { 13582 if (source[i] == '\n') { 13583 line += 1; 13584 column = 0; 13585 } else { 13586 column += 1; 13587 } 13588 } 13589 astgen.source_offset = i; 13590 astgen.source_line = line; 13591 astgen.source_column = column; 13592 } 13593 13594 /// Detects name conflicts for decls and fields, and populates `namespace.decls` with all named declarations. 13595 /// Returns the number of declarations in the namespace, including unnamed declarations (e.g. `comptime` decls). 13596 fn scanContainer( 13597 astgen: *AstGen, 13598 namespace: *Scope.Namespace, 13599 members: []const Ast.Node.Index, 13600 container_kind: enum { @"struct", @"union", @"enum", @"opaque" }, 13601 ) !u32 { 13602 const gpa = astgen.gpa; 13603 const tree = astgen.tree; 13604 const node_tags = tree.nodes.items(.tag); 13605 const main_tokens = tree.nodes.items(.main_token); 13606 const token_tags = tree.tokens.items(.tag); 13607 13608 // This type forms a linked list of source tokens declaring the same name. 13609 const NameEntry = struct { 13610 tok: Ast.TokenIndex, 13611 /// Using a linked list here simplifies memory management, and is acceptable since 13612 ///ewntries are only allocated in error situations. The entries are allocated into the 13613 /// AstGen arena. 13614 next: ?*@This(), 13615 }; 13616 13617 // The maps below are allocated into this SFBA to avoid using the GPA for small namespaces. 13618 var sfba_state = std.heap.stackFallback(512, astgen.gpa); 13619 const sfba = sfba_state.get(); 13620 13621 var names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty; 13622 var test_names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty; 13623 var decltest_names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty; 13624 defer { 13625 names.deinit(sfba); 13626 test_names.deinit(sfba); 13627 decltest_names.deinit(sfba); 13628 } 13629 13630 var any_duplicates = false; 13631 var decl_count: u32 = 0; 13632 for (members) |member_node| { 13633 const Kind = enum { decl, field }; 13634 const kind: Kind, const name_token = switch (node_tags[member_node]) { 13635 .container_field_init, 13636 .container_field_align, 13637 .container_field, 13638 => blk: { 13639 var full = tree.fullContainerField(member_node).?; 13640 switch (container_kind) { 13641 .@"struct", .@"opaque" => {}, 13642 .@"union", .@"enum" => full.convertToNonTupleLike(astgen.tree.nodes), 13643 } 13644 if (full.ast.tuple_like) continue; 13645 break :blk .{ .field, full.ast.main_token }; 13646 }, 13647 13648 .global_var_decl, 13649 .local_var_decl, 13650 .simple_var_decl, 13651 .aligned_var_decl, 13652 => blk: { 13653 decl_count += 1; 13654 break :blk .{ .decl, main_tokens[member_node] + 1 }; 13655 }, 13656 13657 .fn_proto_simple, 13658 .fn_proto_multi, 13659 .fn_proto_one, 13660 .fn_proto, 13661 .fn_decl, 13662 => blk: { 13663 decl_count += 1; 13664 const ident = main_tokens[member_node] + 1; 13665 if (token_tags[ident] != .identifier) { 13666 try astgen.appendErrorNode(member_node, "missing function name", .{}); 13667 continue; 13668 } 13669 break :blk .{ .decl, ident }; 13670 }, 13671 13672 .@"comptime", .@"usingnamespace" => { 13673 decl_count += 1; 13674 continue; 13675 }, 13676 13677 .test_decl => { 13678 decl_count += 1; 13679 // We don't want shadowing detection here, and test names work a bit differently, so 13680 // we must do the redeclaration detection ourselves. 13681 const test_name_token = main_tokens[member_node] + 1; 13682 const new_ent: NameEntry = .{ 13683 .tok = test_name_token, 13684 .next = null, 13685 }; 13686 switch (token_tags[test_name_token]) { 13687 else => {}, // unnamed test 13688 .string_literal => { 13689 const name = try astgen.strLitAsString(test_name_token); 13690 const gop = try test_names.getOrPut(sfba, name.index); 13691 if (gop.found_existing) { 13692 var e = gop.value_ptr; 13693 while (e.next) |n| e = n; 13694 e.next = try astgen.arena.create(NameEntry); 13695 e.next.?.* = new_ent; 13696 any_duplicates = true; 13697 } else { 13698 gop.value_ptr.* = new_ent; 13699 } 13700 }, 13701 .identifier => { 13702 const name = try astgen.identAsString(test_name_token); 13703 const gop = try decltest_names.getOrPut(sfba, name); 13704 if (gop.found_existing) { 13705 var e = gop.value_ptr; 13706 while (e.next) |n| e = n; 13707 e.next = try astgen.arena.create(NameEntry); 13708 e.next.?.* = new_ent; 13709 any_duplicates = true; 13710 } else { 13711 gop.value_ptr.* = new_ent; 13712 } 13713 }, 13714 } 13715 continue; 13716 }, 13717 13718 else => unreachable, 13719 }; 13720 13721 const name_str_index = try astgen.identAsString(name_token); 13722 13723 if (kind == .decl) { 13724 // Put the name straight into `decls`, even if there are compile errors. 13725 // This avoids incorrect "undeclared identifier" errors later on. 13726 try namespace.decls.put(gpa, name_str_index, member_node); 13727 } 13728 13729 { 13730 const gop = try names.getOrPut(sfba, name_str_index); 13731 const new_ent: NameEntry = .{ 13732 .tok = name_token, 13733 .next = null, 13734 }; 13735 if (gop.found_existing) { 13736 var e = gop.value_ptr; 13737 while (e.next) |n| e = n; 13738 e.next = try astgen.arena.create(NameEntry); 13739 e.next.?.* = new_ent; 13740 any_duplicates = true; 13741 continue; 13742 } else { 13743 gop.value_ptr.* = new_ent; 13744 } 13745 } 13746 13747 // For fields, we only needed the duplicate check! Decls have some more checks to do, though. 13748 switch (kind) { 13749 .decl => {}, 13750 .field => continue, 13751 } 13752 13753 const token_bytes = astgen.tree.tokenSlice(name_token); 13754 if (token_bytes[0] != '@' and isPrimitive(token_bytes)) { 13755 try astgen.appendErrorTokNotes(name_token, "name shadows primitive '{s}'", .{ 13756 token_bytes, 13757 }, &.{ 13758 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{ 13759 token_bytes, 13760 }), 13761 }); 13762 continue; 13763 } 13764 13765 var s = namespace.parent; 13766 while (true) switch (s.tag) { 13767 .local_val => { 13768 const local_val = s.cast(Scope.LocalVal).?; 13769 if (local_val.name == name_str_index) { 13770 try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{ 13771 token_bytes, @tagName(local_val.id_cat), 13772 }, &.{ 13773 try astgen.errNoteTok( 13774 local_val.token_src, 13775 "previous declaration here", 13776 .{}, 13777 ), 13778 }); 13779 break; 13780 } 13781 s = local_val.parent; 13782 }, 13783 .local_ptr => { 13784 const local_ptr = s.cast(Scope.LocalPtr).?; 13785 if (local_ptr.name == name_str_index) { 13786 try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{ 13787 token_bytes, @tagName(local_ptr.id_cat), 13788 }, &.{ 13789 try astgen.errNoteTok( 13790 local_ptr.token_src, 13791 "previous declaration here", 13792 .{}, 13793 ), 13794 }); 13795 break; 13796 } 13797 s = local_ptr.parent; 13798 }, 13799 .namespace => s = s.cast(Scope.Namespace).?.parent, 13800 .gen_zir => s = s.cast(GenZir).?.parent, 13801 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, 13802 .top => break, 13803 }; 13804 } 13805 13806 if (!any_duplicates) return decl_count; 13807 13808 for (names.keys(), names.values()) |name, first| { 13809 if (first.next == null) continue; 13810 var notes: std.ArrayListUnmanaged(u32) = .empty; 13811 var prev: NameEntry = first; 13812 while (prev.next) |cur| : (prev = cur.*) { 13813 try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate name here", .{})); 13814 } 13815 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)})); 13816 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name))); 13817 try astgen.appendErrorTokNotes(first.tok, "duplicate {s} member name '{s}'", .{ @tagName(container_kind), name_duped }, notes.items); 13818 } 13819 13820 for (test_names.keys(), test_names.values()) |name, first| { 13821 if (first.next == null) continue; 13822 var notes: std.ArrayListUnmanaged(u32) = .empty; 13823 var prev: NameEntry = first; 13824 while (prev.next) |cur| : (prev = cur.*) { 13825 try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate test here", .{})); 13826 } 13827 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)})); 13828 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name))); 13829 try astgen.appendErrorTokNotes(first.tok, "duplicate test name '{s}'", .{name_duped}, notes.items); 13830 } 13831 13832 for (decltest_names.keys(), decltest_names.values()) |name, first| { 13833 if (first.next == null) continue; 13834 var notes: std.ArrayListUnmanaged(u32) = .empty; 13835 var prev: NameEntry = first; 13836 while (prev.next) |cur| : (prev = cur.*) { 13837 try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate decltest here", .{})); 13838 } 13839 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)})); 13840 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name))); 13841 try astgen.appendErrorTokNotes(first.tok, "duplicate decltest '{s}'", .{name_duped}, notes.items); 13842 } 13843 13844 return decl_count; 13845 } 13846 13847 fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool { 13848 const inst = ref.toIndex() orelse return false; 13849 const zir_tags = astgen.instructions.items(.tag); 13850 return switch (zir_tags[@intFromEnum(inst)]) { 13851 .alloc_inferred, 13852 .alloc_inferred_mut, 13853 .alloc_inferred_comptime, 13854 .alloc_inferred_comptime_mut, 13855 => true, 13856 13857 .extended => { 13858 const zir_data = astgen.instructions.items(.data); 13859 if (zir_data[@intFromEnum(inst)].extended.opcode != .alloc) return false; 13860 const small: Zir.Inst.AllocExtended.Small = @bitCast(zir_data[@intFromEnum(inst)].extended.small); 13861 return !small.has_type; 13862 }, 13863 13864 else => false, 13865 }; 13866 } 13867 13868 /// Assumes capacity for body has already been added. Needed capacity taking into 13869 /// account fixups can be found with `countBodyLenAfterFixups`. 13870 fn appendBodyWithFixups(astgen: *AstGen, body: []const Zir.Inst.Index) void { 13871 return appendBodyWithFixupsArrayList(astgen, &astgen.extra, body); 13872 } 13873 13874 fn appendBodyWithFixupsArrayList( 13875 astgen: *AstGen, 13876 list: *std.ArrayListUnmanaged(u32), 13877 body: []const Zir.Inst.Index, 13878 ) void { 13879 for (body) |body_inst| { 13880 appendPossiblyRefdBodyInst(astgen, list, body_inst); 13881 } 13882 } 13883 13884 fn appendPossiblyRefdBodyInst( 13885 astgen: *AstGen, 13886 list: *std.ArrayListUnmanaged(u32), 13887 body_inst: Zir.Inst.Index, 13888 ) void { 13889 list.appendAssumeCapacity(@intFromEnum(body_inst)); 13890 const kv = astgen.ref_table.fetchRemove(body_inst) orelse return; 13891 const ref_inst = kv.value; 13892 return appendPossiblyRefdBodyInst(astgen, list, ref_inst); 13893 } 13894 13895 fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 { 13896 var count = body.len; 13897 for (body) |body_inst| { 13898 var check_inst = body_inst; 13899 while (astgen.ref_table.get(check_inst)) |ref_inst| { 13900 count += 1; 13901 check_inst = ref_inst; 13902 } 13903 } 13904 return @intCast(count); 13905 } 13906 13907 fn emitDbgStmt(gz: *GenZir, lc: LineColumn) !void { 13908 if (gz.is_comptime) return; 13909 if (gz.instructions.items.len > gz.instructions_top) { 13910 const astgen = gz.astgen; 13911 const last = gz.instructions.items[gz.instructions.items.len - 1]; 13912 if (astgen.instructions.items(.tag)[@intFromEnum(last)] == .dbg_stmt) { 13913 astgen.instructions.items(.data)[@intFromEnum(last)].dbg_stmt = .{ 13914 .line = lc[0], 13915 .column = lc[1], 13916 }; 13917 return; 13918 } 13919 } 13920 13921 _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{ 13922 .dbg_stmt = .{ 13923 .line = lc[0], 13924 .column = lc[1], 13925 }, 13926 } }); 13927 } 13928 13929 /// In some cases, Sema expects us to generate a `dbg_stmt` at the instruction 13930 /// *index* directly preceding the next instruction (e.g. if a call is %10, it 13931 /// expects a dbg_stmt at %9). TODO: this logic may allow redundant dbg_stmt 13932 /// instructions; fix up Sema so we don't need it! 13933 fn emitDbgStmtForceCurrentIndex(gz: *GenZir, lc: LineColumn) !void { 13934 const astgen = gz.astgen; 13935 if (gz.instructions.items.len > gz.instructions_top and 13936 @intFromEnum(gz.instructions.items[gz.instructions.items.len - 1]) == astgen.instructions.len - 1) 13937 { 13938 const last = astgen.instructions.len - 1; 13939 if (astgen.instructions.items(.tag)[last] == .dbg_stmt) { 13940 astgen.instructions.items(.data)[last].dbg_stmt = .{ 13941 .line = lc[0], 13942 .column = lc[1], 13943 }; 13944 return; 13945 } 13946 } 13947 13948 _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{ 13949 .dbg_stmt = .{ 13950 .line = lc[0], 13951 .column = lc[1], 13952 }, 13953 } }); 13954 } 13955 13956 fn lowerAstErrors(astgen: *AstGen) !void { 13957 const tree = astgen.tree; 13958 assert(tree.errors.len > 0); 13959 13960 const gpa = astgen.gpa; 13961 const parse_err = tree.errors[0]; 13962 13963 var msg: std.ArrayListUnmanaged(u8) = .empty; 13964 defer msg.deinit(gpa); 13965 13966 var notes: std.ArrayListUnmanaged(u32) = .empty; 13967 defer notes.deinit(gpa); 13968 13969 for (tree.errors[1..]) |note| { 13970 if (!note.is_note) break; 13971 13972 msg.clearRetainingCapacity(); 13973 try tree.renderError(note, msg.writer(gpa)); 13974 try notes.append(gpa, try astgen.errNoteTok(note.token, "{s}", .{msg.items})); 13975 } 13976 13977 const extra_offset = tree.errorOffset(parse_err); 13978 msg.clearRetainingCapacity(); 13979 try tree.renderError(parse_err, msg.writer(gpa)); 13980 try astgen.appendErrorTokNotesOff(parse_err.token, extra_offset, "{s}", .{msg.items}, notes.items); 13981 } 13982 13983 const DeclarationName = union(enum) { 13984 named: Ast.TokenIndex, 13985 named_test: Ast.TokenIndex, 13986 unnamed_test, 13987 decltest: Zir.NullTerminatedString, 13988 @"comptime", 13989 @"usingnamespace", 13990 }; 13991 13992 /// Sets all extra data for a `declaration` instruction. 13993 /// Unstacks `value_gz`, `align_gz`, `linksection_gz`, and `addrspace_gz`. 13994 fn setDeclaration( 13995 decl_inst: Zir.Inst.Index, 13996 src_hash: std.zig.SrcHash, 13997 name: DeclarationName, 13998 src_line: u32, 13999 is_pub: bool, 14000 is_export: bool, 14001 doc_comment: Zir.NullTerminatedString, 14002 value_gz: *GenZir, 14003 /// May be `null` if all these blocks would be empty. 14004 /// If `null`, then `value_gz` must have nothing stacked on it. 14005 extra_gzs: ?struct { 14006 /// Must be stacked on `value_gz`. 14007 align_gz: *GenZir, 14008 /// Must be stacked on `align_gz`. 14009 linksection_gz: *GenZir, 14010 /// Must be stacked on `linksection_gz`, and have nothing stacked on it. 14011 addrspace_gz: *GenZir, 14012 }, 14013 ) !void { 14014 const astgen = value_gz.astgen; 14015 const gpa = astgen.gpa; 14016 14017 const empty_body: []Zir.Inst.Index = &.{}; 14018 const value_body, const align_body, const linksection_body, const addrspace_body = if (extra_gzs) |e| .{ 14019 value_gz.instructionsSliceUpto(e.align_gz), 14020 e.align_gz.instructionsSliceUpto(e.linksection_gz), 14021 e.linksection_gz.instructionsSliceUpto(e.addrspace_gz), 14022 e.addrspace_gz.instructionsSlice(), 14023 } else .{ value_gz.instructionsSlice(), empty_body, empty_body, empty_body }; 14024 14025 const value_len = astgen.countBodyLenAfterFixups(value_body); 14026 const align_len = astgen.countBodyLenAfterFixups(align_body); 14027 const linksection_len = astgen.countBodyLenAfterFixups(linksection_body); 14028 const addrspace_len = astgen.countBodyLenAfterFixups(addrspace_body); 14029 14030 const true_doc_comment: Zir.NullTerminatedString = switch (name) { 14031 .decltest => |test_name| test_name, 14032 else => doc_comment, 14033 }; 14034 14035 const src_hash_arr: [4]u32 = @bitCast(src_hash); 14036 14037 const extra: Zir.Inst.Declaration = .{ 14038 .src_hash_0 = src_hash_arr[0], 14039 .src_hash_1 = src_hash_arr[1], 14040 .src_hash_2 = src_hash_arr[2], 14041 .src_hash_3 = src_hash_arr[3], 14042 .name = switch (name) { 14043 .named => |tok| @enumFromInt(@intFromEnum(try astgen.identAsString(tok))), 14044 .named_test => |tok| @enumFromInt(@intFromEnum(try astgen.testNameString(tok))), 14045 .unnamed_test => .unnamed_test, 14046 .decltest => .decltest, 14047 .@"comptime" => .@"comptime", 14048 .@"usingnamespace" => .@"usingnamespace", 14049 }, 14050 .src_line = src_line, 14051 .flags = .{ 14052 .value_body_len = @intCast(value_len), 14053 .is_pub = is_pub, 14054 .is_export = is_export, 14055 .has_doc_comment = true_doc_comment != .empty, 14056 .has_align_linksection_addrspace = align_len != 0 or linksection_len != 0 or addrspace_len != 0, 14057 }, 14058 }; 14059 astgen.instructions.items(.data)[@intFromEnum(decl_inst)].declaration.payload_index = try astgen.addExtra(extra); 14060 if (extra.flags.has_doc_comment) { 14061 try astgen.extra.append(gpa, @intFromEnum(true_doc_comment)); 14062 } 14063 if (extra.flags.has_align_linksection_addrspace) { 14064 try astgen.extra.appendSlice(gpa, &.{ 14065 align_len, 14066 linksection_len, 14067 addrspace_len, 14068 }); 14069 } 14070 try astgen.extra.ensureUnusedCapacity(gpa, value_len + align_len + linksection_len + addrspace_len); 14071 astgen.appendBodyWithFixups(value_body); 14072 if (extra.flags.has_align_linksection_addrspace) { 14073 astgen.appendBodyWithFixups(align_body); 14074 astgen.appendBodyWithFixups(linksection_body); 14075 astgen.appendBodyWithFixups(addrspace_body); 14076 } 14077 14078 if (extra_gzs) |e| { 14079 e.addrspace_gz.unstack(); 14080 e.linksection_gz.unstack(); 14081 e.align_gz.unstack(); 14082 } 14083 value_gz.unstack(); 14084 }