From ac77c23aff39272f99369c247ded29d960122ecd Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Sun, 27 Sep 2026 14:57:00 +0100 Subject: [PATCH] Zir: prevent AST node offsets of field and backing type expressions changing We represent AST nodes in ZIR as relative to a "baseline" node so that source locations within unchanged declarations/types do not get invalidated by incremental updates. However, we were missing a bit of granualarity in container types: because container types have declarations, but type resolution is only re-run if the fields change, there is no single baseline node in a type declaration relative to which the fields' AST nodes do not change. Instead, we need to track a few different baseline nodes, and put the right one in the `LazySrcLoc`. --- lib/std/zig/AstGen.zig | 102 ++++++++--- lib/std/zig/Zir.zig | 67 +++++-- src/Sema.zig | 158 +++++++++-------- src/Sema/LowerZon.zig | 106 ++++++------ src/Sema/type_resolution.zig | 77 +++++++-- src/Type.zig | 15 +- src/Zcu.zig | 163 ++++++++++++------ src/Zcu/PerThread.zig | 24 ++- src/crash_report.zig | 9 +- .../add_decls_around_bad_field_expr | 40 +++++ 10 files changed, 511 insertions(+), 250 deletions(-) create mode 100644 test/incremental/add_decls_around_bad_field_expr diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 3e66c8747d02b708de072d507eef08ed8c203357..0b98fcf2b92d6b3f80e187f54f8060343fd8e24e 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -176,7 +176,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { var gen_scope: GenZir = .{ .is_comptime = true, .parent = &top_scope.base, - .decl_node_index = .root, + .src_baseline = .root, .decl_line = 0, .astgen = &astgen, .instructions = &gz_instructions, @@ -4048,7 +4048,7 @@ fn fnDecl( var type_gz: GenZir = .{ .is_comptime = true, - .decl_node_index = fn_proto.ast.proto_node, + .src_baseline = fn_proto.ast.proto_node, .decl_line = astgen.source_line, .parent = scope, .astgen = astgen, @@ -4314,7 +4314,7 @@ fn fnDeclInner( var body_gz: GenZir = .{ .is_comptime = false, - .decl_node_index = fn_proto.ast.proto_node, + .src_baseline = fn_proto.ast.proto_node, .decl_line = decl_gz.decl_line, .parent = params_scope, .astgen = astgen, @@ -4468,7 +4468,7 @@ fn globalVarDecl( var type_gz: GenZir = .{ .parent = scope, - .decl_node_index = node, + .src_baseline = node, .decl_line = astgen.source_line, .astgen = astgen, .is_comptime = true, @@ -4570,7 +4570,7 @@ fn comptimeDecl( var comptime_gz: GenZir = .{ .is_comptime = true, - .decl_node_index = node, + .src_baseline = node, .decl_line = astgen.source_line, .parent = scope, .astgen = astgen, @@ -4634,7 +4634,7 @@ fn testDecl( var decl_block: GenZir = .{ .is_comptime = true, - .decl_node_index = node, + .src_baseline = node, .decl_line = astgen.source_line, .parent = scope, .astgen = astgen, @@ -4732,7 +4732,7 @@ fn testDecl( var fn_block: GenZir = .{ .is_comptime = false, - .decl_node_index = node, + .src_baseline = node, .decl_line = decl_block.decl_line, .parent = &decl_block.base, .astgen = astgen, @@ -4857,6 +4857,8 @@ fn structDeclInner( .src_column = src_column, .src_node = node, .name_strat = name_strat, + .arg_baseline_src_node = .none, + .fields_baseline_src_node = .none, .layout = layout, .backing_int_type_body_len = null, .decls_len = 0, @@ -4886,7 +4888,7 @@ fn structDeclInner( // can refer to decls within the struct itself. var block_scope: GenZir = .{ .parent = &namespace.base, - .decl_node_index = node, + .src_baseline = node, .decl_line = gz.decl_line, .astgen = astgen, .is_comptime = true, @@ -4933,6 +4935,7 @@ fn structDeclInner( break :len body_len; } else null; + var fields_src_baseline: Ast.Node.OptionalIndex = .none; var next_field_idx: u32 = 0; for (container_decl.ast.members) |member_node| { var member = switch (try containerMember(&block_scope, &namespace.base, &wip_decls, member_node)) { @@ -4942,6 +4945,13 @@ fn structDeclInner( const field_idx = next_field_idx; next_field_idx += 1; + if (field_idx == 0) { + assert(fields_src_baseline == .none); + fields_src_baseline = member_node.toOptional(); + } + block_scope.src_baseline = fields_src_baseline.unwrap().?; + defer block_scope.src_baseline = node; + astgen.src_hasher.update(tree.getNodeSource(member_node)); member.convertToNonTupleLike(astgen.tree); @@ -5013,6 +5023,8 @@ fn structDeclInner( .src_column = src_column, .src_node = node, .name_strat = name_strat, + .arg_baseline_src_node = maybe_backing_int_node, + .fields_baseline_src_node = fields_src_baseline, .layout = layout, .backing_int_type_body_len = backing_int_type_body_len, .decls_len = scan_result.decls_len, @@ -5176,7 +5188,7 @@ fn unionDeclInner( // can refer to decls within the union itself. var block_scope: GenZir = .{ .parent = &namespace.base, - .decl_node_index = node, + .src_baseline = node, .decl_line = gz.decl_line, .astgen = astgen, .is_comptime = true, @@ -5213,6 +5225,7 @@ fn unionDeclInner( break :len body_len; } else null; + var fields_src_baseline: Ast.Node.OptionalIndex = .none; var next_field_idx: u32 = 0; for (members) |member_node| { var member = switch (try containerMember(&block_scope, &namespace.base, &wip_decls, member_node)) { @@ -5222,6 +5235,13 @@ fn unionDeclInner( const field_idx = next_field_idx; next_field_idx += 1; + if (field_idx == 0) { + assert(fields_src_baseline == .none); + fields_src_baseline = member_node.toOptional(); + } + block_scope.src_baseline = fields_src_baseline.unwrap().?; + defer block_scope.src_baseline = node; + astgen.src_hasher.update(astgen.tree.getNodeSource(member_node)); member.convertToNonTupleLike(astgen.tree); if (member.ast.tuple_like) { @@ -5298,6 +5318,8 @@ fn unionDeclInner( .src_column = src_column, .src_node = node, .name_strat = name_strat, + .arg_baseline_src_node = opt_arg_node, + .fields_baseline_src_node = fields_src_baseline, .kind = switch (layout) { .auto => if (auto_enum_tok == null) l: { break :l if (opt_arg_node == .none) .auto else .tagged_explicit; @@ -5386,7 +5408,7 @@ fn containerDecl( // are in scope, so that tag values can refer to decls within the enum itself. var block_scope: GenZir = .{ .parent = &namespace.base, - .decl_node_index = node, + .src_baseline = node, .decl_line = gz.decl_line, .astgen = astgen, .is_comptime = true, @@ -5424,6 +5446,7 @@ fn containerDecl( } else null; var next_field_idx: u32 = 0; + var fields_src_baseline: Ast.Node.OptionalIndex = .none; var opt_nonexhaustive_node: Ast.Node.OptionalIndex = .none; for (container_decl.ast.members) |member_node| { var member = switch (try containerMember(&block_scope, &namespace.base, &wip_decls, member_node)) { @@ -5466,6 +5489,13 @@ fn containerDecl( const field_idx = next_field_idx; next_field_idx += 1; + if (field_idx == 0) { + assert(fields_src_baseline == .none); + fields_src_baseline = member_node.toOptional(); + } + block_scope.src_baseline = fields_src_baseline.unwrap().?; + defer block_scope.src_baseline = node; + astgen.src_hasher.update(tree.getNodeSource(member_node)); field_names.get(astgen)[field_idx] = @backingInt(try astgen.identAsString(member.ast.main_token)); @@ -5500,6 +5530,8 @@ fn containerDecl( .src_column = src_column, .src_node = node, .name_strat = name_strat, + .arg_baseline_src_node = container_decl.ast.arg, + .fields_baseline_src_node = fields_src_baseline, .tag_type_body_len = tag_type_body_len, .nonexhaustive = scan_result.has_underscore_field, .decls_len = scan_result.decls_len, @@ -5534,7 +5566,7 @@ fn containerDecl( var block_scope: GenZir = .{ .parent = &namespace.base, - .decl_node_index = node, + .src_baseline = node, .decl_line = gz.decl_line, .astgen = astgen, .is_comptime = true, @@ -11252,7 +11284,7 @@ const GenZir = struct { /// exits from this block should use `break_inline` rather than `break`. is_inline: bool = false, /// The containing decl AST node. - decl_node_index: Ast.Node.Index, + src_baseline: Ast.Node.Index, /// The containing decl line index, absolute. decl_line: u32, /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. @@ -11338,7 +11370,7 @@ const GenZir = struct { return .{ .is_comptime = gz.is_comptime, .is_typeof = gz.is_typeof, - .decl_node_index = gz.decl_node_index, + .src_baseline = gz.src_baseline, .decl_line = gz.decl_line, .parent = scope, .astgen = gz.astgen, @@ -11374,15 +11406,11 @@ const GenZir = struct { } fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) Ast.Node.Offset { - return gz.decl_node_index.toOffset(node_index); + return gz.src_baseline.toOffset(node_index); } fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) Ast.TokenOffset { - return .init(gz.srcToken(), token); - } - - fn srcToken(gz: GenZir) Ast.TokenIndex { - return gz.astgen.tree.firstToken(gz.decl_node_index); + return .init(gz.astgen.tree.firstToken(gz.src_baseline), token); } fn setBreakResultInfo(gz: *GenZir, parent_ri: AstGen.ResultInfo) void { @@ -12420,6 +12448,8 @@ const GenZir = struct { src_column: u32, src_node: Ast.Node.Index, name_strat: Zir.Inst.NameStrategy, + arg_baseline_src_node: Ast.Node.OptionalIndex, + fields_baseline_src_node: Ast.Node.OptionalIndex, layout: std.lang.Type.ContainerLayout, backing_int_type_body_len: ?u32, decls_len: u32, @@ -12445,7 +12475,9 @@ const GenZir = struct { const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.StructDecl).@"struct".field_names.len + - 4 + // `captures_len`, `decls_len`, `fields_len`, `backing_int_type_body_len` + 3 + // `captures_len`, `decls_len`, `fields_len` + 2 + // `arg_baseline_src_node`, `fields_baseline_src_node` + 1 + // `backing_int_type_body_len` captures_len * 2 + // `capture`, `capture_name` args.remaining.len); @@ -12462,6 +12494,12 @@ const GenZir = struct { if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len); if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len); if (args.fields_len != 0) astgen.extra.appendAssumeCapacity(args.fields_len); + if (args.backing_int_type_body_len != null) astgen.extra.appendAssumeCapacity( + @backingInt(args.arg_baseline_src_node.unwrap().?), + ); + if (args.fields_len != 0) astgen.extra.appendAssumeCapacity( + @backingInt(args.fields_baseline_src_node.unwrap().?), + ); if (args.backing_int_type_body_len) |n| astgen.extra.appendAssumeCapacity(n); astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.captures)); astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.capture_names)); @@ -12492,6 +12530,8 @@ const GenZir = struct { src_column: u32, src_node: Ast.Node.Index, name_strat: Zir.Inst.NameStrategy, + arg_baseline_src_node: Ast.Node.OptionalIndex, + fields_baseline_src_node: Ast.Node.OptionalIndex, kind: Zir.Inst.UnionDecl.Kind, arg_type_body_len: ?u32, decls_len: u32, @@ -12515,7 +12555,9 @@ const GenZir = struct { const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).@"struct".field_names.len + - 4 + // `captures_len`, `decls_len`, `fields_len`, `arg_type_body_len` + 3 + // `captures_len`, `decls_len`, `fields_len` + 2 + // `arg_baseline_src_node`, `fields_baseline_src_node` + 1 + // `arg_type_body_len` captures_len * 2 + // `capture`, `capture_name` args.remaining.len); @@ -12532,6 +12574,12 @@ const GenZir = struct { if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len); if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len); if (args.fields_len != 0) astgen.extra.appendAssumeCapacity(args.fields_len); + if (args.kind.hasArgType()) astgen.extra.appendAssumeCapacity( + @backingInt(args.arg_baseline_src_node.unwrap().?), + ); + if (args.fields_len != 0) astgen.extra.appendAssumeCapacity( + @backingInt(args.fields_baseline_src_node.unwrap().?), + ); if (args.kind.hasArgType()) { astgen.extra.appendAssumeCapacity(args.arg_type_body_len.?); } else { @@ -12564,6 +12612,8 @@ const GenZir = struct { src_column: u32, src_node: Ast.Node.Index, name_strat: Zir.Inst.NameStrategy, + arg_baseline_src_node: Ast.Node.OptionalIndex, + fields_baseline_src_node: Ast.Node.OptionalIndex, tag_type_body_len: ?u32, nonexhaustive: bool, decls_len: u32, @@ -12586,7 +12636,9 @@ const GenZir = struct { const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash); try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.EnumDecl).@"struct".field_names.len + - 4 + // `captures_len`, `decls_len`, `fields_len`, `tag_type_body_len` + 3 + // `captures_len`, `decls_len`, `fields_len` + 2 + // `arg_baseline_src_node`, `fields_baseline_src_node` + 1 + // `tag_type_body_len` captures_len * 2 + // `capture`, `capture_name` args.remaining.len); @@ -12603,6 +12655,12 @@ const GenZir = struct { if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len); if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len); if (args.fields_len != 0) astgen.extra.appendAssumeCapacity(args.fields_len); + if (args.tag_type_body_len != null) astgen.extra.appendAssumeCapacity( + @backingInt(args.arg_baseline_src_node.unwrap().?), + ); + if (args.fields_len != 0) astgen.extra.appendAssumeCapacity( + @backingInt(args.fields_baseline_src_node.unwrap().?), + ); if (args.tag_type_body_len) |n| astgen.extra.appendAssumeCapacity(n); astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.captures)); astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.capture_names)); diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index e506d768c93851e8da5db298649a80f199e41834..17874289d8ed6b38f65309b86bbd0cddea8a8aee 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -3493,18 +3493,20 @@ pub const Inst = struct { /// 0. captures_len: u32 // if `has_captures_len` /// 1. decls_len: u32, // if `has_decls_len` /// 2. fields_len: u32, // if `has_fields_len` - /// 3. backing_int_body_len: u32 // if `has_backing_int` - /// 4. capture: Capture // for every `captures_len` - /// 5. capture_name: NullTerminatedString // for every `captures_len` - /// 6. decl: Index, // for every `decls_len`; points to a `declaration` instruction - /// 7. field_name: NullTerminatedString // for every `fields_len` - /// 8. field_type_body_len: u32 // for every `fields_len` - /// 9. field_align_body_len: u32 // for every `fields_len` if `any_field_aligns` - /// 10. field_default_body_len: u32 // for every `fields_len` if `any_field_defaults` - /// 11. field_comptime_bits: u32 // one bit per `fields_len` if `any_comptime_fields` + /// 3. arg_baseline_src_node: Ast.Node.Index // if `has_backing_int` + /// 4. fields_baseline_src_node: Ast.Node.Index // if `has_fields_len` + /// 5. backing_int_body_len: u32 // if `has_backing_int` + /// 6. capture: Capture // for every `captures_len` + /// 7. capture_name: NullTerminatedString // for every `captures_len` + /// 8. decl: Index, // for every `decls_len`; points to a `declaration` instruction + /// 9. field_name: NullTerminatedString // for every `fields_len` + /// 10. field_type_body_len: u32 // for every `fields_len` + /// 11. field_align_body_len: u32 // for every `fields_len` if `any_field_aligns` + /// 12. field_default_body_len: u32 // for every `fields_len` if `any_field_defaults` + /// 13. field_comptime_bits: u32 // one bit per `fields_len` if `any_comptime_fields` /// // LSB is first field, minimum number of `u32` needed - /// 12. backing_int_body_inst: Inst.Index // for each `backing_int_body_len` - /// 13. body_inst: Inst.Index // type body, then align body, then default body, for each field + /// 14. backing_int_body_inst: Inst.Index // for each `backing_int_body_len` + /// 15. body_inst: Inst.Index // type body, then align body, then default body, for each field pub const StructDecl = struct { // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`. // This hash contains the source of all fields, and any specified attributes (`extern`, backing type, etc). @@ -5227,12 +5229,12 @@ pub fn assertTrackable(zir: Zir, inst_idx: Zir.Inst.Index) void { .struct_init_anon, => {}, // tracked in order, as the owner instructions of anonymous struct types .func, .func_inferred => { - // These are tracked provided they are actual function declarations, not just bodies. + // These are tracked if they are actual function declarations rather than prototypes. const extra = zir.extraData(Inst.Func, inst.data.pl_node.payload_index); assert(extra.data.body_len != 0); }, .func_fancy => { - // These are tracked provided they are actual function declarations, not just bodies. + // These are tracked if they are actual function declarations rather than prototypes. const extra = zir.extraData(Inst.FuncFancy, inst.data.pl_node.payload_index); assert(extra.data.body_len != 0); }, @@ -5287,6 +5289,14 @@ pub fn getStructDecl(zir: *const Zir, struct_decl: Inst.Index) UnwrappedStructDe extra_index += 1; break :blk fields_len; } else 0; + const arg_baseline_src_node: Ast.Node.Index = if (small.has_backing_int_type) node: { + defer extra_index += 1; + break :node @fromBackingInt(zir.extra[extra_index]); + } else extra.data.src_node; + const fields_baseline_src_node: Ast.Node.Index = if (small.has_fields_len) node: { + defer extra_index += 1; + break :node @fromBackingInt(zir.extra[extra_index]); + } else extra.data.src_node; const backing_int_type_body_len: u32 = if (small.has_backing_int_type) len: { const body_len = zir.extra[extra_index]; extra_index += 1; @@ -5329,6 +5339,8 @@ pub fn getStructDecl(zir: *const Zir, struct_decl: Inst.Index) UnwrappedStructDe .src_column = extra.data.src_column, .src_node = extra.data.src_node, .name_strategy = small.name_strategy, + .arg_baseline_src_node = arg_baseline_src_node, + .fields_baseline_src_node = fields_baseline_src_node, .captures = captures, .capture_names = capture_names, .decls = decls, @@ -5348,6 +5360,9 @@ pub const UnwrappedStructDecl = struct { src_node: Ast.Node.Index, name_strategy: Inst.NameStrategy, + arg_baseline_src_node: Ast.Node.Index, + fields_baseline_src_node: Ast.Node.Index, + captures: []const Inst.Capture, capture_names: []const NullTerminatedString, @@ -5440,6 +5455,14 @@ pub fn getUnionDecl(zir: *const Zir, union_decl: Inst.Index) UnwrappedUnionDecl extra_index += 1; break :blk fields_len; } else 0; + const arg_baseline_src_node: Ast.Node.Index = if (small.kind.hasArgType()) node: { + defer extra_index += 1; + break :node @fromBackingInt(zir.extra[extra_index]); + } else extra.data.src_node; + const fields_baseline_src_node: Ast.Node.Index = if (small.has_fields_len) node: { + defer extra_index += 1; + break :node @fromBackingInt(zir.extra[extra_index]); + } else extra.data.src_node; const arg_type_body_len: u32 = if (small.kind.hasArgType()) len: { const body_len = zir.extra[extra_index]; extra_index += 1; @@ -5476,6 +5499,8 @@ pub fn getUnionDecl(zir: *const Zir, union_decl: Inst.Index) UnwrappedUnionDecl .src_column = extra.data.src_column, .src_node = extra.data.src_node, .name_strategy = small.name_strategy, + .arg_baseline_src_node = arg_baseline_src_node, + .fields_baseline_src_node = fields_baseline_src_node, .captures = captures, .capture_names = capture_names, .decls = decls, @@ -5494,6 +5519,9 @@ pub const UnwrappedUnionDecl = struct { src_node: Ast.Node.Index, name_strategy: Inst.NameStrategy, + arg_baseline_src_node: Ast.Node.Index, + fields_baseline_src_node: Ast.Node.Index, + captures: []const Inst.Capture, capture_names: []const NullTerminatedString, @@ -5576,6 +5604,14 @@ pub fn getEnumDecl(zir: *const Zir, enum_decl: Inst.Index) UnwrappedEnumDecl { extra_index += 1; break :blk fields_len; } else 0; + const arg_baseline_src_node: Ast.Node.Index = if (small.has_tag_type) node: { + defer extra_index += 1; + break :node @fromBackingInt(zir.extra[extra_index]); + } else extra.data.src_node; + const fields_baseline_src_node: Ast.Node.Index = if (small.has_fields_len) node: { + defer extra_index += 1; + break :node @fromBackingInt(zir.extra[extra_index]); + } else extra.data.src_node; const tag_type_body_len: u32 = if (small.has_tag_type) len: { const body_len = zir.extra[extra_index]; extra_index += 1; @@ -5605,6 +5641,8 @@ pub fn getEnumDecl(zir: *const Zir, enum_decl: Inst.Index) UnwrappedEnumDecl { .src_column = extra.data.src_column, .src_node = extra.data.src_node, .name_strategy = small.name_strategy, + .arg_baseline_src_node = arg_baseline_src_node, + .fields_baseline_src_node = fields_baseline_src_node, .captures = captures, .capture_names = capture_names, .decls = decls, @@ -5621,6 +5659,9 @@ pub const UnwrappedEnumDecl = struct { src_node: Ast.Node.Index, name_strategy: Inst.NameStrategy, + arg_baseline_src_node: Ast.Node.Index, + fields_baseline_src_node: Ast.Node.Index, + captures: []const Inst.Capture, capture_names: []const NullTerminatedString, diff --git a/src/Sema.zig b/src/Sema.zig index e209f453cae9ceb95b5d222fb281d5e3bff7da2e..432bd454709edf302f1659826b2032391e324e5e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -395,8 +395,8 @@ pub const Block = struct { need_debug_scope: ?*bool = null, /// Relative source locations encountered while traversing this block should be - /// treated as relative to the AST node of this ZIR instruction. - src_base_inst: InternPool.TrackedInst.Index, + /// treated as relative to this baseline AST node. + src_baseline: Zcu.LazySrcLoc.Baseline, /// The name of the current "context" for naming namespace types. /// The interpretation of this depends on the name strategy in ZIR, but the name @@ -406,10 +406,10 @@ pub const Block = struct { type_fqn_ctx: InternPool.NullTerminatedString, /// Create a `LazySrcLoc` based on an `Offset` from the code being analyzed in this block. - /// Specifically, the given `Offset` is treated as relative to `block.src_base_inst`. + /// Specifically, the given `Offset` is treated as relative to `block.src_baseline`. pub fn src(block: Block, offset: LazySrcLoc.Offset) LazySrcLoc { return .{ - .base_node_inst = block.src_base_inst, + .baseline = block.src_baseline, .offset = offset, }; } @@ -530,7 +530,7 @@ pub const Block = struct { .float_mode = parent.float_mode, .error_return_trace_index = parent.error_return_trace_index, .need_debug_scope = parent.need_debug_scope, - .src_base_inst = parent.src_base_inst, + .src_baseline = parent.src_baseline, .type_name_ctx = parent.type_name_ctx, .type_fqn_ctx = parent.type_fqn_ctx, }; @@ -1110,14 +1110,17 @@ fn analyzeBodyInner( block: *Block, body: []const Zir.Inst.Index, ) CompileError!void { - try sema.inst_map.ensureSpaceForInstructions(sema.gpa, body); - const pt = sema.pt; const zcu = pt.zcu; + const comp = zcu.comp; + const gpa = comp.gpa; + const map = &sema.inst_map; const tags = sema.code.instructions.items(.tag); const datas = sema.code.instructions.items(.data); + try map.ensureSpaceForInstructions(gpa, body); + var crash_info: crash_report.AnalyzeBody = undefined; crash_info.push(sema, block, body); defer crash_info.pop(); @@ -1130,13 +1133,10 @@ fn analyzeBodyInner( crash_info.setBodyIndex(i); const inst = body[i]; - // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away. + // This log is a bit expensive, and LLVM fails to optimize it away. if (build_options.enable_logging) { - std.log.scoped(.sema_zir).debug("sema ZIR {f} %{d}", .{ path: { - const file_index = block.src_base_inst.resolveFile(&zcu.intern_pool); - const file = zcu.fileByIndex(file_index); - break :path file.path.fmt(zcu.comp); - }, inst }); + const path = zcu.namespacePtr(block.namespace).fileScope(zcu).path; + std.log.scoped(.sema_zir).debug("sema ZIR {f} %{d}", .{ path.fmt(comp), inst }); } const air_ref: Air.Inst.Ref = inst: switch (tags[@backingInt(inst)]) { @@ -1757,7 +1757,6 @@ fn analyzeBodyInner( const inst_data = datas[@backingInt(inst)].pl_node; const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); - const gpa = sema.gpa; const BreakResult = struct { block_inst: Zir.Inst.Index, @@ -1902,7 +1901,7 @@ fn analyzeBodyInner( if (err_union_ty.zigTypeTag(zcu) != .error_union) { return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); + errdefer msg.destroy(gpa); try sema.addDeclaredHereNote(msg, err_union_ty); try sema.errNote(operand_src, msg, "consider omitting 'try'", .{}); break :msg msg; @@ -2511,7 +2510,7 @@ fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazyS const struct_type = zcu.typeToStruct(container_ty) orelse break :msg msg; try sema.errNote(.{ - .base_node_inst = struct_type.zir_index, + .baseline = .{ .inst = struct_type.zir_index, .node = .main }, .offset = .{ .container_field_value = @intCast(field_index) }, }, msg, "default value set here", .{}); break :msg msg; @@ -2633,7 +2632,7 @@ fn addFieldErrNote( @branchHint(.cold); const type_src = container_ty.srcLocOrNull(sema.pt.zcu) orelse return; const field_src: LazySrcLoc = .{ - .base_node_inst = type_src.base_node_inst, + .baseline = type_src.baseline, .offset = .{ .container_field_name = @intCast(field_index) }, }; try sema.errNote(field_src, parent, format, args); @@ -5091,7 +5090,7 @@ fn zirCompileLog( gop.value_ptr.last_line = line_idx; } else { gop.value_ptr.* = .{ - .base_node_inst = block.src_base_inst, + .baseline = block.src_baseline, .node_offset = src_node, .first_line = line_idx, .last_line = line_idx, @@ -5290,7 +5289,7 @@ fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErro .runtime_loop = parent_block.runtime_loop, .runtime_index = parent_block.runtime_index, .error_return_trace_index = parent_block.error_return_trace_index, - .src_base_inst = parent_block.src_base_inst, + .src_baseline = parent_block.src_baseline, .type_name_ctx = parent_block.type_name_ctx, .type_fqn_ctx = parent_block.type_fqn_ctx, }; @@ -6370,7 +6369,7 @@ fn checkCallArgumentCount( if (maybe_func_inst) |func_inst| { try sema.errNote(.{ - .base_node_inst = func_inst, + .baseline = .{ .inst = func_inst, .node = .main }, .offset = LazySrcLoc.Offset.nodeOffset(.zero), }, msg, "function declared here", .{}); } @@ -6528,7 +6527,7 @@ const CallArgsInfo = union(enum) { .r = .{ .comptime_param = .{ .comptime_src = if (maybe_func_src_inst) |src_inst| .{ - .base_node_inst = src_inst, + .baseline = .{ .inst = src_inst, .node = .main }, .offset = .{ .func_decl_param_comptime = @intCast(arg_index) }, } else unreachable, // should be non-null because the function is generic }, @@ -6621,7 +6620,7 @@ fn analyzeCall( const maybe_func_inst = try sema.funcDeclSrcInst(callee); const func_ret_ty_src: LazySrcLoc = if (maybe_func_inst) |fn_decl_inst| .{ - .base_node_inst = fn_decl_inst, + .baseline = .{ .inst = fn_decl_inst, .node = .main }, .offset = .{ .node_offset_fn_type_ret_ty = .zero }, } else func_src; @@ -6650,7 +6649,7 @@ fn analyzeCall( ); errdefer msg.destroy(gpa); if (maybe_func_inst) |func_inst| try sema.errNote(.{ - .base_node_inst = func_inst, + .baseline = .{ .inst = func_inst, .node = .main }, .offset = .nodeOffset(.zero), }, msg, "function declared here", .{}); break :msg msg; @@ -6774,7 +6773,7 @@ fn analyzeCall( .namespace = fn_nav.analysis.?.namespace, .instructions = .empty, .inlining = &generic_inlining, - .src_base_inst = fn_nav.analysis.?.zir_index, + .src_baseline = .{ .inst = fn_nav.analysis.?.zir_index, .node = .main }, .type_name_ctx = fn_nav.name, .type_fqn_ctx = fn_nav.fqn, } else undefined; @@ -6853,7 +6852,10 @@ fn analyzeCall( assert(!declared_comptime); // `analyzeArg` handles this const arg_src = args_info.argSrc(block, arg_idx); const param_ty_src: LazySrcLoc = .{ - .base_node_inst = maybe_func_inst.?, // the function is generic + .baseline = .{ + .inst = maybe_func_inst.?, // the function is generic + .node = .main, + }, .offset = .{ .func_decl_param_ty = @intCast(arg_idx) }, }; return sema.failWithNeededComptime( @@ -7311,7 +7313,7 @@ fn analyzeCall( .runtime_cond = block.runtime_cond, .runtime_loop = block.runtime_loop, .runtime_index = block.runtime_index, - .src_base_inst = fn_nav.analysis.?.zir_index, + .src_baseline = .{ .inst = fn_nav.analysis.?.zir_index, .node = .main }, .type_name_ctx = fn_nav.name, .type_fqn_ctx = fn_nav.fqn, }; @@ -8848,7 +8850,7 @@ fn validateResolvedFuncType( const param_ty: Type = .fromInterned(param_ty_ip); if (!param_ty.validateExtern(.param_ty, zcu)) { const param_src: LazySrcLoc = if (maybe_func_decl_inst) |inst| .{ - .base_node_inst = inst, + .baseline = .{ .inst = inst, .node = .main }, .offset = .{ .fn_proto_param = .{ .fn_proto_node_offset = .zero, .param_index = @intCast(param_index), @@ -8868,7 +8870,7 @@ fn validateResolvedFuncType( // Check that the return type is extern-compatible. if (!ret_ty.validateExtern(.ret_ty, zcu)) { const ret_ty_src: LazySrcLoc = if (maybe_func_decl_inst) |inst| .{ - .base_node_inst = inst, + .baseline = .{ .inst = inst, .node = .main }, .offset = .{ .node_offset_fn_type_ret_ty = .zero }, } else src; return sema.failWithOwnedErrorMsg(block, msg: { @@ -12205,7 +12207,7 @@ fn analyzeSwitchCaptures( if (has_tag_capture) { const tag_capture_src: LazySrcLoc = .{ - .base_node_inst = capture_src.base_node_inst, + .baseline = capture_src.baseline, .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture }, }; return sema.failWithInvalidSwitchTagCapture(case_block, tag_capture_src, operand_ty); @@ -12307,7 +12309,7 @@ fn analyzeSwitchPayloadCaptureTaggedUnion( const item_srcs = try sema.arena.alloc(?LazySrcLoc, item_refs.len); for (item_srcs, 0..) |*item_src, item_i| { item_src.* = .{ - .base_node_inst = capture_src.base_node_inst, + .baseline = capture_src.baseline, .offset = .{ .switch_case_item = .{ .switch_node_offset = switch_node_offset, .case_idx = capture_src.offset.switch_capture.case_idx, @@ -12347,7 +12349,7 @@ fn analyzeSwitchPayloadCaptureTaggedUnion( const item_srcs = try sema.arena.alloc(?LazySrcLoc, item_refs.len); for (item_srcs, 0..) |*item_src, item_i| { item_src.* = .{ - .base_node_inst = capture_src.base_node_inst, + .baseline = capture_src.baseline, .offset = .{ .switch_case_item = .{ .switch_node_offset = switch_node_offset, .case_idx = capture_src.offset.switch_capture.case_idx, @@ -12429,7 +12431,7 @@ fn analyzeSwitchPayloadCaptureTaggedUnion( defer coerce_block.instructions.deinit(sema.gpa); const case_src: LazySrcLoc = .{ - .base_node_inst = capture_src.base_node_inst, + .baseline = capture_src.baseline, .offset = .{ .switch_case_item = .{ .switch_node_offset = switch_node_offset, .case_idx = capture_src.offset.switch_capture.case_idx, @@ -15907,10 +15909,10 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat const msg = msg: { const name = name: { // TODO: we should probably store this name in the ZIR to avoid this complexity. - const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?; - const tree = file.getTree(zcu) catch |err| { + const file, const src_base_node = block.src_baseline.resolve(zcu).?; + const tree = zcu.fileByIndex(file).getTree(zcu) catch |err| { // In this case we emit a warning + a less precise source location. - log.warn("failed loading {qf}: {t}", .{ file.path.fmt(zcu.comp), err }); + log.warn("failed loading {qf}: {t}", .{ zcu.fileByIndex(file).path.fmt(zcu.comp), err }); break :name null; }; const node = src_node.toAbsolute(src_base_node); @@ -15933,10 +15935,10 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat if (!block.is_typeof and !block.isComptime() and sema.func_index != .none) { const msg = msg: { const name = name: { - const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?; - const tree = file.getTree(zcu) catch |err| { + const file, const src_base_node = block.src_baseline.resolve(zcu).?; + const tree = zcu.fileByIndex(file).getTree(zcu) catch |err| { // In this case we emit a warning + a less precise source location. - log.warn("failed loading {qf}: {t}", .{ file.path.fmt(zcu.comp), err }); + log.warn("failed loading {qf}: {t}", .{ zcu.fileByIndex(file).path.fmt(zcu.comp), err }); break :name null; }; const node = src_node.toAbsolute(src_base_node); @@ -17313,7 +17315,7 @@ fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr .is_typeof = true, .want_safety = false, .error_return_trace_index = block.error_return_trace_index, - .src_base_inst = block.src_base_inst, + .src_baseline = block.src_baseline, .type_name_ctx = block.type_name_ctx, .type_fqn_ctx = block.type_fqn_ctx, }; @@ -17380,7 +17382,7 @@ fn zirTypeofPeer( .runtime_cond = block.runtime_cond, .runtime_loop = block.runtime_loop, .runtime_index = block.runtime_index, - .src_base_inst = block.src_base_inst, + .src_baseline = block.src_baseline, .type_name_ctx = block.type_name_ctx, .type_fqn_ctx = block.type_fqn_ctx, }; @@ -17940,7 +17942,7 @@ fn ensurePostHoc(sema: *Sema, block: *Block, dest_block: Zir.Inst.Index) !*Label .label = &labeled_block.label, .inlining = block.inlining, .comptime_reason = block.comptime_reason, - .src_base_inst = block.src_base_inst, + .src_baseline = block.src_baseline, .type_name_ctx = block.type_name_ctx, .type_fqn_ctx = block.type_fqn_ctx, }, @@ -20180,40 +20182,40 @@ fn zirReifyStruct( const tracked_inst = try block.trackZir(inst); const src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .nodeOffset(.zero), }; const layout_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 0, } }, }; const backing_ty_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 1, } }, }; const field_names_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 2, } }, }; const field_types_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 3, } }, }; const field_attrs_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 4, @@ -20460,40 +20462,40 @@ fn zirReifyUnion( const extra = sema.code.extraData(Zir.Inst.ReifyUnion, extended.operand).data; const tracked_inst = try block.trackZir(inst); const src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .nodeOffset(.zero), }; const layout_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 0, } }, }; const arg_ty_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 1, } }, }; const field_names_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 2, } }, }; const field_types_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 3, } }, }; const field_attrs_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 4, @@ -20681,33 +20683,33 @@ fn zirReifyEnum( const extra = sema.code.extraData(Zir.Inst.ReifyEnum, extended.operand).data; const tracked_inst = try block.trackZir(inst); const src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .nodeOffset(.zero), }; const tag_ty_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 0, } }, }; const mode_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 1, } }, }; const field_names_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 2, } }, }; const field_values_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 3, @@ -20833,11 +20835,11 @@ fn zirReifySpirvType( const extra = sema.code.extraData(Zir.Inst.ReifySpirvType, extended.operand).data; const tracked_inst = try block.trackZir(inst); const src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .nodeOffset(.zero), }; const operand_src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .{ .node_offset_builtin_call_arg = .{ .builtin_call_node = .zero, .arg_index = 0, @@ -25675,7 +25677,7 @@ fn explainWhyTypeIsComptime( const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); if (!field_ty.comptimeOnly(zcu)) continue; const field_src: LazySrcLoc = .{ - .base_node_inst = struct_type.zir_index, + .baseline = .{ .inst = struct_type.zir_index, .node = .main }, .offset = .{ .container_field_type = @intCast(i) }, }; try sema.errNote(field_src, msg, "struct requires comptime because of this field", .{}); @@ -25700,7 +25702,7 @@ fn explainWhyTypeIsComptime( const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[i]); if (!field_ty.comptimeOnly(zcu)) continue; const field_src: LazySrcLoc = .{ - .base_node_inst = union_obj.zir_index, + .baseline = .{ .inst = union_obj.zir_index, .node = .main }, .offset = .{ .container_field_type = @intCast(i) }, }; try sema.errNote(field_src, msg, "union requires comptime because of this field", .{}); @@ -25914,7 +25916,7 @@ fn addSafetyCheck( .instructions = .empty, .inlining = parent_block.inlining, .comptime_reason = null, - .src_base_inst = parent_block.src_base_inst, + .src_baseline = parent_block.src_baseline, .type_name_ctx = parent_block.type_name_ctx, .type_fqn_ctx = parent_block.type_fqn_ctx, }; @@ -26009,7 +26011,7 @@ fn addSafetyCheckUnwrapError( .instructions = .empty, .inlining = parent_block.inlining, .comptime_reason = null, - .src_base_inst = parent_block.src_base_inst, + .src_baseline = parent_block.src_baseline, .type_name_ctx = parent_block.type_name_ctx, .type_fqn_ctx = parent_block.type_fqn_ctx, }; @@ -26133,7 +26135,7 @@ fn addSafetyCheckCall( .instructions = .empty, .inlining = parent_block.inlining, .comptime_reason = null, - .src_base_inst = parent_block.src_base_inst, + .src_baseline = parent_block.src_baseline, .type_name_ctx = parent_block.type_name_ctx, .type_fqn_ctx = parent_block.type_fqn_ctx, }; @@ -27966,7 +27968,7 @@ const CoerceOpts = struct { if (info.func_inst == .none) return null; const func_inst = try sema.funcDeclSrcInst(info.func_inst) orelse return null; return .{ - .base_node_inst = func_inst, + .baseline = .{ .inst = func_inst, .node = .main }, .offset = .{ .fn_proto_param_type = .{ .fn_proto_node_offset = .zero, .param_index = info.param_i, @@ -28684,7 +28686,10 @@ fn coerceExtra( errdefer msg.destroy(sema.gpa); const ret_ty_src: LazySrcLoc = .{ - .base_node_inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip), + .baseline = .{ + .inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip), + .node = .main, + }, .offset = .{ .node_offset_fn_type_ret_ty = .zero }, }; try sema.errNote(ret_ty_src, msg, "'noreturn' declared here", .{}); @@ -28724,7 +28729,10 @@ fn coerceExtra( !zcu.test_functions.contains(zcu.funcInfo(sema.func_index).owner_nav)) { const ret_ty_src: LazySrcLoc = .{ - .base_node_inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip), + .baseline = .{ + .inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip), + .node = .main, + }, .offset = .{ .node_offset_fn_type_ret_ty = .zero }, }; if (inst_ty.isError(zcu) and !dest_ty.isError(zcu)) { @@ -35008,7 +35016,7 @@ pub fn analyzeMemoizedState(sema: *Sema, stage: InternPool.MemoizedStateStage) C .instructions = .empty, .inlining = null, .comptime_reason = null, - .src_base_inst = std_type.typeDeclInst(zcu).?, + .src_baseline = .{ .inst = std_type.typeDeclInst(zcu).?, .node = .main }, .type_name_ctx = .empty, .type_fqn_ctx = .empty, }; @@ -35023,7 +35031,7 @@ pub fn analyzeMemoizedState(sema: *Sema, stage: InternPool.MemoizedStateStage) C }; const uncoerced_val = try sema.analyzeNavVal(&block, std_src, nav); const decl_src: LazySrcLoc = .{ - .base_node_inst = ip.getNav(nav).srcInst(ip), + .baseline = .{ .inst = ip.getNav(nav).srcInst(ip), .node = .main }, .offset = .nodeOffset(.zero), }; break :ty try sema.analyzeAsType(&block, decl_src, .std_lang_decl, uncoerced_val); @@ -35053,7 +35061,7 @@ pub fn analyzeMemoizedState(sema: *Sema, stage: InternPool.MemoizedStateStage) C const uncoerced_val = try sema.analyzeNavVal(&block, parent_ty_src, nav); const decl_src: LazySrcLoc = .{ - .base_node_inst = ip.getNav(nav).srcInst(ip), + .baseline = .{ .inst = ip.getNav(nav).srcInst(ip), .node = .main }, .offset = .nodeOffset(.zero), }; @@ -35343,7 +35351,7 @@ fn zirStructDecl( const tracked_inst = try block.trackZir(inst); const src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .nodeOffset(.zero), }; @@ -35401,7 +35409,7 @@ fn zirUnionDecl( const tracked_inst = try block.trackZir(inst); const src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .nodeOffset(.zero), }; @@ -35477,7 +35485,7 @@ fn zirEnumDecl( const tracked_inst = try block.trackZir(inst); const src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .nodeOffset(.zero), }; @@ -35532,7 +35540,7 @@ fn zirOpaqueDecl( const tracked_inst = try block.trackZir(inst); const src: LazySrcLoc = .{ - .base_node_inst = tracked_inst, + .baseline = .{ .inst = tracked_inst, .node = .main }, .offset = .nodeOffset(.zero), }; diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig index dd8c91c244518ac23d1d5d0be6caa30861f2c4c7..0400ddd32655a4f2adcc776afa4edd9b4a3444d6 100644 --- a/src/Sema/LowerZon.zig +++ b/src/Sema/LowerZon.zig @@ -22,11 +22,11 @@ const Zoir = std.zig.Zoir; const LowerZon = @This(); sema: *Sema, -file: *File, -file_index: Zcu.File.Index, -import_loc: LazySrcLoc, block: *Sema.Block, -base_node_inst: InternPool.TrackedInst.Index, +tracked_inst: InternPool.TrackedInst.Index, +zoir: *const Zoir, +file_index: Zcu.File.Index, +import_src: LazySrcLoc, /// Lowers the given file as ZON. pub fn run( @@ -34,28 +34,26 @@ pub fn run( file: *File, file_index: Zcu.File.Index, res_ty_interned: InternPool.Index, - import_loc: LazySrcLoc, + import_src: LazySrcLoc, block: *Sema.Block, ) CompileError!InternPool.Index { const pt = sema.pt; - const comp = pt.zcu.comp; + const zcu = pt.zcu; + const comp = zcu.comp; const gpa = comp.gpa; const io = comp.io; - const tracked_inst = try pt.zcu.intern_pool.trackZir(gpa, io, pt.tid, .{ - .file = file_index, - .inst = .main_struct_inst, // this is the only trackable instruction in a ZON file - }); - var lower_zon: LowerZon = .{ .sema = sema, - .file = file, - .file_index = file_index, - .import_loc = import_loc, .block = block, - .base_node_inst = tracked_inst, + .tracked_inst = try zcu.intern_pool.trackZir(gpa, io, pt.tid, .{ + .file = file_index, + .inst = .main_struct_inst, // this is the only trackable instruction in a ZON file + }), + .zoir = &file.zoir.?, + .file_index = file_index, + .import_src = import_src, }; - if (res_ty_interned == .none) { return lower_zon.lowerExprAnonResTy(.root); } else { @@ -71,7 +69,7 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter const gpa = comp.gpa; const io = comp.io; const ip = &pt.zcu.intern_pool; - switch (node.get(self.file.zoir.?)) { + switch (node.get(self.zoir.*)) { .true => return .bool_true, .false => return .bool_false, .null => return .null_value, @@ -101,7 +99,7 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter gpa, io, pt.tid, - val.get(self.file.zoir.?), + val.get(self.zoir.*), .no_embedded_nulls, ), }), @@ -130,7 +128,7 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter elems[i] = try self.lowerExprAnonResTy(init.vals.at(@intCast(i))); } const struct_ty: Type = switch (try ip.getReifiedStructType(gpa, io, pt.tid, .{ - .zir_index = self.base_node_inst, + .zir_index = self.tracked_inst, .type_hash = hash: { var hasher: std.hash.Wyhash = .init(0); hasher.update(std.mem.asBytes(&node)); @@ -150,7 +148,7 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter errdefer wip.cancel(ip, pt.tid); const block = self.block; const zcu = pt.zcu; - try self.sema.setTypeName(block, &wip, .anon, "struct", self.base_node_inst.resolve(ip).?); + try self.sema.setTypeName(block, &wip, .anon, "struct", .main_struct_inst); // Reified structs have field information populated immediately. @memcpy(wip.field_values.get(ip), elems); @@ -174,7 +172,7 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter gpa, io, pt.tid, - zoir_name.get(self.file.zoir.?), + zoir_name.get(self.zoir.*), .no_embedded_nulls, ); field_ty.* = ip.typeOf(field_val); @@ -289,7 +287,7 @@ fn checkTypeInner( } else { const gop = try visited.getOrPut(sema.arena, ty.toIntern()); if (gop.found_existing) return; - try sema.ensureLayoutResolved(ty, self.import_loc, .init); + try sema.ensureLayoutResolved(ty, self.import_src, .init); const struct_info = zcu.typeToStruct(ty).?; for (struct_info.field_types.get(ip)) |field_type| { try self.checkTypeInner(.fromInterned(field_type), null, visited); @@ -298,7 +296,7 @@ fn checkTypeInner( .@"union" => { const gop = try visited.getOrPut(sema.arena, ty.toIntern()); if (gop.found_existing) return; - try sema.ensureLayoutResolved(ty, self.import_loc, .init); + try sema.ensureLayoutResolved(ty, self.import_src, .init); const union_info = zcu.typeToUnion(ty).?; for (union_info.field_types.get(ip)) |field_type| { if (field_type != .void_type) { @@ -311,8 +309,8 @@ fn checkTypeInner( fn nodeSrc(self: *LowerZon, node: Zoir.Node.Index) LazySrcLoc { return .{ - .base_node_inst = self.base_node_inst, - .offset = .{ .node_abs = node.getAstNode(self.file.zoir.?) }, + .baseline = .{ .inst = self.tracked_inst, .node = .main }, + .offset = .{ .node_abs = node.getAstNode(self.zoir.*) }, }; } @@ -326,9 +324,9 @@ fn failUnsupportedResultType( const gpa = sema.gpa; const pt = sema.pt; return sema.failWithOwnedErrorMsg(self.block, msg: { - const msg = try sema.errMsg(self.import_loc, "type '{f}' is not available in ZON", .{ty.fmt(pt)}); + const msg = try sema.errMsg(self.import_src, "type '{f}' is not available in ZON", .{ty.fmt(pt)}); errdefer msg.destroy(gpa); - if (opt_note) |n| try sema.errNote(self.import_loc, msg, "{s}", .{n}); + if (opt_note) |n| try sema.errNote(self.import_src, msg, "{s}", .{n}); break :msg msg; }); } @@ -341,7 +339,7 @@ fn fail( ) Zcu.SemaError { @branchHint(.cold); const err_msg = try Zcu.ErrorMsg.create(self.sema.pt.zcu.gpa, self.nodeSrc(node), format, args); - try self.sema.pt.zcu.errNote(self.import_loc, err_msg, "imported here", .{}); + try self.sema.pt.zcu.errNote(self.import_src, err_msg, "imported here", .{}); return self.sema.failWithOwnedErrorMsg(self.block, err_msg); } @@ -363,7 +361,7 @@ fn lowerExprKnownResTyInner( .optional => return pt.intern(.{ .opt = .{ .ty = res_ty.toIntern(), - .val = if (node.get(self.file.zoir.?) == .null) b: { + .val = if (node.get(self.zoir.*) == .null) b: { break :b .none; } else b: { const child_type = res_ty.optionalChild(pt.zcu); @@ -418,7 +416,7 @@ fn lowerExprKnownResTyInner( } fn lowerBool(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index { - return switch (node.get(self.file.zoir.?)) { + return switch (node.get(self.zoir.*)) { .true => .bool_true, .false => .bool_false, else => return error.WrongType, @@ -431,7 +429,7 @@ fn lowerInt( res_ty: Type, ) !InternPool.Index { @setFloatMode(.strict); - return switch (node.get(self.file.zoir.?)) { + return switch (node.get(self.zoir.*)) { .int_literal => |int| switch (int) { .small => |val| { const rhs: i32 = val; @@ -555,7 +553,7 @@ fn lowerFloat( res_ty: Type, ) !InternPool.Index { @setFloatMode(.strict); - const value = switch (node.get(self.file.zoir.?)) { + const value = switch (node.get(self.zoir.*)) { .int_literal => |int| switch (int) { .small => |val| try self.sema.pt.floatValue(res_ty, @as(f128, @floatFromInt(val))), .big => |val| try self.sema.pt.floatValue(res_ty, val.toFloat(f128, .nearest_even)[0]), @@ -592,7 +590,7 @@ fn lowerFloat( } fn lowerNull(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index { - switch (node.get(self.file.zoir.?)) { + switch (node.get(self.zoir.*)) { .null => return .null_value, else => return error.WrongType, } @@ -600,7 +598,7 @@ fn lowerNull(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index { fn lowerArray(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index { const array_info = res_ty.arrayInfo(self.sema.pt.zcu); - const nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) { + const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir.*)) { .array_literal => |nodes| nodes, .empty_literal => .{ .start = node, .len = 0 }, else => return error.WrongType, @@ -632,14 +630,14 @@ fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.I const gpa = comp.gpa; const io = comp.io; const ip = &pt.zcu.intern_pool; - try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init); - switch (node.get(self.file.zoir.?)) { + try self.sema.ensureLayoutResolved(res_ty, self.import_src, .init); + switch (node.get(self.zoir.*)) { .enum_literal => |field_name| { const field_name_interned = try ip.getOrPutString( gpa, io, self.sema.pt.tid, - field_name.get(self.file.zoir.?), + field_name.get(self.zoir.*), .no_embedded_nulls, ); const field_index = res_ty.enumFieldIndex(field_name_interned, self.sema.pt.zcu) orelse { @@ -648,7 +646,7 @@ fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.I "enum {f} has no member named '{f}'", .{ res_ty.fmt(self.sema.pt), - std.zig.fmtId(field_name.get(self.file.zoir.?)), + std.zig.fmtId(field_name.get(self.zoir.*)), }, ); }; @@ -667,13 +665,13 @@ fn lowerEnumLiteral(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index { const gpa = comp.gpa; const io = comp.io; const ip = &pt.zcu.intern_pool; - switch (node.get(self.file.zoir.?)) { + switch (node.get(self.zoir.*)) { .enum_literal => |field_name| { const field_name_interned = try ip.getOrPutString( gpa, io, self.sema.pt.tid, - field_name.get(self.file.zoir.?), + field_name.get(self.zoir.*), .no_embedded_nulls, ); return self.sema.pt.intern(.{ .enum_literal = field_name_interned }); @@ -696,7 +694,7 @@ fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. const tuple_info = ip.indexToKey(res_ty.toIntern()).tuple_type; - const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) { + const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir.*)) { .array_literal => |nodes| nodes, .empty_literal => .{ .start = node, .len = 0 }, else => return error.WrongType, @@ -720,7 +718,7 @@ fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. "index {} outside tuple of length {}", .{ elems.len, - elem_nodes.at(@intCast(i)).getAstNode(self.file.zoir.?), + elem_nodes.at(@intCast(i)).getAstNode(self.zoir.*), }, ); } @@ -756,11 +754,11 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool const io = comp.io; const ip = &pt.zcu.intern_pool; - try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init); - try self.sema.ensureStructDefaultsResolved(res_ty, self.import_loc); + try self.sema.ensureLayoutResolved(res_ty, self.import_src, .init); + try self.sema.ensureStructDefaultsResolved(res_ty, self.import_src); const struct_info = self.sema.pt.zcu.typeToStruct(res_ty).?; - const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) { + const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.zoir.*)) { .struct_literal => |fields| fields, .empty_literal => .{ .names = &.{}, .vals = .{ .start = node, .len = 0 } }, else => return error.WrongType, @@ -780,7 +778,7 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool gpa, io, self.sema.pt.tid, - fields.names[i].get(self.file.zoir.?), + fields.names[i].get(self.zoir.*), .no_embedded_nulls, ); const field_node = fields.vals.at(@intCast(i)); @@ -844,7 +842,7 @@ fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. const string_alignment = ptr_info.flags.alignment == .none or ptr_info.flags.alignment == .@"1"; const string_sentinel = ptr_info.sentinel == .none or ptr_info.sentinel == .zero_u8; if (string_alignment and ptr_info.child == .u8_type and string_sentinel) { - switch (node.get(self.file.zoir.?)) { + switch (node.get(self.zoir.*)) { .string_literal => |val| { const ip_str = try ip.getOrPutString(gpa, io, self.sema.pt.tid, val, .maybe_embedded_nulls); const str_ref = try self.sema.addStrLit(ip_str, val.len); @@ -860,7 +858,7 @@ fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. } // Slice literals - const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) { + const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir.*)) { .array_literal => |nodes| nodes, .empty_literal => .{ .start = node, .len = 0 }, else => return error.WrongType, @@ -923,23 +921,23 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. const gpa = comp.gpa; const io = comp.io; const ip = &pt.zcu.intern_pool; - try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init); + try self.sema.ensureLayoutResolved(res_ty, self.import_src, .init); const union_info = pt.zcu.typeToUnion(res_ty).?; const enum_tag_info = ip.loadEnumType(union_info.enum_tag_type); - const field_name, const maybe_field_node = switch (node.get(self.file.zoir.?)) { + const field_name, const maybe_field_node = switch (node.get(self.zoir.*)) { .enum_literal => |name| b: { const field_name = try ip.getOrPutString( gpa, io, self.sema.pt.tid, - name.get(self.file.zoir.?), + name.get(self.zoir.*), .no_embedded_nulls, ); break :b .{ field_name, null }; }, .struct_literal => b: { - const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) { + const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.zoir.*)) { .struct_literal => |fields| fields, else => return self.fail(node, "expected type '{f}'", .{res_ty.fmt(self.sema.pt)}), }; @@ -950,7 +948,7 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. gpa, io, self.sema.pt.tid, - fields.names[0].get(self.file.zoir.?), + fields.names[0].get(self.zoir.*), .no_embedded_nulls, ); break :b .{ field_name, fields.vals.at(0) }; @@ -986,7 +984,7 @@ fn lowerVector(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool const vector_info = ip.indexToKey(res_ty.toIntern()).vector_type; - const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) { + const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir.*)) { .array_literal => |nodes| nodes, .empty_literal => .{ .start = node, .len = 0 }, else => return error.WrongType, diff --git a/src/Sema/type_resolution.zig b/src/Sema/type_resolution.zig index 164fe262037b8262cc1324319751a7e3173be4e4..f655654cd2bbd42d7317d1737cb9dec9100b3b19 100644 --- a/src/Sema/type_resolution.zig +++ b/src/Sema/type_resolution.zig @@ -205,7 +205,10 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void { .instructions = .empty, .inlining = null, .comptime_reason = undefined, // always set before using `block` - .src_base_inst = struct_obj.zir_index, + .src_baseline = .{ + .inst = struct_obj.zir_index, + .node = .main, + }, .type_name_ctx = struct_obj.name, .type_fqn_ctx = struct_obj.fqn, }; @@ -261,6 +264,8 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void { { const field_ty_src = block.src(.{ .container_field_type = zir_field.idx }); const field_ty: Type = field_ty: { + block.src_baseline.node = .type_decl_fields; + defer block.src_baseline.node = .main; block.comptime_reason = .{ .reason = .{ .src = field_ty_src, .r = .{ .simple = .struct_field_types }, @@ -276,6 +281,8 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void { } else { const field_align_src = block.src(.{ .container_field_align = zir_field.idx }); const field_align: Alignment = a: { + block.src_baseline.node = .type_decl_fields; + defer block.src_baseline.node = .main; block.comptime_reason = .{ .reason = .{ .src = field_align_src, .r = .{ .simple = .struct_field_attrs }, @@ -538,6 +545,8 @@ fn resolvePackedStructLayout( field_bits += field_ty.bitSize(zcu); } + const backing_int_ty_src = block.src(.container_arg); + const explicit_backing_int_ty: ?Type = if (struct_obj.is_reified) ty: { break :ty switch (struct_obj.packed_backing_mode) { .explicit => .fromInterned(struct_obj.packed_backing_int_type), @@ -550,20 +559,21 @@ fn resolvePackedStructLayout( break :ty null; // inferred backing type }; // Explicitly specified, so evaluate the backing int type expression. - const backing_int_type_src = block.src(.container_arg); + block.src_baseline.node = .type_decl_arg; + defer block.src_baseline.node = .main; block.comptime_reason = .{ .reason = .{ - .src = backing_int_type_src, + .src = backing_int_ty_src, .r = .{ .simple = .packed_struct_backing_int_type }, } }; const type_ref = try sema.resolveInlineBody(block, backing_int_type_body, zir_index); - break :ty try sema.analyzeAsType(block, backing_int_type_src, .packed_struct_backing_int_type, type_ref); + break :ty try sema.analyzeAsType(block, backing_int_ty_src, .packed_struct_backing_int_type, type_ref); }; // Finally, either validate or infer the backing int type. const backing_int_ty: Type = if (explicit_backing_int_ty) |backing_ty| ty: { if (backing_ty.zigTypeTag(zcu) != .int) return sema.fail( block, - block.src(.container_arg), + backing_int_ty_src, "expected backing integer type, found '{f}'", .{backing_ty.fmt(pt)}, ); @@ -572,7 +582,7 @@ fn resolvePackedStructLayout( const msg = try sema.errMsg(src, "backing integer bit width does not match total bit width of fields", .{}); errdefer msg.destroy(gpa); try sema.errNote( - block.src(.container_arg), + backing_int_ty_src, msg, "backing integer '{f}' has bit width '{d}'", .{ backing_ty.fmt(pt), backing_ty.bitSize(zcu) }, @@ -652,7 +662,10 @@ pub fn resolveStructDefaults(sema: *Sema, struct_ty: Type) CompileError!void { .instructions = .empty, .inlining = null, .comptime_reason = undefined, // always set before using `block` - .src_base_inst = struct_obj.zir_index, + .src_baseline = .{ + .inst = struct_obj.zir_index, + .node = .main, + }, .type_name_ctx = struct_obj.name, .type_fqn_ctx = struct_obj.fqn, }; @@ -675,6 +688,7 @@ fn resolveStructDefaultsInner( const ip = &zcu.intern_pool; assert(struct_obj.field_defaults.len > 0); + assert(block.src_baseline.node == .main); // We'll need to map the struct decl instruction to provide result types const zir_index = struct_obj.zir_index.resolve(ip) orelse { @@ -693,6 +707,8 @@ fn resolveStructDefaultsInner( } const default_val_src = block.src(.{ .container_field_value = zir_field.idx }); + block.src_baseline.node = .type_decl_fields; + defer block.src_baseline.node = .main; block.comptime_reason = .{ .reason = .{ .src = default_val_src, .r = .{ .simple = .struct_field_default_value }, @@ -747,7 +763,10 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void { .instructions = .empty, .inlining = null, .comptime_reason = undefined, // always set before using `block` - .src_base_inst = union_obj.zir_index, + .src_baseline = .{ + .inst = union_obj.zir_index, + .node = .main, + }, .type_name_ctx = union_obj.name, .type_fqn_ctx = union_obj.fqn, }; @@ -764,6 +783,8 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void { assert(zir_union.kind == .tagged_explicit); // `Zcu.mapOldZirToNew` guarantees that the ZIR mapping preserves `kind` const tag_type_body = zir_union.arg_type_body.?; const tag_type_src = block.src(.container_arg); + block.src_baseline.node = .type_decl_fields; + defer block.src_baseline.node = .main; block.comptime_reason = .{ .reason = .{ .src = tag_type_src, .r = .{ .simple = .union_enum_tag_type }, @@ -882,6 +903,8 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void { while (field_it.next()) |zir_field| { const field_ty_src = block.src(.{ .container_field_type = zir_field.idx }); const field_ty: Type = field_ty: { + block.src_baseline.node = .type_decl_fields; + defer block.src_baseline.node = .main; block.comptime_reason = .{ .reason = .{ .src = field_ty_src, .r = .{ .simple = .union_field_types }, @@ -894,6 +917,8 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void { const field_align_src = block.src(.{ .container_field_align = zir_field.idx }); const explicit_field_align: Alignment = a: { + block.src_baseline.node = .type_decl_fields; + defer block.src_baseline.node = .main; block.comptime_reason = .{ .reason = .{ .src = field_align_src, .r = .{ .simple = .union_field_attrs }, @@ -1066,7 +1091,10 @@ fn failUnionFieldMismatch(sema: *Sema, block: *Block, union_field_names: []const if (union_field_index != null) continue; const field_name_ip = enum_obj.field_names.get(ip)[enum_field_index]; const enum_field_src: LazySrcLoc = .{ - .base_node_inst = enum_tag_ty.typeDeclInstAllowGeneratedTag(zcu).?, + .baseline = .{ + .inst = enum_tag_ty.typeDeclInstAllowGeneratedTag(zcu).?, + .node = .main, + }, .offset = .{ .container_field_name = @intCast(enum_field_index) }, }; return sema.failWithOwnedErrorMsg(block, msg: { @@ -1082,7 +1110,10 @@ fn failUnionFieldMismatch(sema: *Sema, block: *Block, union_field_names: []const const field_name = enum_obj.field_names.get(ip)[enum_field_index]; const union_field_src = block.src(.{ .container_field_name = union_field_index.? }); const enum_field_src: LazySrcLoc = .{ - .base_node_inst = enum_tag_ty.typeDeclInstAllowGeneratedTag(zcu).?, + .baseline = .{ + .inst = enum_tag_ty.typeDeclInstAllowGeneratedTag(zcu).?, + .node = .main, + }, .offset = .{ .container_field_name = @intCast(enum_field_index) }, }; return sema.failWithOwnedErrorMsg(block, msg: { @@ -1138,6 +1169,8 @@ fn resolvePackedUnionLayout( assert(!field_ty.comptimeOnly(zcu)); // packable types are not comptime-only } + const backing_int_ty_src = block.src(.container_arg); + const explicit_backing_int_ty: ?Type = if (union_obj.is_reified) ty: { switch (union_obj.packed_backing_mode) { .explicit => break :ty .fromInterned(union_obj.packed_backing_int_type), @@ -1150,20 +1183,21 @@ fn resolvePackedUnionLayout( break :ty null; // inferred backing type }; // Explicitly specified, so evaluate the backing int type expression. - const backing_int_type_src = block.src(.container_arg); + block.src_baseline.node = .type_decl_arg; + defer block.src_baseline.node = .main; block.comptime_reason = .{ .reason = .{ - .src = backing_int_type_src, + .src = backing_int_ty_src, .r = .{ .simple = .packed_union_backing_int_type }, } }; const type_ref = try sema.resolveInlineBody(block, backing_int_type_body, zir_index); - break :ty try sema.analyzeAsType(block, backing_int_type_src, .packed_union_backing_int_type, type_ref); + break :ty try sema.analyzeAsType(block, backing_int_ty_src, .packed_union_backing_int_type, type_ref); }; // Finally, either validate or infer the backing int type. const backing_int_ty: Type = if (explicit_backing_int_ty) |backing_ty| ty: { if (backing_ty.zigTypeTag(zcu) != .int) return sema.fail( block, - block.src(.container_arg), + backing_int_ty_src, "expected backing integer type, found '{f}'", .{backing_ty.fmt(pt)}, ); @@ -1177,7 +1211,7 @@ fn resolvePackedUnionLayout( errdefer msg.destroy(gpa); try sema.errNote(field_ty_src, msg, "field type '{f}' has bit width '{d}'", .{ field_type.fmt(pt), field_bits }); try sema.errNote( - block.src(.container_arg), + backing_int_ty_src, msg, "backing integer '{f}' has bit width '{d}'", .{ backing_ty.fmt(pt), backing_int_bits }, @@ -1256,7 +1290,10 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { .instructions = .empty, .inlining = null, .comptime_reason = undefined, // always set before using `block` - .src_base_inst = tracked_inst, + .src_baseline = .{ + .inst = tracked_inst, + .node = .main, + }, .type_name_ctx = enum_obj.name, .type_fqn_ctx = enum_obj.fqn, }; @@ -1341,6 +1378,8 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { // Explicitly specified, so evaluate the int tag type expression. const tag_type_body = zir_union.arg_type_body.?; const tag_type_src = block.src(.container_arg); + block.src_baseline.node = .type_decl_arg; + defer block.src_baseline.node = .main; block.comptime_reason = .{ .reason = .{ .src = tag_type_src, .r = .{ .simple = .enum_int_tag_type }, @@ -1354,6 +1393,8 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { }; // Explicitly specified, so evaluate the int tag type expression. const tag_type_src = block.src(.container_arg); + block.src_baseline.node = .type_decl_arg; + defer block.src_baseline.node = .main; block.comptime_reason = .{ .reason = .{ .src = tag_type_src, .r = .{ .simple = .enum_int_tag_type }, @@ -1425,6 +1466,8 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { var field_it = zir_union.iterateFields(); while (field_it.next()) |zir_field| { const field_val_src = block.src(.{ .container_field_value = zir_field.idx }); + block.src_baseline.node = .type_decl_fields; + defer block.src_baseline.node = .main; block.comptime_reason = .{ .reason = .{ .src = field_val_src, .r = .{ .simple = .enum_field_values }, @@ -1447,6 +1490,8 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { var field_it = zir_enum.iterateFields(); while (field_it.next()) |zir_field| { const field_val_src = block.src(.{ .container_field_value = zir_field.idx }); + block.src_baseline.node = .type_decl_fields; + defer block.src_baseline.node = .main; block.comptime_reason = .{ .reason = .{ .src = field_val_src, .r = .{ .simple = .enum_field_values }, diff --git a/src/Type.zig b/src/Type.zig index b95b0df556f68de7bc40f3ef8c07ca7767ee7a11..529dd5a2b37f16b36dd79b9edb3728114dbcac4e 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -2586,13 +2586,16 @@ pub fn structFieldOffset(ty: Type, index: usize, zcu: *const Zcu) u64 { pub fn srcLocOrNull(ty: Type, zcu: *Zcu) ?Zcu.LazySrcLoc { const ip = &zcu.intern_pool; return .{ - .base_node_inst = switch (ip.indexToKey(ty.toIntern())) { - .struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) { - .declared => |d| d.zir_index, - .reified => |r| r.zir_index, - .generated_union_tag => |union_ty| ip.loadUnionType(union_ty).zir_index, + .baseline = .{ + .inst = switch (ip.indexToKey(ty.toIntern())) { + .struct_type, .union_type, .opaque_type, .enum_type => |info| switch (info) { + .declared => |d| d.zir_index, + .reified => |r| r.zir_index, + .generated_union_tag => |union_ty| ip.loadUnionType(union_ty).zir_index, + }, + else => return null, }, - else => return null, + .node = .main, }, .offset = Zcu.LazySrcLoc.Offset.nodeOffset(.zero), }; diff --git a/src/Zcu.zig b/src/Zcu.zig index 81684fad8e4d22bd26de82f1b6680a213ad4d609..0f2c93bb92336704727aad07a387ebe8c9812af4 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -209,14 +209,14 @@ dependency_loop_nodes: std.array_hash_map.Auto(AnalUnit, struct { /// Keep track of `@compileLog`s per `AnalUnit`. /// We track the source location of the first `@compileLog` call, and all logged lines as a linked list. /// The list is singly linked, but we do track its tail for fast appends (optimizing many logs in one unit). -compile_logs: std.array_hash_map.Auto(AnalUnit, extern struct { - base_node_inst: InternPool.TrackedInst.Index, +compile_logs: std.array_hash_map.Auto(AnalUnit, struct { + baseline: LazySrcLoc.Baseline, node_offset: Ast.Node.Offset, first_line: CompileLogLine.Index, last_line: CompileLogLine.Index, pub fn src(self: @This()) LazySrcLoc { return .{ - .base_node_inst = self.base_node_inst, + .baseline = self.baseline, .offset = LazySrcLoc.Offset.nodeOffset(self.node_offset), }; } @@ -2341,21 +2341,115 @@ pub const SrcLoc = struct { }; pub const LazySrcLoc = struct { - /// This instruction provides the source node locations are resolved relative to. - /// It is a `declaration`, `struct_decl`, `union_decl`, `enum_decl`, or `opaque_decl`. - /// This must be valid even if `relative` is an absolute value, since it is required to - /// determine the file which the `LazySrcLoc` refers to. - base_node_inst: InternPool.TrackedInst.Index, - /// This field determines the source location relative to `base_node_inst`. + /// The baseline is a reference to a single AST node which `offset` is relative to. We use this + /// offset-based approach because AST node indices change across incremental updates, but we can + /// track the changes for certain instructions (e.g. declarations). `Baseline` is a stable + /// reference to a source location in such an instruction. By representing all source locations + /// as relative to one of these baselines, we make them independent of changes in a file which + /// happen outside of (e.g.) a specific declaration. + baseline: Baseline, + /// This field determines the source location relative to `baseline`. offset: Offset, + pub const Baseline = struct { + inst: InternPool.TrackedInst.Index, + node: enum { + /// `inst` refers to any trackable instruction (see `Zir.assertTrackable`). + /// + /// The baseline node is its main source node. + main, + /// `inst` refers to a `.struct_decl`, `.union_decl`, or `.enum_decl`. + /// + /// The baseline node is the `Zir.Unwrapped[Type]Decl.fields_baseline_src_node`. + type_decl_fields, + /// `inst` refers to a `.struct_decl`, `.union_decl`, or `.enum_decl`. + /// + /// The baseline node is the `Zir.Unwrapped[Type]Decl.arg_baseline_src_node`. + type_decl_arg, + }, + + /// Returns `null` if the ZIR instruction has been lost across incremental updates. + pub fn resolve(b: Baseline, zcu: *Zcu) ?struct { File.Index, Ast.Node.Index } { + const ip = &zcu.intern_pool; + const resolved_ti = b.inst.resolveFull(ip) orelse return null; + const file = zcu.fileByIndex(resolved_ti.file); + const zir = switch (file.getMode()) { + .zig => &file.zir.?, + .zon => { + // ZON files don't have ZIR. Instead they will always set their baseline to a + // specific dummy value which must resolve to the file's root node. + assert(resolved_ti.inst == .main_struct_inst); + assert(b.node == .main); + return .{ resolved_ti.file, .root }; + }, + }; + + comptime assert(Zir.inst_tracking_version == 0); + const inst = zir.instructions.get(@backingInt(resolved_ti.inst)); + return .{ + resolved_ti.file, + switch (b.node) { + .main => switch (inst.tag) { + .declaration => inst.data.declaration.src_node, + .struct_init, .struct_init_ref => zir.extraData( + Zir.Inst.StructInit, + inst.data.pl_node.payload_index, + ).data.abs_node, + .struct_init_anon => zir.extraData( + Zir.Inst.StructInitAnon, + inst.data.pl_node.payload_index, + ).data.abs_node, + .extended => switch (inst.data.extended.opcode) { + .struct_decl => zir.getStructDecl(resolved_ti.inst).src_node, + .union_decl => zir.getUnionDecl(resolved_ti.inst).src_node, + .enum_decl => zir.getEnumDecl(resolved_ti.inst).src_node, + .opaque_decl => zir.getOpaqueDecl(resolved_ti.inst).src_node, + .reify_enum => zir.extraData( + Zir.Inst.ReifyEnum, + inst.data.extended.operand, + ).data.node, + .reify_struct => zir.extraData( + Zir.Inst.ReifyStruct, + inst.data.extended.operand, + ).data.node, + .reify_union => zir.extraData( + Zir.Inst.ReifyUnion, + inst.data.extended.operand, + ).data.node, + .reify_spirv_type => zir.extraData( + Zir.Inst.ReifySpirvType, + inst.data.extended.operand, + ).data.node, + else => unreachable, + }, + else => unreachable, + }, + .type_decl_fields => switch (inst.data.extended.opcode) { + .struct_decl => zir.getStructDecl(resolved_ti.inst).fields_baseline_src_node, + .union_decl => zir.getUnionDecl(resolved_ti.inst).fields_baseline_src_node, + .enum_decl => zir.getEnumDecl(resolved_ti.inst).fields_baseline_src_node, + .opaque_decl => unreachable, // opaque type decls do not have fields + else => unreachable, + }, + .type_decl_arg => switch (inst.data.extended.opcode) { + .struct_decl => zir.getStructDecl(resolved_ti.inst).arg_baseline_src_node, + .union_decl => zir.getUnionDecl(resolved_ti.inst).arg_baseline_src_node, + .enum_decl => zir.getEnumDecl(resolved_ti.inst).arg_baseline_src_node, + .opaque_decl => unreachable, // opaque type decls do not accept an argument ('opaque(...)') + else => unreachable, + }, + }, + }; + } + }; + pub const Offset = union(enum) { /// When this tag is set, the code that constructed this `LazySrcLoc` is asserting /// that all code paths which would need to resolve the source location are /// unreachable. If you are debugging this tag incorrectly being this value, /// look into using reverse-continue with a memory watchpoint to see where the /// value is being set to this tag. - /// `base_node_inst` is unused. + /// `baseline` is unused. unneeded, /// The source location points to a byte offset within a source file, /// offset from 0. The source file is determined contextually. @@ -2734,50 +2828,11 @@ pub const LazySrcLoc = struct { }; pub const unneeded: LazySrcLoc = .{ - .base_node_inst = undefined, + .baseline = undefined, .offset = .unneeded, }; - /// Returns `null` if the ZIR instruction has been lost across incremental updates. - pub fn resolveBaseNode(base_node_inst: InternPool.TrackedInst.Index, zcu: *Zcu) ?struct { *File, Ast.Node.Index } { - comptime assert(Zir.inst_tracking_version == 0); - - const ip = &zcu.intern_pool; - const file_index, const zir_inst = inst: { - const info = base_node_inst.resolveFull(ip) orelse return null; - break :inst .{ info.file, info.inst }; - }; - const file = zcu.fileByIndex(file_index); - - // If we're relative to .main_struct_inst, we know the ast node is the root and don't need to resolve the ZIR, - // which may not exist e.g. in the case of errors in ZON files. - if (zir_inst == .main_struct_inst) return .{ file, .root }; - - // Otherwise, make sure ZIR is loaded. - const zir = &file.zir.?; - - const inst = zir.instructions.get(@backingInt(zir_inst)); - const base_node: Ast.Node.Index = switch (inst.tag) { - .declaration => inst.data.declaration.src_node, - .struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.abs_node, - .struct_init_anon => zir.extraData(Zir.Inst.StructInitAnon, inst.data.pl_node.payload_index).data.abs_node, - .extended => switch (inst.data.extended.opcode) { - .struct_decl => zir.getStructDecl(zir_inst).src_node, - .union_decl => zir.getUnionDecl(zir_inst).src_node, - .enum_decl => zir.getEnumDecl(zir_inst).src_node, - .opaque_decl => zir.getOpaqueDecl(zir_inst).src_node, - .reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.node, - .reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.node, - .reify_union => zir.extraData(Zir.Inst.ReifyUnion, inst.data.extended.operand).data.node, - .reify_spirv_type => zir.extraData(Zir.Inst.ReifySpirvType, inst.data.extended.operand).data.node, - else => unreachable, - }, - else => unreachable, - }; - return .{ file, base_node }; - } - - /// Resolve the file and AST node of `base_node_inst` to get a resolved `SrcLoc`. + /// Resolve the file and AST node of `baseline` to get a resolved `SrcLoc`. /// The resulting `SrcLoc` should only be used ephemerally, as it is not correct across incremental updates. pub fn upgrade(lazy: LazySrcLoc, zcu: *Zcu) SrcLoc { return lazy.upgradeOrLost(zcu).?; @@ -2785,9 +2840,9 @@ pub const LazySrcLoc = struct { /// Like `upgrade`, but returns `null` if the source location has been lost across incremental updates. pub fn upgradeOrLost(lazy: LazySrcLoc, zcu: *Zcu) ?SrcLoc { - const file, const base_node: Ast.Node.Index = resolveBaseNode(lazy.base_node_inst, zcu) orelse return null; + const file, const base_node: Ast.Node.Index = lazy.baseline.resolve(zcu) orelse return null; return .{ - .file_scope = file, + .file_scope = zcu.fileByIndex(file), .base_node = base_node, .lazy = lazy.offset, }; @@ -4459,7 +4514,7 @@ pub fn setFileRootType(zcu: *Zcu, file_index: File.Index, root_type: InternPool. pub fn navSrcLoc(zcu: *const Zcu, nav_index: InternPool.Nav.Index) LazySrcLoc { const ip = &zcu.intern_pool; return .{ - .base_node_inst = ip.getNav(nav_index).srcInst(ip), + .baseline = .{ .inst = ip.getNav(nav_index).srcInst(ip), .node = .main }, .offset = LazySrcLoc.Offset.nodeOffset(.zero), }; } diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index a143fcd93f6caac80b87b4ed943e9262e113bbf4..45f09e3988dff5f32b7caee0407ebc91ef7f3e2d 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -1317,12 +1317,15 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu .inlining = null, .comptime_reason = .{ .reason = .{ .src = .{ - .base_node_inst = comptime_unit.zir_index, + .baseline = .{ .inst = comptime_unit.zir_index, .node = .main }, .offset = .{ .token_offset = .zero }, }, .r = .{ .simple = .comptime_keyword }, } }, - .src_base_inst = comptime_unit.zir_index, + .src_baseline = .{ + .inst = comptime_unit.zir_index, + .node = .main, + }, .type_name_ctx = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.comptime", .{ parent_ns.name.fmt(ip), }, .no_embedded_nulls), @@ -1723,7 +1726,10 @@ fn analyzeNavVal( .instructions = .empty, .inlining = null, .comptime_reason = undefined, // set below - .src_base_inst = old_nav.analysis.?.zir_index, + .src_baseline = .{ + .inst = old_nav.analysis.?.zir_index, + .node = .main, + }, .type_name_ctx = old_nav.name, .type_fqn_ctx = old_nav.fqn, }; @@ -2093,7 +2099,10 @@ fn analyzeNavType( .instructions = .empty, .inlining = null, .comptime_reason = undefined, // set below - .src_base_inst = old_nav.analysis.?.zir_index, + .src_baseline = .{ + .inst = old_nav.analysis.?.zir_index, + .node = .main, + }, .type_name_ctx = old_nav.name, .type_fqn_ctx = old_nav.fqn, }; @@ -3373,7 +3382,10 @@ fn analyzeFuncBodyInner( .instructions = .empty, .inlining = null, .comptime_reason = null, - .src_base_inst = decl_analysis.zir_index, + .src_baseline = .{ + .inst = decl_analysis.zir_index, + .node = .main, + }, .type_name_ctx = func_nav.name, .type_fqn_ctx = func_nav.fqn, }; @@ -3511,7 +3523,7 @@ fn analyzeFuncBodyInner( // can be emitted here. if (sema.fn_ret_ty_ies) |ies| { sema.resolveInferredErrorSetPtr(&inner_block, .{ - .base_node_inst = inner_block.src_base_inst, + .baseline = inner_block.src_baseline, .offset = Zcu.LazySrcLoc.Offset.nodeOffset(.zero), }, ies) catch |err| switch (err) { error.ComptimeReturn => unreachable, diff --git a/src/crash_report.zig b/src/crash_report.zig index 8ca3c2f836a5dbd25c81645f18f5bb88e88340ab..87c8f8a6e9d08e27998be39b03b3b21f55b9ba4c 100644 --- a/src/crash_report.zig +++ b/src/crash_report.zig @@ -150,11 +150,12 @@ fn dumpCrashContextSema(anal: *AnalyzeBody, stderr: *Io.Writer, crash_heap: []u8 var fba: std.heap.FixedBufferAllocator = .init(crash_heap); - const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu) orelse { - const file = zcu.fileByIndex(block.src_base_inst.resolveFile(&zcu.intern_pool)); + const file_index, const src_base_node = block.src_baseline.resolve(zcu) orelse { + const file = zcu.fileByIndex(block.src_baseline.inst.resolveFile(&zcu.intern_pool)); try stderr.print("Analyzing lost instruction in file '{f}'. This should not happen!\n\n", .{file.path.fmt(comp)}); return; }; + const file = zcu.fileByIndex(file_index); try stderr.print("Analyzing '{f}'\n", .{file.path.fmt(comp)}); @@ -180,9 +181,9 @@ fn dumpCrashContextSema(anal: *AnalyzeBody, stderr: *Io.Writer, crash_heap: []u8 var parent = anal.parent; while (parent) |curr| { fba.reset(); - const cur_block_file = zcu.fileByIndex(curr.block.src_base_inst.resolveFile(&zcu.intern_pool)); + const cur_block_file = zcu.fileByIndex(curr.block.src_baseline.inst.resolveFile(&zcu.intern_pool)); try stderr.print(" in {f}\n", .{cur_block_file.path.fmt(comp)}); - _, const cur_block_src_base_node = Zcu.LazySrcLoc.resolveBaseNode(curr.block.src_base_inst, zcu) orelse { + _, const cur_block_src_base_node = curr.block.src_baseline.resolve(zcu) orelse { try stderr.writeAll(" > [lost instruction; this should not happen]\n"); parent = curr.parent; continue; diff --git a/test/incremental/add_decls_around_bad_field_expr b/test/incremental/add_decls_around_bad_field_expr new file mode 100644 index 0000000000000000000000000000000000000000..b084be50e0df8e4ee192f39c658595baff996a4d --- /dev/null +++ b/test/incremental/add_decls_around_bad_field_expr @@ -0,0 +1,40 @@ +#exe { +-Mroot=main.zig +#} + +#write main.zig { +const pre = {}; + +thing: @This().does_not_exist, + +const post = {}; + +pub fn main() void { + _ = @as(@This(), undefined); +} +#} +#update +#check errors { +main.zig:3:15: error: root source file struct 'main' has no member named 'does_not_exist' +main.zig:1:1: note: struct declared here +#} + +#write main.zig { +const pre = {}; +const pre_1 = {}; + +thing: @This().does_not_exist, + +const post = {}; +const post_1 = {}; + +pub fn main() void { + _ = @as(@This(), undefined); +} +#} +#update { +add more decls around error#} +#check errors { +main.zig:4:15: error: root source file struct 'main' has no member named 'does_not_exist' +main.zig:1:1: note: struct declared here +#} -- 2.54.0