authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-28 23:10:48-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-28 23:22:09-07:00
logefb7148a4574c608b21359fcbf2edf06afdb5e0c
tree23e47a49f95e60ea315603407bd5793310978c62
parent91619cdf57f54accbdbb3ff616856eaf79b537a3

Sema: more union fixes

* `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 {
11041104
11051105 pub fn getLayout(u: Union, target: Target, have_tag: bool) Layout {
11061106 assert(u.status == .have_layout);
1107 var most_aligned_field: usize = undefined;
1107 var most_aligned_field: u32 = undefined;
11081108 var most_aligned_field_size: u64 = undefined;
1109 var biggest_field: usize = undefined;
1109 var biggest_field: u32 = undefined;
11101110 var payload_size: u64 = 0;
11111111 var payload_align: u32 = 0;
11121112 for (u.fields.values()) |field, i| {
......@@ -1122,20 +1122,21 @@ pub const Union = struct {
11221122 const field_size = field.ty.abiSize(target);
11231123 if (field_size > payload_size) {
11241124 payload_size = field_size;
1125 biggest_field = i;
1125 biggest_field = @intCast(u32, i);
11261126 }
11271127 if (field_align > payload_align) {
11281128 payload_align = field_align;
1129 most_aligned_field = i;
1129 most_aligned_field = @intCast(u32, i);
11301130 most_aligned_field_size = field_size;
11311131 }
11321132 }
1133 payload_align = @maximum(payload_align, 1);
11331134 if (!have_tag) return .{
11341135 .abi_size = std.mem.alignForwardGeneric(u64, payload_size, payload_align),
11351136 .abi_align = payload_align,
1136 .most_aligned_field = @intCast(u32, most_aligned_field),
1137 .most_aligned_field = most_aligned_field,
11371138 .most_aligned_field_size = most_aligned_field_size,
1138 .biggest_field = @intCast(u32, biggest_field),
1139 .biggest_field = biggest_field,
11391140 .payload_size = payload_size,
11401141 .payload_align = payload_align,
11411142 .tag_align = 0,
......@@ -1144,7 +1145,7 @@ pub const Union = struct {
11441145 // Put the tag before or after the payload depending on which one's
11451146 // alignment is greater.
11461147 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));
11481149 var size: u64 = 0;
11491150 if (tag_align >= payload_align) {
11501151 // {Tag, Payload}
......@@ -1162,9 +1163,9 @@ pub const Union = struct {
11621163 return .{
11631164 .abi_size = size,
11641165 .abi_align = @maximum(tag_align, payload_align),
1165 .most_aligned_field = @intCast(u32, most_aligned_field),
1166 .most_aligned_field = most_aligned_field,
11661167 .most_aligned_field_size = most_aligned_field_size,
1167 .biggest_field = @intCast(u32, biggest_field),
1168 .biggest_field = biggest_field,
11681169 .payload_size = payload_size,
11691170 .payload_align = payload_align,
11701171 .tag_align = tag_align,
src/Sema.zig+123-12
......@@ -5799,8 +5799,12 @@ fn zirSwitchCond(
57995799) CompileError!Air.Inst.Ref {
58005800 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
58015801 const src = inst_data.src();
5802 const operand_src = src; // TODO make this point at the switch operand
58025803 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;
58045808 const operand_ty = sema.typeOf(operand);
58055809
58065810 switch (operand_ty.zigTypeTag()) {
......@@ -5817,18 +5821,19 @@ fn zirSwitchCond(
58175821 .ErrorSet,
58185822 .Enum,
58195823 => {
5820 if ((try sema.typeHasOnePossibleValue(block, src, operand_ty))) |opv| {
5824 if ((try sema.typeHasOnePossibleValue(block, operand_src, operand_ty))) |opv| {
58215825 return sema.addConstant(operand_ty, opv);
58225826 }
58235827 return operand;
58245828 },
58255829
58265830 .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 {
58285833 const msg = msg: {
58295834 const msg = try sema.errMsg(block, src, "switch on untagged union", .{});
58305835 errdefer msg.destroy(sema.gpa);
5831 try sema.addDeclaredHereNote(msg, operand_ty);
5836 try sema.addDeclaredHereNote(msg, union_ty);
58325837 break :msg msg;
58335838 };
58345839 return sema.failWithOwnedErrorMsg(msg);
......@@ -9154,9 +9159,107 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
91549159 }),
91559160 );
91569161 },
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 },
91579261 .Struct => return sema.fail(block, src, "TODO: implement zirTypeInfo for Struct", .{}),
91589262 .ErrorSet => return sema.fail(block, src, "TODO: implement zirTypeInfo for ErrorSet", .{}),
9159 .Union => return sema.fail(block, src, "TODO: implement zirTypeInfo for Union", .{}),
91609263 .BoundFn => @panic("TODO remove this type from the language and compiler"),
91619264 .Opaque => return sema.fail(block, src, "TODO: implement zirTypeInfo for Opaque", .{}),
91629265 .Frame => return sema.fail(block, src, "TODO: implement zirTypeInfo for Frame", .{}),
......@@ -11847,12 +11950,14 @@ fn fieldVal(
1184711950 );
1184811951 },
1184911952 .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| {
1185111956 if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
1185211957 return inst;
1185311958 }
1185411959 }
11855 if (child_type.unionTagType()) |enum_ty| {
11960 if (union_ty.unionTagType()) |enum_ty| {
1185611961 if (enum_ty.enumFieldIndex(field_name)) |field_index_usize| {
1185711962 const field_index = @intCast(u32, field_index_usize);
1185811963 return sema.addConstant(
......@@ -11861,7 +11966,7 @@ fn fieldVal(
1186111966 );
1186211967 }
1186311968 }
11864 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
11969 return sema.failWithBadMemberAccess(block, union_ty, field_name_src, field_name);
1186511970 },
1186611971 .Enum => {
1186711972 if (child_type.getNamespace()) |namespace| {
......@@ -15185,8 +15290,9 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
1518515290 // The provided type is an integer type and we must construct the enum tag type here.
1518615291 int_tag_ty = provided_ty;
1518715292 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;
15189 enum_value_map = &union_obj.tag_ty.castTag(.enum_numbered).?.data.values;
15293 const enum_obj = union_obj.tag_ty.castTag(.enum_numbered).?.data;
15294 enum_field_names = &enum_obj.fields;
15295 enum_value_map = &enum_obj.values;
1519015296 } else {
1519115297 // The provided type is the enum tag type.
1519215298 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 {
1523915345 const tag_ref: Zir.Inst.Ref = if (has_tag) blk: {
1524015346 const tag_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
1524115347 extra_index += 1;
15242 break :blk tag_ref;
15348 break :blk sema.resolveInst(tag_ref);
1524315349 } else .none;
1524415350
1524515351 if (enum_value_map) |map| {
1524615352 const tag_src = src; // TODO better source location
1524715353 const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, tag_src);
1524815354 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 });
1525015361 }
1525115362
1525215363 // This string needs to outlive the ZIR code.
src/type.zig+5-1
......@@ -2868,7 +2868,11 @@ pub const Type = extern union {
28682868 /// Otherwise, returns `null`.
28692869 pub fn unionTagType(ty: Type) ?Type {
28702870 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 },
28722876
28732877 .atomic_order,
28742878 .atomic_rmw_op,
test/behavior/switch.zig+13
......@@ -313,3 +313,16 @@ fn returnsFalse() bool {
313313test "switch on const enum with var" {
314314 try expect(!returnsFalse());
315315}
316
317test "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" {
3535 try expect(x == 1);
3636}
3737
38test "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
5138test "else prong of switch on error set excludes other cases" {
5239 const S = struct {
5340 fn doTheTest() !void {
test/behavior/union.zig+205
......@@ -221,6 +221,12 @@ fn testCastUnionToTag() !void {
221221 try expect(@as(TheTag, u) == TheTag.B);
222222}
223223
224test "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
224230test "cast tag type of union to union" {
225231 var x: Value2 = Letter2.B;
226232 try expect(@as(Letter2, x) == Letter2.B);
......@@ -255,3 +261,202 @@ test "constant packed union" {
255261fn testConstPackedUnion(expected_tokens: []const PackThis) !void {
256262 try expect(expected_tokens[0].StringLiteral == 1);
257263}
264
265const MultipleChoice = union(enum(u32)) {
266 A = 20,
267 B = 40,
268 C = 60,
269 D = 1000,
270};
271test "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
277const PackedPtrOrInt = packed union {
278 ptr: *u8,
279 int: u64,
280};
281test "packed union size" {
282 comptime try expect(@sizeOf(PackedPtrOrInt) == 8);
283}
284
285const ZeroBits = union {
286 OnlyField: void,
287};
288test "union with only 1 field which is void should be zero bits" {
289 comptime try expect(@sizeOf(ZeroBits) == 0);
290}
291
292test "tagged union initialization with runtime void" {
293 try expect(testTaggedUnionInit({}));
294}
295
296const TaggedUnionWithAVoid = union(enum) {
297 A,
298 B: i32,
299};
300
301fn testTaggedUnionInit(x: anytype) bool {
302 const y = TaggedUnionWithAVoid{ .A = x };
303 return @as(Tag(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A;
304}
305
306pub const UnionEnumNoPayloads = union(enum) { A, B };
307
308test "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
316test "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
333test "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
340const Foo1 = union(enum) {
341 f: struct {
342 x: usize,
343 },
344};
345var glbl: Foo1 = undefined;
346
347test "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
354pub const FooUnion = union(enum) {
355 U0: usize,
356 U1: u8,
357};
358
359var glbl_array: [2]FooUnion = undefined;
360
361test "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
368test "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
379test "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
388test "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
397test "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
419const Point = struct {
420 x: u64,
421 y: u64,
422};
423const TaggedFoo = union(enum) {
424 One: i32,
425 Two: Point,
426 Three: void,
427};
428const FooNoVoid = union(enum) {
429 One: i32,
430 Two: Point,
431};
432const Baz = enum { A, B, C, D };
433
434test "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
453test "tagged union as return value" {
454 switch (returnAnInt(13)) {
455 TaggedFoo.One => |value| try expect(value == 13),
456 else => unreachable,
457 }
458}
459
460fn 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;
33const expectEqual = std.testing.expectEqual;
44const Tag = std.meta.Tag;
55
6const MultipleChoice = union(enum(u32)) {
7 A = 20,
8 B = 40,
9 C = 60,
10 D = 1000,
11};
12test "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
186const MultipleChoice2 = union(enum(u32)) {
197 Unspecified1: i32,
208 A: f32 = 20,
......@@ -48,33 +36,6 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void {
4836 });
4937}
5038
51const PackedPtrOrInt = packed union {
52 ptr: *u8,
53 int: u64,
54};
55test "packed union size" {
56 comptime try expect(@sizeOf(PackedPtrOrInt) == 8);
57}
58
59const ZeroBits = union {
60 OnlyField: void,
61};
62test "union with only 1 field which is void should be zero bits" {
63 comptime try expect(@sizeOf(ZeroBits) == 0);
64}
65
66const TheTag = enum { A, B, C };
67const TheUnion = union(TheTag) {
68 A: i32,
69 B: i32,
70 C: i32,
71};
72test "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
7839test "switch on union with only 1 field" {
7940 var r: PartialInst = undefined;
8041 r = PartialInst.Compiled;
......@@ -101,47 +62,6 @@ const PartialInstWithPayload = union(enum) {
10162 Compiled: i32,
10263};
10364
104test "tagged union initialization with runtime void" {
105 try expect(testTaggedUnionInit({}));
106}
107
108const TaggedUnionWithAVoid = union(enum) {
109 A,
110 B: i32,
111};
112
113fn testTaggedUnionInit(x: anytype) bool {
114 const y = TaggedUnionWithAVoid{ .A = x };
115 return @as(Tag(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A;
116}
117
118pub const UnionEnumNoPayloads = union(enum) { A, B };
119
120test "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
128test "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
14565test "union with only 1 field casted to its enum type which has enum value specified" {
14666 const Literal = union(enum) {
14767 Number: f64,
......@@ -285,13 +205,6 @@ test "union no tag with struct member" {
285205 u.foo();
286206}
287207
288test "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
295208test "union with comptime_int tag" {
296209 const Union = union(enum(comptime_int)) {
297210 X: u32,
......@@ -311,34 +224,6 @@ test "extern union doesn't trigger field check at comptime" {
311224 comptime try expect(x.y == 0x55);
312225}
313226
314const Foo1 = union(enum) {
315 f: struct {
316 x: usize,
317 },
318};
319var glbl: Foo1 = undefined;
320
321test "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
328pub const FooUnion = union(enum) {
329 U0: usize,
330 U1: u8,
331};
332
333var glbl_array: [2]FooUnion = undefined;
334
335test "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
342227test "anonymous union literal syntax" {
343228 const S = struct {
344229 const Number = union {
......@@ -361,17 +246,6 @@ test "anonymous union literal syntax" {
361246 comptime try S.doTheTest();
362247}
363248
364test "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
375249test "function call result coerces from tagged union to the tag" {
376250 const S = struct {
377251 const Arch = union(enum) {
......@@ -401,24 +275,6 @@ test "function call result coerces from tagged union to the tag" {
401275 comptime try S.doTheTest();
402276}
403277
404test "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
413test "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
422278test "cast from anonymous struct to union" {
423279 const S = struct {
424280 const U = union(enum) {
......@@ -473,28 +329,6 @@ test "cast from pointer to anonymous struct to pointer to union" {
473329 comptime try S.doTheTest();
474330}
475331
476test "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
498332test "switching on non exhaustive union" {
499333 const S = struct {
500334 const E = enum(u8) {
......@@ -590,48 +424,3 @@ test "anytype union field: issue #9233" {
590424 const Quux = union(enum) { bar: anytype };
591425 _ = Quux;
592426}
593
594const Point = struct {
595 x: u64,
596 y: u64,
597};
598const TaggedFoo = union(enum) {
599 One: i32,
600 Two: Point,
601 Three: void,
602};
603const FooNoVoid = union(enum) {
604 One: i32,
605 Two: Point,
606};
607const Baz = enum { A, B, C, D };
608
609test "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
628test "tagged union as return value" {
629 switch (returnAnInt(13)) {
630 TaggedFoo.One => |value| try expect(value == 13),
631 else => unreachable,
632 }
633}
634
635fn returnAnInt(x: i32) TaggedFoo {
636 return TaggedFoo{ .One = x };
637}