| author | |
| committer | |
| log | efb7148a4574c608b21359fcbf2edf06afdb5e0c |
| tree | 23e47a49f95e60ea315603407bd5793310978c62 |
| parent | 91619cdf57f54accbdbb3ff616856eaf79b537a3 |
* `Module.Union.getLayout`: fixes to support components of the union
being 0 bits.
* Implement `@typeInfo` for unions.
* Add missing calls to `resolveTypeFields`.
* Fix explicitly-provided union tag types passing a `Zir.Inst.Ref`
where an `Air.Inst.Ref` was expected. We don't have any type safety
for this; these typess are aliases.
* Fix explicitly-provided `union(enum)` tag Values allocated to the
wrong arena.7 files changed, 356 insertions(+), 246 deletions(-)
src/Module.zig+10-9| ... | @@ -1104,9 +1104,9 @@ pub const Union = struct { | ... | @@ -1104,9 +1104,9 @@ pub const Union = struct { |
| 1104 | 1104 | ||
| 1105 | pub fn getLayout(u: Union, target: Target, have_tag: bool) Layout { | 1105 | pub fn getLayout(u: Union, target: Target, have_tag: bool) Layout { |
| 1106 | assert(u.status == .have_layout); | 1106 | assert(u.status == .have_layout); |
| 1107 | var most_aligned_field: usize = undefined; | 1107 | var most_aligned_field: u32 = undefined; |
| 1108 | var most_aligned_field_size: u64 = undefined; | 1108 | var most_aligned_field_size: u64 = undefined; |
| 1109 | var biggest_field: usize = undefined; | 1109 | var biggest_field: u32 = undefined; |
| 1110 | var payload_size: u64 = 0; | 1110 | var payload_size: u64 = 0; |
| 1111 | var payload_align: u32 = 0; | 1111 | var payload_align: u32 = 0; |
| 1112 | for (u.fields.values()) |field, i| { | 1112 | for (u.fields.values()) |field, i| { |
| ... | @@ -1122,20 +1122,21 @@ pub const Union = struct { | ... | @@ -1122,20 +1122,21 @@ pub const Union = struct { |
| 1122 | const field_size = field.ty.abiSize(target); | 1122 | const field_size = field.ty.abiSize(target); |
| 1123 | if (field_size > payload_size) { | 1123 | if (field_size > payload_size) { |
| 1124 | payload_size = field_size; | 1124 | payload_size = field_size; |
| 1125 | biggest_field = i; | 1125 | biggest_field = @intCast(u32, i); |
| 1126 | } | 1126 | } |
| 1127 | if (field_align > payload_align) { | 1127 | if (field_align > payload_align) { |
| 1128 | payload_align = field_align; | 1128 | payload_align = field_align; |
| 1129 | most_aligned_field = i; | 1129 | most_aligned_field = @intCast(u32, i); |
| 1130 | most_aligned_field_size = field_size; | 1130 | most_aligned_field_size = field_size; |
| 1131 | } | 1131 | } |
| 1132 | } | 1132 | } |
| 1133 | payload_align = @maximum(payload_align, 1); | ||
| 1133 | if (!have_tag) return .{ | 1134 | if (!have_tag) return .{ |
| 1134 | .abi_size = std.mem.alignForwardGeneric(u64, payload_size, payload_align), | 1135 | .abi_size = std.mem.alignForwardGeneric(u64, payload_size, payload_align), |
| 1135 | .abi_align = payload_align, | 1136 | .abi_align = payload_align, |
| 1136 | .most_aligned_field = @intCast(u32, most_aligned_field), | 1137 | .most_aligned_field = most_aligned_field, |
| 1137 | .most_aligned_field_size = most_aligned_field_size, | 1138 | .most_aligned_field_size = most_aligned_field_size, |
| 1138 | .biggest_field = @intCast(u32, biggest_field), | 1139 | .biggest_field = biggest_field, |
| 1139 | .payload_size = payload_size, | 1140 | .payload_size = payload_size, |
| 1140 | .payload_align = payload_align, | 1141 | .payload_align = payload_align, |
| 1141 | .tag_align = 0, | 1142 | .tag_align = 0, |
| ... | @@ -1144,7 +1145,7 @@ pub const Union = struct { | ... | @@ -1144,7 +1145,7 @@ pub const Union = struct { |
| 1144 | // Put the tag before or after the payload depending on which one's | 1145 | // Put the tag before or after the payload depending on which one's |
| 1145 | // alignment is greater. | 1146 | // alignment is greater. |
| 1146 | const tag_size = u.tag_ty.abiSize(target); | 1147 | const tag_size = u.tag_ty.abiSize(target); |
| 1147 | const tag_align = u.tag_ty.abiAlignment(target); | 1148 | const tag_align = @maximum(1, u.tag_ty.abiAlignment(target)); |
| 1148 | var size: u64 = 0; | 1149 | var size: u64 = 0; |
| 1149 | if (tag_align >= payload_align) { | 1150 | if (tag_align >= payload_align) { |
| 1150 | // {Tag, Payload} | 1151 | // {Tag, Payload} |
| ... | @@ -1162,9 +1163,9 @@ pub const Union = struct { | ... | @@ -1162,9 +1163,9 @@ pub const Union = struct { |
| 1162 | return .{ | 1163 | return .{ |
| 1163 | .abi_size = size, | 1164 | .abi_size = size, |
| 1164 | .abi_align = @maximum(tag_align, payload_align), | 1165 | .abi_align = @maximum(tag_align, payload_align), |
| 1165 | .most_aligned_field = @intCast(u32, most_aligned_field), | 1166 | .most_aligned_field = most_aligned_field, |
| 1166 | .most_aligned_field_size = most_aligned_field_size, | 1167 | .most_aligned_field_size = most_aligned_field_size, |
| 1167 | .biggest_field = @intCast(u32, biggest_field), | 1168 | .biggest_field = biggest_field, |
| 1168 | .payload_size = payload_size, | 1169 | .payload_size = payload_size, |
| 1169 | .payload_align = payload_align, | 1170 | .payload_align = payload_align, |
| 1170 | .tag_align = tag_align, | 1171 | .tag_align = tag_align, |
src/Sema.zig+123-12| ... | @@ -5799,8 +5799,12 @@ fn zirSwitchCond( | ... | @@ -5799,8 +5799,12 @@ fn zirSwitchCond( |
| 5799 | ) CompileError!Air.Inst.Ref { | 5799 | ) CompileError!Air.Inst.Ref { |
| 5800 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 5800 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 5801 | const src = inst_data.src(); | 5801 | const src = inst_data.src(); |
| 5802 | const operand_src = src; // TODO make this point at the switch operand | ||
| 5802 | const operand_ptr = sema.resolveInst(inst_data.operand); | 5803 | const operand_ptr = sema.resolveInst(inst_data.operand); |
| 5803 | const operand = if (is_ref) try sema.analyzeLoad(block, src, operand_ptr, src) else operand_ptr; | 5804 | const operand = if (is_ref) |
| 5805 | try sema.analyzeLoad(block, src, operand_ptr, operand_src) | ||
| 5806 | else | ||
| 5807 | operand_ptr; | ||
| 5804 | const operand_ty = sema.typeOf(operand); | 5808 | const operand_ty = sema.typeOf(operand); |
| 5805 | 5809 | ||
| 5806 | switch (operand_ty.zigTypeTag()) { | 5810 | switch (operand_ty.zigTypeTag()) { |
| ... | @@ -5817,18 +5821,19 @@ fn zirSwitchCond( | ... | @@ -5817,18 +5821,19 @@ fn zirSwitchCond( |
| 5817 | .ErrorSet, | 5821 | .ErrorSet, |
| 5818 | .Enum, | 5822 | .Enum, |
| 5819 | => { | 5823 | => { |
| 5820 | if ((try sema.typeHasOnePossibleValue(block, src, operand_ty))) |opv| { | 5824 | if ((try sema.typeHasOnePossibleValue(block, operand_src, operand_ty))) |opv| { |
| 5821 | return sema.addConstant(operand_ty, opv); | 5825 | return sema.addConstant(operand_ty, opv); |
| 5822 | } | 5826 | } |
| 5823 | return operand; | 5827 | return operand; |
| 5824 | }, | 5828 | }, |
| 5825 | 5829 | ||
| 5826 | .Union => { | 5830 | .Union => { |
| 5827 | const enum_ty = operand_ty.unionTagType() orelse { | 5831 | const union_ty = try sema.resolveTypeFields(block, operand_src, operand_ty); |
| 5832 | const enum_ty = union_ty.unionTagType() orelse { | ||
| 5828 | const msg = msg: { | 5833 | const msg = msg: { |
| 5829 | const msg = try sema.errMsg(block, src, "switch on untagged union", .{}); | 5834 | const msg = try sema.errMsg(block, src, "switch on untagged union", .{}); |
| 5830 | errdefer msg.destroy(sema.gpa); | 5835 | errdefer msg.destroy(sema.gpa); |
| 5831 | try sema.addDeclaredHereNote(msg, operand_ty); | 5836 | try sema.addDeclaredHereNote(msg, union_ty); |
| 5832 | break :msg msg; | 5837 | break :msg msg; |
| 5833 | }; | 5838 | }; |
| 5834 | return sema.failWithOwnedErrorMsg(msg); | 5839 | return sema.failWithOwnedErrorMsg(msg); |
| ... | @@ -9154,9 +9159,107 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -9154,9 +9159,107 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 9154 | }), | 9159 | }), |
| 9155 | ); | 9160 | ); |
| 9156 | }, | 9161 | }, |
| 9162 | .Union => { | ||
| 9163 | // TODO: look into memoizing this result. | ||
| 9164 | |||
| 9165 | var fields_anon_decl = try block.startAnonDecl(); | ||
| 9166 | defer fields_anon_decl.deinit(); | ||
| 9167 | |||
| 9168 | const union_field_ty = t: { | ||
| 9169 | const union_field_ty_decl = (try sema.namespaceLookup( | ||
| 9170 | block, | ||
| 9171 | src, | ||
| 9172 | type_info_ty.getNamespace().?, | ||
| 9173 | "UnionField", | ||
| 9174 | )).?; | ||
| 9175 | try sema.mod.declareDeclDependency(sema.owner_decl, union_field_ty_decl); | ||
| 9176 | try sema.ensureDeclAnalyzed(union_field_ty_decl); | ||
| 9177 | var buffer: Value.ToTypeBuffer = undefined; | ||
| 9178 | break :t try union_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena()); | ||
| 9179 | }; | ||
| 9180 | |||
| 9181 | const union_ty = try sema.resolveTypeFields(block, src, ty); | ||
| 9182 | const union_fields = union_ty.unionFields(); | ||
| 9183 | const union_field_vals = try fields_anon_decl.arena().alloc(Value, union_fields.count()); | ||
| 9184 | |||
| 9185 | for (union_field_vals) |*field_val, i| { | ||
| 9186 | const field = union_fields.values()[i]; | ||
| 9187 | const name = union_fields.keys()[i]; | ||
| 9188 | const name_val = v: { | ||
| 9189 | var anon_decl = try block.startAnonDecl(); | ||
| 9190 | defer anon_decl.deinit(); | ||
| 9191 | const bytes = try anon_decl.arena().dupeZ(u8, name); | ||
| 9192 | const new_decl = try anon_decl.finish( | ||
| 9193 | try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), | ||
| 9194 | try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), | ||
| 9195 | ); | ||
| 9196 | break :v try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl); | ||
| 9197 | }; | ||
| 9198 | |||
| 9199 | const union_field_fields = try fields_anon_decl.arena().create([3]Value); | ||
| 9200 | union_field_fields.* = .{ | ||
| 9201 | // name: []const u8, | ||
| 9202 | name_val, | ||
| 9203 | // field_type: type, | ||
| 9204 | try Value.Tag.ty.create(fields_anon_decl.arena(), field.ty), | ||
| 9205 | // alignment: comptime_int, | ||
| 9206 | try field.abi_align.copy(fields_anon_decl.arena()), | ||
| 9207 | }; | ||
| 9208 | field_val.* = try Value.Tag.@"struct".create(fields_anon_decl.arena(), union_field_fields); | ||
| 9209 | } | ||
| 9210 | |||
| 9211 | const fields_val = v: { | ||
| 9212 | const new_decl = try fields_anon_decl.finish( | ||
| 9213 | try Type.Tag.array.create(fields_anon_decl.arena(), .{ | ||
| 9214 | .len = union_field_vals.len, | ||
| 9215 | .elem_type = union_field_ty, | ||
| 9216 | }), | ||
| 9217 | try Value.Tag.array.create( | ||
| 9218 | fields_anon_decl.arena(), | ||
| 9219 | try fields_anon_decl.arena().dupe(Value, union_field_vals), | ||
| 9220 | ), | ||
| 9221 | ); | ||
| 9222 | break :v try Value.Tag.decl_ref.create(sema.arena, new_decl); | ||
| 9223 | }; | ||
| 9224 | |||
| 9225 | if (ty.getNamespace()) |namespace| { | ||
| 9226 | if (namespace.decls.count() != 0) { | ||
| 9227 | return sema.fail(block, src, "TODO: implement zirTypeInfo for Union which has declarations", .{}); | ||
| 9228 | } | ||
| 9229 | } | ||
| 9230 | const decls_val = Value.initTag(.empty_array); | ||
| 9231 | |||
| 9232 | const enum_tag_ty_val = if (union_ty.unionTagType()) |tag_ty| v: { | ||
| 9233 | const ty_val = try Value.Tag.ty.create(sema.arena, tag_ty); | ||
| 9234 | break :v try Value.Tag.opt_payload.create(sema.arena, ty_val); | ||
| 9235 | } else Value.@"null"; | ||
| 9236 | |||
| 9237 | const field_values = try sema.arena.create([4]Value); | ||
| 9238 | field_values.* = .{ | ||
| 9239 | // layout: ContainerLayout, | ||
| 9240 | try Value.Tag.enum_field_index.create( | ||
| 9241 | sema.arena, | ||
| 9242 | @enumToInt(std.builtin.TypeInfo.ContainerLayout.Auto), | ||
| 9243 | ), | ||
| 9244 | |||
| 9245 | // tag_type: ?type, | ||
| 9246 | enum_tag_ty_val, | ||
| 9247 | // fields: []const UnionField, | ||
| 9248 | fields_val, | ||
| 9249 | // decls: []const Declaration, | ||
| 9250 | decls_val, | ||
| 9251 | }; | ||
| 9252 | |||
| 9253 | return sema.addConstant( | ||
| 9254 | type_info_ty, | ||
| 9255 | try Value.Tag.@"union".create(sema.arena, .{ | ||
| 9256 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Union)), | ||
| 9257 | .val = try Value.Tag.@"struct".create(sema.arena, field_values), | ||
| 9258 | }), | ||
| 9259 | ); | ||
| 9260 | }, | ||
| 9157 | .Struct => return sema.fail(block, src, "TODO: implement zirTypeInfo for Struct", .{}), | 9261 | .Struct => return sema.fail(block, src, "TODO: implement zirTypeInfo for Struct", .{}), |
| 9158 | .ErrorSet => return sema.fail(block, src, "TODO: implement zirTypeInfo for ErrorSet", .{}), | 9262 | .ErrorSet => return sema.fail(block, src, "TODO: implement zirTypeInfo for ErrorSet", .{}), |
| 9159 | .Union => return sema.fail(block, src, "TODO: implement zirTypeInfo for Union", .{}), | ||
| 9160 | .BoundFn => @panic("TODO remove this type from the language and compiler"), | 9263 | .BoundFn => @panic("TODO remove this type from the language and compiler"), |
| 9161 | .Opaque => return sema.fail(block, src, "TODO: implement zirTypeInfo for Opaque", .{}), | 9264 | .Opaque => return sema.fail(block, src, "TODO: implement zirTypeInfo for Opaque", .{}), |
| 9162 | .Frame => return sema.fail(block, src, "TODO: implement zirTypeInfo for Frame", .{}), | 9265 | .Frame => return sema.fail(block, src, "TODO: implement zirTypeInfo for Frame", .{}), |
| ... | @@ -11847,12 +11950,14 @@ fn fieldVal( | ... | @@ -11847,12 +11950,14 @@ fn fieldVal( |
| 11847 | ); | 11950 | ); |
| 11848 | }, | 11951 | }, |
| 11849 | .Union => { | 11952 | .Union => { |
| 11850 | if (child_type.getNamespace()) |namespace| { | 11953 | const union_ty = try sema.resolveTypeFields(block, src, child_type); |
| 11954 | |||
| 11955 | if (union_ty.getNamespace()) |namespace| { | ||
| 11851 | if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| { | 11956 | if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| { |
| 11852 | return inst; | 11957 | return inst; |
| 11853 | } | 11958 | } |
| 11854 | } | 11959 | } |
| 11855 | if (child_type.unionTagType()) |enum_ty| { | 11960 | if (union_ty.unionTagType()) |enum_ty| { |
| 11856 | if (enum_ty.enumFieldIndex(field_name)) |field_index_usize| { | 11961 | if (enum_ty.enumFieldIndex(field_name)) |field_index_usize| { |
| 11857 | const field_index = @intCast(u32, field_index_usize); | 11962 | const field_index = @intCast(u32, field_index_usize); |
| 11858 | return sema.addConstant( | 11963 | return sema.addConstant( |
| ... | @@ -11861,7 +11966,7 @@ fn fieldVal( | ... | @@ -11861,7 +11966,7 @@ fn fieldVal( |
| 11861 | ); | 11966 | ); |
| 11862 | } | 11967 | } |
| 11863 | } | 11968 | } |
| 11864 | return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); | 11969 | return sema.failWithBadMemberAccess(block, union_ty, field_name_src, field_name); |
| 11865 | }, | 11970 | }, |
| 11866 | .Enum => { | 11971 | .Enum => { |
| 11867 | if (child_type.getNamespace()) |namespace| { | 11972 | if (child_type.getNamespace()) |namespace| { |
| ... | @@ -15185,8 +15290,9 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { | ... | @@ -15185,8 +15290,9 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { |
| 15185 | // The provided type is an integer type and we must construct the enum tag type here. | 15290 | // The provided type is an integer type and we must construct the enum tag type here. |
| 15186 | int_tag_ty = provided_ty; | 15291 | int_tag_ty = provided_ty; |
| 15187 | union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, fields_len, provided_ty); | 15292 | union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, fields_len, provided_ty); |
| 15188 | enum_field_names = &union_obj.tag_ty.castTag(.enum_numbered).?.data.fields; | 15293 | const enum_obj = union_obj.tag_ty.castTag(.enum_numbered).?.data; |
| 15189 | enum_value_map = &union_obj.tag_ty.castTag(.enum_numbered).?.data.values; | 15294 | enum_field_names = &enum_obj.fields; |
| 15295 | enum_value_map = &enum_obj.values; | ||
| 15190 | } else { | 15296 | } else { |
| 15191 | // The provided type is the enum tag type. | 15297 | // The provided type is the enum tag type. |
| 15192 | union_obj.tag_ty = try provided_ty.copy(decl_arena_allocator); | 15298 | union_obj.tag_ty = try provided_ty.copy(decl_arena_allocator); |
| ... | @@ -15239,14 +15345,19 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { | ... | @@ -15239,14 +15345,19 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { |
| 15239 | const tag_ref: Zir.Inst.Ref = if (has_tag) blk: { | 15345 | const tag_ref: Zir.Inst.Ref = if (has_tag) blk: { |
| 15240 | const tag_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); | 15346 | const tag_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); |
| 15241 | extra_index += 1; | 15347 | extra_index += 1; |
| 15242 | break :blk tag_ref; | 15348 | break :blk sema.resolveInst(tag_ref); |
| 15243 | } else .none; | 15349 | } else .none; |
| 15244 | 15350 | ||
| 15245 | if (enum_value_map) |map| { | 15351 | if (enum_value_map) |map| { |
| 15246 | const tag_src = src; // TODO better source location | 15352 | const tag_src = src; // TODO better source location |
| 15247 | const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, tag_src); | 15353 | const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, tag_src); |
| 15248 | const val = try sema.resolveConstValue(&block_scope, tag_src, coerced); | 15354 | const val = try sema.resolveConstValue(&block_scope, tag_src, coerced); |
| 15249 | map.putAssumeCapacityContext(val, {}, .{ .ty = int_tag_ty }); | 15355 | |
| 15356 | // This puts the memory into the union arena, not the enum arena, but | ||
| 15357 | // it is OK since they share the same lifetime. | ||
| 15358 | const copied_val = try val.copy(decl_arena_allocator); | ||
| 15359 | |||
| 15360 | map.putAssumeCapacityContext(copied_val, {}, .{ .ty = int_tag_ty }); | ||
| 15250 | } | 15361 | } |
| 15251 | 15362 | ||
| 15252 | // This string needs to outlive the ZIR code. | 15363 | // This string needs to outlive the ZIR code. |
src/type.zig+5-1| ... | @@ -2868,7 +2868,11 @@ pub const Type = extern union { | ... | @@ -2868,7 +2868,11 @@ pub const Type = extern union { |
| 2868 | /// Otherwise, returns `null`. | 2868 | /// Otherwise, returns `null`. |
| 2869 | pub fn unionTagType(ty: Type) ?Type { | 2869 | pub fn unionTagType(ty: Type) ?Type { |
| 2870 | return switch (ty.tag()) { | 2870 | return switch (ty.tag()) { |
| 2871 | .union_tagged => ty.castTag(.union_tagged).?.data.tag_ty, | 2871 | .union_tagged => { |
| 2872 | const union_obj = ty.castTag(.union_tagged).?.data; | ||
| 2873 | assert(union_obj.haveFieldTypes()); | ||
| 2874 | return union_obj.tag_ty; | ||
| 2875 | }, | ||
| 2872 | 2876 | ||
| 2873 | .atomic_order, | 2877 | .atomic_order, |
| 2874 | .atomic_rmw_op, | 2878 | .atomic_rmw_op, |
test/behavior/switch.zig+13| ... | @@ -313,3 +313,16 @@ fn returnsFalse() bool { | ... | @@ -313,3 +313,16 @@ fn returnsFalse() bool { |
| 313 | test "switch on const enum with var" { | 313 | test "switch on const enum with var" { |
| 314 | try expect(!returnsFalse()); | 314 | try expect(!returnsFalse()); |
| 315 | } | 315 | } |
| 316 | |||
| 317 | test "anon enum literal used in switch on union enum" { | ||
| 318 | const Foo = union(enum) { | ||
| 319 | a: i32, | ||
| 320 | }; | ||
| 321 | |||
| 322 | var foo = Foo{ .a = 1234 }; | ||
| 323 | switch (foo) { | ||
| 324 | .a => |x| { | ||
| 325 | try expect(x == 1234); | ||
| 326 | }, | ||
| 327 | } | ||
| 328 | } |
test/behavior/switch_stage1.zig-13| ... | @@ -35,19 +35,6 @@ test "capture value of switch with all unreachable prongs" { | ... | @@ -35,19 +35,6 @@ test "capture value of switch with all unreachable prongs" { |
| 35 | try expect(x == 1); | 35 | try expect(x == 1); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | test "anon enum literal used in switch on union enum" { | ||
| 39 | const Foo = union(enum) { | ||
| 40 | a: i32, | ||
| 41 | }; | ||
| 42 | |||
| 43 | var foo = Foo{ .a = 1234 }; | ||
| 44 | switch (foo) { | ||
| 45 | .a => |x| { | ||
| 46 | try expect(x == 1234); | ||
| 47 | }, | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | test "else prong of switch on error set excludes other cases" { | 38 | test "else prong of switch on error set excludes other cases" { |
| 52 | const S = struct { | 39 | const S = struct { |
| 53 | fn doTheTest() !void { | 40 | fn doTheTest() !void { |
test/behavior/union.zig+205| ... | @@ -221,6 +221,12 @@ fn testCastUnionToTag() !void { | ... | @@ -221,6 +221,12 @@ fn testCastUnionToTag() !void { |
| 221 | try expect(@as(TheTag, u) == TheTag.B); | 221 | try expect(@as(TheTag, u) == TheTag.B); |
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | test "union field access gives the enum values" { | ||
| 225 | try expect(TheUnion.A == TheTag.A); | ||
| 226 | try expect(TheUnion.B == TheTag.B); | ||
| 227 | try expect(TheUnion.C == TheTag.C); | ||
| 228 | } | ||
| 229 | |||
| 224 | test "cast tag type of union to union" { | 230 | test "cast tag type of union to union" { |
| 225 | var x: Value2 = Letter2.B; | 231 | var x: Value2 = Letter2.B; |
| 226 | try expect(@as(Letter2, x) == Letter2.B); | 232 | try expect(@as(Letter2, x) == Letter2.B); |
| ... | @@ -255,3 +261,202 @@ test "constant packed union" { | ... | @@ -255,3 +261,202 @@ test "constant packed union" { |
| 255 | fn testConstPackedUnion(expected_tokens: []const PackThis) !void { | 261 | fn testConstPackedUnion(expected_tokens: []const PackThis) !void { |
| 256 | try expect(expected_tokens[0].StringLiteral == 1); | 262 | try expect(expected_tokens[0].StringLiteral == 1); |
| 257 | } | 263 | } |
| 264 | |||
| 265 | const MultipleChoice = union(enum(u32)) { | ||
| 266 | A = 20, | ||
| 267 | B = 40, | ||
| 268 | C = 60, | ||
| 269 | D = 1000, | ||
| 270 | }; | ||
| 271 | test "simple union(enum(u32))" { | ||
| 272 | var x = MultipleChoice.C; | ||
| 273 | try expect(x == MultipleChoice.C); | ||
| 274 | try expect(@enumToInt(@as(Tag(MultipleChoice), x)) == 60); | ||
| 275 | } | ||
| 276 | |||
| 277 | const PackedPtrOrInt = packed union { | ||
| 278 | ptr: *u8, | ||
| 279 | int: u64, | ||
| 280 | }; | ||
| 281 | test "packed union size" { | ||
| 282 | comptime try expect(@sizeOf(PackedPtrOrInt) == 8); | ||
| 283 | } | ||
| 284 | |||
| 285 | const ZeroBits = union { | ||
| 286 | OnlyField: void, | ||
| 287 | }; | ||
| 288 | test "union with only 1 field which is void should be zero bits" { | ||
| 289 | comptime try expect(@sizeOf(ZeroBits) == 0); | ||
| 290 | } | ||
| 291 | |||
| 292 | test "tagged union initialization with runtime void" { | ||
| 293 | try expect(testTaggedUnionInit({})); | ||
| 294 | } | ||
| 295 | |||
| 296 | const TaggedUnionWithAVoid = union(enum) { | ||
| 297 | A, | ||
| 298 | B: i32, | ||
| 299 | }; | ||
| 300 | |||
| 301 | fn testTaggedUnionInit(x: anytype) bool { | ||
| 302 | const y = TaggedUnionWithAVoid{ .A = x }; | ||
| 303 | return @as(Tag(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A; | ||
| 304 | } | ||
| 305 | |||
| 306 | pub const UnionEnumNoPayloads = union(enum) { A, B }; | ||
| 307 | |||
| 308 | test "tagged union with no payloads" { | ||
| 309 | const a = UnionEnumNoPayloads{ .B = {} }; | ||
| 310 | switch (a) { | ||
| 311 | Tag(UnionEnumNoPayloads).A => @panic("wrong"), | ||
| 312 | Tag(UnionEnumNoPayloads).B => {}, | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | test "union with only 1 field casted to its enum type" { | ||
| 317 | const Literal = union(enum) { | ||
| 318 | Number: f64, | ||
| 319 | Bool: bool, | ||
| 320 | }; | ||
| 321 | |||
| 322 | const Expr = union(enum) { | ||
| 323 | Literal: Literal, | ||
| 324 | }; | ||
| 325 | |||
| 326 | var e = Expr{ .Literal = Literal{ .Bool = true } }; | ||
| 327 | const ExprTag = Tag(Expr); | ||
| 328 | comptime try expect(Tag(ExprTag) == u0); | ||
| 329 | var t = @as(ExprTag, e); | ||
| 330 | try expect(t == Expr.Literal); | ||
| 331 | } | ||
| 332 | |||
| 333 | test "union with one member defaults to u0 tag type" { | ||
| 334 | const U0 = union(enum) { | ||
| 335 | X: u32, | ||
| 336 | }; | ||
| 337 | comptime try expect(Tag(Tag(U0)) == u0); | ||
| 338 | } | ||
| 339 | |||
| 340 | const Foo1 = union(enum) { | ||
| 341 | f: struct { | ||
| 342 | x: usize, | ||
| 343 | }, | ||
| 344 | }; | ||
| 345 | var glbl: Foo1 = undefined; | ||
| 346 | |||
| 347 | test "global union with single field is correctly initialized" { | ||
| 348 | glbl = Foo1{ | ||
| 349 | .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 }, | ||
| 350 | }; | ||
| 351 | try expect(glbl.f.x == 123); | ||
| 352 | } | ||
| 353 | |||
| 354 | pub const FooUnion = union(enum) { | ||
| 355 | U0: usize, | ||
| 356 | U1: u8, | ||
| 357 | }; | ||
| 358 | |||
| 359 | var glbl_array: [2]FooUnion = undefined; | ||
| 360 | |||
| 361 | test "initialize global array of union" { | ||
| 362 | glbl_array[1] = FooUnion{ .U1 = 2 }; | ||
| 363 | glbl_array[0] = FooUnion{ .U0 = 1 }; | ||
| 364 | try expect(glbl_array[0].U0 == 1); | ||
| 365 | try expect(glbl_array[1].U1 == 2); | ||
| 366 | } | ||
| 367 | |||
| 368 | test "update the tag value for zero-sized unions" { | ||
| 369 | const S = union(enum) { | ||
| 370 | U0: void, | ||
| 371 | U1: void, | ||
| 372 | }; | ||
| 373 | var x = S{ .U0 = {} }; | ||
| 374 | try expect(x == .U0); | ||
| 375 | x = S{ .U1 = {} }; | ||
| 376 | try expect(x == .U1); | ||
| 377 | } | ||
| 378 | |||
| 379 | test "union initializer generates padding only if needed" { | ||
| 380 | const U = union(enum) { | ||
| 381 | A: u24, | ||
| 382 | }; | ||
| 383 | |||
| 384 | var v = U{ .A = 532 }; | ||
| 385 | try expect(v.A == 532); | ||
| 386 | } | ||
| 387 | |||
| 388 | test "runtime tag name with single field" { | ||
| 389 | const U = union(enum) { | ||
| 390 | A: i32, | ||
| 391 | }; | ||
| 392 | |||
| 393 | var v = U{ .A = 42 }; | ||
| 394 | try expect(std.mem.eql(u8, @tagName(v), "A")); | ||
| 395 | } | ||
| 396 | |||
| 397 | test "method call on an empty union" { | ||
| 398 | const S = struct { | ||
| 399 | const MyUnion = union(MyUnionTag) { | ||
| 400 | pub const MyUnionTag = enum { X1, X2 }; | ||
| 401 | X1: [0]u8, | ||
| 402 | X2: [0]u8, | ||
| 403 | |||
| 404 | pub fn useIt(self: *@This()) bool { | ||
| 405 | _ = self; | ||
| 406 | return true; | ||
| 407 | } | ||
| 408 | }; | ||
| 409 | |||
| 410 | fn doTheTest() !void { | ||
| 411 | var u = MyUnion{ .X1 = [0]u8{} }; | ||
| 412 | try expect(u.useIt()); | ||
| 413 | } | ||
| 414 | }; | ||
| 415 | try S.doTheTest(); | ||
| 416 | comptime try S.doTheTest(); | ||
| 417 | } | ||
| 418 | |||
| 419 | const Point = struct { | ||
| 420 | x: u64, | ||
| 421 | y: u64, | ||
| 422 | }; | ||
| 423 | const TaggedFoo = union(enum) { | ||
| 424 | One: i32, | ||
| 425 | Two: Point, | ||
| 426 | Three: void, | ||
| 427 | }; | ||
| 428 | const FooNoVoid = union(enum) { | ||
| 429 | One: i32, | ||
| 430 | Two: Point, | ||
| 431 | }; | ||
| 432 | const Baz = enum { A, B, C, D }; | ||
| 433 | |||
| 434 | test "tagged union type" { | ||
| 435 | const foo1 = TaggedFoo{ .One = 13 }; | ||
| 436 | const foo2 = TaggedFoo{ | ||
| 437 | .Two = Point{ | ||
| 438 | .x = 1234, | ||
| 439 | .y = 5678, | ||
| 440 | }, | ||
| 441 | }; | ||
| 442 | try expect(foo1.One == 13); | ||
| 443 | try expect(foo2.Two.x == 1234 and foo2.Two.y == 5678); | ||
| 444 | const baz = Baz.B; | ||
| 445 | |||
| 446 | try expect(baz == Baz.B); | ||
| 447 | try expect(@typeInfo(TaggedFoo).Union.fields.len == 3); | ||
| 448 | try expect(@typeInfo(Baz).Enum.fields.len == 4); | ||
| 449 | try expect(@sizeOf(TaggedFoo) == @sizeOf(FooNoVoid)); | ||
| 450 | try expect(@sizeOf(Baz) == 1); | ||
| 451 | } | ||
| 452 | |||
| 453 | test "tagged union as return value" { | ||
| 454 | switch (returnAnInt(13)) { | ||
| 455 | TaggedFoo.One => |value| try expect(value == 13), | ||
| 456 | else => unreachable, | ||
| 457 | } | ||
| 458 | } | ||
| 459 | |||
| 460 | fn returnAnInt(x: i32) TaggedFoo { | ||
| 461 | return TaggedFoo{ .One = x }; | ||
| 462 | } |
test/behavior/union_stage1.zig-211| ... | @@ -3,18 +3,6 @@ const expect = std.testing.expect; | ... | @@ -3,18 +3,6 @@ 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 | 5 | ||
| 6 | const MultipleChoice = union(enum(u32)) { | ||
| 7 | A = 20, | ||
| 8 | B = 40, | ||
| 9 | C = 60, | ||
| 10 | D = 1000, | ||
| 11 | }; | ||
| 12 | test "simple union(enum(u32))" { | ||
| 13 | var x = MultipleChoice.C; | ||
| 14 | try expect(x == MultipleChoice.C); | ||
| 15 | try expect(@enumToInt(@as(Tag(MultipleChoice), x)) == 60); | ||
| 16 | } | ||
| 17 | |||
| 18 | const MultipleChoice2 = union(enum(u32)) { | 6 | const MultipleChoice2 = union(enum(u32)) { |
| 19 | Unspecified1: i32, | 7 | Unspecified1: i32, |
| 20 | A: f32 = 20, | 8 | A: f32 = 20, |
| ... | @@ -48,33 +36,6 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void { | ... | @@ -48,33 +36,6 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void { |
| 48 | }); | 36 | }); |
| 49 | } | 37 | } |
| 50 | 38 | ||
| 51 | const PackedPtrOrInt = packed union { | ||
| 52 | ptr: *u8, | ||
| 53 | int: u64, | ||
| 54 | }; | ||
| 55 | test "packed union size" { | ||
| 56 | comptime try expect(@sizeOf(PackedPtrOrInt) == 8); | ||
| 57 | } | ||
| 58 | |||
| 59 | const ZeroBits = union { | ||
| 60 | OnlyField: void, | ||
| 61 | }; | ||
| 62 | test "union with only 1 field which is void should be zero bits" { | ||
| 63 | comptime try expect(@sizeOf(ZeroBits) == 0); | ||
| 64 | } | ||
| 65 | |||
| 66 | const TheTag = enum { A, B, C }; | ||
| 67 | const TheUnion = union(TheTag) { | ||
| 68 | A: i32, | ||
| 69 | B: i32, | ||
| 70 | C: i32, | ||
| 71 | }; | ||
| 72 | test "union field access gives the enum values" { | ||
| 73 | try expect(TheUnion.A == TheTag.A); | ||
| 74 | try expect(TheUnion.B == TheTag.B); | ||
| 75 | try expect(TheUnion.C == TheTag.C); | ||
| 76 | } | ||
| 77 | |||
| 78 | test "switch on union with only 1 field" { | 39 | test "switch on union with only 1 field" { |
| 79 | var r: PartialInst = undefined; | 40 | var r: PartialInst = undefined; |
| 80 | r = PartialInst.Compiled; | 41 | r = PartialInst.Compiled; |
| ... | @@ -101,47 +62,6 @@ const PartialInstWithPayload = union(enum) { | ... | @@ -101,47 +62,6 @@ const PartialInstWithPayload = union(enum) { |
| 101 | Compiled: i32, | 62 | Compiled: i32, |
| 102 | }; | 63 | }; |
| 103 | 64 | ||
| 104 | test "tagged union initialization with runtime void" { | ||
| 105 | try expect(testTaggedUnionInit({})); | ||
| 106 | } | ||
| 107 | |||
| 108 | const TaggedUnionWithAVoid = union(enum) { | ||
| 109 | A, | ||
| 110 | B: i32, | ||
| 111 | }; | ||
| 112 | |||
| 113 | fn testTaggedUnionInit(x: anytype) bool { | ||
| 114 | const y = TaggedUnionWithAVoid{ .A = x }; | ||
| 115 | return @as(Tag(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A; | ||
| 116 | } | ||
| 117 | |||
| 118 | pub const UnionEnumNoPayloads = union(enum) { A, B }; | ||
| 119 | |||
| 120 | test "tagged union with no payloads" { | ||
| 121 | const a = UnionEnumNoPayloads{ .B = {} }; | ||
| 122 | switch (a) { | ||
| 123 | Tag(UnionEnumNoPayloads).A => @panic("wrong"), | ||
| 124 | Tag(UnionEnumNoPayloads).B => {}, | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | test "union with only 1 field casted to its enum type" { | ||
| 129 | const Literal = union(enum) { | ||
| 130 | Number: f64, | ||
| 131 | Bool: bool, | ||
| 132 | }; | ||
| 133 | |||
| 134 | const Expr = union(enum) { | ||
| 135 | Literal: Literal, | ||
| 136 | }; | ||
| 137 | |||
| 138 | var e = Expr{ .Literal = Literal{ .Bool = true } }; | ||
| 139 | const ExprTag = Tag(Expr); | ||
| 140 | comptime try expect(Tag(ExprTag) == u0); | ||
| 141 | var t = @as(ExprTag, e); | ||
| 142 | try expect(t == Expr.Literal); | ||
| 143 | } | ||
| 144 | |||
| 145 | test "union with only 1 field casted to its enum type which has enum value specified" { | 65 | test "union with only 1 field casted to its enum type which has enum value specified" { |
| 146 | const Literal = union(enum) { | 66 | const Literal = union(enum) { |
| 147 | Number: f64, | 67 | Number: f64, |
| ... | @@ -285,13 +205,6 @@ test "union no tag with struct member" { | ... | @@ -285,13 +205,6 @@ test "union no tag with struct member" { |
| 285 | u.foo(); | 205 | u.foo(); |
| 286 | } | 206 | } |
| 287 | 207 | ||
| 288 | test "union with one member defaults to u0 tag type" { | ||
| 289 | const U0 = union(enum) { | ||
| 290 | X: u32, | ||
| 291 | }; | ||
| 292 | comptime try expect(Tag(Tag(U0)) == u0); | ||
| 293 | } | ||
| 294 | |||
| 295 | test "union with comptime_int tag" { | 208 | test "union with comptime_int tag" { |
| 296 | const Union = union(enum(comptime_int)) { | 209 | const Union = union(enum(comptime_int)) { |
| 297 | X: u32, | 210 | X: u32, |
| ... | @@ -311,34 +224,6 @@ test "extern union doesn't trigger field check at comptime" { | ... | @@ -311,34 +224,6 @@ test "extern union doesn't trigger field check at comptime" { |
| 311 | comptime try expect(x.y == 0x55); | 224 | comptime try expect(x.y == 0x55); |
| 312 | } | 225 | } |
| 313 | 226 | ||
| 314 | const Foo1 = union(enum) { | ||
| 315 | f: struct { | ||
| 316 | x: usize, | ||
| 317 | }, | ||
| 318 | }; | ||
| 319 | var glbl: Foo1 = undefined; | ||
| 320 | |||
| 321 | test "global union with single field is correctly initialized" { | ||
| 322 | glbl = Foo1{ | ||
| 323 | .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 }, | ||
| 324 | }; | ||
| 325 | try expect(glbl.f.x == 123); | ||
| 326 | } | ||
| 327 | |||
| 328 | pub const FooUnion = union(enum) { | ||
| 329 | U0: usize, | ||
| 330 | U1: u8, | ||
| 331 | }; | ||
| 332 | |||
| 333 | var glbl_array: [2]FooUnion = undefined; | ||
| 334 | |||
| 335 | test "initialize global array of union" { | ||
| 336 | glbl_array[1] = FooUnion{ .U1 = 2 }; | ||
| 337 | glbl_array[0] = FooUnion{ .U0 = 1 }; | ||
| 338 | try expect(glbl_array[0].U0 == 1); | ||
| 339 | try expect(glbl_array[1].U1 == 2); | ||
| 340 | } | ||
| 341 | |||
| 342 | test "anonymous union literal syntax" { | 227 | test "anonymous union literal syntax" { |
| 343 | const S = struct { | 228 | const S = struct { |
| 344 | const Number = union { | 229 | const Number = union { |
| ... | @@ -361,17 +246,6 @@ test "anonymous union literal syntax" { | ... | @@ -361,17 +246,6 @@ test "anonymous union literal syntax" { |
| 361 | comptime try S.doTheTest(); | 246 | comptime try S.doTheTest(); |
| 362 | } | 247 | } |
| 363 | 248 | ||
| 364 | test "update the tag value for zero-sized unions" { | ||
| 365 | const S = union(enum) { | ||
| 366 | U0: void, | ||
| 367 | U1: void, | ||
| 368 | }; | ||
| 369 | var x = S{ .U0 = {} }; | ||
| 370 | try expect(x == .U0); | ||
| 371 | x = S{ .U1 = {} }; | ||
| 372 | try expect(x == .U1); | ||
| 373 | } | ||
| 374 | |||
| 375 | test "function call result coerces from tagged union to the tag" { | 249 | test "function call result coerces from tagged union to the tag" { |
| 376 | const S = struct { | 250 | const S = struct { |
| 377 | const Arch = union(enum) { | 251 | const Arch = union(enum) { |
| ... | @@ -401,24 +275,6 @@ test "function call result coerces from tagged union to the tag" { | ... | @@ -401,24 +275,6 @@ test "function call result coerces from tagged union to the tag" { |
| 401 | comptime try S.doTheTest(); | 275 | comptime try S.doTheTest(); |
| 402 | } | 276 | } |
| 403 | 277 | ||
| 404 | test "union initializer generates padding only if needed" { | ||
| 405 | const U = union(enum) { | ||
| 406 | A: u24, | ||
| 407 | }; | ||
| 408 | |||
| 409 | var v = U{ .A = 532 }; | ||
| 410 | try expect(v.A == 532); | ||
| 411 | } | ||
| 412 | |||
| 413 | test "runtime tag name with single field" { | ||
| 414 | const U = union(enum) { | ||
| 415 | A: i32, | ||
| 416 | }; | ||
| 417 | |||
| 418 | var v = U{ .A = 42 }; | ||
| 419 | try expect(std.mem.eql(u8, @tagName(v), "A")); | ||
| 420 | } | ||
| 421 | |||
| 422 | test "cast from anonymous struct to union" { | 278 | test "cast from anonymous struct to union" { |
| 423 | const S = struct { | 279 | const S = struct { |
| 424 | const U = union(enum) { | 280 | const U = union(enum) { |
| ... | @@ -473,28 +329,6 @@ test "cast from pointer to anonymous struct to pointer to union" { | ... | @@ -473,28 +329,6 @@ test "cast from pointer to anonymous struct to pointer to union" { |
| 473 | comptime try S.doTheTest(); | 329 | comptime try S.doTheTest(); |
| 474 | } | 330 | } |
| 475 | 331 | ||
| 476 | test "method call on an empty union" { | ||
| 477 | const S = struct { | ||
| 478 | const MyUnion = union(MyUnionTag) { | ||
| 479 | pub const MyUnionTag = enum { X1, X2 }; | ||
| 480 | X1: [0]u8, | ||
| 481 | X2: [0]u8, | ||
| 482 | |||
| 483 | pub fn useIt(self: *@This()) bool { | ||
| 484 | _ = self; | ||
| 485 | return true; | ||
| 486 | } | ||
| 487 | }; | ||
| 488 | |||
| 489 | fn doTheTest() !void { | ||
| 490 | var u = MyUnion{ .X1 = [0]u8{} }; | ||
| 491 | try expect(u.useIt()); | ||
| 492 | } | ||
| 493 | }; | ||
| 494 | try S.doTheTest(); | ||
| 495 | comptime try S.doTheTest(); | ||
| 496 | } | ||
| 497 | |||
| 498 | test "switching on non exhaustive union" { | 332 | test "switching on non exhaustive union" { |
| 499 | const S = struct { | 333 | const S = struct { |
| 500 | const E = enum(u8) { | 334 | const E = enum(u8) { |
| ... | @@ -590,48 +424,3 @@ test "anytype union field: issue #9233" { | ... | @@ -590,48 +424,3 @@ test "anytype union field: issue #9233" { |
| 590 | const Quux = union(enum) { bar: anytype }; | 424 | const Quux = union(enum) { bar: anytype }; |
| 591 | _ = Quux; | 425 | _ = Quux; |
| 592 | } | 426 | } |
| 593 | |||
| 594 | const Point = struct { | ||
| 595 | x: u64, | ||
| 596 | y: u64, | ||
| 597 | }; | ||
| 598 | const TaggedFoo = union(enum) { | ||
| 599 | One: i32, | ||
| 600 | Two: Point, | ||
| 601 | Three: void, | ||
| 602 | }; | ||
| 603 | const FooNoVoid = union(enum) { | ||
| 604 | One: i32, | ||
| 605 | Two: Point, | ||
| 606 | }; | ||
| 607 | const Baz = enum { A, B, C, D }; | ||
| 608 | |||
| 609 | test "tagged union type" { | ||
| 610 | const foo1 = TaggedFoo{ .One = 13 }; | ||
| 611 | const foo2 = TaggedFoo{ | ||
| 612 | .Two = Point{ | ||
| 613 | .x = 1234, | ||
| 614 | .y = 5678, | ||
| 615 | }, | ||
| 616 | }; | ||
| 617 | try expect(foo1.One == 13); | ||
| 618 | try expect(foo2.Two.x == 1234 and foo2.Two.y == 5678); | ||
| 619 | const baz = Baz.B; | ||
| 620 | |||
| 621 | try expect(baz == Baz.B); | ||
| 622 | try expect(@typeInfo(TaggedFoo).Union.fields.len == 3); | ||
| 623 | try expect(@typeInfo(Baz).Enum.fields.len == 4); | ||
| 624 | try expect(@sizeOf(TaggedFoo) == @sizeOf(FooNoVoid)); | ||
| 625 | try expect(@sizeOf(Baz) == 1); | ||
| 626 | } | ||
| 627 | |||
| 628 | test "tagged union as return value" { | ||
| 629 | switch (returnAnInt(13)) { | ||
| 630 | TaggedFoo.One => |value| try expect(value == 13), | ||
| 631 | else => unreachable, | ||
| 632 | } | ||
| 633 | } | ||
| 634 | |||
| 635 | fn returnAnInt(x: i32) TaggedFoo { | ||
| 636 | return TaggedFoo{ .One = x }; | ||
| 637 | } |