authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-27 19:48:42-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-27 19:53:29-07:00
logc0aa4a1a42b3e0d312bd274799be67d60a1c0238
tree6af32089a5fd3b6f79d7eb8a5fcb26bd0a46c39b
parent25266d08046df6032007b46346faf01a2f40ef31

stage2: implement basic unions

* AIR instructions struct_field_ptr and related functions now are also emitted by the frontend for unions. Backends must inspect the type of the pointer operand to lower the instructions correctly. - These will be renamed to `agg_field_ptr` (short for "aggregate") in the future. * Introduce the new `set_union_tag` AIR instruction. * Introduce `Module.EnumNumbered` and associated `Type` methods. This is for enums which have no decls, but do have the possibility of overriding the integer tag type and tag values. * Sema: Implement support for union tag types in both the auto-generated and explicitly-provided cases, as well as explicitly provided enum tag values in union declarations. * LLVM backend: implement lowering union types, union field pointer instructions, and the new `set_union_tag` instruction.

11 files changed, 576 insertions(+), 161 deletions(-)

src/Air.zig+11-3
......@@ -270,19 +270,26 @@ pub const Inst = struct {
270270 /// wrap from E to E!T
271271 /// Uses the `ty_op` field.
272272 wrap_errunion_err,
273 /// Given a pointer to a struct and a field index, returns a pointer to the field.
273 /// Given a pointer to a struct or union and a field index, returns a pointer to the field.
274274 /// Uses the `ty_pl` field, payload is `StructField`.
275 /// TODO rename to `agg_field_ptr`.
275276 struct_field_ptr,
276 /// Given a pointer to a struct, returns a pointer to the field.
277 /// Given a pointer to a struct or union, returns a pointer to the field.
277278 /// The field index is the number at the end of the name.
278279 /// Uses `ty_op` field.
280 /// TODO rename to `agg_field_ptr_index_X`
279281 struct_field_ptr_index_0,
280282 struct_field_ptr_index_1,
281283 struct_field_ptr_index_2,
282284 struct_field_ptr_index_3,
283 /// Given a byval struct and a field index, returns the field byval.
285 /// Given a byval struct or union and a field index, returns the field byval.
284286 /// Uses the `ty_pl` field, payload is `StructField`.
287 /// TODO rename to `agg_field_val`
285288 struct_field_val,
289 /// Given a pointer to a tagged union, set its tag to the provided value.
290 /// Result type is always void.
291 /// Uses the `bin_op` field. LHS is union pointer, RHS is new tag value.
292 set_union_tag,
286293 /// Given a slice value, return the length.
287294 /// Result type is always usize.
288295 /// Uses the `ty_op` field.
......@@ -643,6 +650,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
643650 .atomic_store_seq_cst,
644651 .memset,
645652 .memcpy,
653 .set_union_tag,
646654 => return Type.initTag(.void),
647655
648656 .ptrtoint,
src/Liveness.zig+1
......@@ -256,6 +256,7 @@ fn analyzeInst(
256256 .atomic_store_monotonic,
257257 .atomic_store_release,
258258 .atomic_store_seq_cst,
259 .set_union_tag,
259260 => {
260261 const o = inst_datas[inst].bin_op;
261262 return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });
src/Module.zig+105-2
......@@ -859,6 +859,36 @@ pub const EnumSimple = struct {
859859 }
860860};
861861
862/// Represents the data that an enum declaration provides, when there are no
863/// declarations. However an integer tag type is provided, and the enum tag values
864/// are explicitly provided.
865pub const EnumNumbered = struct {
866 /// The Decl that corresponds to the enum itself.
867 owner_decl: *Decl,
868 /// An integer type which is used for the numerical value of the enum.
869 /// Whether zig chooses this type or the user specifies it, it is stored here.
870 tag_ty: Type,
871 /// Set of field names in declaration order.
872 fields: NameMap,
873 /// Maps integer tag value to field index.
874 /// Entries are in declaration order, same as `fields`.
875 /// If this hash map is empty, it means the enum tags are auto-numbered.
876 values: ValueMap,
877 /// Offset from `owner_decl`, points to the enum decl AST node.
878 node_offset: i32,
879
880 pub const NameMap = EnumFull.NameMap;
881 pub const ValueMap = EnumFull.ValueMap;
882
883 pub fn srcLoc(self: EnumNumbered) SrcLoc {
884 return .{
885 .file_scope = self.owner_decl.getFileScope(),
886 .parent_decl_node = self.owner_decl.src_node,
887 .lazy = .{ .node_offset = self.node_offset },
888 };
889 }
890};
891
862892/// Represents the data that an enum declaration provides, when there is
863893/// at least one tag value explicitly specified, or at least one declaration.
864894pub const EnumFull = struct {
......@@ -868,16 +898,17 @@ pub const EnumFull = struct {
868898 /// Whether zig chooses this type or the user specifies it, it is stored here.
869899 tag_ty: Type,
870900 /// Set of field names in declaration order.
871 fields: std.StringArrayHashMapUnmanaged(void),
901 fields: NameMap,
872902 /// Maps integer tag value to field index.
873903 /// Entries are in declaration order, same as `fields`.
874904 /// If this hash map is empty, it means the enum tags are auto-numbered.
875905 values: ValueMap,
876 /// Represents the declarations inside this struct.
906 /// Represents the declarations inside this enum.
877907 namespace: Scope.Namespace,
878908 /// Offset from `owner_decl`, points to the enum decl AST node.
879909 node_offset: i32,
880910
911 pub const NameMap = std.StringArrayHashMapUnmanaged(void);
881912 pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false);
882913
883914 pub fn srcLoc(self: EnumFull) SrcLoc {
......@@ -933,6 +964,44 @@ pub const Union = struct {
933964 .lazy = .{ .node_offset = self.node_offset },
934965 };
935966 }
967
968 pub fn haveFieldTypes(u: Union) bool {
969 return switch (u.status) {
970 .none,
971 .field_types_wip,
972 => false,
973 .have_field_types,
974 .layout_wip,
975 .have_layout,
976 => true,
977 };
978 }
979
980 pub fn onlyTagHasCodegenBits(u: Union) bool {
981 assert(u.haveFieldTypes());
982 for (u.fields.values()) |field| {
983 if (field.ty.hasCodeGenBits()) return false;
984 }
985 return true;
986 }
987
988 pub fn mostAlignedField(u: Union, target: Target) u32 {
989 assert(u.haveFieldTypes());
990 var most_alignment: u64 = 0;
991 var most_index: usize = undefined;
992 for (u.fields.values()) |field, i| {
993 if (!field.ty.hasCodeGenBits()) continue;
994 const field_align = if (field.abi_align.tag() == .abi_align_default)
995 field.ty.abiAlignment(target)
996 else
997 field.abi_align.toUnsignedInt();
998 if (field_align > most_alignment) {
999 most_alignment = field_align;
1000 most_index = i;
1001 }
1002 }
1003 return @intCast(u32, most_index);
1004 }
9361005};
9371006
9381007/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
......@@ -1543,6 +1612,40 @@ pub const Scope = struct {
15431612 });
15441613 }
15451614
1615 pub fn addStructFieldPtr(
1616 block: *Block,
1617 struct_ptr: Air.Inst.Ref,
1618 field_index: u32,
1619 ptr_field_ty: Type,
1620 ) !Air.Inst.Ref {
1621 const ty = try block.sema.addType(ptr_field_ty);
1622 const tag: Air.Inst.Tag = switch (field_index) {
1623 0 => .struct_field_ptr_index_0,
1624 1 => .struct_field_ptr_index_1,
1625 2 => .struct_field_ptr_index_2,
1626 3 => .struct_field_ptr_index_3,
1627 else => {
1628 return block.addInst(.{
1629 .tag = .struct_field_ptr,
1630 .data = .{ .ty_pl = .{
1631 .ty = ty,
1632 .payload = try block.sema.addExtra(Air.StructField{
1633 .struct_operand = struct_ptr,
1634 .field_index = @intCast(u32, field_index),
1635 }),
1636 } },
1637 });
1638 },
1639 };
1640 return block.addInst(.{
1641 .tag = tag,
1642 .data = .{ .ty_op = .{
1643 .ty = ty,
1644 .operand = struct_ptr,
1645 } },
1646 });
1647 }
1648
15461649 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
15471650 return Air.indexToRef(try block.addInstAsIndex(inst));
15481651 }
src/Sema.zig+263-57
......@@ -1625,7 +1625,7 @@ fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
16251625 if (block.is_comptime) {
16261626 return sema.analyzeComptimeAlloc(block, var_type);
16271627 }
1628 try sema.validateVarType(block, ty_src, var_type);
1628 try sema.validateVarType(block, ty_src, var_type, false);
16291629 const ptr_type = try Type.ptr(sema.arena, .{
16301630 .pointee_type = var_type,
16311631 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
......@@ -1711,7 +1711,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
17111711 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
17121712 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);
17131713 if (var_is_mut) {
1714 try sema.validateVarType(block, ty_src, final_elem_ty);
1714 try sema.validateVarType(block, ty_src, final_elem_ty, false);
17151715 }
17161716 // Change it to a normal alloc.
17171717 const final_ptr_ty = try Type.ptr(sema.arena, .{
......@@ -1730,19 +1730,82 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind
17301730 const tracy = trace(@src());
17311731 defer tracy.end();
17321732
1733 const gpa = sema.gpa;
1734 const mod = sema.mod;
17351733 const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;
1736 const struct_init_src = validate_inst.src();
1734 const init_src = validate_inst.src();
17371735 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
17381736 const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];
1737 const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
1738 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
1739 const object_ptr = sema.resolveInst(field_ptr_extra.lhs);
1740 const agg_ty = sema.typeOf(object_ptr).elemType();
1741 switch (agg_ty.zigTypeTag()) {
1742 .Struct => return sema.validateStructInitPtr(
1743 block,
1744 agg_ty.castTag(.@"struct").?.data,
1745 init_src,
1746 instrs,
1747 ),
1748 .Union => return sema.validateUnionInitPtr(
1749 block,
1750 agg_ty.cast(Type.Payload.Union).?.data,
1751 init_src,
1752 instrs,
1753 object_ptr,
1754 ),
1755 else => unreachable,
1756 }
1757}
17391758
1740 const struct_obj: *Module.Struct = s: {
1741 const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
1742 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
1743 const object_ptr = sema.resolveInst(field_ptr_extra.lhs);
1744 break :s sema.typeOf(object_ptr).elemType().castTag(.@"struct").?.data;
1745 };
1759fn validateUnionInitPtr(
1760 sema: *Sema,
1761 block: *Scope.Block,
1762 union_obj: *Module.Union,
1763 init_src: LazySrcLoc,
1764 instrs: []const Zir.Inst.Index,
1765 union_ptr: Air.Inst.Ref,
1766) CompileError!void {
1767 const mod = sema.mod;
1768
1769 if (instrs.len != 1) {
1770 // TODO add note for other field
1771 // TODO add note for union declared here
1772 return mod.fail(&block.base, init_src, "only one union field can be active at once", .{});
1773 }
1774
1775 const field_ptr = instrs[0];
1776 const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node;
1777 const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_ptr_data.src_node };
1778 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
1779 const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start);
1780 const field_index_big = union_obj.fields.getIndex(field_name) orelse
1781 return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
1782 const field_index = @intCast(u32, field_index_big);
1783
1784 // TODO here we need to go back and see if we need to convert the union
1785 // to a comptime-known value. This will involve editing the AIR code we have
1786 // generated so far - in particular deleting some runtime pointer bitcast
1787 // instructions which are not actually needed if the initialization expression
1788 // ends up being comptime-known.
1789
1790 // Otherwise, we set the new union tag now.
1791 const new_tag = try sema.addConstant(
1792 union_obj.tag_ty,
1793 try Value.Tag.enum_field_index.create(sema.arena, field_index),
1794 );
1795
1796 try sema.requireRuntimeBlock(block, init_src);
1797 _ = try block.addBinOp(.set_union_tag, union_ptr, new_tag);
1798}
1799
1800fn validateStructInitPtr(
1801 sema: *Sema,
1802 block: *Scope.Block,
1803 struct_obj: *Module.Struct,
1804 init_src: LazySrcLoc,
1805 instrs: []const Zir.Inst.Index,
1806) CompileError!void {
1807 const gpa = sema.gpa;
1808 const mod = sema.mod;
17461809
17471810 // Maps field index to field_ptr index of where it was already initialized.
17481811 const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count());
......@@ -1781,9 +1844,9 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind
17811844 const template = "missing struct field: {s}";
17821845 const args = .{field_name};
17831846 if (root_msg) |msg| {
1784 try mod.errNote(&block.base, struct_init_src, msg, template, args);
1847 try mod.errNote(&block.base, init_src, msg, template, args);
17851848 } else {
1786 root_msg = try mod.errMsg(&block.base, struct_init_src, template, args);
1849 root_msg = try mod.errMsg(&block.base, init_src, template, args);
17871850 }
17881851 }
17891852 if (root_msg) |msg| {
......@@ -8037,7 +8100,7 @@ fn checkAtomicOperandType(
80378100 const max_atomic_bits = target_util.largestAtomicBits(target);
80388101 const int_ty = switch (ty.zigTypeTag()) {
80398102 .Int => ty,
8040 .Enum => ty.enumTagType(&buffer),
8103 .Enum => ty.intTagType(&buffer),
80418104 .Float => {
80428105 const bit_count = ty.floatBits(target);
80438106 if (bit_count > max_atomic_bits) {
......@@ -8621,11 +8684,7 @@ fn zirVarExtended(
86218684 return sema.failWithNeededComptime(block, init_src);
86228685 } else Value.initTag(.unreachable_value);
86238686
8624 if (!var_ty.isValidVarType(small.is_extern)) {
8625 return sema.mod.fail(&block.base, mut_src, "variable of type '{}' must be const", .{
8626 var_ty,
8627 });
8628 }
8687 try sema.validateVarType(block, mut_src, var_ty, small.is_extern);
86298688
86308689 if (lib_name != null) {
86318690 // Look at the sema code for functions which has this logic, it just needs to
......@@ -8810,9 +8869,54 @@ fn requireIntegerType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Typ
88108869 }
88118870}
88128871
8813fn validateVarType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void {
8814 if (!ty.isValidVarType(false)) {
8815 return sema.mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{ty});
8872/// Emit a compile error if type cannot be used for a runtime variable.
8873fn validateVarType(
8874 sema: *Sema,
8875 block: *Scope.Block,
8876 src: LazySrcLoc,
8877 var_ty: Type,
8878 is_extern: bool,
8879) CompileError!void {
8880 var ty = var_ty;
8881 const ok: bool = while (true) switch (ty.zigTypeTag()) {
8882 .Bool,
8883 .Int,
8884 .Float,
8885 .ErrorSet,
8886 .Enum,
8887 .Frame,
8888 .AnyFrame,
8889 => break true,
8890
8891 .BoundFn,
8892 .ComptimeFloat,
8893 .ComptimeInt,
8894 .EnumLiteral,
8895 .NoReturn,
8896 .Type,
8897 .Void,
8898 .Undefined,
8899 .Null,
8900 => break false,
8901
8902 .Opaque => break is_extern,
8903
8904 .Optional => {
8905 var buf: Type.Payload.ElemType = undefined;
8906 const child_ty = ty.optionalChild(&buf);
8907 return validateVarType(sema, block, src, child_ty, is_extern);
8908 },
8909 .Pointer, .Array, .Vector => ty = ty.elemType(),
8910 .ErrorUnion => ty = ty.errorUnionPayload(),
8911
8912 .Fn => @panic("TODO fn validateVarType"),
8913 .Struct, .Union => {
8914 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
8915 break !resolved_ty.requiresComptime();
8916 },
8917 } else unreachable; // TODO should not need else unreachable
8918 if (!ok) {
8919 return sema.mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{var_ty});
88168920 }
88178921}
88188922
......@@ -9393,8 +9497,9 @@ fn structFieldPtr(
93939497 const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty);
93949498 const struct_obj = struct_ty.castTag(.@"struct").?.data;
93959499
9396 const field_index = struct_obj.fields.getIndex(field_name) orelse
9500 const field_index_big = struct_obj.fields.getIndex(field_name) orelse
93979501 return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);
9502 const field_index = @intCast(u32, field_index_big);
93989503 const field = struct_obj.fields.values()[field_index];
93999504 const ptr_field_ty = try Type.ptr(arena, .{
94009505 .pointee_type = field.ty,
......@@ -9413,31 +9518,7 @@ fn structFieldPtr(
94139518 }
94149519
94159520 try sema.requireRuntimeBlock(block, src);
9416 const tag: Air.Inst.Tag = switch (field_index) {
9417 0 => .struct_field_ptr_index_0,
9418 1 => .struct_field_ptr_index_1,
9419 2 => .struct_field_ptr_index_2,
9420 3 => .struct_field_ptr_index_3,
9421 else => {
9422 return block.addInst(.{
9423 .tag = .struct_field_ptr,
9424 .data = .{ .ty_pl = .{
9425 .ty = try sema.addType(ptr_field_ty),
9426 .payload = try sema.addExtra(Air.StructField{
9427 .struct_operand = struct_ptr,
9428 .field_index = @intCast(u32, field_index),
9429 }),
9430 } },
9431 });
9432 },
9433 };
9434 return block.addInst(.{
9435 .tag = tag,
9436 .data = .{ .ty_op = .{
9437 .ty = try sema.addType(ptr_field_ty),
9438 .operand = struct_ptr,
9439 } },
9440 });
9521 return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty);
94419522}
94429523
94439524fn structFieldVal(
......@@ -9487,7 +9568,6 @@ fn unionFieldPtr(
94879568 field_name_src: LazySrcLoc,
94889569 unresolved_union_ty: Type,
94899570) CompileError!Air.Inst.Ref {
9490 const mod = sema.mod;
94919571 const arena = sema.arena;
94929572 assert(unresolved_union_ty.zigTypeTag() == .Union);
94939573
......@@ -9495,8 +9575,9 @@ fn unionFieldPtr(
94959575 const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
94969576 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
94979577
9498 const field_index = union_obj.fields.getIndex(field_name) orelse
9578 const field_index_big = union_obj.fields.getIndex(field_name) orelse
94999579 return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
9580 const field_index = @intCast(u32, field_index_big);
95009581
95019582 const field = union_obj.fields.values()[field_index];
95029583 const ptr_field_ty = try Type.ptr(arena, .{
......@@ -9517,7 +9598,7 @@ fn unionFieldPtr(
95179598 }
95189599
95199600 try sema.requireRuntimeBlock(block, src);
9520 return mod.fail(&block.base, src, "TODO implement runtime union field access", .{});
9601 return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty);
95219602}
95229603
95239604fn unionFieldVal(
......@@ -11160,6 +11241,28 @@ fn analyzeUnionFields(
1116011241 if (body.len != 0) {
1116111242 _ = try sema.analyzeBody(block, body);
1116211243 }
11244 var int_tag_ty: Type = undefined;
11245 var enum_field_names: ?*Module.EnumNumbered.NameMap = null;
11246 var enum_value_map: ?*Module.EnumNumbered.ValueMap = null;
11247 if (tag_type_ref != .none) {
11248 const provided_ty = try sema.resolveType(block, src, tag_type_ref);
11249 if (small.auto_enum_tag) {
11250 // The provided type is an integer type and we must construct the enum tag type here.
11251 int_tag_ty = provided_ty;
11252 union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(block, fields_len, provided_ty);
11253 enum_field_names = &union_obj.tag_ty.castTag(.enum_numbered).?.data.fields;
11254 enum_value_map = &union_obj.tag_ty.castTag(.enum_numbered).?.data.values;
11255 } else {
11256 // The provided type is the enum tag type.
11257 union_obj.tag_ty = provided_ty;
11258 }
11259 } else {
11260 // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis
11261 // purposes, we still auto-generate an enum tag type the same way. That the union is
11262 // untagged is represented by the Type tag (union vs union_tagged).
11263 union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, fields_len);
11264 enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields;
11265 }
1116311266
1116411267 const bits_per_field = 4;
1116511268 const fields_per_u32 = 32 / bits_per_field;
......@@ -11198,12 +11301,25 @@ fn analyzeUnionFields(
1119811301 break :blk align_ref;
1119911302 } else .none;
1120011303
11201 if (has_tag) {
11304 const tag_ref: Zir.Inst.Ref = if (has_tag) blk: {
11305 const tag_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
1120211306 extra_index += 1;
11307 break :blk tag_ref;
11308 } else .none;
11309
11310 if (enum_value_map) |map| {
11311 const tag_src = src; // TODO better source location
11312 const coerced = try sema.coerce(block, int_tag_ty, tag_ref, tag_src);
11313 const val = try sema.resolveConstValue(block, tag_src, coerced);
11314 map.putAssumeCapacityContext(val, {}, .{ .ty = int_tag_ty });
1120311315 }
1120411316
1120511317 // This string needs to outlive the ZIR code.
1120611318 const field_name = try decl_arena.allocator.dupe(u8, field_name_zir);
11319 if (enum_field_names) |set| {
11320 set.putAssumeCapacity(field_name, {});
11321 }
11322
1120711323 const field_ty: Type = if (field_type_ref == .none)
1120811324 Type.initTag(.void)
1120911325 else
......@@ -11225,11 +11341,84 @@ fn analyzeUnionFields(
1122511341 // But only resolve the source location if we need to emit a compile error.
1122611342 const abi_align_val = (try sema.resolveInstConst(block, src, align_ref)).val;
1122711343 gop.value_ptr.abi_align = try abi_align_val.copy(&decl_arena.allocator);
11344 } else {
11345 gop.value_ptr.abi_align = Value.initTag(.abi_align_default);
1122811346 }
1122911347 }
11348}
11349
11350fn generateUnionTagTypeNumbered(
11351 sema: *Sema,
11352 block: *Scope.Block,
11353 fields_len: u32,
11354 int_ty: Type,
11355) !Type {
11356 const mod = sema.mod;
11357
11358 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
11359 errdefer new_decl_arena.deinit();
1123011360
11231 // TODO resolve the union tag_type_ref
11232 _ = tag_type_ref;
11361 const enum_obj = try new_decl_arena.allocator.create(Module.EnumNumbered);
11362 const enum_ty_payload = try new_decl_arena.allocator.create(Type.Payload.EnumNumbered);
11363 enum_ty_payload.* = .{
11364 .base = .{ .tag = .enum_numbered },
11365 .data = enum_obj,
11366 };
11367 const enum_ty = Type.initPayload(&enum_ty_payload.base);
11368 const enum_val = try Value.Tag.ty.create(&new_decl_arena.allocator, enum_ty);
11369 // TODO better type name
11370 const new_decl = try mod.createAnonymousDecl(&block.base, .{
11371 .ty = Type.initTag(.type),
11372 .val = enum_val,
11373 });
11374 new_decl.owns_tv = true;
11375 errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
11376
11377 enum_obj.* = .{
11378 .owner_decl = new_decl,
11379 .tag_ty = int_ty,
11380 .fields = .{},
11381 .values = .{},
11382 .node_offset = 0,
11383 };
11384 // Here we pre-allocate the maps using the decl arena.
11385 try enum_obj.fields.ensureTotalCapacity(&new_decl_arena.allocator, fields_len);
11386 try enum_obj.values.ensureTotalCapacityContext(&new_decl_arena.allocator, fields_len, .{ .ty = int_ty });
11387 try new_decl.finalizeNewArena(&new_decl_arena);
11388 return enum_ty;
11389}
11390
11391fn generateUnionTagTypeSimple(sema: *Sema, block: *Scope.Block, fields_len: u32) !Type {
11392 const mod = sema.mod;
11393
11394 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
11395 errdefer new_decl_arena.deinit();
11396
11397 const enum_obj = try new_decl_arena.allocator.create(Module.EnumSimple);
11398 const enum_ty_payload = try new_decl_arena.allocator.create(Type.Payload.EnumSimple);
11399 enum_ty_payload.* = .{
11400 .base = .{ .tag = .enum_simple },
11401 .data = enum_obj,
11402 };
11403 const enum_ty = Type.initPayload(&enum_ty_payload.base);
11404 const enum_val = try Value.Tag.ty.create(&new_decl_arena.allocator, enum_ty);
11405 // TODO better type name
11406 const new_decl = try mod.createAnonymousDecl(&block.base, .{
11407 .ty = Type.initTag(.type),
11408 .val = enum_val,
11409 });
11410 new_decl.owns_tv = true;
11411 errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
11412
11413 enum_obj.* = .{
11414 .owner_decl = new_decl,
11415 .fields = .{},
11416 .node_offset = 0,
11417 };
11418 // Here we pre-allocate the maps using the decl arena.
11419 try enum_obj.fields.ensureTotalCapacity(&new_decl_arena.allocator, fields_len);
11420 try new_decl.finalizeNewArena(&new_decl_arena);
11421 return enum_ty;
1123311422}
1123411423
1123511424fn getBuiltin(
......@@ -11367,11 +11556,28 @@ fn typeHasOnePossibleValue(
1136711556 }
1136811557 return Value.initTag(.empty_struct_value);
1136911558 },
11559 .enum_numbered => {
11560 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
11561 const enum_obj = resolved_ty.castTag(.enum_numbered).?.data;
11562 if (enum_obj.fields.count() == 1) {
11563 if (enum_obj.values.count() == 0) {
11564 return Value.initTag(.zero); // auto-numbered
11565 } else {
11566 return enum_obj.values.keys()[0];
11567 }
11568 } else {
11569 return null;
11570 }
11571 },
1137011572 .enum_full => {
1137111573 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
11372 const enum_full = resolved_ty.castTag(.enum_full).?.data;
11373 if (enum_full.fields.count() == 1) {
11374 return enum_full.values.keys()[0];
11574 const enum_obj = resolved_ty.castTag(.enum_full).?.data;
11575 if (enum_obj.fields.count() == 1) {
11576 if (enum_obj.values.count() == 0) {
11577 return Value.initTag(.zero); // auto-numbered
11578 } else {
11579 return enum_obj.values.keys()[0];
11580 }
1137511581 } else {
1137611582 return null;
1137711583 }
src/codegen.zig+9
......@@ -889,6 +889,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
889889 .atomic_load => try self.airAtomicLoad(inst),
890890 .memcpy => try self.airMemcpy(inst),
891891 .memset => try self.airMemset(inst),
892 .set_union_tag => try self.airSetUnionTag(inst),
892893
893894 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
894895 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
......@@ -1543,6 +1544,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15431544 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
15441545 }
15451546
1547 fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
1548 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1549 const result: MCValue = switch (arch) {
1550 else => return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch}),
1551 };
1552 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1553 }
1554
15461555 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
15471556 if (!self.liveness.operandDies(inst, op_index))
15481557 return false;
src/codegen/c.zig+16
......@@ -955,6 +955,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
955955 .atomic_load => try airAtomicLoad(f, inst),
956956 .memset => try airMemset(f, inst),
957957 .memcpy => try airMemcpy(f, inst),
958 .set_union_tag => try airSetUnionTag(f, inst),
958959
959960 .int_to_float,
960961 .float_to_int,
......@@ -2080,6 +2081,21 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {
20802081 return CValue.none;
20812082}
20822083
2084fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
2085 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
2086 const union_ptr = try f.resolveInst(bin_op.lhs);
2087 const new_tag = try f.resolveInst(bin_op.rhs);
2088 const writer = f.object.writer();
2089
2090 try writer.writeAll("*");
2091 try f.writeCValue(writer, union_ptr);
2092 try writer.writeAll(" = ");
2093 try f.writeCValue(writer, new_tag);
2094 try writer.writeAll(";\n");
2095
2096 return CValue.none;
2097}
2098
20832099fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
20842100 return switch (order) {
20852101 .Unordered => "memory_order_relaxed",
src/codegen/llvm.zig+74-6
......@@ -735,7 +735,7 @@ pub const DeclGen = struct {
735735 },
736736 .Enum => {
737737 var buffer: Type.Payload.Bits = undefined;
738 const int_ty = t.enumTagType(&buffer);
738 const int_ty = t.intTagType(&buffer);
739739 const bit_count = int_ty.intInfo(self.module.getTarget()).bits;
740740 return self.context.intType(bit_count);
741741 },
......@@ -812,6 +812,29 @@ pub const DeclGen = struct {
812812 .False,
813813 );
814814 },
815 .Union => {
816 const union_obj = t.castTag(.@"union").?.data;
817 assert(union_obj.haveFieldTypes());
818
819 const enum_tag_ty = union_obj.tag_ty;
820 const enum_tag_llvm_ty = try self.llvmType(enum_tag_ty);
821
822 if (union_obj.onlyTagHasCodegenBits()) {
823 return enum_tag_llvm_ty;
824 }
825
826 const target = self.module.getTarget();
827 const most_aligned_field_index = union_obj.mostAlignedField(target);
828 const most_aligned_field = union_obj.fields.values()[most_aligned_field_index];
829 // TODO handle when the most aligned field is different than the
830 // biggest sized field.
831
832 const llvm_fields = [_]*const llvm.Type{
833 try self.llvmType(most_aligned_field.ty),
834 enum_tag_llvm_ty,
835 };
836 return self.context.structType(&llvm_fields, llvm_fields.len, .False);
837 },
815838 .Fn => {
816839 const ret_ty = try self.llvmType(t.fnReturnType());
817840 const params_len = t.fnParamLen();
......@@ -840,7 +863,6 @@ pub const DeclGen = struct {
840863
841864 .BoundFn => @panic("TODO remove BoundFn from the language"),
842865
843 .Union,
844866 .Opaque,
845867 .Frame,
846868 .AnyFrame,
......@@ -1131,7 +1153,7 @@ pub const DeclGen = struct {
11311153 var buffer: Type.Payload.Bits = undefined;
11321154 const int_ty = switch (ty.zigTypeTag()) {
11331155 .Int => ty,
1134 .Enum => ty.enumTagType(&buffer),
1156 .Enum => ty.intTagType(&buffer),
11351157 .Float => {
11361158 if (!is_rmw_xchg) return null;
11371159 return dg.context.intType(@intCast(c_uint, ty.abiSize(target) * 8));
......@@ -1281,6 +1303,7 @@ pub const FuncGen = struct {
12811303 .atomic_load => try self.airAtomicLoad(inst),
12821304 .memset => try self.airMemset(inst),
12831305 .memcpy => try self.airMemcpy(inst),
1306 .set_union_tag => try self.airSetUnionTag(inst),
12841307
12851308 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
12861309 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
......@@ -1381,7 +1404,7 @@ pub const FuncGen = struct {
13811404 const int_ty = switch (operand_ty.zigTypeTag()) {
13821405 .Enum => blk: {
13831406 var buffer: Type.Payload.Bits = undefined;
1384 const int_ty = operand_ty.enumTagType(&buffer);
1407 const int_ty = operand_ty.intTagType(&buffer);
13851408 break :blk int_ty;
13861409 },
13871410 .Int, .Bool, .Pointer, .ErrorSet => operand_ty,
......@@ -1660,8 +1683,9 @@ pub const FuncGen = struct {
16601683 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
16611684 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
16621685 const struct_ptr = try self.resolveInst(struct_field.struct_operand);
1686 const struct_ptr_ty = self.air.typeOf(struct_field.struct_operand);
16631687 const field_index = @intCast(c_uint, struct_field.field_index);
1664 return self.builder.buildStructGEP(struct_ptr, field_index, "");
1688 return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, field_index);
16651689 }
16661690
16671691 fn airStructFieldPtrIndex(self: *FuncGen, inst: Air.Inst.Index, field_index: c_uint) !?*const llvm.Value {
......@@ -1670,7 +1694,8 @@ pub const FuncGen = struct {
16701694
16711695 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
16721696 const struct_ptr = try self.resolveInst(ty_op.operand);
1673 return self.builder.buildStructGEP(struct_ptr, field_index, "");
1697 const struct_ptr_ty = self.air.typeOf(ty_op.operand);
1698 return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, field_index);
16741699 }
16751700
16761701 fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
......@@ -2521,6 +2546,49 @@ pub const FuncGen = struct {
25212546 return null;
25222547 }
25232548
2549 fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2550 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2551 const union_ptr = try self.resolveInst(bin_op.lhs);
2552 // TODO handle when onlyTagHasCodegenBits() == true
2553 const new_tag = try self.resolveInst(bin_op.rhs);
2554 const tag_field_ptr = self.builder.buildStructGEP(union_ptr, 1, "");
2555
2556 _ = self.builder.buildStore(new_tag, tag_field_ptr);
2557 return null;
2558 }
2559
2560 fn fieldPtr(
2561 self: *FuncGen,
2562 inst: Air.Inst.Index,
2563 struct_ptr: *const llvm.Value,
2564 struct_ptr_ty: Type,
2565 field_index: c_uint,
2566 ) !?*const llvm.Value {
2567 const struct_ty = struct_ptr_ty.childType();
2568 switch (struct_ty.zigTypeTag()) {
2569 .Struct => return self.builder.buildStructGEP(struct_ptr, field_index, ""),
2570 .Union => return self.unionFieldPtr(inst, struct_ptr, struct_ty, field_index),
2571 else => unreachable,
2572 }
2573 }
2574
2575 fn unionFieldPtr(
2576 self: *FuncGen,
2577 inst: Air.Inst.Index,
2578 union_ptr: *const llvm.Value,
2579 union_ty: Type,
2580 field_index: c_uint,
2581 ) !?*const llvm.Value {
2582 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
2583 const field = &union_obj.fields.values()[field_index];
2584 const result_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst));
2585 if (!field.ty.hasCodeGenBits()) {
2586 return null;
2587 }
2588 const union_field_ptr = self.builder.buildStructGEP(union_ptr, 0, "");
2589 return self.builder.buildBitCast(union_field_ptr, result_llvm_ty, "");
2590 }
2591
25242592 fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value {
25252593 const id = llvm.lookupIntrinsicID(name.ptr, name.len);
25262594 assert(id != 0);
src/print_air.zig+1
......@@ -130,6 +130,7 @@ const Writer = struct {
130130 .ptr_ptr_elem_val,
131131 .shl,
132132 .shr,
133 .set_union_tag,
133134 => try w.writeBinOp(s, inst),
134135
135136 .is_null,
src/type.zig+84-86
......@@ -124,6 +124,7 @@ pub const Type = extern union {
124124 .enum_full,
125125 .enum_nonexhaustive,
126126 .enum_simple,
127 .enum_numbered,
127128 .atomic_order,
128129 .atomic_rmw_op,
129130 .calling_convention,
......@@ -874,6 +875,7 @@ pub const Type = extern union {
874875 .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
875876 .@"union", .union_tagged => return self.copyPayloadShallow(allocator, Payload.Union),
876877 .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple),
878 .enum_numbered => return self.copyPayloadShallow(allocator, Payload.EnumNumbered),
877879 .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull),
878880 .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque),
879881 }
......@@ -958,6 +960,10 @@ pub const Type = extern union {
958960 const enum_simple = ty.castTag(.enum_simple).?.data;
959961 return enum_simple.owner_decl.renderFullyQualifiedName(writer);
960962 },
963 .enum_numbered => {
964 const enum_numbered = ty.castTag(.enum_numbered).?.data;
965 return enum_numbered.owner_decl.renderFullyQualifiedName(writer);
966 },
961967 .@"opaque" => {
962968 // TODO use declaration name
963969 return writer.writeAll("opaque {}");
......@@ -1268,6 +1274,7 @@ pub const Type = extern union {
12681274 .@"union",
12691275 .union_tagged,
12701276 .enum_simple,
1277 .enum_numbered,
12711278 .enum_full,
12721279 .enum_nonexhaustive,
12731280 => false, // TODO some of these should be `true` depending on their child types
......@@ -1421,7 +1428,7 @@ pub const Type = extern union {
14211428 const enum_simple = self.castTag(.enum_simple).?.data;
14221429 return enum_simple.fields.count() >= 2;
14231430 },
1424 .enum_nonexhaustive => {
1431 .enum_numbered, .enum_nonexhaustive => {
14251432 var buffer: Payload.Bits = undefined;
14261433 const int_tag_ty = self.intTagType(&buffer);
14271434 return int_tag_ty.hasCodeGenBits();
......@@ -1682,7 +1689,7 @@ pub const Type = extern union {
16821689 assert(biggest != 0);
16831690 return biggest;
16841691 },
1685 .enum_full, .enum_nonexhaustive, .enum_simple => {
1692 .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => {
16861693 var buffer: Payload.Bits = undefined;
16871694 const int_tag_ty = self.intTagType(&buffer);
16881695 return int_tag_ty.abiAlignment(target);
......@@ -1781,7 +1788,7 @@ pub const Type = extern union {
17811788 }
17821789 return size;
17831790 },
1784 .enum_simple, .enum_full, .enum_nonexhaustive => {
1791 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
17851792 var buffer: Payload.Bits = undefined;
17861793 const int_tag_ty = self.intTagType(&buffer);
17871794 return int_tag_ty.abiSize(target);
......@@ -1948,7 +1955,7 @@ pub const Type = extern union {
19481955 .@"struct" => {
19491956 @panic("TODO bitSize struct");
19501957 },
1951 .enum_simple, .enum_full, .enum_nonexhaustive => {
1958 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
19521959 var buffer: Payload.Bits = undefined;
19531960 const int_tag_ty = self.intTagType(&buffer);
19541961 return int_tag_ty.bitSize(target);
......@@ -2094,23 +2101,6 @@ pub const Type = extern union {
20942101 };
20952102 }
20962103
2097 /// Asserts the type is an enum.
2098 pub fn intTagType(self: Type, buffer: *Payload.Bits) Type {
2099 switch (self.tag()) {
2100 .enum_full, .enum_nonexhaustive => return self.cast(Payload.EnumFull).?.data.tag_ty,
2101 .enum_simple => {
2102 const enum_simple = self.castTag(.enum_simple).?.data;
2103 const bits = std.math.log2_int_ceil(usize, enum_simple.fields.count());
2104 buffer.* = .{
2105 .base = .{ .tag = .int_unsigned },
2106 .data = bits,
2107 };
2108 return Type.initPayload(&buffer.base);
2109 },
2110 else => unreachable,
2111 }
2112 }
2113
21142104 pub fn isSinglePointer(self: Type) bool {
21152105 return switch (self.tag()) {
21162106 .single_const_pointer,
......@@ -2363,48 +2353,6 @@ pub const Type = extern union {
23632353 }
23642354 }
23652355
2366 /// Returns if type can be used for a runtime variable
2367 pub fn isValidVarType(self: Type, is_extern: bool) bool {
2368 var ty = self;
2369 while (true) switch (ty.zigTypeTag()) {
2370 .Bool,
2371 .Int,
2372 .Float,
2373 .ErrorSet,
2374 .Enum,
2375 .Frame,
2376 .AnyFrame,
2377 => return true,
2378
2379 .Opaque => return is_extern,
2380 .BoundFn,
2381 .ComptimeFloat,
2382 .ComptimeInt,
2383 .EnumLiteral,
2384 .NoReturn,
2385 .Type,
2386 .Void,
2387 .Undefined,
2388 .Null,
2389 => return false,
2390
2391 .Optional => {
2392 var buf: Payload.ElemType = undefined;
2393 return ty.optionalChild(&buf).isValidVarType(is_extern);
2394 },
2395 .Pointer, .Array, .Vector => ty = ty.elemType(),
2396 .ErrorUnion => ty = ty.errorUnionPayload(),
2397
2398 .Fn => @panic("TODO fn isValidVarType"),
2399 .Struct => {
2400 // TODO this is not always correct; introduce lazy value mechanism
2401 // and here we need to force a resolve of "type requires comptime".
2402 return true;
2403 },
2404 .Union => @panic("TODO union isValidVarType"),
2405 };
2406 }
2407
24082356 pub fn childType(ty: Type) Type {
24092357 return switch (ty.tag()) {
24102358 .vector => ty.castTag(.vector).?.data.elem_type,
......@@ -2530,6 +2478,15 @@ pub const Type = extern union {
25302478 }
25312479 }
25322480
2481 /// Returns the tag type of a union, if the type is a union and it has a tag type.
2482 /// Otherwise, returns `null`.
2483 pub fn unionTagType(ty: Type) ?Type {
2484 return switch (ty.tag()) {
2485 .union_tagged => ty.castTag(.union_tagged).?.data.tag_ty,
2486 else => null,
2487 };
2488 }
2489
25332490 /// Asserts that the type is an error union.
25342491 pub fn errorUnionPayload(self: Type) Type {
25352492 return switch (self.tag()) {
......@@ -3000,6 +2957,7 @@ pub const Type = extern union {
30002957 }
30012958 },
30022959 .enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty,
2960 .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty,
30032961 .@"union" => {
30042962 return null; // TODO
30052963 },
......@@ -3114,31 +3072,21 @@ pub const Type = extern union {
31143072 }
31153073 }
31163074
3117 /// Returns the integer tag type of the enum.
3118 pub fn enumTagType(ty: Type, buffer: *Payload.Bits) Type {
3119 switch (ty.tag()) {
3120 .enum_full, .enum_nonexhaustive => {
3121 const enum_full = ty.cast(Payload.EnumFull).?.data;
3122 return enum_full.tag_ty;
3123 },
3075 /// Asserts the type is an enum or a union.
3076 /// TODO support unions
3077 pub fn intTagType(self: Type, buffer: *Payload.Bits) Type {
3078 switch (self.tag()) {
3079 .enum_full, .enum_nonexhaustive => return self.cast(Payload.EnumFull).?.data.tag_ty,
3080 .enum_numbered => return self.castTag(.enum_numbered).?.data.tag_ty,
31243081 .enum_simple => {
3125 const enum_simple = ty.castTag(.enum_simple).?.data;
3082 const enum_simple = self.castTag(.enum_simple).?.data;
3083 const bits = std.math.log2_int_ceil(usize, enum_simple.fields.count());
31263084 buffer.* = .{
31273085 .base = .{ .tag = .int_unsigned },
3128 .data = std.math.log2_int_ceil(usize, enum_simple.fields.count()),
3086 .data = bits,
31293087 };
31303088 return Type.initPayload(&buffer.base);
31313089 },
3132 .atomic_order,
3133 .atomic_rmw_op,
3134 .calling_convention,
3135 .float_mode,
3136 .reduce_op,
3137 .call_options,
3138 .export_options,
3139 .extern_options,
3140 => @panic("TODO resolve std.builtin types"),
3141
31423090 else => unreachable,
31433091 }
31443092 }
......@@ -3156,10 +3104,8 @@ pub const Type = extern union {
31563104 const enum_full = ty.cast(Payload.EnumFull).?.data;
31573105 return enum_full.fields.count();
31583106 },
3159 .enum_simple => {
3160 const enum_simple = ty.castTag(.enum_simple).?.data;
3161 return enum_simple.fields.count();
3162 },
3107 .enum_simple => return ty.castTag(.enum_simple).?.data.fields.count(),
3108 .enum_numbered => return ty.castTag(.enum_numbered).?.data.fields.count(),
31633109 .atomic_order,
31643110 .atomic_rmw_op,
31653111 .calling_convention,
......@@ -3185,6 +3131,10 @@ pub const Type = extern union {
31853131 const enum_simple = ty.castTag(.enum_simple).?.data;
31863132 return enum_simple.fields.keys()[field_index];
31873133 },
3134 .enum_numbered => {
3135 const enum_numbered = ty.castTag(.enum_numbered).?.data;
3136 return enum_numbered.fields.keys()[field_index];
3137 },
31883138 .atomic_order,
31893139 .atomic_rmw_op,
31903140 .calling_convention,
......@@ -3209,6 +3159,10 @@ pub const Type = extern union {
32093159 const enum_simple = ty.castTag(.enum_simple).?.data;
32103160 return enum_simple.fields.getIndex(field_name);
32113161 },
3162 .enum_numbered => {
3163 const enum_numbered = ty.castTag(.enum_numbered).?.data;
3164 return enum_numbered.fields.getIndex(field_name);
3165 },
32123166 .atomic_order,
32133167 .atomic_rmw_op,
32143168 .calling_convention,
......@@ -3252,6 +3206,15 @@ pub const Type = extern union {
32523206 return enum_full.values.getIndexContext(enum_tag, .{ .ty = tag_ty });
32533207 }
32543208 },
3209 .enum_numbered => {
3210 const enum_obj = ty.castTag(.enum_numbered).?.data;
3211 const tag_ty = enum_obj.tag_ty;
3212 if (enum_obj.values.count() == 0) {
3213 return S.fieldWithRange(tag_ty, enum_tag, enum_obj.fields.count());
3214 } else {
3215 return enum_obj.values.getIndexContext(enum_tag, .{ .ty = tag_ty });
3216 }
3217 },
32553218 .enum_simple => {
32563219 const enum_simple = ty.castTag(.enum_simple).?.data;
32573220 const fields_len = enum_simple.fields.count();
......@@ -3303,6 +3266,7 @@ pub const Type = extern union {
33033266 const enum_full = ty.cast(Payload.EnumFull).?.data;
33043267 return enum_full.srcLoc();
33053268 },
3269 .enum_numbered => return ty.castTag(.enum_numbered).?.data.srcLoc(),
33063270 .enum_simple => {
33073271 const enum_simple = ty.castTag(.enum_simple).?.data;
33083272 return enum_simple.srcLoc();
......@@ -3340,6 +3304,7 @@ pub const Type = extern union {
33403304 const enum_full = ty.cast(Payload.EnumFull).?.data;
33413305 return enum_full.owner_decl;
33423306 },
3307 .enum_numbered => return ty.castTag(.enum_numbered).?.data.owner_decl,
33433308 .enum_simple => {
33443309 const enum_simple = ty.castTag(.enum_simple).?.data;
33453310 return enum_simple.owner_decl;
......@@ -3397,6 +3362,15 @@ pub const Type = extern union {
33973362 return enum_full.values.containsContext(int, .{ .ty = tag_ty });
33983363 }
33993364 },
3365 .enum_numbered => {
3366 const enum_obj = ty.castTag(.enum_numbered).?.data;
3367 const tag_ty = enum_obj.tag_ty;
3368 if (enum_obj.values.count() == 0) {
3369 return S.intInRange(tag_ty, int, enum_obj.fields.count());
3370 } else {
3371 return enum_obj.values.containsContext(int, .{ .ty = tag_ty });
3372 }
3373 },
34003374 .enum_simple => {
34013375 const enum_simple = ty.castTag(.enum_simple).?.data;
34023376 const fields_len = enum_simple.fields.count();
......@@ -3534,6 +3508,7 @@ pub const Type = extern union {
35343508 @"union",
35353509 union_tagged,
35363510 enum_simple,
3511 enum_numbered,
35373512 enum_full,
35383513 enum_nonexhaustive,
35393514
......@@ -3642,6 +3617,7 @@ pub const Type = extern union {
36423617 .@"union", .union_tagged => Payload.Union,
36433618 .enum_full, .enum_nonexhaustive => Payload.EnumFull,
36443619 .enum_simple => Payload.EnumSimple,
3620 .enum_numbered => Payload.EnumNumbered,
36453621 .empty_struct => Payload.ContainerScope,
36463622 };
36473623 }
......@@ -3818,6 +3794,11 @@ pub const Type = extern union {
38183794 base: Payload = .{ .tag = .enum_simple },
38193795 data: *Module.EnumSimple,
38203796 };
3797
3798 pub const EnumNumbered = struct {
3799 base: Payload = .{ .tag = .enum_numbered },
3800 data: *Module.EnumNumbered,
3801 };
38213802 };
38223803
38233804 pub fn ptr(arena: *Allocator, d: Payload.Pointer.Data) !Type {
......@@ -3850,6 +3831,23 @@ pub const Type = extern union {
38503831 };
38513832 return Type.initPayload(&type_payload.base);
38523833 }
3834
3835 pub fn smallestUnsignedInt(arena: *Allocator, max: u64) !Type {
3836 const bits = bits: {
3837 if (max == 0) break :bits 0;
3838 const base = std.math.log2(max);
3839 const upper = (@as(u64, 1) << base) - 1;
3840 break :bits base + @boolToInt(upper < max);
3841 };
3842 return switch (bits) {
3843 1 => initTag(.u1),
3844 8 => initTag(.u8),
3845 16 => initTag(.u16),
3846 32 => initTag(.u32),
3847 64 => initTag(.u64),
3848 else => return Tag.int_unsigned.create(arena, bits),
3849 };
3850 }
38533851};
38543852
38553853pub const CType = enum {
test/behavior/union.zig+12
......@@ -2,3 +2,15 @@ const std = @import("std");
22const expect = std.testing.expect;
33const expectEqual = std.testing.expectEqual;
44const Tag = std.meta.Tag;
5
6const Foo = union {
7 float: f64,
8 int: i32,
9};
10
11test "basic unions" {
12 var foo = Foo{ .int = 1 };
13 try expect(foo.int == 1);
14 foo = Foo{ .float = 12.34 };
15 try expect(foo.float == 12.34);
16}
test/behavior/union_stage1.zig-7
......@@ -39,13 +39,6 @@ const Foo = union {
3939 int: i32,
4040};
4141
42test "basic unions" {
43 var foo = Foo{ .int = 1 };
44 try expect(foo.int == 1);
45 foo = Foo{ .float = 12.34 };
46 try expect(foo.float == 12.34);
47}
48
4942test "comptime union field access" {
5043 comptime {
5144 var foo = Foo{ .int = 0 };