authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-12 18:40:47-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-15 19:06:39-07:00
log0e50a0c1e53a062e7c68ceeb1cfab9597a9caa23
treec156e6679c354c74c1ef2d18ef374441a3689c00
parentbcfebb4b2b17dcac445fc5dedbbd259cc8c2f306

stage2: implement non-trivial enums


3 files changed, 337 insertions(+), 29 deletions(-)

src/AstGen.zig+89-2
......@@ -1932,7 +1932,7 @@ fn containerDecl(
19321932 // ZIR for all the field types, alignments, and default value expressions.
19331933
19341934 const arg_inst: zir.Inst.Ref = if (container_decl.ast.arg != 0)
1935 try comptimeExpr(gz, scope, .none, container_decl.ast.arg)
1935 try comptimeExpr(gz, scope, .{ .ty = .type_type }, container_decl.ast.arg)
19361936 else
19371937 .none;
19381938
......@@ -2006,6 +2006,9 @@ fn containerDecl(
20062006 }
20072007 total_fields += 1;
20082008 if (member.ast.value_expr != 0) {
2009 if (arg_inst == .none) {
2010 return mod.failNode(scope, member.ast.value_expr, "value assigned to enum tag with inferred tag type", .{});
2011 }
20092012 values += 1;
20102013 }
20112014 }
......@@ -2118,7 +2121,91 @@ fn containerDecl(
21182121 // In this case we must generate ZIR code for the tag values, similar to
21192122 // how structs are handled above. The new anonymous Decl will be created in
21202123 // Sema, not AstGen.
2121 return mod.failNode(scope, node, "TODO AstGen for enum decl with decls or explicitly provided field values", .{});
2124 const tag: zir.Inst.Tag = if (counts.nonexhaustive_node == 0)
2125 .enum_decl
2126 else
2127 .enum_decl_nonexhaustive;
2128 if (counts.total_fields == 0) {
2129 return gz.addPlNode(tag, node, zir.Inst.EnumDecl{
2130 .tag_type = arg_inst,
2131 .fields_len = 0,
2132 .body_len = 0,
2133 });
2134 }
2135
2136 // The enum_decl instruction introduces a scope in which the decls of the enum
2137 // are in scope, so that tag values can refer to decls within the enum itself.
2138 var block_scope: GenZir = .{
2139 .parent = scope,
2140 .astgen = astgen,
2141 .force_comptime = true,
2142 };
2143 defer block_scope.instructions.deinit(gpa);
2144
2145 var fields_data = ArrayListUnmanaged(u32){};
2146 defer fields_data.deinit(gpa);
2147
2148 try fields_data.ensureCapacity(gpa, counts.total_fields + counts.values);
2149
2150 // We only need this if there are greater than 32 fields.
2151 var bit_bag = ArrayListUnmanaged(u32){};
2152 defer bit_bag.deinit(gpa);
2153
2154 var cur_bit_bag: u32 = 0;
2155 var field_index: usize = 0;
2156 for (container_decl.ast.members) |member_node| {
2157 if (member_node == counts.nonexhaustive_node)
2158 continue;
2159 const member = switch (node_tags[member_node]) {
2160 .container_field_init => tree.containerFieldInit(member_node),
2161 .container_field_align => tree.containerFieldAlign(member_node),
2162 .container_field => tree.containerField(member_node),
2163 else => continue,
2164 };
2165 if (field_index % 32 == 0 and field_index != 0) {
2166 try bit_bag.append(gpa, cur_bit_bag);
2167 cur_bit_bag = 0;
2168 }
2169 assert(member.comptime_token == null);
2170 assert(member.ast.type_expr == 0);
2171 assert(member.ast.align_expr == 0);
2172
2173 const field_name = try gz.identAsString(member.ast.name_token);
2174 fields_data.appendAssumeCapacity(field_name);
2175
2176 const have_value = member.ast.value_expr != 0;
2177 cur_bit_bag = (cur_bit_bag >> 1) |
2178 (@as(u32, @boolToInt(have_value)) << 31);
2179
2180 if (have_value) {
2181 const tag_value_inst = try expr(&block_scope, &block_scope.base, .{ .ty = arg_inst }, member.ast.value_expr);
2182 fields_data.appendAssumeCapacity(@enumToInt(tag_value_inst));
2183 }
2184
2185 field_index += 1;
2186 }
2187 const empty_slot_count = 32 - (field_index % 32);
2188 cur_bit_bag >>= @intCast(u5, empty_slot_count);
2189
2190 const decl_inst = try gz.addBlock(tag, node);
2191 try gz.instructions.append(gpa, decl_inst);
2192 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
2193
2194 try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len +
2195 @typeInfo(zir.Inst.EnumDecl).Struct.fields.len +
2196 bit_bag.items.len + 1 + fields_data.items.len +
2197 block_scope.instructions.items.len);
2198 const zir_datas = astgen.instructions.items(.data);
2199 zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.EnumDecl{
2200 .tag_type = arg_inst,
2201 .body_len = @intCast(u32, block_scope.instructions.items.len),
2202 .fields_len = @intCast(u32, field_index),
2203 });
2204 astgen.extra.appendSliceAssumeCapacity(block_scope.instructions.items);
2205 astgen.extra.appendSliceAssumeCapacity(bit_bag.items); // Likely empty.
2206 astgen.extra.appendAssumeCapacity(cur_bit_bag);
2207 astgen.extra.appendSliceAssumeCapacity(fields_data.items);
2208 return rvalue(gz, scope, rl, astgen.indexToRef(decl_inst), node);
21222209 },
21232210 .keyword_opaque => {
21242211 const result = try gz.addNode(.opaque_decl, node);
src/Sema.zig+166-13
......@@ -542,7 +542,7 @@ fn zirStructDecl(
542542 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
543543 const fields_len = extra.data.fields_len;
544544
545 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
545 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
546546
547547 const struct_obj = try new_decl_arena.allocator.create(Module.Struct);
548548 const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj);
......@@ -602,7 +602,7 @@ fn zirStructDecl(
602602 // should be the struct itself. Thus we need a new Sema.
603603 var struct_sema: Sema = .{
604604 .mod = sema.mod,
605 .gpa = sema.mod.gpa,
605 .gpa = gpa,
606606 .arena = &new_decl_arena.allocator,
607607 .code = sema.code,
608608 .inst_map = sema.inst_map,
......@@ -632,7 +632,7 @@ fn zirStructDecl(
632632 }
633633 const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable;
634634 const body_end = extra.end + body.len;
635 var field_index: usize = body_end + bit_bags_count;
635 var extra_index: usize = body_end + bit_bags_count;
636636 var bit_bag_index: usize = body_end;
637637 var cur_bit_bag: u32 = undefined;
638638 var field_i: u32 = 0;
......@@ -646,10 +646,10 @@ fn zirStructDecl(
646646 const has_default = @truncate(u1, cur_bit_bag) != 0;
647647 cur_bit_bag >>= 1;
648648
649 const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[field_index]);
650 field_index += 1;
651 const field_type_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]);
652 field_index += 1;
649 const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
650 extra_index += 1;
651 const field_type_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
652 extra_index += 1;
653653
654654 // This string needs to outlive the ZIR code.
655655 const field_name = try new_decl_arena.allocator.dupe(u8, field_name_zir);
......@@ -667,16 +667,16 @@ fn zirStructDecl(
667667 };
668668
669669 if (has_align) {
670 const align_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]);
671 field_index += 1;
670 const align_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
671 extra_index += 1;
672672 // TODO: if we need to report an error here, use a source location
673673 // that points to this alignment expression rather than the struct.
674674 // But only resolve the source location if we need to emit a compile error.
675675 gop.entry.value.abi_align = (try sema.resolveInstConst(block, src, align_ref)).val;
676676 }
677677 if (has_default) {
678 const default_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[field_index]);
679 field_index += 1;
678 const default_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
679 extra_index += 1;
680680 // TODO: if we need to report an error here, use a source location
681681 // that points to this default value expression rather than the struct.
682682 // But only resolve the source location if we need to emit a compile error.
......@@ -696,11 +696,164 @@ fn zirEnumDecl(
696696 const tracy = trace(@src());
697697 defer tracy.end();
698698
699 const gpa = sema.gpa;
699700 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
700701 const src = inst_data.src();
701 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
702 const extra = sema.code.extraData(zir.Inst.EnumDecl, inst_data.payload_index);
703 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
704 const fields_len = extra.data.fields_len;
705
706 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
707
708 const tag_ty = blk: {
709 if (extra.data.tag_type != .none) {
710 // TODO better source location
711 // TODO (needs AstGen fix too) move this eval to the block so it gets allocated
712 // in the new decl arena.
713 break :blk try sema.resolveType(block, src, extra.data.tag_type);
714 }
715 const bits = std.math.log2_int_ceil(usize, fields_len);
716 break :blk try Type.Tag.int_unsigned.create(&new_decl_arena.allocator, bits);
717 };
718
719 const enum_obj = try new_decl_arena.allocator.create(Module.EnumFull);
720 const enum_ty_payload = try gpa.create(Type.Payload.EnumFull);
721 enum_ty_payload.* = .{
722 .base = .{ .tag = if (nonexhaustive) .enum_nonexhaustive else .enum_full },
723 .data = enum_obj,
724 };
725 const enum_ty = Type.initPayload(&enum_ty_payload.base);
726 const enum_val = try Value.Tag.ty.create(&new_decl_arena.allocator, enum_ty);
727 const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{
728 .ty = Type.initTag(.type),
729 .val = enum_val,
730 });
731 enum_obj.* = .{
732 .owner_decl = sema.owner_decl,
733 .tag_ty = tag_ty,
734 .fields = .{},
735 .values = .{},
736 .node_offset = inst_data.src_node,
737 .namespace = .{
738 .parent = sema.owner_decl.namespace,
739 .parent_name_hash = new_decl.fullyQualifiedNameHash(),
740 .ty = enum_ty,
741 .file_scope = block.getFileScope(),
742 },
743 };
744
745 {
746 const ast = std.zig.ast;
747 const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node);
748 const tree: *const ast.Tree = &enum_obj.namespace.file_scope.tree;
749 const node_tags = tree.nodes.items(.tag);
750 var buf: [2]ast.Node.Index = undefined;
751 const members: []const ast.Node.Index = switch (node_tags[node]) {
752 .container_decl,
753 .container_decl_trailing,
754 => tree.containerDecl(node).ast.members,
755
756 .container_decl_two,
757 .container_decl_two_trailing,
758 => tree.containerDeclTwo(&buf, node).ast.members,
759
760 .container_decl_arg,
761 .container_decl_arg_trailing,
762 => tree.containerDeclArg(node).ast.members,
763
764 .root => tree.rootDecls(),
765 else => unreachable,
766 };
767 try sema.mod.analyzeNamespace(&enum_obj.namespace, members);
768 }
769
770 if (fields_len == 0) {
771 assert(body.len == 0);
772 return sema.analyzeDeclVal(block, src, new_decl);
773 }
774
775 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
776 const body_end = extra.end + body.len;
777
778 try enum_obj.fields.ensureCapacity(&new_decl_arena.allocator, fields_len);
779 const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| {
780 if (bag != 0) break true;
781 } else false;
782 if (any_values) {
783 try enum_obj.values.ensureCapacity(&new_decl_arena.allocator, fields_len);
784 }
785
786 {
787 // We create a block for the field type instructions because they
788 // may need to reference Decls from inside the enum namespace.
789 // Within the field type, default value, and alignment expressions, the "owner decl"
790 // should be the enum itself. Thus we need a new Sema.
791 var enum_sema: Sema = .{
792 .mod = sema.mod,
793 .gpa = gpa,
794 .arena = &new_decl_arena.allocator,
795 .code = sema.code,
796 .inst_map = sema.inst_map,
797 .owner_decl = new_decl,
798 .namespace = &enum_obj.namespace,
799 .owner_func = null,
800 .func = null,
801 .param_inst_list = &.{},
802 .branch_quota = sema.branch_quota,
803 .branch_count = sema.branch_count,
804 };
805
806 var enum_block: Scope.Block = .{
807 .parent = null,
808 .sema = &enum_sema,
809 .src_decl = new_decl,
810 .instructions = .{},
811 .inlining = null,
812 .is_comptime = true,
813 };
814 defer assert(enum_block.instructions.items.len == 0); // should all be comptime instructions
815
816 _ = try enum_sema.analyzeBody(&enum_block, body);
817
818 sema.branch_count = enum_sema.branch_count;
819 sema.branch_quota = enum_sema.branch_quota;
820 }
821 var extra_index: usize = body_end + bit_bags_count;
822 var bit_bag_index: usize = body_end;
823 var cur_bit_bag: u32 = undefined;
824 var field_i: u32 = 0;
825 while (field_i < fields_len) : (field_i += 1) {
826 if (field_i % 32 == 0) {
827 cur_bit_bag = sema.code.extra[bit_bag_index];
828 bit_bag_index += 1;
829 }
830 const has_tag_value = @truncate(u1, cur_bit_bag) != 0;
831 cur_bit_bag >>= 1;
832
833 const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
834 extra_index += 1;
702835
703 return sema.mod.fail(&block.base, sema.src, "TODO implement zirEnumDecl", .{});
836 // This string needs to outlive the ZIR code.
837 const field_name = try new_decl_arena.allocator.dupe(u8, field_name_zir);
838
839 const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name);
840 assert(!gop.found_existing);
841
842 if (has_tag_value) {
843 const tag_val_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
844 extra_index += 1;
845 // TODO: if we need to report an error here, use a source location
846 // that points to this default value expression rather than the struct.
847 // But only resolve the source location if we need to emit a compile error.
848 const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref)).val;
849 enum_obj.values.putAssumeCapacityNoClobber(tag_val, {});
850 } else if (any_values) {
851 const tag_val = try Value.Tag.int_u64.create(&new_decl_arena.allocator, field_i);
852 enum_obj.values.putAssumeCapacityNoClobber(tag_val, {});
853 }
854 }
855
856 return sema.analyzeDeclVal(block, src, new_decl);
704857}
705858
706859fn zirUnionDecl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
src/zir.zig+82-14
......@@ -1532,13 +1532,17 @@ pub const Inst = struct {
15321532 };
15331533
15341534 /// Trailing:
1535 /// 0. has_bits: u32 // for every 32 fields
1535 /// 0. inst: Index // for every body_len
1536 /// 1. has_bits: u32 // for every 32 fields
15361537 /// - the bit is whether corresponding field has an value expression
1537 /// 1. field_name: u32 // for every field: null terminated string index
1538 /// 2. value: Ref // for every field for which corresponding bit is set
1538 /// 2. fields: { // for every fields_len
1539 /// field_name: u32,
1540 /// value: Ref, // if corresponding bit is set
1541 /// }
15391542 pub const EnumDecl = struct {
15401543 /// Can be `Ref.none`.
15411544 tag_type: Ref,
1545 body_len: u32,
15421546 fields_len: u32,
15431547 };
15441548
......@@ -1704,8 +1708,6 @@ const Writer = struct {
17041708 .slice_end,
17051709 .slice_sentinel,
17061710 .union_decl,
1707 .enum_decl,
1708 .enum_decl_nonexhaustive,
17091711 .struct_init,
17101712 .field_type,
17111713 => try self.writePlNode(stream, inst),
......@@ -1761,6 +1763,10 @@ const Writer = struct {
17611763 .struct_decl_extern,
17621764 => try self.writeStructDecl(stream, inst),
17631765
1766 .enum_decl,
1767 .enum_decl_nonexhaustive,
1768 => try self.writeEnumDecl(stream, inst),
1769
17641770 .switch_block => try self.writePlNodeSwitchBr(stream, inst, .none),
17651771 .switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
17661772 .switch_block_under => try self.writePlNodeSwitchBr(stream, inst, .under),
......@@ -2031,7 +2037,7 @@ const Writer = struct {
20312037
20322038 const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable;
20332039 const body_end = extra.end + body.len;
2034 var field_index: usize = body_end + bit_bags_count;
2040 var extra_index: usize = body_end + bit_bags_count;
20352041 var bit_bag_index: usize = body_end;
20362042 var cur_bit_bag: u32 = undefined;
20372043 var field_i: u32 = 0;
......@@ -2045,26 +2051,26 @@ const Writer = struct {
20452051 const has_default = @truncate(u1, cur_bit_bag) != 0;
20462052 cur_bit_bag >>= 1;
20472053
2048 const field_name = self.code.nullTerminatedString(self.code.extra[field_index]);
2049 field_index += 1;
2050 const field_type = @intToEnum(Inst.Ref, self.code.extra[field_index]);
2051 field_index += 1;
2054 const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]);
2055 extra_index += 1;
2056 const field_type = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
2057 extra_index += 1;
20522058
20532059 try stream.writeByteNTimes(' ', self.indent);
20542060 try stream.print("{}: ", .{std.zig.fmtId(field_name)});
20552061 try self.writeInstRef(stream, field_type);
20562062
20572063 if (has_align) {
2058 const align_ref = @intToEnum(Inst.Ref, self.code.extra[field_index]);
2059 field_index += 1;
2064 const align_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
2065 extra_index += 1;
20602066
20612067 try stream.writeAll(" align(");
20622068 try self.writeInstRef(stream, align_ref);
20632069 try stream.writeAll(")");
20642070 }
20652071 if (has_default) {
2066 const default_ref = @intToEnum(Inst.Ref, self.code.extra[field_index]);
2067 field_index += 1;
2072 const default_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
2073 extra_index += 1;
20682074
20692075 try stream.writeAll(" = ");
20702076 try self.writeInstRef(stream, default_ref);
......@@ -2078,6 +2084,68 @@ const Writer = struct {
20782084 try self.writeSrc(stream, inst_data.src());
20792085 }
20802086
2087 fn writeEnumDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2088 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
2089 const extra = self.code.extraData(Inst.EnumDecl, inst_data.payload_index);
2090 const body = self.code.extra[extra.end..][0..extra.data.body_len];
2091 const fields_len = extra.data.fields_len;
2092 const tag_ty_ref = extra.data.tag_type;
2093
2094 if (tag_ty_ref != .none) {
2095 try self.writeInstRef(stream, tag_ty_ref);
2096 try stream.writeAll(", ");
2097 }
2098
2099 if (fields_len == 0) {
2100 assert(body.len == 0);
2101 try stream.writeAll("{}, {}) ");
2102 try self.writeSrc(stream, inst_data.src());
2103 return;
2104 }
2105
2106 try stream.writeAll("{\n");
2107 self.indent += 2;
2108 try self.writeBody(stream, body);
2109
2110 try stream.writeByteNTimes(' ', self.indent - 2);
2111 try stream.writeAll("}, {\n");
2112
2113 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
2114 const body_end = extra.end + body.len;
2115 var extra_index: usize = body_end + bit_bags_count;
2116 var bit_bag_index: usize = body_end;
2117 var cur_bit_bag: u32 = undefined;
2118 var field_i: u32 = 0;
2119 while (field_i < fields_len) : (field_i += 1) {
2120 if (field_i % 32 == 0) {
2121 cur_bit_bag = self.code.extra[bit_bag_index];
2122 bit_bag_index += 1;
2123 }
2124 const has_tag_value = @truncate(u1, cur_bit_bag) != 0;
2125 cur_bit_bag >>= 1;
2126
2127 const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]);
2128 extra_index += 1;
2129
2130 try stream.writeByteNTimes(' ', self.indent);
2131 try stream.print("{}", .{std.zig.fmtId(field_name)});
2132
2133 if (has_tag_value) {
2134 const tag_value_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
2135 extra_index += 1;
2136
2137 try stream.writeAll(" = ");
2138 try self.writeInstRef(stream, tag_value_ref);
2139 }
2140 try stream.writeAll(",\n");
2141 }
2142
2143 self.indent -= 2;
2144 try stream.writeByteNTimes(' ', self.indent);
2145 try stream.writeAll("}) ");
2146 try self.writeSrc(stream, inst_data.src());
2147 }
2148
20812149 fn writePlNodeSwitchBr(
20822150 self: *Writer,
20832151 stream: anytype,