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(...@@ -20888,7 +20888,7 @@ fn zirReify(
20888 enum_field_names[i] = field_name;20888 enum_field_names[i] = field_name;
20889 }20889 }
2089020890
20891 if (explicit_tags_seen.len > 0) {20891 if (enum_tag_ty != .none) {
20892 const tag_info = ip.indexToKey(enum_tag_ty).enum_type;20892 const tag_info = ip.indexToKey(enum_tag_ty).enum_type;
20893 const enum_index = tag_info.nameIndex(ip, field_name) orelse {20893 const enum_index = tag_info.nameIndex(ip, field_name) orelse {
20894 const msg = msg: {20894 const msg = msg: {
...@@ -20902,6 +20902,7 @@ fn zirReify(...@@ -20902,6 +20902,7 @@ fn zirReify(
20902 };20902 };
20903 return sema.failWithOwnedErrorMsg(block, msg);20903 return sema.failWithOwnedErrorMsg(block, msg);
20904 };20904 };
20905 assert(explicit_tags_seen.len == tag_info.names.len);
20905 // No check for duplicate because the check already happened in order20906 // No check for duplicate because the check already happened in order
20906 // to create the enum type in the first place.20907 // to create the enum type in the first place.
20907 assert(!explicit_tags_seen[enum_index]);20908 assert(!explicit_tags_seen[enum_index]);
...@@ -20967,13 +20968,14 @@ fn zirReify(...@@ -20967,13 +20968,14 @@ fn zirReify(
20967 }20968 }
20968 }20969 }
2096920970
20970 if (explicit_tags_seen.len > 0) {20971 if (enum_tag_ty != .none) {
20971 const tag_info = ip.indexToKey(enum_tag_ty).enum_type;20972 const tag_info = ip.indexToKey(enum_tag_ty).enum_type;
20972 if (tag_info.names.len > fields_len) {20973 if (tag_info.names.len > fields_len) {
20973 const msg = msg: {20974 const msg = msg: {
20974 const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});20975 const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});
20975 errdefer msg.destroy(gpa);20976 errdefer msg.destroy(gpa);
2097620977
20978 assert(explicit_tags_seen.len == tag_info.names.len);
20977 for (tag_info.names.get(ip), 0..) |field_name, field_index| {20979 for (tag_info.names.get(ip), 0..) |field_name, field_index| {
20978 if (explicit_tags_seen[field_index]) continue;20980 if (explicit_tags_seen[field_index]) continue;
20979 try sema.addFieldErrNote(Type.fromInterned(enum_tag_ty), field_index, msg, "field '{}' missing, declared here", .{20981 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" {...@@ -482,6 +482,39 @@ test "Type.Union from regular enum" {
482 _ = @typeInfo(T).Union;482 _ = @typeInfo(T).Union;
483}483}
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
485test "Type.Fn" {518test "Type.Fn" {
486 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO519 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
487 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO520 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