authorgravatar for carl@astholm.seCarl Åstholm <carl@astholm.se> 2024-01-04 14:30:52+01:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2024-01-04 22:38:31+02:00
log501a2350ab804f3e5d4253826bcdfb7a3f5d92fb
treedd98936b4c0578f027d7ce82f8b4f2353c4184d2
parent15f7a477d04b0a4a20c9a4489ea819e95939315b

sema: Prevent reifying non-empty union with empty tag type


4 files changed, 99 insertions(+), 2 deletions(-)

src/Sema.zig+4-2
......@@ -20888,7 +20888,7 @@ fn zirReify(
2088820888 enum_field_names[i] = field_name;
2088920889 }
2089020890
20891 if (explicit_tags_seen.len > 0) {
20891 if (enum_tag_ty != .none) {
2089220892 const tag_info = ip.indexToKey(enum_tag_ty).enum_type;
2089320893 const enum_index = tag_info.nameIndex(ip, field_name) orelse {
2089420894 const msg = msg: {
......@@ -20902,6 +20902,7 @@ fn zirReify(
2090220902 };
2090320903 return sema.failWithOwnedErrorMsg(block, msg);
2090420904 };
20905 assert(explicit_tags_seen.len == tag_info.names.len);
2090520906 // No check for duplicate because the check already happened in order
2090620907 // to create the enum type in the first place.
2090720908 assert(!explicit_tags_seen[enum_index]);
......@@ -20967,13 +20968,14 @@ fn zirReify(
2096720968 }
2096820969 }
2096920970
20970 if (explicit_tags_seen.len > 0) {
20971 if (enum_tag_ty != .none) {
2097120972 const tag_info = ip.indexToKey(enum_tag_ty).enum_type;
2097220973 if (tag_info.names.len > fields_len) {
2097320974 const msg = msg: {
2097420975 const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});
2097520976 errdefer msg.destroy(gpa);
2097620977
20978 assert(explicit_tags_seen.len == tag_info.names.len);
2097720979 for (tag_info.names.get(ip), 0..) |field_name, field_index| {
2097820980 if (explicit_tags_seen[field_index]) continue;
2097920981 try sema.addFieldErrNote(Type.fromInterned(enum_tag_ty), field_index, msg, "field '{}' missing, declared here", .{
test/behavior/type.zig+33
......@@ -482,6 +482,39 @@ test "Type.Union from regular enum" {
482482 _ = @typeInfo(T).Union;
483483}
484484
485test "Type.Union from empty regular enum" {
486 const E = enum {};
487 const U = @Type(.{
488 .Union = .{
489 .layout = .Auto,
490 .tag_type = E,
491 .fields = &.{},
492 .decls = &.{},
493 },
494 });
495 try testing.expectEqual(@sizeOf(U), 0);
496}
497
498test "Type.Union from empty Type.Enum" {
499 const E = @Type(.{
500 .Enum = .{
501 .tag_type = u0,
502 .fields = &.{},
503 .decls = &.{},
504 .is_exhaustive = true,
505 },
506 });
507 const U = @Type(.{
508 .Union = .{
509 .layout = .Auto,
510 .tag_type = E,
511 .fields = &.{},
512 .decls = &.{},
513 },
514 });
515 try testing.expectEqual(@sizeOf(U), 0);
516}
517
485518test "Type.Fn" {
486519 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
487520 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
test/cases/compile_errors/reify_type_for_tagged_union_with_no_enum_fields.zig created+30
......@@ -0,0 +1,30 @@
1const Tag = @Type(.{
2 .Enum = .{
3 .tag_type = u0,
4 .fields = &.{},
5 .decls = &.{},
6 .is_exhaustive = true,
7 },
8});
9const Tagged = @Type(.{
10 .Union = .{
11 .layout = .Auto,
12 .tag_type = Tag,
13 .fields = &.{
14 .{ .name = "signed", .type = i32, .alignment = @alignOf(i32) },
15 .{ .name = "unsigned", .type = u32, .alignment = @alignOf(u32) },
16 },
17 .decls = &.{},
18 },
19});
20export fn entry() void {
21 const tagged: Tagged = undefined;
22 _ = tagged;
23}
24
25// error
26// backend=stage2
27// target=native
28//
29// :9:16: error: no field named 'signed' in enum 'tmp.Tag'
30// :1:13: note: enum declared here
test/cases/compile_errors/reify_type_for_tagged_union_with_no_union_fields.zig created+32
......@@ -0,0 +1,32 @@
1const Tag = @Type(.{
2 .Enum = .{
3 .tag_type = u1,
4 .fields = &.{
5 .{ .name = "signed", .value = 0 },
6 .{ .name = "unsigned", .value = 1 },
7 },
8 .decls = &.{},
9 .is_exhaustive = true,
10 },
11});
12const Tagged = @Type(.{
13 .Union = .{
14 .layout = .Auto,
15 .tag_type = Tag,
16 .fields = &.{},
17 .decls = &.{},
18 },
19});
20export fn entry() void {
21 const tagged: Tagged = undefined;
22 _ = tagged;
23}
24
25// error
26// backend=stage2
27// target=native
28//
29// :12:16: error: enum field(s) missing in union
30// :1:13: note: field 'signed' missing, declared here
31// :1:13: note: field 'unsigned' missing, declared here
32// :1:13: note: enum declared here