| author | |
| committer | |
| log | c0aa4a1a42b3e0d312bd274799be67d60a1c0238 |
| tree | 6af32089a5fd3b6f79d7eb8a5fcb26bd0a46c39b |
| parent | 25266d08046df6032007b46346faf01a2f40ef31 |
* 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!T | 270 | /// 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), |
| 647 | 655 | ||
| 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 | }; |
| 861 | 861 | ||
| 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. | ||
| 865 | pub 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 is | 892 | /// 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. |
| 864 | pub const EnumFull = struct { | 894 | pub 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, |
| 880 | 910 | ||
| 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); |
| 882 | 913 | ||
| 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 | }; |
| 937 | 1006 | ||
| 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 | } |
| 1545 | 1614 | ||
| 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(); |
| 1732 | 1732 | ||
| 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 | } | ||
| 1739 | 1758 | ||
| 1740 | const struct_obj: *Module.Struct = s: { | 1759 | fn 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 | |||
| 1800 | fn 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; | ||
| 1746 | 1809 | ||
| 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); |
| 8623 | 8686 | ||
| 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 | } | ||
| 8629 | 8688 | ||
| 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 to | 8690 | // 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 | } |
| 8812 | 8871 | ||
| 8813 | fn 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)) { | 8873 | fn 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 | } |
| 8818 | 8922 | ||
| ... | @@ -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; |
| 9395 | 9499 | ||
| 9396 | const field_index = struct_obj.fields.getIndex(field_name) orelse | 9500 | 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 | } |
| 9414 | 9519 | ||
| 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 | } |
| 9442 | 9523 | ||
| 9443 | fn structFieldVal( | 9524 | fn 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); |
| 9493 | 9573 | ||
| ... | @@ -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; |
| 9497 | 9577 | ||
| 9498 | const field_index = union_obj.fields.getIndex(field_name) orelse | 9578 | 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); | ||
| 9500 | 9581 | ||
| 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 | } |
| 9518 | 9599 | ||
| 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 | } |
| 9522 | 9603 | ||
| 9523 | fn unionFieldVal( | 9604 | fn 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 | } | ||
| 11163 | 11266 | ||
| 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; |
| 11200 | 11303 | ||
| 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 | } |
| 11204 | 11316 | ||
| 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 | else | 11325 | 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 | |||
| 11350 | fn 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(); | ||
| 11230 | 11360 | ||
| 11231 | // TODO resolve the union tag_type_ref | 11361 | 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 | |||
| 11391 | fn 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 | } |
| 11234 | 11423 | ||
| 11235 | fn getBuiltin( | 11424 | fn 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), | ||
| 892 | 893 | ||
| 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 | } |
| 1545 | 1546 | ||
| 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), | ||
| 958 | 959 | ||
| 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 | } |
| 2082 | 2083 | ||
| 2084 | fn 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 | |||
| 2083 | fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { | 2099 | fn 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 { |
| 840 | 863 | ||
| 841 | .BoundFn => @panic("TODO remove BoundFn from the language"), | 864 | .BoundFn => @panic("TODO remove BoundFn from the language"), |
| 842 | 865 | ||
| 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), | ||
| 1284 | 1307 | ||
| 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 | } |
| 1666 | 1690 | ||
| 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 { |
| 1670 | 1694 | ||
| 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 | } |
| 1675 | 1700 | ||
| 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 | } |
| 2523 | 2548 | ||
| 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), |
| 134 | 135 | ||
| 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 name | 968 | // 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 types | 1280 | => 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 | } |
| 2096 | 2103 | ||
| 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 | } |
| 2365 | 2355 | ||
| 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 | } |
| 2532 | 2480 | ||
| 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; // TODO | 2962 | 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 | } |
| 3116 | 3074 | ||
| 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, |
| 3539 | 3514 | ||
| ... | @@ -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 | }; |
| 3822 | 3803 | ||
| 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 | }; |
| 3854 | 3852 | ||
| 3855 | pub const CType = enum { | 3853 | pub const CType = enum { |
test/behavior/union.zig+12| ... | @@ -2,3 +2,15 @@ const std = @import("std"); | ... | @@ -2,3 +2,15 @@ const std = @import("std"); |
| 2 | const expect = std.testing.expect; | 2 | const expect = std.testing.expect; |
| 3 | const expectEqual = std.testing.expectEqual; | 3 | const expectEqual = std.testing.expectEqual; |
| 4 | const Tag = std.meta.Tag; | 4 | const Tag = std.meta.Tag; |
| 5 | |||
| 6 | const Foo = union { | ||
| 7 | float: f64, | ||
| 8 | int: i32, | ||
| 9 | }; | ||
| 10 | |||
| 11 | test "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 | }; |
| 41 | 41 | ||
| 42 | test "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 | |||
| 49 | test "comptime union field access" { | 42 | test "comptime union field access" { |
| 50 | comptime { | 43 | comptime { |
| 51 | var foo = Foo{ .int = 0 }; | 44 | var foo = Foo{ .int = 0 }; |