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

This commit is contained in:
Carl Åstholm
2024-01-04 14:30:52 +01:00
committed by Veikka Tuominen
parent 15f7a477d0
commit 501a2350ab
4 changed files with 99 additions and 2 deletions

View File

@@ -20888,7 +20888,7 @@ fn zirReify(
enum_field_names[i] = field_name;
}
if (explicit_tags_seen.len > 0) {
if (enum_tag_ty != .none) {
const tag_info = ip.indexToKey(enum_tag_ty).enum_type;
const enum_index = tag_info.nameIndex(ip, field_name) orelse {
const msg = msg: {
@@ -20902,6 +20902,7 @@ fn zirReify(
};
return sema.failWithOwnedErrorMsg(block, msg);
};
assert(explicit_tags_seen.len == tag_info.names.len);
// No check for duplicate because the check already happened in order
// to create the enum type in the first place.
assert(!explicit_tags_seen[enum_index]);
@@ -20967,13 +20968,14 @@ fn zirReify(
}
}
if (explicit_tags_seen.len > 0) {
if (enum_tag_ty != .none) {
const tag_info = ip.indexToKey(enum_tag_ty).enum_type;
if (tag_info.names.len > fields_len) {
const msg = msg: {
const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});
errdefer msg.destroy(gpa);
assert(explicit_tags_seen.len == tag_info.names.len);
for (tag_info.names.get(ip), 0..) |field_name, field_index| {
if (explicit_tags_seen[field_index]) continue;
try sema.addFieldErrNote(Type.fromInterned(enum_tag_ty), field_index, msg, "field '{}' missing, declared here", .{

View File

@@ -482,6 +482,39 @@ test "Type.Union from regular enum" {
_ = @typeInfo(T).Union;
}
test "Type.Union from empty regular enum" {
const E = enum {};
const U = @Type(.{
.Union = .{
.layout = .Auto,
.tag_type = E,
.fields = &.{},
.decls = &.{},
},
});
try testing.expectEqual(@sizeOf(U), 0);
}
test "Type.Union from empty Type.Enum" {
const E = @Type(.{
.Enum = .{
.tag_type = u0,
.fields = &.{},
.decls = &.{},
.is_exhaustive = true,
},
});
const U = @Type(.{
.Union = .{
.layout = .Auto,
.tag_type = E,
.fields = &.{},
.decls = &.{},
},
});
try testing.expectEqual(@sizeOf(U), 0);
}
test "Type.Fn" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO

View File

@@ -0,0 +1,30 @@
const Tag = @Type(.{
.Enum = .{
.tag_type = u0,
.fields = &.{},
.decls = &.{},
.is_exhaustive = true,
},
});
const Tagged = @Type(.{
.Union = .{
.layout = .Auto,
.tag_type = Tag,
.fields = &.{
.{ .name = "signed", .type = i32, .alignment = @alignOf(i32) },
.{ .name = "unsigned", .type = u32, .alignment = @alignOf(u32) },
},
.decls = &.{},
},
});
export fn entry() void {
const tagged: Tagged = undefined;
_ = tagged;
}
// error
// backend=stage2
// target=native
//
// :9:16: error: no field named 'signed' in enum 'tmp.Tag'
// :1:13: note: enum declared here

View File

@@ -0,0 +1,32 @@
const Tag = @Type(.{
.Enum = .{
.tag_type = u1,
.fields = &.{
.{ .name = "signed", .value = 0 },
.{ .name = "unsigned", .value = 1 },
},
.decls = &.{},
.is_exhaustive = true,
},
});
const Tagged = @Type(.{
.Union = .{
.layout = .Auto,
.tag_type = Tag,
.fields = &.{},
.decls = &.{},
},
});
export fn entry() void {
const tagged: Tagged = undefined;
_ = tagged;
}
// error
// backend=stage2
// target=native
//
// :12:16: error: enum field(s) missing in union
// :1:13: note: field 'signed' missing, declared here
// :1:13: note: field 'unsigned' missing, declared here
// :1:13: note: enum declared here