Commit 80575face7

Veikka Tuominen <git@vexu.eu>
2022-11-21 23:23:08
AstGen: implement tuple declarations
1 parent 6fb689e
src/AstGen.zig
@@ -4386,6 +4386,7 @@ fn structDeclInner(
             .backing_int_body_len = 0,
             .known_non_opv = false,
             .known_comptime_only = false,
+            .is_tuple = false,
         });
         return indexToRef(decl_inst);
     }
@@ -4467,22 +4468,59 @@ fn structDeclInner(
     // No defer needed here because it is handled by `wip_members.deinit()` above.
     const bodies_start = astgen.scratch.items.len;
 
+    var is_tuple = false;
+    const node_tags = tree.nodes.items(.tag);
+    for (container_decl.ast.members) |member_node| {
+        switch (node_tags[member_node]) {
+            .container_field_init => is_tuple = tree.containerFieldInit(member_node).ast.tuple_like,
+            .container_field_align => is_tuple = tree.containerFieldAlign(member_node).ast.tuple_like,
+            .container_field => is_tuple = tree.containerField(member_node).ast.tuple_like,
+            else => continue,
+        }
+        if (is_tuple) break;
+    }
+    if (is_tuple) for (container_decl.ast.members) |member_node| {
+        switch (node_tags[member_node]) {
+            .container_field_init,
+            .container_field_align,
+            .container_field,
+            .@"comptime",
+            => continue,
+            else => {
+                return astgen.failNode(member_node, "tuple declarations cannot contain declarations", .{});
+            },
+        }
+    };
+
     var known_non_opv = false;
     var known_comptime_only = false;
     for (container_decl.ast.members) |member_node| {
-        const member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
+        var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
             .decl => continue,
             .field => |field| field,
         };
+        if (member.ast.tuple_like and !is_tuple and member.ast.type_expr != 0 and
+            astgen.tree.nodes.items(.tag)[member.ast.type_expr] == .identifier)
+        {
+            const ident = astgen.tree.nodes.items(.main_token)[member.ast.type_expr];
+            member.ast.tuple_like = false;
+            member.ast.main_token = ident;
+            member.ast.type_expr = 0;
+        } else if (is_tuple and !member.ast.tuple_like) {
+            return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});
+        }
 
-        const field_name = try astgen.identAsString(member.ast.name_token);
-        wip_members.appendToField(field_name);
+        if (!is_tuple) {
+            if (member.ast.tuple_like) return astgen.failTok(member.ast.main_token, "struct field missing name", .{});
+            const field_name = try astgen.identAsString(member.ast.main_token);
+            wip_members.appendToField(field_name);
+        }
 
         const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
         wip_members.appendToField(doc_comment_index);
 
         if (member.ast.type_expr == 0) {
-            return astgen.failTok(member.ast.name_token, "struct field missing type", .{});
+            return astgen.failTok(member.ast.main_token, "struct field missing type", .{});
         }
 
         const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
@@ -4562,6 +4600,7 @@ fn structDeclInner(
         .backing_int_body_len = @intCast(u32, backing_int_body_len),
         .known_non_opv = known_non_opv,
         .known_comptime_only = known_comptime_only,
+        .is_tuple = is_tuple,
     });
 
     wip_members.finishBits(bits_per_field);
@@ -4640,15 +4679,25 @@ fn unionDeclInner(
     defer wip_members.deinit();
 
     for (members) |member_node| {
-        const member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
+        var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
             .decl => continue,
             .field => |field| field,
         };
+        if (member.ast.tuple_like and member.ast.type_expr != 0 and
+            astgen.tree.nodes.items(.tag)[member.ast.type_expr] == .identifier)
+        {
+            const ident = astgen.tree.nodes.items(.main_token)[member.ast.type_expr];
+            member.ast.tuple_like = false;
+            member.ast.main_token = ident;
+            member.ast.type_expr = 0;
+        } else if (member.ast.tuple_like) {
+            return astgen.failTok(member.ast.main_token, "union field missing name", .{});
+        }
         if (member.comptime_token) |comptime_token| {
             return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{});
         }
 
-        const field_name = try astgen.identAsString(member.ast.name_token);
+        const field_name = try astgen.identAsString(member.ast.main_token);
         wip_members.appendToField(field_name);
 
         const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
