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 {...@@ -270,19 +270,26 @@ pub const Inst = struct {
270 /// wrap from E to E!T270 /// wrap from E to E!T
271 /// Uses the `ty_op` field.271 /// Uses the `ty_op` field.
272 wrap_errunion_err,272 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.
274 /// Uses the `ty_pl` field, payload is `StructField`.274 /// Uses the `ty_pl` field, payload is `StructField`.
275 /// TODO rename to `agg_field_ptr`.
275 struct_field_ptr,276 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.
277 /// The field index is the number at the end of the name.278 /// The field index is the number at the end of the name.
278 /// Uses `ty_op` field.279 /// Uses `ty_op` field.
280 /// TODO rename to `agg_field_ptr_index_X`
279 struct_field_ptr_index_0,281 struct_field_ptr_index_0,
280 struct_field_ptr_index_1,282 struct_field_ptr_index_1,
281 struct_field_ptr_index_2,283 struct_field_ptr_index_2,
282 struct_field_ptr_index_3,284 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.
284 /// Uses the `ty_pl` field, payload is `StructField`.286 /// Uses the `ty_pl` field, payload is `StructField`.
287 /// TODO rename to `agg_field_val`
285 struct_field_val,288 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,
286 /// Given a slice value, return the length.293 /// Given a slice value, return the length.
287 /// Result type is always usize.294 /// Result type is always usize.
288 /// Uses the `ty_op` field.295 /// Uses the `ty_op` field.
...@@ -643,6 +650,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -643,6 +650,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
643 .atomic_store_seq_cst,650 .atomic_store_seq_cst,
644 .memset,651 .memset,
645 .memcpy,652 .memcpy,
653 .set_union_tag,
646 => return Type.initTag(.void),654 => return Type.initTag(.void),
647655
648 .ptrtoint,656 .ptrtoint,
src/Liveness.zig+1
...@@ -256,6 +256,7 @@ fn analyzeInst(...@@ -256,6 +256,7 @@ fn analyzeInst(
256 .atomic_store_monotonic,256 .atomic_store_monotonic,
257 .atomic_store_release,257 .atomic_store_release,
258 .atomic_store_seq_cst,258 .atomic_store_seq_cst,
259 .set_union_tag,
259 => {260 => {
260 const o = inst_datas[inst].bin_op;261 const o = inst_datas[inst].bin_op;
261 return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });262 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 {...@@ -859,6 +859,36 @@ pub const EnumSimple = struct {
859 }859 }
860};860};
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
862/// Represents the data that an enum declaration provides, when there is892/// Represents the data that an enum declaration provides, when there is
863/// at least one tag value explicitly specified, or at least one declaration.893/// at least one tag value explicitly specified, or at least one declaration.
864pub const EnumFull = struct {894pub const EnumFull = struct {
...@@ -868,16 +898,17 @@ pub const EnumFull = struct {...@@ -868,16 +898,17 @@ pub const EnumFull = struct {
868 /// Whether zig chooses this type or the user specifies it, it is stored here.898 /// Whether zig chooses this type or the user specifies it, it is stored here.
869 tag_ty: Type,899 tag_ty: Type,
870 /// Set of field names in declaration order.900 /// Set of field names in declaration order.
871 fields: std.StringArrayHashMapUnmanaged(void),901 fields: NameMap,
872 /// Maps integer tag value to field index.902 /// Maps integer tag value to field index.
873 /// Entries are in declaration order, same as `fields`.903 /// Entries are in declaration order, same as `fields`.
874 /// If this hash map is empty, it means the enum tags are auto-numbered.904 /// If this hash map is empty, it means the enum tags are auto-numbered.
875 values: ValueMap,905 values: ValueMap,
876 /// Represents the declarations inside this struct.906 /// Represents the declarations inside this enum.
877 namespace: Scope.Namespace,907 namespace: Scope.Namespace,
878 /// Offset from `owner_decl`, points to the enum decl AST node.908 /// Offset from `owner_decl`, points to the enum decl AST node.
879 node_offset: i32,909 node_offset: i32,
880910
911 pub const NameMap = std.StringArrayHashMapUnmanaged(void);
881 pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false);912 pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false);
882913
883 pub fn srcLoc(self: EnumFull) SrcLoc {914 pub fn srcLoc(self: EnumFull) SrcLoc {
...@@ -933,6 +964,44 @@ pub const Union = struct {...@@ -933,6 +964,44 @@ pub const Union = struct {
933 .lazy = .{ .node_offset = self.node_offset },964 .lazy = .{ .node_offset = self.node_offset },
934 };965 };
935 }966 }
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 }
936};1005};
9371006
938/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.1007/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
...@@ -1543,6 +1612,40 @@ pub const Scope = struct {...@@ -1543,6 +1612,40 @@ pub const Scope = struct {
1543 });1612 });
1544 }1613 }
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
1546 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {1649 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
1547 return Air.indexToRef(try block.addInstAsIndex(inst));1650 return Air.indexToRef(try block.addInstAsIndex(inst));
1548 }1651 }
src/Sema.zig+263-57
...@@ -1625,7 +1625,7 @@ fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr...@@ -1625,7 +1625,7 @@ fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
1625 if (block.is_comptime) {1625 if (block.is_comptime) {
1626 return sema.analyzeComptimeAlloc(block, var_type);1626 return sema.analyzeComptimeAlloc(block, var_type);
1627 }1627 }
1628 try sema.validateVarType(block, ty_src, var_type);1628 try sema.validateVarType(block, ty_src, var_type, false);
1629 const ptr_type = try Type.ptr(sema.arena, .{1629 const ptr_type = try Type.ptr(sema.arena, .{
1630 .pointee_type = var_type,1630 .pointee_type = var_type,
1631 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),1631 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
...@@ -1711,7 +1711,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde...@@ -1711,7 +1711,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
1711 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;1711 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
1712 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);1712 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);
1713 if (var_is_mut) {1713 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);
1715 }1715 }
1716 // Change it to a normal alloc.1716 // Change it to a normal alloc.
1717 const final_ptr_ty = try Type.ptr(sema.arena, .{1717 const final_ptr_ty = try Type.ptr(sema.arena, .{
...@@ -1730,19 +1730,82 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind...@@ -1730,19 +1730,82 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind
1730 const tracy = trace(@src());1730 const tracy = trace(@src());
1731 defer tracy.end();1731 defer tracy.end();
17321732
1733 const gpa = sema.gpa;
1734 const mod = sema.mod;
1735 const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;1733 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();
1737 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);1735 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
1738 const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];1736 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: {1759fn validateUnionInitPtr(
1741 const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;1760 sema: *Sema,
1742 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;1761 block: *Scope.Block,
1743 const object_ptr = sema.resolveInst(field_ptr_extra.lhs);1762 union_obj: *Module.Union,
1744 break :s sema.typeOf(object_ptr).elemType().castTag(.@"struct").?.data;1763 init_src: LazySrcLoc,
1745 };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
1747 // Maps field index to field_ptr index of where it was already initialized.1810 // Maps field index to field_ptr index of where it was already initialized.
1748 const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count());1811 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...@@ -1781,9 +1844,9 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind
1781 const template = "missing struct field: {s}";1844 const template = "missing struct field: {s}";
1782 const args = .{field_name};1845 const args = .{field_name};
1783 if (root_msg) |msg| {1846 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);
1785 } else {1848 } 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);
1787 }1850 }
1788 }1851 }
1789 if (root_msg) |msg| {1852 if (root_msg) |msg| {
...@@ -8037,7 +8100,7 @@ fn checkAtomicOperandType(...@@ -8037,7 +8100,7 @@ fn checkAtomicOperandType(
8037 const max_atomic_bits = target_util.largestAtomicBits(target);8100 const max_atomic_bits = target_util.largestAtomicBits(target);
8038 const int_ty = switch (ty.zigTypeTag()) {8101 const int_ty = switch (ty.zigTypeTag()) {
8039 .Int => ty,8102 .Int => ty,
8040 .Enum => ty.enumTagType(&buffer),8103 .Enum => ty.intTagType(&buffer),
8041 .Float => {8104 .Float => {
8042 const bit_count = ty.floatBits(target);8105 const bit_count = ty.floatBits(target);
8043 if (bit_count > max_atomic_bits) {8106 if (bit_count > max_atomic_bits) {
...@@ -8621,11 +8684,7 @@ fn zirVarExtended(...@@ -8621,11 +8684,7 @@ fn zirVarExtended(
8621 return sema.failWithNeededComptime(block, init_src);8684 return sema.failWithNeededComptime(block, init_src);
8622 } else Value.initTag(.unreachable_value);8685 } else Value.initTag(.unreachable_value);
86238686
8624 if (!var_ty.isValidVarType(small.is_extern)) {8687 try sema.validateVarType(block, mut_src, var_ty, small.is_extern);
8625 return sema.mod.fail(&block.base, mut_src, "variable of type '{}' must be const", .{
8626 var_ty,
8627 });
8628 }
86298688
8630 if (lib_name != null) {8689 if (lib_name != null) {
8631 // Look at the sema code for functions which has this logic, it just needs to8690 // 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...@@ -8810,9 +8869,54 @@ fn requireIntegerType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Typ
8810 }8869 }
8811}8870}
88128871
8813fn validateVarType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void {8872/// Emit a compile error if type cannot be used for a runtime variable.
8814 if (!ty.isValidVarType(false)) {8873fn validateVarType(
8815 return sema.mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{ty});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});
8816 }8920 }
8817}8921}
88188922
...@@ -9393,8 +9497,9 @@ fn structFieldPtr(...@@ -9393,8 +9497,9 @@ fn structFieldPtr(
9393 const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty);9497 const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty);
9394 const struct_obj = struct_ty.castTag(.@"struct").?.data;9498 const struct_obj = struct_ty.castTag(.@"struct").?.data;
93959499
9396 const field_index = struct_obj.fields.getIndex(field_name) orelse9500 const field_index_big = struct_obj.fields.getIndex(field_name) orelse
9397 return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);9501 return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);
9502 const field_index = @intCast(u32, field_index_big);
9398 const field = struct_obj.fields.values()[field_index];9503 const field = struct_obj.fields.values()[field_index];
9399 const ptr_field_ty = try Type.ptr(arena, .{9504 const ptr_field_ty = try Type.ptr(arena, .{
9400 .pointee_type = field.ty,9505 .pointee_type = field.ty,
...@@ -9413,31 +9518,7 @@ fn structFieldPtr(...@@ -9413,31 +9518,7 @@ fn structFieldPtr(
9413 }9518 }
94149519
9415 try sema.requireRuntimeBlock(block, src);9520 try sema.requireRuntimeBlock(block, src);
9416 const tag: Air.Inst.Tag = switch (field_index) {9521 return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty);
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 });
9441}9522}
94429523
9443fn structFieldVal(9524fn structFieldVal(
...@@ -9487,7 +9568,6 @@ fn unionFieldPtr(...@@ -9487,7 +9568,6 @@ fn unionFieldPtr(
9487 field_name_src: LazySrcLoc,9568 field_name_src: LazySrcLoc,
9488 unresolved_union_ty: Type,9569 unresolved_union_ty: Type,
9489) CompileError!Air.Inst.Ref {9570) CompileError!Air.Inst.Ref {
9490 const mod = sema.mod;
9491 const arena = sema.arena;9571 const arena = sema.arena;
9492 assert(unresolved_union_ty.zigTypeTag() == .Union);9572 assert(unresolved_union_ty.zigTypeTag() == .Union);
94939573
...@@ -9495,8 +9575,9 @@ fn unionFieldPtr(...@@ -9495,8 +9575,9 @@ fn unionFieldPtr(
9495 const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);9575 const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
9496 const union_obj = union_ty.cast(Type.Payload.Union).?.data;9576 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
94979577
9498 const field_index = union_obj.fields.getIndex(field_name) orelse9578 const field_index_big = union_obj.fields.getIndex(field_name) orelse
9499 return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);9579 return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
9580 const field_index = @intCast(u32, field_index_big);
95009581
9501 const field = union_obj.fields.values()[field_index];9582 const field = union_obj.fields.values()[field_index];
9502 const ptr_field_ty = try Type.ptr(arena, .{9583 const ptr_field_ty = try Type.ptr(arena, .{
...@@ -9517,7 +9598,7 @@ fn unionFieldPtr(...@@ -9517,7 +9598,7 @@ fn unionFieldPtr(
9517 }9598 }
95189599
9519 try sema.requireRuntimeBlock(block, src);9600 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);
9521}9602}
95229603
9523fn unionFieldVal(9604fn unionFieldVal(
...@@ -11160,6 +11241,28 @@ fn analyzeUnionFields(...@@ -11160,6 +11241,28 @@ fn analyzeUnionFields(
11160 if (body.len != 0) {11241 if (body.len != 0) {
11161 _ = try sema.analyzeBody(block, body);11242 _ = try sema.analyzeBody(block, body);
11162 }11243 }
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
11164 const bits_per_field = 4;11267 const bits_per_field = 4;
11165 const fields_per_u32 = 32 / bits_per_field;11268 const fields_per_u32 = 32 / bits_per_field;
...@@ -11198,12 +11301,25 @@ fn analyzeUnionFields(...@@ -11198,12 +11301,25 @@ fn analyzeUnionFields(
11198 break :blk align_ref;11301 break :blk align_ref;
11199 } else .none;11302 } 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]);
11202 extra_index += 1;11306 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 });
11203 }11315 }
1120411316
11205 // This string needs to outlive the ZIR code.11317 // This string needs to outlive the ZIR code.
11206 const field_name = try decl_arena.allocator.dupe(u8, field_name_zir);11318 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
11207 const field_ty: Type = if (field_type_ref == .none)11323 const field_ty: Type = if (field_type_ref == .none)
11208 Type.initTag(.void)11324 Type.initTag(.void)
11209 else11325 else
...@@ -11225,11 +11341,84 @@ fn analyzeUnionFields(...@@ -11225,11 +11341,84 @@ fn analyzeUnionFields(
11225 // But only resolve the source location if we need to emit a compile error.11341 // But only resolve the source location if we need to emit a compile error.
11226 const abi_align_val = (try sema.resolveInstConst(block, src, align_ref)).val;11342 const abi_align_val = (try sema.resolveInstConst(block, src, align_ref)).val;
11227 gop.value_ptr.abi_align = try abi_align_val.copy(&decl_arena.allocator);11343 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);
11228 }11346 }
11229 }11347 }
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_ref11361 const enum_obj = try new_decl_arena.allocator.create(Module.EnumNumbered);
11232 _ = tag_type_ref;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;
11233}11422}
1123411423
11235fn getBuiltin(11424fn getBuiltin(
...@@ -11367,11 +11556,28 @@ fn typeHasOnePossibleValue(...@@ -11367,11 +11556,28 @@ fn typeHasOnePossibleValue(
11367 }11556 }
11368 return Value.initTag(.empty_struct_value);11557 return Value.initTag(.empty_struct_value);
11369 },11558 },
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 },
11370 .enum_full => {11572 .enum_full => {
11371 const resolved_ty = try sema.resolveTypeFields(block, src, ty);11573 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
11372 const enum_full = resolved_ty.castTag(.enum_full).?.data;11574 const enum_obj = resolved_ty.castTag(.enum_full).?.data;
11373 if (enum_full.fields.count() == 1) {11575 if (enum_obj.fields.count() == 1) {
11374 return enum_full.values.keys()[0];11576 if (enum_obj.values.count() == 0) {
11577 return Value.initTag(.zero); // auto-numbered
11578 } else {
11579 return enum_obj.values.keys()[0];
11580 }
11375 } else {11581 } else {
11376 return null;11582 return null;
11377 }11583 }
src/codegen.zig+9
...@@ -889,6 +889,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -889,6 +889,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
889 .atomic_load => try self.airAtomicLoad(inst),889 .atomic_load => try self.airAtomicLoad(inst),
890 .memcpy => try self.airMemcpy(inst),890 .memcpy => try self.airMemcpy(inst),
891 .memset => try self.airMemset(inst),891 .memset => try self.airMemset(inst),
892 .set_union_tag => try self.airSetUnionTag(inst),
892893
893 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),894 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
894 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),895 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
...@@ -1543,6 +1544,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1543,6 +1544,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1543 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1544 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1544 }1545 }
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
1546 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {1555 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1547 if (!self.liveness.operandDies(inst, op_index))1556 if (!self.liveness.operandDies(inst, op_index))
1548 return false;1557 return false;
src/codegen/c.zig+16
...@@ -955,6 +955,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -955,6 +955,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
955 .atomic_load => try airAtomicLoad(f, inst),955 .atomic_load => try airAtomicLoad(f, inst),
956 .memset => try airMemset(f, inst),956 .memset => try airMemset(f, inst),
957 .memcpy => try airMemcpy(f, inst),957 .memcpy => try airMemcpy(f, inst),
958 .set_union_tag => try airSetUnionTag(f, inst),
958959
959 .int_to_float,960 .int_to_float,
960 .float_to_int,961 .float_to_int,
...@@ -2080,6 +2081,21 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -2080,6 +2081,21 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {
2080 return CValue.none;2081 return CValue.none;
2081}2082}
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
2083fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {2099fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
2084 return switch (order) {2100 return switch (order) {
2085 .Unordered => "memory_order_relaxed",2101 .Unordered => "memory_order_relaxed",
src/codegen/llvm.zig+74-6
...@@ -735,7 +735,7 @@ pub const DeclGen = struct {...@@ -735,7 +735,7 @@ pub const DeclGen = struct {
735 },735 },
736 .Enum => {736 .Enum => {
737 var buffer: Type.Payload.Bits = undefined;737 var buffer: Type.Payload.Bits = undefined;
738 const int_ty = t.enumTagType(&buffer);738 const int_ty = t.intTagType(&buffer);
739 const bit_count = int_ty.intInfo(self.module.getTarget()).bits;739 const bit_count = int_ty.intInfo(self.module.getTarget()).bits;
740 return self.context.intType(bit_count);740 return self.context.intType(bit_count);
741 },741 },
...@@ -812,6 +812,29 @@ pub const DeclGen = struct {...@@ -812,6 +812,29 @@ pub const DeclGen = struct {
812 .False,812 .False,
813 );813 );
814 },814 },
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 },
815 .Fn => {838 .Fn => {
816 const ret_ty = try self.llvmType(t.fnReturnType());839 const ret_ty = try self.llvmType(t.fnReturnType());
817 const params_len = t.fnParamLen();840 const params_len = t.fnParamLen();
...@@ -840,7 +863,6 @@ pub const DeclGen = struct {...@@ -840,7 +863,6 @@ pub const DeclGen = struct {
840863
841 .BoundFn => @panic("TODO remove BoundFn from the language"),864 .BoundFn => @panic("TODO remove BoundFn from the language"),
842865
843 .Union,
844 .Opaque,866 .Opaque,
845 .Frame,867 .Frame,
846 .AnyFrame,868 .AnyFrame,
...@@ -1131,7 +1153,7 @@ pub const DeclGen = struct {...@@ -1131,7 +1153,7 @@ pub const DeclGen = struct {
1131 var buffer: Type.Payload.Bits = undefined;1153 var buffer: Type.Payload.Bits = undefined;
1132 const int_ty = switch (ty.zigTypeTag()) {1154 const int_ty = switch (ty.zigTypeTag()) {
1133 .Int => ty,1155 .Int => ty,
1134 .Enum => ty.enumTagType(&buffer),1156 .Enum => ty.intTagType(&buffer),
1135 .Float => {1157 .Float => {
1136 if (!is_rmw_xchg) return null;1158 if (!is_rmw_xchg) return null;
1137 return dg.context.intType(@intCast(c_uint, ty.abiSize(target) * 8));1159 return dg.context.intType(@intCast(c_uint, ty.abiSize(target) * 8));
...@@ -1281,6 +1303,7 @@ pub const FuncGen = struct {...@@ -1281,6 +1303,7 @@ pub const FuncGen = struct {
1281 .atomic_load => try self.airAtomicLoad(inst),1303 .atomic_load => try self.airAtomicLoad(inst),
1282 .memset => try self.airMemset(inst),1304 .memset => try self.airMemset(inst),
1283 .memcpy => try self.airMemcpy(inst),1305 .memcpy => try self.airMemcpy(inst),
1306 .set_union_tag => try self.airSetUnionTag(inst),
12841307
1285 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),1308 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
1286 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),1309 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
...@@ -1381,7 +1404,7 @@ pub const FuncGen = struct {...@@ -1381,7 +1404,7 @@ pub const FuncGen = struct {
1381 const int_ty = switch (operand_ty.zigTypeTag()) {1404 const int_ty = switch (operand_ty.zigTypeTag()) {
1382 .Enum => blk: {1405 .Enum => blk: {
1383 var buffer: Type.Payload.Bits = undefined;1406 var buffer: Type.Payload.Bits = undefined;
1384 const int_ty = operand_ty.enumTagType(&buffer);1407 const int_ty = operand_ty.intTagType(&buffer);
1385 break :blk int_ty;1408 break :blk int_ty;
1386 },1409 },
1387 .Int, .Bool, .Pointer, .ErrorSet => operand_ty,1410 .Int, .Bool, .Pointer, .ErrorSet => operand_ty,
...@@ -1660,8 +1683,9 @@ pub const FuncGen = struct {...@@ -1660,8 +1683,9 @@ pub const FuncGen = struct {
1660 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1683 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1661 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;1684 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
1662 const struct_ptr = try self.resolveInst(struct_field.struct_operand);1685 const struct_ptr = try self.resolveInst(struct_field.struct_operand);
1686 const struct_ptr_ty = self.air.typeOf(struct_field.struct_operand);
1663 const field_index = @intCast(c_uint, struct_field.field_index);1687 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);
1665 }1689 }
16661690
1667 fn airStructFieldPtrIndex(self: *FuncGen, inst: Air.Inst.Index, field_index: c_uint) !?*const llvm.Value {1691 fn airStructFieldPtrIndex(self: *FuncGen, inst: Air.Inst.Index, field_index: c_uint) !?*const llvm.Value {
...@@ -1670,7 +1694,8 @@ pub const FuncGen = struct {...@@ -1670,7 +1694,8 @@ pub const FuncGen = struct {
16701694
1671 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1695 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1672 const struct_ptr = try self.resolveInst(ty_op.operand);1696 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);
1674 }1699 }
16751700
1676 fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1701 fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -2521,6 +2546,49 @@ pub const FuncGen = struct {...@@ -2521,6 +2546,49 @@ pub const FuncGen = struct {
2521 return null;2546 return null;
2522 }2547 }
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
2524 fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value {2592 fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value {
2525 const id = llvm.lookupIntrinsicID(name.ptr, name.len);2593 const id = llvm.lookupIntrinsicID(name.ptr, name.len);
2526 assert(id != 0);2594 assert(id != 0);
src/print_air.zig+1
...@@ -130,6 +130,7 @@ const Writer = struct {...@@ -130,6 +130,7 @@ const Writer = struct {
130 .ptr_ptr_elem_val,130 .ptr_ptr_elem_val,
131 .shl,131 .shl,
132 .shr,132 .shr,
133 .set_union_tag,
133 => try w.writeBinOp(s, inst),134 => try w.writeBinOp(s, inst),
134135
135 .is_null,136 .is_null,
src/type.zig+84-86
...@@ -124,6 +124,7 @@ pub const Type = extern union {...@@ -124,6 +124,7 @@ pub const Type = extern union {
124 .enum_full,124 .enum_full,
125 .enum_nonexhaustive,125 .enum_nonexhaustive,
126 .enum_simple,126 .enum_simple,
127 .enum_numbered,
127 .atomic_order,128 .atomic_order,
128 .atomic_rmw_op,129 .atomic_rmw_op,
129 .calling_convention,130 .calling_convention,
...@@ -874,6 +875,7 @@ pub const Type = extern union {...@@ -874,6 +875,7 @@ pub const Type = extern union {
874 .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),875 .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
875 .@"union", .union_tagged => return self.copyPayloadShallow(allocator, Payload.Union),876 .@"union", .union_tagged => return self.copyPayloadShallow(allocator, Payload.Union),
876 .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple),877 .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple),
878 .enum_numbered => return self.copyPayloadShallow(allocator, Payload.EnumNumbered),
877 .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull),879 .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull),
878 .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque),880 .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque),
879 }881 }
...@@ -958,6 +960,10 @@ pub const Type = extern union {...@@ -958,6 +960,10 @@ pub const Type = extern union {
958 const enum_simple = ty.castTag(.enum_simple).?.data;960 const enum_simple = ty.castTag(.enum_simple).?.data;
959 return enum_simple.owner_decl.renderFullyQualifiedName(writer);961 return enum_simple.owner_decl.renderFullyQualifiedName(writer);
960 },962 },
963 .enum_numbered => {
964 const enum_numbered = ty.castTag(.enum_numbered).?.data;
965 return enum_numbered.owner_decl.renderFullyQualifiedName(writer);
966 },
961 .@"opaque" => {967 .@"opaque" => {
962 // TODO use declaration name968 // TODO use declaration name
963 return writer.writeAll("opaque {}");969 return writer.writeAll("opaque {}");
...@@ -1268,6 +1274,7 @@ pub const Type = extern union {...@@ -1268,6 +1274,7 @@ pub const Type = extern union {
1268 .@"union",1274 .@"union",
1269 .union_tagged,1275 .union_tagged,
1270 .enum_simple,1276 .enum_simple,
1277 .enum_numbered,
1271 .enum_full,1278 .enum_full,
1272 .enum_nonexhaustive,1279 .enum_nonexhaustive,
1273 => false, // TODO some of these should be `true` depending on their child types1280 => false, // TODO some of these should be `true` depending on their child types
...@@ -1421,7 +1428,7 @@ pub const Type = extern union {...@@ -1421,7 +1428,7 @@ pub const Type = extern union {
1421 const enum_simple = self.castTag(.enum_simple).?.data;1428 const enum_simple = self.castTag(.enum_simple).?.data;
1422 return enum_simple.fields.count() >= 2;1429 return enum_simple.fields.count() >= 2;
1423 },1430 },
1424 .enum_nonexhaustive => {1431 .enum_numbered, .enum_nonexhaustive => {
1425 var buffer: Payload.Bits = undefined;1432 var buffer: Payload.Bits = undefined;
1426 const int_tag_ty = self.intTagType(&buffer);1433 const int_tag_ty = self.intTagType(&buffer);
1427 return int_tag_ty.hasCodeGenBits();1434 return int_tag_ty.hasCodeGenBits();
...@@ -1682,7 +1689,7 @@ pub const Type = extern union {...@@ -1682,7 +1689,7 @@ pub const Type = extern union {
1682 assert(biggest != 0);1689 assert(biggest != 0);
1683 return biggest;1690 return biggest;
1684 },1691 },
1685 .enum_full, .enum_nonexhaustive, .enum_simple => {1692 .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => {
1686 var buffer: Payload.Bits = undefined;1693 var buffer: Payload.Bits = undefined;
1687 const int_tag_ty = self.intTagType(&buffer);1694 const int_tag_ty = self.intTagType(&buffer);
1688 return int_tag_ty.abiAlignment(target);1695 return int_tag_ty.abiAlignment(target);
...@@ -1781,7 +1788,7 @@ pub const Type = extern union {...@@ -1781,7 +1788,7 @@ pub const Type = extern union {
1781 }1788 }
1782 return size;1789 return size;
1783 },1790 },
1784 .enum_simple, .enum_full, .enum_nonexhaustive => {1791 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
1785 var buffer: Payload.Bits = undefined;1792 var buffer: Payload.Bits = undefined;
1786 const int_tag_ty = self.intTagType(&buffer);1793 const int_tag_ty = self.intTagType(&buffer);
1787 return int_tag_ty.abiSize(target);1794 return int_tag_ty.abiSize(target);
...@@ -1948,7 +1955,7 @@ pub const Type = extern union {...@@ -1948,7 +1955,7 @@ pub const Type = extern union {
1948 .@"struct" => {1955 .@"struct" => {
1949 @panic("TODO bitSize struct");1956 @panic("TODO bitSize struct");
1950 },1957 },
1951 .enum_simple, .enum_full, .enum_nonexhaustive => {1958 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
1952 var buffer: Payload.Bits = undefined;1959 var buffer: Payload.Bits = undefined;
1953 const int_tag_ty = self.intTagType(&buffer);1960 const int_tag_ty = self.intTagType(&buffer);
1954 return int_tag_ty.bitSize(target);1961 return int_tag_ty.bitSize(target);
...@@ -2094,23 +2101,6 @@ pub const Type = extern union {...@@ -2094,23 +2101,6 @@ pub const Type = extern union {
2094 };2101 };
2095 }2102 }
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
2114 pub fn isSinglePointer(self: Type) bool {2104 pub fn isSinglePointer(self: Type) bool {
2115 return switch (self.tag()) {2105 return switch (self.tag()) {
2116 .single_const_pointer,2106 .single_const_pointer,
...@@ -2363,48 +2353,6 @@ pub const Type = extern union {...@@ -2363,48 +2353,6 @@ pub const Type = extern union {
2363 }2353 }
2364 }2354 }
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
2408 pub fn childType(ty: Type) Type {2356 pub fn childType(ty: Type) Type {
2409 return switch (ty.tag()) {2357 return switch (ty.tag()) {
2410 .vector => ty.castTag(.vector).?.data.elem_type,2358 .vector => ty.castTag(.vector).?.data.elem_type,
...@@ -2530,6 +2478,15 @@ pub const Type = extern union {...@@ -2530,6 +2478,15 @@ pub const Type = extern union {
2530 }2478 }
2531 }2479 }
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
2533 /// Asserts that the type is an error union.2490 /// Asserts that the type is an error union.
2534 pub fn errorUnionPayload(self: Type) Type {2491 pub fn errorUnionPayload(self: Type) Type {
2535 return switch (self.tag()) {2492 return switch (self.tag()) {
...@@ -3000,6 +2957,7 @@ pub const Type = extern union {...@@ -3000,6 +2957,7 @@ pub const Type = extern union {
3000 }2957 }
3001 },2958 },
3002 .enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty,2959 .enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty,
2960 .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty,
3003 .@"union" => {2961 .@"union" => {
3004 return null; // TODO2962 return null; // TODO
3005 },2963 },
...@@ -3114,31 +3072,21 @@ pub const Type = extern union {...@@ -3114,31 +3072,21 @@ pub const Type = extern union {
3114 }3072 }
3115 }3073 }
31163074
3117 /// Returns the integer tag type of the enum.3075 /// Asserts the type is an enum or a union.
3118 pub fn enumTagType(ty: Type, buffer: *Payload.Bits) Type {3076 /// TODO support unions
3119 switch (ty.tag()) {3077 pub fn intTagType(self: Type, buffer: *Payload.Bits) Type {
3120 .enum_full, .enum_nonexhaustive => {3078 switch (self.tag()) {
3121 const enum_full = ty.cast(Payload.EnumFull).?.data;3079 .enum_full, .enum_nonexhaustive => return self.cast(Payload.EnumFull).?.data.tag_ty,
3122 return enum_full.tag_ty;3080 .enum_numbered => return self.castTag(.enum_numbered).?.data.tag_ty,
3123 },
3124 .enum_simple => {3081 .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());
3126 buffer.* = .{3084 buffer.* = .{
3127 .base = .{ .tag = .int_unsigned },3085 .base = .{ .tag = .int_unsigned },
3128 .data = std.math.log2_int_ceil(usize, enum_simple.fields.count()),3086 .data = bits,
3129 };3087 };
3130 return Type.initPayload(&buffer.base);3088 return Type.initPayload(&buffer.base);
3131 },3089 },
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
3142 else => unreachable,3090 else => unreachable,
3143 }3091 }
3144 }3092 }
...@@ -3156,10 +3104,8 @@ pub const Type = extern union {...@@ -3156,10 +3104,8 @@ pub const Type = extern union {
3156 const enum_full = ty.cast(Payload.EnumFull).?.data;3104 const enum_full = ty.cast(Payload.EnumFull).?.data;
3157 return enum_full.fields.count();3105 return enum_full.fields.count();
3158 },3106 },
3159 .enum_simple => {3107 .enum_simple => return ty.castTag(.enum_simple).?.data.fields.count(),
3160 const enum_simple = ty.castTag(.enum_simple).?.data;3108 .enum_numbered => return ty.castTag(.enum_numbered).?.data.fields.count(),
3161 return enum_simple.fields.count();
3162 },
3163 .atomic_order,3109 .atomic_order,
3164 .atomic_rmw_op,3110 .atomic_rmw_op,
3165 .calling_convention,3111 .calling_convention,
...@@ -3185,6 +3131,10 @@ pub const Type = extern union {...@@ -3185,6 +3131,10 @@ pub const Type = extern union {
3185 const enum_simple = ty.castTag(.enum_simple).?.data;3131 const enum_simple = ty.castTag(.enum_simple).?.data;
3186 return enum_simple.fields.keys()[field_index];3132 return enum_simple.fields.keys()[field_index];
3187 },3133 },
3134 .enum_numbered => {
3135 const enum_numbered = ty.castTag(.enum_numbered).?.data;
3136 return enum_numbered.fields.keys()[field_index];
3137 },
3188 .atomic_order,3138 .atomic_order,
3189 .atomic_rmw_op,3139 .atomic_rmw_op,
3190 .calling_convention,3140 .calling_convention,
...@@ -3209,6 +3159,10 @@ pub const Type = extern union {...@@ -3209,6 +3159,10 @@ pub const Type = extern union {
3209 const enum_simple = ty.castTag(.enum_simple).?.data;3159 const enum_simple = ty.castTag(.enum_simple).?.data;
3210 return enum_simple.fields.getIndex(field_name);3160 return enum_simple.fields.getIndex(field_name);
3211 },3161 },
3162 .enum_numbered => {
3163 const enum_numbered = ty.castTag(.enum_numbered).?.data;
3164 return enum_numbered.fields.getIndex(field_name);
3165 },
3212 .atomic_order,3166 .atomic_order,
3213 .atomic_rmw_op,3167 .atomic_rmw_op,
3214 .calling_convention,3168 .calling_convention,
...@@ -3252,6 +3206,15 @@ pub const Type = extern union {...@@ -3252,6 +3206,15 @@ pub const Type = extern union {
3252 return enum_full.values.getIndexContext(enum_tag, .{ .ty = tag_ty });3206 return enum_full.values.getIndexContext(enum_tag, .{ .ty = tag_ty });
3253 }3207 }
3254 },3208 },
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 },
3255 .enum_simple => {3218 .enum_simple => {
3256 const enum_simple = ty.castTag(.enum_simple).?.data;3219 const enum_simple = ty.castTag(.enum_simple).?.data;
3257 const fields_len = enum_simple.fields.count();3220 const fields_len = enum_simple.fields.count();
...@@ -3303,6 +3266,7 @@ pub const Type = extern union {...@@ -3303,6 +3266,7 @@ pub const Type = extern union {
3303 const enum_full = ty.cast(Payload.EnumFull).?.data;3266 const enum_full = ty.cast(Payload.EnumFull).?.data;
3304 return enum_full.srcLoc();3267 return enum_full.srcLoc();
3305 },3268 },
3269 .enum_numbered => return ty.castTag(.enum_numbered).?.data.srcLoc(),
3306 .enum_simple => {3270 .enum_simple => {
3307 const enum_simple = ty.castTag(.enum_simple).?.data;3271 const enum_simple = ty.castTag(.enum_simple).?.data;
3308 return enum_simple.srcLoc();3272 return enum_simple.srcLoc();
...@@ -3340,6 +3304,7 @@ pub const Type = extern union {...@@ -3340,6 +3304,7 @@ pub const Type = extern union {
3340 const enum_full = ty.cast(Payload.EnumFull).?.data;3304 const enum_full = ty.cast(Payload.EnumFull).?.data;
3341 return enum_full.owner_decl;3305 return enum_full.owner_decl;
3342 },3306 },
3307 .enum_numbered => return ty.castTag(.enum_numbered).?.data.owner_decl,
3343 .enum_simple => {3308 .enum_simple => {
3344 const enum_simple = ty.castTag(.enum_simple).?.data;3309 const enum_simple = ty.castTag(.enum_simple).?.data;
3345 return enum_simple.owner_decl;3310 return enum_simple.owner_decl;
...@@ -3397,6 +3362,15 @@ pub const Type = extern union {...@@ -3397,6 +3362,15 @@ pub const Type = extern union {
3397 return enum_full.values.containsContext(int, .{ .ty = tag_ty });3362 return enum_full.values.containsContext(int, .{ .ty = tag_ty });
3398 }3363 }
3399 },3364 },
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 },
3400 .enum_simple => {3374 .enum_simple => {
3401 const enum_simple = ty.castTag(.enum_simple).?.data;3375 const enum_simple = ty.castTag(.enum_simple).?.data;
3402 const fields_len = enum_simple.fields.count();3376 const fields_len = enum_simple.fields.count();
...@@ -3534,6 +3508,7 @@ pub const Type = extern union {...@@ -3534,6 +3508,7 @@ pub const Type = extern union {
3534 @"union",3508 @"union",
3535 union_tagged,3509 union_tagged,
3536 enum_simple,3510 enum_simple,
3511 enum_numbered,
3537 enum_full,3512 enum_full,
3538 enum_nonexhaustive,3513 enum_nonexhaustive,
35393514
...@@ -3642,6 +3617,7 @@ pub const Type = extern union {...@@ -3642,6 +3617,7 @@ pub const Type = extern union {
3642 .@"union", .union_tagged => Payload.Union,3617 .@"union", .union_tagged => Payload.Union,
3643 .enum_full, .enum_nonexhaustive => Payload.EnumFull,3618 .enum_full, .enum_nonexhaustive => Payload.EnumFull,
3644 .enum_simple => Payload.EnumSimple,3619 .enum_simple => Payload.EnumSimple,
3620 .enum_numbered => Payload.EnumNumbered,
3645 .empty_struct => Payload.ContainerScope,3621 .empty_struct => Payload.ContainerScope,
3646 };3622 };
3647 }3623 }
...@@ -3818,6 +3794,11 @@ pub const Type = extern union {...@@ -3818,6 +3794,11 @@ pub const Type = extern union {
3818 base: Payload = .{ .tag = .enum_simple },3794 base: Payload = .{ .tag = .enum_simple },
3819 data: *Module.EnumSimple,3795 data: *Module.EnumSimple,
3820 };3796 };
3797
3798 pub const EnumNumbered = struct {
3799 base: Payload = .{ .tag = .enum_numbered },
3800 data: *Module.EnumNumbered,
3801 };
3821 };3802 };
38223803
3823 pub fn ptr(arena: *Allocator, d: Payload.Pointer.Data) !Type {3804 pub fn ptr(arena: *Allocator, d: Payload.Pointer.Data) !Type {
...@@ -3850,6 +3831,23 @@ pub const Type = extern union {...@@ -3850,6 +3831,23 @@ pub const Type = extern union {
3850 };3831 };
3851 return Type.initPayload(&type_payload.base);3832 return Type.initPayload(&type_payload.base);
3852 }3833 }
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 }
3853};3851};
38543852
3855pub const CType = enum {3853pub const CType = enum {
test/behavior/union.zig+12
...@@ -2,3 +2,15 @@ const std = @import("std");...@@ -2,3 +2,15 @@ const std = @import("std");
2const expect = std.testing.expect;2const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;3const expectEqual = std.testing.expectEqual;
4const Tag = std.meta.Tag;4const 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 {...@@ -39,13 +39,6 @@ const Foo = union {
39 int: i32,39 int: i32,
40};40};
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
49test "comptime union field access" {42test "comptime union field access" {
50 comptime {43 comptime {
51 var foo = Foo{ .int = 0 };44 var foo = Foo{ .int = 0 };