Commit 847c34ac66

mlugg <mlugg@mlugg.co.uk>
2024-12-15 17:32:55
compiler: remove doc comments from Zir
This code was left over from the legacy Autodoc implementation. No component of the compiler pipeline actually requires doc comments, so it is a waste of time and space to store them in ZIR.
1 parent af89bb0
lib/std/zig/AstGen.zig
@@ -1377,7 +1377,7 @@ fn fnProtoExpr(
                 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
                 // We pass `prev_param_insts` as `&.{}` here because a function prototype can't refer to previous
                 // arguments (we haven't set up scopes here).
-                const param_inst = try block_scope.addParam(&param_gz, &.{}, tag, name_token, param_name, param.first_doc_comment);
+                const param_inst = try block_scope.addParam(&param_gz, &.{}, tag, name_token, param_name);
                 assert(param_inst_expected == param_inst);
             }
         }
@@ -4172,8 +4172,6 @@ fn fnDecl(
         break :blk token_tags[maybe_noinline_token] == .keyword_noinline;
     };
 
-    const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken());
-
     wip_members.nextDecl(decl_inst);
 
     // Note that the capacity here may not be sufficient, as this does not include `anytype` parameters.
@@ -4263,7 +4261,7 @@ fn fnDecl(
                 const main_tokens = tree.nodes.items(.main_token);
                 const name_token = param.name_token orelse main_tokens[param_type_node];
                 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
-                const param_inst = try decl_gz.addParam(&param_gz, param_insts.items, tag, name_token, param_name, param.first_doc_comment);
+                const param_inst = try decl_gz.addParam(&param_gz, param_insts.items, tag, name_token, param_name);
                 assert(param_inst_expected == param_inst);
                 break :param param_inst.toRef();
             };