@@ -4787,7 +4836,7 @@ fn containerDecl(
                 var nonexhaustive_node: Ast.Node.Index = 0;
                 var nonfinal_nonexhaustive = false;
                 for (container_decl.ast.members) |member_node| {
-                    const member = switch (node_tags[member_node]) {
+                    var member = switch (node_tags[member_node]) {
                         .container_field_init => tree.containerFieldInit(member_node),
                         .container_field_align => tree.containerFieldAlign(member_node),
                         .container_field => tree.containerField(member_node),
@@ -4796,6 +4845,16 @@ fn containerDecl(
                             continue;
                         },
                     };
+                    if (member.ast.tuple_like and member.ast.type_expr != 0 and
+                        astgen.tree.nodes.items(.tag)[member.ast.type_expr] == .identifier)
+                    {
+                        const ident = astgen.tree.nodes.items(.main_token)[member.ast.type_expr];
+                        member.ast.tuple_like = false;
+                        member.ast.main_token = ident;
+                        member.ast.type_expr = 0;
+                    } else if (member.ast.tuple_like) {
+                        return astgen.failTok(member.ast.main_token, "enum field missing name", .{});
+                    }
                     if (member.comptime_token) |comptime_token| {
                         return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{});
                     }
@@ -4816,7 +4875,7 @@ fn containerDecl(
                     // Alignment expressions in enums are caught by the parser.
                     assert(member.ast.align_expr == 0);
 
-                    const name_token = member.ast.name_token;
+                    const name_token = member.ast.main_token;
                     if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
                         if (nonexhaustive_node != 0) {
                             return astgen.failNodeNotes(
@@ -4915,15 +4974,23 @@ fn containerDecl(
             for (container_decl.ast.members) |member_node| {
                 if (member_node == counts.nonexhaustive_node)
                     continue;
-                const member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
+                var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
                     .decl => continue,
                     .field => |field| field,
                 };
+                if (member.ast.tuple_like and member.ast.type_expr != 0 and
+                    astgen.tree.nodes.items(.tag)[member.ast.type_expr] == .identifier)
+                {
+                    const ident = astgen.tree.nodes.items(.main_token)[member.ast.type_expr];
+                    member.ast.tuple_like = false;
+                    member.ast.main_token = ident;
+                    member.ast.type_expr = 0;
+                }
                 assert(member.comptime_token == null);
                 assert(member.ast.type_expr == 0);
                 assert(member.ast.align_expr == 0);
 
-                const field_name = try astgen.identAsString(member.ast.name_token);
+                const field_name = try astgen.identAsString(member.ast.main_token);
                 wip_members.appendToField(field_name);
 
                 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
@@ -11786,6 +11853,7 @@ const GenZir = struct {
         layout: std.builtin.Type.ContainerLayout,
         known_non_opv: bool,
         known_comptime_only: bool,
+        is_tuple: bool,
     }) !void {
         const astgen = gz.astgen;
         const gpa = astgen.gpa;
@@ -11820,6 +11888,7 @@ const GenZir = struct {
                     .has_backing_int = args.backing_int_ref != .none,
                     .known_non_opv = args.known_non_opv,
                     .known_comptime_only = args.known_comptime_only,
+                    .is_tuple = args.is_tuple,
                     .name_strategy = gz.anon_name_strategy,
                     .layout = args.layout,
                 }),
src/Module.zig
@@ -6138,7 +6138,7 @@ fn queryFieldSrc(
                 .name => .{
                     .file_scope = file_scope,
                     .parent_decl_node = 0,
-                    .lazy = .{ .token_abs = field.ast.name_token },
+                    .lazy = .{ .token_abs = field.ast.main_token },
                 },
                 .type => .{
                     .file_scope = file_scope,
src/print_zir.zig
@@ -1262,6 +1262,7 @@ const Writer = struct {
 
         try self.writeFlag(stream, "known_non_opv, ", small.known_non_opv);
         try self.writeFlag(stream, "known_comptime_only, ", small.known_comptime_only);
+        try self.writeFlag(stream, "tuple, ", small.is_tuple);
 
         try stream.print("{s}, ", .{@tagName(small.name_strategy)});
 
@@ -1335,8 +1336,11 @@ const Writer = struct {
                     const has_type_body = @truncate(u1, cur_bit_bag) != 0;
                     cur_bit_bag >>= 1;
 
-                    const field_name = self.code.extra[extra_index];
-                    extra_index += 1;
+                    var field_name: u32 = 0;
+                    if (!small.is_tuple) {
+                        field_name = self.code.extra[extra_index];
+                        extra_index += 1;
+                    }
                     const doc_comment_index = self.code.extra[extra_index];
                     extra_index += 1;
 
@@ -1370,13 +1374,16 @@ const Writer = struct {
             try stream.writeAll("{\n");
             self.indent += 2;
 
-            for (fields) |field| {
-                const field_name = self.code.nullTerminatedString(field.name);
-
+            for (fields) |field, i| {
                 try self.writeDocComment(stream, field.doc_comment_index);
                 try stream.writeByteNTimes(' ', self.indent);
                 try self.writeFlag(stream, "comptime ", field.is_comptime);
-                try stream.print("{}: ", .{std.zig.fmtId(field_name)});
+                if (field.name != 0) {
+                    const field_name = self.code.nullTerminatedString(field.name);
+                    try stream.print("{}: ", .{std.zig.fmtId(field_name)});
+                } else {
+                    try stream.print("@\"{d}\": ", .{i});
+                }
                 if (field.field_type != .none) {
                     try self.writeInstRef(stream, field.field_type);
                 }
src/Zir.zig
@@ -3166,7 +3166,7 @@ pub const Inst = struct {
     ///      0b0X00: whether corresponding field is comptime
     ///      0bX000: whether corresponding field has a type expression
     /// 9. fields: { // for every fields_len
-    ///        field_name: u32,
+    ///        field_name: u32, // if !is_tuple
     ///        doc_comment: u32, // 0 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
@@ -3186,9 +3186,10 @@ pub const Inst = struct {
             has_backing_int: bool,
             known_non_opv: bool,
             known_comptime_only: bool,
+            is_tuple: bool,
             name_strategy: NameStrategy,
             layout: std.builtin.Type.ContainerLayout,
-            _: u6 = undefined,
+            _: u5 = undefined,
         };
     };