@@ -4525,7 +4523,6 @@ fn fnDecl(
         decl_gz.decl_line,
         is_pub,
         is_export,
-        doc_comment_index,
         &decl_gz,
         // align, linksection, and addrspace are passed in the func instruction in this case.
         // TODO: move them from the function instruction to the declaration instruction?
@@ -4598,8 +4595,6 @@ fn globalVarDecl(
         break :blk lib_name_str.index;
     } else .empty;
 
-    const doc_comment_index = try astgen.docCommentAsString(var_decl.firstToken());
-
     assert(var_decl.comptime_token == null); // handled by parser
 
     const var_inst: Zir.Inst.Ref = if (var_decl.ast.init_node != 0) vi: {
@@ -4698,7 +4693,6 @@ fn globalVarDecl(
         block_scope.decl_line,
         is_pub,
         is_export,
-        doc_comment_index,
         &block_scope,
         .{
             .align_gz = &align_gz,
@@ -4756,7 +4750,6 @@ fn comptimeDecl(
         decl_block.decl_line,
         false,
         false,
-        .empty,
         &decl_block,
         null,
     );
@@ -4814,7 +4807,6 @@ fn usingnamespaceDecl(
         decl_block.decl_line,
         is_pub,
         false,
-        .empty,
         &decl_block,
         null,
     );
@@ -4932,7 +4924,7 @@ fn testDecl(
                 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
             }
 
-            break :blk .{ .decltest = name_str_index };
+            break :blk .{ .decltest = test_name_token };
         },
     };
 
@@ -5021,7 +5013,6 @@ fn testDecl(
         decl_block.decl_line,
         false,
         false,
-        .empty,
         &decl_block,
         null,
     );
@@ -5174,9 +5165,6 @@ fn structDeclInner(
         assert(!member.ast.tuple_like);
         wip_members.appendToField(@intFromEnum(field_name));
 
-        const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
-        wip_members.appendToField(@intFromEnum(doc_comment_index));
-
         if (member.ast.type_expr == 0) {
             return astgen.failTok(member.ast.main_token, "struct field missing type", .{});
         }
@@ -5448,7 +5436,7 @@ fn unionDeclInner(
         .none;
 
     const bits_per_field = 4;
-    const max_field_size = 5;
+    const max_field_size = 4;
     var any_aligned_fields = false;
     var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
     defer wip_members.deinit();
@@ -5479,9 +5467,6 @@ fn unionDeclInner(
         const field_name = try astgen.identAsString(member.ast.main_token);
         wip_members.appendToField(@intFromEnum(field_name));
 
-        const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
-        wip_members.appendToField(@intFromEnum(doc_comment_index));
-
         const have_type = member.ast.type_expr != 0;
         const have_align = member.ast.align_expr != 0;
         const have_value = member.ast.value_expr != 0;
@@ -5744,7 +5729,7 @@ fn containerDecl(
                 .none;
 
             const bits_per_field = 1;
-            const max_field_size = 3;
+            const max_field_size = 2;
             var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(counts.decls), @intCast(counts.total_fields), bits_per_field, max_field_size);
             defer wip_members.deinit();
 
@@ -5772,9 +5757,6 @@ fn containerDecl(
                 const field_name = try astgen.identAsString(member.ast.main_token);
                 wip_members.appendToField(@intFromEnum(field_name));
 
-                const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
-                wip_members.appendToField(@intFromEnum(doc_comment_index));
-
                 const have_value = member.ast.value_expr != 0;
                 wip_members.nextField(bits_per_field, .{have_value});
 
@@ -6054,10 +6036,7 @@ fn errorSetDecl(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zi
                     }
                     gop.value_ptr.* = tok_i;
 
-                    try astgen.extra.ensureUnusedCapacity(gpa, 2);
-                    astgen.extra.appendAssumeCapacity(@intFromEnum(str_index));
-                    const doc_comment_index = try astgen.docCommentAsString(tok_i);
-                    astgen.extra.appendAssumeCapacity(@intFromEnum(doc_comment_index));
+                    try astgen.extra.append(gpa, @intFromEnum(str_index));
                     fields_len += 1;
                 },
                 .r_brace => break,
@@ -11719,73 +11698,6 @@ fn identAsString(astgen: *AstGen, ident_token: Ast.TokenIndex) !Zir.NullTerminat
     }
 }
 
-/// Adds a doc comment block to `string_bytes` by walking backwards from `end_token`.
-/// `end_token` must point at the first token after the last doc comment line.
-/// Returns 0 if no doc comment is present.
-fn docCommentAsString(astgen: *AstGen, end_token: Ast.TokenIndex) !Zir.NullTerminatedString {
-    if (end_token == 0) return .empty;
-
-    const token_tags = astgen.tree.tokens.items(.tag);
-
-    var tok = end_token - 1;
-    while (token_tags[tok] == .doc_comment) {
-        if (tok == 0) break;
-        tok -= 1;
-    } else {
-        tok += 1;
-    }
-
-    return docCommentAsStringFromFirst(astgen, end_token, tok);
-}
-
-/// end_token must be > the index of the last doc comment.
-fn docCommentAsStringFromFirst(
-    astgen: *AstGen,
-    end_token: Ast.TokenIndex,
-    start_token: Ast.TokenIndex,
-) !Zir.NullTerminatedString {
-    if (start_token == end_token) return .empty;
-
-    const gpa = astgen.gpa;
-    const string_bytes = &astgen.string_bytes;
-    const str_index: u32 = @intCast(string_bytes.items.len);
-    const token_starts = astgen.tree.tokens.items(.start);
-    const token_tags = astgen.tree.tokens.items(.tag);
-
-    const total_bytes = token_starts[end_token] - token_starts[start_token];
-    try string_bytes.ensureUnusedCapacity(gpa, total_bytes);
-
-    var current_token = start_token;
-    while (current_token < end_token) : (current_token += 1) {
-        switch (token_tags[current_token]) {
-            .doc_comment => {
-                const tok_bytes = astgen.tree.tokenSlice(current_token)[3..];
-                string_bytes.appendSliceAssumeCapacity(tok_bytes);
-                if (current_token != end_token - 1) {
-                    string_bytes.appendAssumeCapacity('\n');
-                }
-            },
-            else => break,
-        }
-    }
-
-    const key: []const u8 = string_bytes.items[str_index..];
-    const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
-        .bytes = string_bytes,
-    }, StringIndexContext{
-        .bytes = string_bytes,
-    });
-
-    if (gop.found_existing) {
-        string_bytes.shrinkRetainingCapacity(str_index);
-        return @enumFromInt(gop.key_ptr.*);
-    } else {
-        gop.key_ptr.* = str_index;
-        try string_bytes.append(gpa, 0);
-        return @enumFromInt(str_index);
-    }
-}
-
 const IndexSlice = struct { index: Zir.NullTerminatedString, len: u32 };
 
 fn strLitAsString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !IndexSlice {
@@ -12722,7 +12634,6 @@ const GenZir = struct {
         /// Absolute token index. This function does the conversion to Decl offset.
         abs_tok_index: Ast.TokenIndex,
         name: Zir.NullTerminatedString,
-        first_doc_comment: ?Ast.TokenIndex,
     ) !Zir.Inst.Index {
         const gpa = gz.astgen.gpa;
         const param_body = param_gz.instructionsSlice();
@@ -12730,14 +12641,8 @@ const GenZir = struct {
         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
         try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).@"struct".fields.len + body_len);
 
-        const doc_comment_index = if (first_doc_comment) |first|
-            try gz.astgen.docCommentAsStringFromFirst(abs_tok_index, first)
-        else
-            .empty;
-
         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{
             .name = name,
-            .doc_comment = doc_comment_index,
             .body_len = @intCast(body_len),
         });
         gz.astgen.appendBodyWithFixupsExtraRefsArrayList(&gz.astgen.extra, param_body, prev_param_insts);
@@ -14143,8 +14048,8 @@ fn lowerAstErrors(astgen: *AstGen) !void {
 const DeclarationName = union(enum) {
     named: Ast.TokenIndex,
     named_test: Ast.TokenIndex,
+    decltest: Ast.TokenIndex,
     unnamed_test,
-    decltest: Zir.NullTerminatedString,
     @"comptime",
     @"usingnamespace",
 };
@@ -14174,7 +14079,6 @@ fn addFailedDeclaration(
         gz.astgen.source_line,
         is_pub,
         false, // we don't care about exports since semantic analysis will fail
-        .empty,
         &decl_gz,
         null,
     );
@@ -14189,7 +14093,6 @@ fn setDeclaration(
     src_line: u32,
     is_pub: bool,
     is_export: bool,
-    doc_comment: Zir.NullTerminatedString,
     value_gz: *GenZir,
     /// May be `null` if all these blocks would be empty.
     /// If `null`, then `value_gz` must have nothing stacked on it.
@@ -14218,11 +14121,6 @@ fn setDeclaration(
     const linksection_len = astgen.countBodyLenAfterFixups(linksection_body);
     const addrspace_len = astgen.countBodyLenAfterFixups(addrspace_body);
 
-    const true_doc_comment: Zir.NullTerminatedString = switch (name) {
-        .decltest => |test_name| test_name,
-        else => doc_comment,
-    };
-
     const src_hash_arr: [4]u32 = @bitCast(src_hash);
 
     const extra: Zir.Inst.Declaration = .{
@@ -14233,8 +14131,14 @@ fn setDeclaration(
         .name = switch (name) {
             .named => |tok| @enumFromInt(@intFromEnum(try astgen.identAsString(tok))),
             .named_test => |tok| @enumFromInt(@intFromEnum(try astgen.testNameString(tok))),
+            .decltest => |tok| @enumFromInt(str_idx: {
+                const idx = astgen.string_bytes.items.len;
+                try astgen.string_bytes.append(gpa, 0); // indicates this is a test
+                try astgen.appendIdentStr(tok, &astgen.string_bytes);
+                try astgen.string_bytes.append(gpa, 0); // end of the string
+                break :str_idx idx;
+            }),
             .unnamed_test => .unnamed_test,
-            .decltest => .decltest,
             .@"comptime" => .@"comptime",
             .@"usingnamespace" => .@"usingnamespace",
         },
@@ -14243,14 +14147,11 @@ fn setDeclaration(
             .value_body_len = @intCast(value_len),
             .is_pub = is_pub,
             .is_export = is_export,
-            .has_doc_comment = true_doc_comment != .empty,
+            .test_is_decltest = name == .decltest,
             .has_align_linksection_addrspace = align_len != 0 or linksection_len != 0 or addrspace_len != 0,
         },
     };
     astgen.instructions.items(.data)[@intFromEnum(decl_inst)].declaration.payload_index = try astgen.addExtra(extra);
-    if (extra.flags.has_doc_comment) {
-        try astgen.extra.append(gpa, @intFromEnum(true_doc_comment));
-    }
     if (extra.flags.has_align_linksection_addrspace) {
         try astgen.extra.appendSlice(gpa, &.{
             align_len,
lib/std/zig/Zir.zig
@@ -2612,20 +2612,19 @@ pub const Inst = struct {
     };
 
     /// Trailing:
-    /// 0. doc_comment: u32          // if `has_doc_comment`; null-terminated string index
-    /// 1. align_body_len: u32       // if `has_align_linksection_addrspace`; 0 means no `align`
-    /// 2. linksection_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `linksection`
-    /// 3. addrspace_body_len: u32   // if `has_align_linksection_addrspace`; 0 means no `addrspace`
-    /// 4. value_body_inst: Zir.Inst.Index
+    /// 0. align_body_len: u32       // if `has_align_linksection_addrspace`; 0 means no `align`
+    /// 1. linksection_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `linksection`
+    /// 2. addrspace_body_len: u32   // if `has_align_linksection_addrspace`; 0 means no `addrspace`
+    /// 3. value_body_inst: Zir.Inst.Index
     ///    - for each `value_body_len`
     ///    - body to be exited via `break_inline` to this `declaration` instruction
-    /// 5. align_body_inst: Zir.Inst.Index
+    /// 4. align_body_inst: Zir.Inst.Index
     ///    - for each `align_body_len`
     ///    - body to be exited via `break_inline` to this `declaration` instruction
-    /// 6. linksection_body_inst: Zir.Inst.Index
+    /// 5. linksection_body_inst: Zir.Inst.Index
     ///    - for each `linksection_body_len`
     ///    - body to be exited via `break_inline` to this `declaration` instruction
-    /// 7. addrspace_body_inst: Zir.Inst.Index
+    /// 6. addrspace_body_inst: Zir.Inst.Index
     ///    - for each `addrspace_body_len`
     ///    - body to be exited via `break_inline` to this `declaration` instruction
     pub const Declaration = struct {
@@ -2643,7 +2642,7 @@ pub const Inst = struct {
             value_body_len: u28,
             is_pub: bool,
             is_export: bool,
-            has_doc_comment: bool,
+            test_is_decltest: bool,
             has_align_linksection_addrspace: bool,
         };
 
@@ -2651,9 +2650,6 @@ pub const Inst = struct {
             @"comptime" = std.math.maxInt(u32),
             @"usingnamespace" = std.math.maxInt(u32) - 1,
             unnamed_test = std.math.maxInt(u32) - 2,
-            /// In this case, `has_doc_comment` will be true, and the doc
-            /// comment body is the identifier name.
-            decltest = std.math.maxInt(u32) - 3,
             /// Other values are `NullTerminatedString` values, i.e. index into
             /// `string_bytes`. If the byte referenced is 0, the decl is a named
             /// test, and the actual name begins at the following byte.
@@ -2661,13 +2657,13 @@ pub const Inst = struct {
 
             pub fn isNamedTest(name: Name, zir: Zir) bool {
                 return switch (name) {
-                    .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => false,
+                    .@"comptime", .@"usingnamespace", .unnamed_test => false,
                     _ => zir.string_bytes[@intFromEnum(name)] == 0,
                 };
             }
             pub fn toString(name: Name, zir: Zir) ?NullTerminatedString {
                 switch (name) {
-                    .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => return null,
+                    .@"comptime", .@"usingnamespace", .unnamed_test => return null,
                     _ => {},
                 }
                 const idx: u32 = @intFromEnum(name);
@@ -2688,7 +2684,6 @@ pub const Inst = struct {
 
         pub fn getBodies(declaration: Declaration, extra_end: u32, zir: Zir) Bodies {
             var extra_index: u32 = extra_end;
-            extra_index += @intFromBool(declaration.flags.has_doc_comment);
             const value_body_len = declaration.flags.value_body_len;
             const align_body_len, const linksection_body_len, const addrspace_body_len = lens: {
                 if (!declaration.flags.has_align_linksection_addrspace) {
@@ -3059,7 +3054,6 @@ pub const Inst = struct {
     ///      0bX000: whether corresponding field has a type expression
     /// 9. fields: { // for every fields_len
     ///        field_name: u32,
-    ///        doc_comment: NullTerminatedString, // .empty if no doc comment
     ///        field_type: Ref, // if corresponding bit is not set. none means anytype.
     ///        field_type_body_len: u32, // if corresponding bit is set
     ///        align_body_len: u32, // if corresponding bit is set
@@ -3220,7 +3214,6 @@ pub const Inst = struct {
     ///    - the bit is whether corresponding field has an value expression
     /// 9. fields: { // for every fields_len
     ///        field_name: u32,
-    ///        doc_comment: u32, // .empty if no doc_comment
     ///        value: Ref, // if corresponding bit is set
     ///    }
     pub const EnumDecl = struct {
@@ -3263,9 +3256,7 @@ pub const Inst = struct {
     ///      0bX000: unused
     /// 9. fields: { // for every fields_len
     ///        field_name: NullTerminatedString, // null terminated string index
-    ///        doc_comment: NullTerminatedString, // .empty if no doc comment
     ///        field_type: Ref, // if corresponding bit is set
-    ///        - if none, means `anytype`.
     ///        align: Ref, // if corresponding bit is set
     ///        tag_value: Ref, // if corresponding bit is set
     ///    }
@@ -3328,10 +3319,7 @@ pub const Inst = struct {
     };
 
     /// Trailing:
-    /// { // for every fields_len
-    ///      field_name: NullTerminatedString // null terminated string index
-    ///     doc_comment: NullTerminatedString // null terminated string index
-    /// }
+    /// 0. field_name: NullTerminatedString // for every fields_len
     pub const ErrorSetDecl = struct {
         fields_len: u32,
     };
@@ -3474,8 +3462,6 @@ pub const Inst = struct {
     pub const Param = struct {
         /// Null-terminated string index.
         name: NullTerminatedString,
-        /// Null-terminated string index.
-        doc_comment: NullTerminatedString,
         /// The body contains the type of the parameter.
         body_len: u32,
     };
@@ -4163,7 +4149,7 @@ fn findTrackableInner(
                         const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
                         cur_bit_bag >>= 1;
 
-                        fields_extra_index += 2; // field_name, doc_comment
+                        fields_extra_index += 1; // field_name
 
                         if (has_type_body) {
                             const field_type_body_len = zir.extra[fields_extra_index];
src/link/Dwarf.zig
@@ -2207,7 +2207,6 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
                         .@"comptime",
                         .@"usingnamespace",
                         .unnamed_test,
-                        .decltest,
                         => DW.ACCESS.private,
                         _ => if (decl_extra.name.isNamedTest(file.zir))
                             DW.ACCESS.private
@@ -2257,7 +2256,6 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
                         .@"comptime",
                         .@"usingnamespace",
                         .unnamed_test,
-                        .decltest,
                         => DW.ACCESS.private,
                         _ => if (decl_extra.name.isNamedTest(file.zir))
                             DW.ACCESS.private
@@ -2305,7 +2303,6 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
                         .@"comptime",
                         .@"usingnamespace",
                         .unnamed_test,
-                        .decltest,
                         => DW.ACCESS.private,
                         _ => if (decl_extra.name.isNamedTest(file.zir))
                             DW.ACCESS.private
@@ -2547,7 +2544,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
     const decl_extra = file.zir.extraData(Zir.Inst.Declaration, decl_inst.data.declaration.payload_index);
 
     const is_test = switch (decl_extra.data.name) {
-        .unnamed_test, .decltest => true,
+        .unnamed_test => true,
         .@"comptime", .@"usingnamespace" => false,
         _ => decl_extra.data.name.isNamedTest(file.zir),
     };
src/Zcu/PerThread.zig
@@ -462,7 +462,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
                 while (it.next()) |decl_inst| {
                     const decl_name = old_zir.getDeclaration(decl_inst)[0].name;
                     switch (decl_name) {
-                        .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
+                        .@"comptime", .@"usingnamespace", .unnamed_test => continue,
                         _ => if (decl_name.isNamedTest(old_zir)) continue,
                     }
                     const name_zir = decl_name.toString(old_zir).?;
@@ -481,7 +481,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
                 while (it.next()) |decl_inst| {
                     const decl_name = new_zir.getDeclaration(decl_inst)[0].name;
                     switch (decl_name) {
-                        .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
+                        .@"comptime", .@"usingnamespace", .unnamed_test => continue,
                         _ => if (decl_name.isNamedTest(new_zir)) continue,
                     }
                     const name_zir = decl_name.toString(new_zir).?;
@@ -1929,22 +1929,12 @@ const ScanDeclIter = struct {
                     false,
                 };
             },
-            .decltest => info: {
-                // We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary.
-                if (iter.pass != .unnamed) return;
-                assert(declaration.flags.has_doc_comment);
-                const name = zir.nullTerminatedString(@enumFromInt(zir.extra[extra.end]));
-                break :info .{
-                    (try iter.avoidNameConflict("decltest.{s}", .{name})).toOptional(),
-                    .@"test",
-                    true,
-                };
-            },
             _ => if (declaration.name.isNamedTest(zir)) info: {
                 // We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary.
                 if (iter.pass != .unnamed) return;
+                const prefix = if (declaration.flags.test_is_decltest) "decltest" else "test";
                 break :info .{
-                    (try iter.avoidNameConflict("test.{s}", .{zir.nullTerminatedString(declaration.name.toString(zir).?)})).toOptional(),
+                    (try iter.avoidNameConflict("{s}.{s}", .{ prefix, zir.nullTerminatedString(declaration.name.toString(zir).?) })).toOptional(),
                     .@"test",
                     true,
                 };
src/print_zir.zig
@@ -952,11 +952,6 @@ const Writer = struct {
             std.zig.fmtEscapes(self.code.nullTerminatedString(extra.data.name)),
         });
 
-        if (extra.data.doc_comment != .empty) {
-            try stream.writeAll("\n");
-            try self.writeDocComment(stream, extra.data.doc_comment);
-            try stream.writeByteNTimes(' ', self.indent);
-        }
         try self.writeBracedBody(stream, body);
         try stream.writeAll(") ");
         try self.writeSrcTok(stream, inst_data.src_tok);
@@ -1482,7 +1477,6 @@ const Writer = struct {
             const fields_per_u32 = 32 / bits_per_field;
             const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
             const Field = struct {
-                doc_comment_index: Zir.NullTerminatedString,
                 type_len: u32 = 0,
                 align_len: u32 = 0,
                 init_len: u32 = 0,
@@ -1512,11 +1506,8 @@ const Writer = struct {
 
                     const field_name_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]);
                     extra_index += 1;
-                    const doc_comment_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]);
-                    extra_index += 1;
 
                     fields[field_i] = .{
-                        .doc_comment_index = doc_comment_index,
                         .is_comptime = is_comptime,
                         .name = field_name_index,
                     };
@@ -1544,7 +1535,6 @@ const Writer = struct {
             self.indent += 2;
 
             for (fields, 0..) |field, i| {
-                try self.writeDocComment(stream, field.doc_comment_index);
                 try stream.writeByteNTimes(' ', self.indent);
                 try self.writeFlag(stream, "comptime ", field.is_comptime);
                 if (field.name != .empty) {
@@ -1721,10 +1711,7 @@ const Writer = struct {
             const field_name_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]);
             const field_name = self.code.nullTerminatedString(field_name_index);
             extra_index += 1;
-            const doc_comment_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]);
-            extra_index += 1;
 
-            try self.writeDocComment(stream, doc_comment_index);
             try stream.writeByteNTimes(' ', self.indent);
             try stream.print("{p}", .{std.zig.fmtId(field_name)});
 
@@ -1870,11 +1857,6 @@ const Writer = struct {
                 const field_name = self.code.nullTerminatedString(@enumFromInt(self.code.extra[extra_index]));
                 extra_index += 1;
 
-                const doc_comment_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]);
-                extra_index += 1;
-
-                try self.writeDocComment(stream, doc_comment_index);
-
                 try stream.writeByteNTimes(' ', self.indent);
                 try stream.print("{p}", .{std.zig.fmtId(field_name)});
 
@@ -1987,12 +1969,10 @@ const Writer = struct {
         self.indent += 2;
 
         var extra_index = @as(u32, @intCast(extra.end));
-        const extra_index_end = extra_index + (extra.data.fields_len * 2);
-        while (extra_index < extra_index_end) : (extra_index += 2) {
+        const extra_index_end = extra_index + extra.data.fields_len;
+        while (extra_index < extra_index_end) : (extra_index += 1) {
             const name_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]);
             const name = self.code.nullTerminatedString(name_index);
-            const doc_comment_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index + 1]);
-            try self.writeDocComment(stream, doc_comment_index);
             try stream.writeByteNTimes(' ', self.indent);
             try stream.print("{p},\n", .{std.zig.fmtId(name)});
         }
@@ -2740,9 +2720,6 @@ const Writer = struct {
     fn writeDeclaration(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
         const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].declaration;
         const extra = self.code.extraData(Zir.Inst.Declaration, inst_data.payload_index);
-        const doc_comment: ?Zir.NullTerminatedString = if (extra.data.flags.has_doc_comment) dc: {
-            break :dc @enumFromInt(self.code.extra[extra.end]);
-        } else null;
 
         const prev_parent_decl_node = self.parent_decl_node;
         defer self.parent_decl_node = prev_parent_decl_node;
@@ -2754,10 +2731,11 @@ const Writer = struct {
             .@"comptime" => try stream.writeAll("comptime"),
             .@"usingnamespace" => try stream.writeAll("usingnamespace"),
             .unnamed_test => try stream.writeAll("test"),
-            .decltest => try stream.print("decltest '{s}'", .{self.code.nullTerminatedString(doc_comment.?)}),
             _ => {
                 const name = extra.data.name.toString(self.code).?;
-                const prefix = if (extra.data.name.isNamedTest(self.code)) "test " else "";
+                const prefix = if (extra.data.name.isNamedTest(self.code)) p: {
+                    break :p if (extra.data.flags.test_is_decltest) "decltest " else "test ";
+                } else "";
                 try stream.print("{s}'{s}'", .{ prefix, self.code.nullTerminatedString(name) });
             },
         }
@@ -2963,17 +2941,6 @@ const Writer = struct {
         }
     }
 
-    fn writeDocComment(self: *Writer, stream: anytype, doc_comment_index: Zir.NullTerminatedString) !void {
-        if (doc_comment_index != .empty) {
-            const doc_comment = self.code.nullTerminatedString(doc_comment_index);
-            var it = std.mem.tokenizeScalar(u8, doc_comment, '\n');
-            while (it.next()) |doc_line| {
-                try stream.writeByteNTimes(' ', self.indent);
-                try stream.print("///{s}\n", .{doc_line});
-            }
-        }
-    }
-
     fn writeBody(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void {
         for (body) |inst| {
             try stream.writeByteNTimes(' ', self.indent);
src/Sema.zig
@@ -3399,8 +3399,8 @@ fn zirErrorSetDecl(
     try names.ensureUnusedCapacity(sema.arena, extra.data.fields_len);
 
     var extra_index: u32 = @intCast(extra.end);
-    const extra_index_end = extra_index + (extra.data.fields_len * 2);
-    while (extra_index < extra_index_end) : (extra_index += 2) { // +2 to skip over doc_string
+    const extra_index_end = extra_index + extra.data.fields_len;
+    while (extra_index < extra_index_end) : (extra_index += 1) {
         const name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_index]);
         const name = sema.code.nullTerminatedString(name_index);
         const name_ip = try zcu.intern_pool.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
@@ -36801,7 +36801,7 @@ fn structFields(
             if (is_comptime) struct_type.setFieldComptime(ip, field_i);
 
             const field_name_zir: [:0]const u8 = zir.nullTerminatedString(@enumFromInt(zir.extra[extra_index]));
-            extra_index += 2; // field_name, doc_comment
+            extra_index += 1; // field_name
 
             fields[field_i] = .{};
 
@@ -36981,7 +36981,7 @@ fn structFieldInits(
             const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
             cur_bit_bag >>= 1;
 
-            extra_index += 2; // field_name, doc_comment
+            extra_index += 1; // field_name
 
             fields[field_i] = .{};
 
@@ -37202,9 +37202,6 @@ fn unionFields(
         const field_name_zir = zir.nullTerminatedString(field_name_index);
         extra_index += 1;
 
-        // doc_comment
-        extra_index += 1;
-
         const field_type_ref: Zir.Inst.Ref = if (has_type) blk: {
             const field_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
             extra_index += 1;
@@ -39069,7 +39066,7 @@ pub fn resolveDeclaredEnum(
 
         const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]);
         const field_name_zir = zir.nullTerminatedString(field_name_index);
-        extra_index += 2; // field name, doc comment
+        extra_index += 1; // field name
 
         const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls);
 
src/Zcu.zig
@@ -2643,6 +2643,9 @@ pub fn mapOldZirToNew(
         // Maps test name to `declaration` instruction.
         var named_tests: std.StringHashMapUnmanaged(Zir.Inst.Index) = .empty;
         defer named_tests.deinit(gpa);
+        // Maps test name to `declaration` instruction.
+        var named_decltests: std.StringHashMapUnmanaged(Zir.Inst.Index) = .empty;
+        defer named_decltests.deinit(gpa);
         // All unnamed tests, in order, for a best-effort match.
         var unnamed_tests: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty;
         defer unnamed_tests.deinit(gpa);
@@ -2660,12 +2663,16 @@ pub fn mapOldZirToNew(
                 switch (old_decl.name) {
                     .@"comptime" => try comptime_decls.append(gpa, old_decl_inst),
                     .@"usingnamespace" => try usingnamespace_decls.append(gpa, old_decl_inst),
-                    .unnamed_test, .decltest => try unnamed_tests.append(gpa, old_decl_inst),
+                    .unnamed_test => try unnamed_tests.append(gpa, old_decl_inst),
                     _ => {
                         const name_nts = old_decl.name.toString(old_zir).?;
                         const name = old_zir.nullTerminatedString(name_nts);
                         if (old_decl.name.isNamedTest(old_zir)) {
-                            try named_tests.put(gpa, name, old_decl_inst);
+                            if (old_decl.flags.test_is_decltest) {
+                                try named_decltests.put(gpa, name, old_decl_inst);
+                            } else {
+                                try named_tests.put(gpa, name, old_decl_inst);
+                            }
                         } else {
                             try named_decls.put(gpa, name, old_decl_inst);
                         }
@@ -2683,8 +2690,8 @@ pub fn mapOldZirToNew(
             const new_decl, _ = new_zir.getDeclaration(new_decl_inst);
             // Attempt to match this to a declaration in the old ZIR:
             // * For named declarations (`const`/`var`/`fn`), we match based on name.
-            // * For named tests (`test "foo"`), we also match based on name.
-            // * For unnamed tests and decltests, we match based on order.
+            // * For named tests (`test "foo"`) and decltests (`test foo`), we also match based on name.
+            // * For unnamed tests, we match based on order.
             // * For comptime blocks, we match based on order.
             // * For usingnamespace decls, we match based on order.
             // If we cannot match this declaration, we can't match anything nested inside of it either, so we just `continue`.
@@ -2699,7 +2706,7 @@ pub fn mapOldZirToNew(
                     defer usingnamespace_decl_idx += 1;
                     break :inst usingnamespace_decls.items[usingnamespace_decl_idx];
                 },
-                .unnamed_test, .decltest => inst: {
+                .unnamed_test => inst: {
                     if (unnamed_test_idx == unnamed_tests.items.len) continue;
                     defer unnamed_test_idx += 1;
                     break :inst unnamed_tests.items[unnamed_test_idx];
@@ -2708,7 +2715,11 @@ pub fn mapOldZirToNew(
                     const name_nts = new_decl.name.toString(new_zir).?;
                     const name = new_zir.nullTerminatedString(name_nts);
                     if (new_decl.name.isNamedTest(new_zir)) {
-                        break :inst named_tests.get(name) orelse continue;
+                        if (new_decl.flags.test_is_decltest) {
+                            break :inst named_decltests.get(name) orelse continue;
+                        } else {
+                            break :inst named_tests.get(name) orelse continue;
+                        }
                     } else {
                         break :inst named_decls.get(name) orelse continue;
                     }
@@ -3329,7 +3340,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
                     else => a: {
                         if (!comp.config.is_test) break :a false;
                         if (file.mod != zcu.main_mod) break :a false;
-                        if (declaration.name.isNamedTest(zir) or declaration.name == .decltest) {
+                        if (declaration.name.isNamedTest(zir)) {
                             const nav = ip.getCau(cau).owner.unwrap().nav;
                             const fqn_slice = ip.getNav(nav).fqn.toSlice(ip);
                             for (comp.test_filters) |test_filter| {