require size for non-exhaustive enums

This commit is contained in:
Vexu 2020-01-15 21:38:11 +02:00
parent b971c7d0ff
commit f3d174aa61
No known key found for this signature in database
GPG Key ID: 59AEB8936E16A6AC
4 changed files with 68 additions and 48 deletions

View File

@ -289,8 +289,7 @@ pub fn translate(
tree.errors = ast.Tree.ErrorList.init(arena);
tree.root_node = try arena.create(ast.Node.Root);
tree.root_node.* = ast.Node.Root{
.base = ast.Node{ .id = ast.Node.Id.Root },
tree.root_node.* = .{
.decls = ast.Node.Root.DeclList.init(arena),
// initialized with the eof token at the end
.eof_token = undefined,
@ -876,25 +875,20 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
// types, while that's not ISO-C compliant many compilers allow this and
// default to the usual integer type used for all the enums.
// TODO only emit this tag type if the enum tag type is not the default.
// I don't know what the default is, need to figure out how clang is deciding.
// it appears to at least be different across gcc/msvc
if (int_type.ptr != null and
!isCBuiltinType(int_type, .UInt) and
!isCBuiltinType(int_type, .Int))
{
_ = try appendToken(c, .LParen, "(");
container_node.init_arg_expr = .{
.Type = transQualType(rp, int_type, enum_loc) catch |err| switch (err) {
_ = try appendToken(c, .LParen, "(");
container_node.init_arg_expr = .{
.Type = if (int_type.ptr != null)
transQualType(rp, int_type, enum_loc) catch |err| switch (err) {
error.UnsupportedType => {
try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
return null;
},
else => |e| return e,
},
};
_ = try appendToken(c, .RParen, ")");
}
}
else
try transCreateNodeIdentifier(c, "c_int"),
};
_ = try appendToken(c, .RParen, ")");
container_node.lbrace_token = try appendToken(c, .LBrace, "{");
@ -2198,6 +2192,19 @@ fn transDoWhileLoop(
.id = .Loop,
};
// if (!cond) break;
const if_node = try transCreateNodeIf(rp.c);
var cond_scope = Scope{
.parent = scope,
.id = .Condition,
};
const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
prefix_op.rhs = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);
_ = try appendToken(rp.c, .RParen, ")");
if_node.condition = &prefix_op.base;
if_node.body = &(try transCreateNodeBreak(rp.c, null)).base;
_ = try appendToken(rp.c, .Semicolon, ";");
const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: {
// there's already a block in C, so we'll append our condition to it.
// c: do {
@ -2209,10 +2216,7 @@ fn transDoWhileLoop(
// zig: b;
// zig: if (!cond) break;
// zig: }
const body = (try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?;
// if this is used as an expression in Zig it needs to be immediately followed by a semicolon
_ = try appendToken(rp.c, .Semicolon, ";");
break :blk body;
break :blk (try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?;
} else blk: {
// the C statement is without a block, so we need to create a block to contain it.
// c: do
@ -2228,19 +2232,6 @@ fn transDoWhileLoop(
break :blk block;
};
// if (!cond) break;
const if_node = try transCreateNodeIf(rp.c);
var cond_scope = Scope{
.parent = scope,
.id = .Condition,
};
const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
prefix_op.rhs = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);
_ = try appendToken(rp.c, .RParen, ")");
if_node.condition = &prefix_op.base;
if_node.body = &(try transCreateNodeBreak(rp.c, null)).base;
_ = try appendToken(rp.c, .Semicolon, ";");
try body_node.statements.push(&if_node.base);
if (new)
body_node.rbrace = try appendToken(rp.c, .RBrace, "}");
@ -4775,8 +4766,7 @@ fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex {
fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node {
const token_index = try appendIdentifier(c, name);
const identifier = try c.a().create(ast.Node.Identifier);
identifier.* = ast.Node.Identifier{
.base = ast.Node{ .id = ast.Node.Id.Identifier },
identifier.* = .{
.token = token_index,
};
return &identifier.base;
@ -4915,8 +4905,7 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u
const token_index = try appendToken(c, .Keyword_var, "var");
const identifier = try c.a().create(ast.Node.Identifier);
identifier.* = ast.Node.Identifier{
.base = ast.Node{ .id = ast.Node.Id.Identifier },
identifier.* = .{
.token = token_index,
};

View File

@ -2652,6 +2652,14 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
AstNode *tag_value = field_node->data.struct_field.value;
if (buf_eql_str(type_enum_field->name, "_")) {
if (decl_node->data.container_decl.init_arg_expr == nullptr) {
add_node_error(g, field_node, buf_sprintf("non-exhaustive enum must specify size"));
enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
}
if (log2_u64(field_count - 1) == enum_type->size_in_bits) {
add_node_error(g, field_node, buf_sprintf("non-exhaustive enum specifies every value"));
enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
}
if (field_i != field_count - 1) {
add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last"));
enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;

View File

@ -3,7 +3,30 @@ const builtin = @import("builtin");
pub fn addCases(cases: *tests.CompileErrorContext) void {
cases.addTest("non-exhaustive enums",
\\const E = enum {
\\const A = enum {
\\ a,
\\ b,
\\ _ = 1,
\\};
\\const B = enum(u1) {
\\ a,
\\ b,
\\ _,
\\ c,
\\};
\\pub export fn entry() void {
\\ _ = A;
\\ _ = B;
\\}
, &[_][]const u8{
"tmp.zig:4:5: error: non-exhaustive enum must specify size",
"error: value assigned to '_' field of non-exhaustive enum",
"error: non-exhaustive enum specifies every value",
"error: '_' field of non-exhaustive enum must be last",
});
cases.addTest("switching with non-exhaustive enums",
\\const E = enum(u8) {
\\ a,
\\ b,
\\ _,

View File

@ -989,7 +989,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\enum enum_ty { FOO };
, &[_][]const u8{
\\pub const FOO = @enumToInt(enum_enum_ty.FOO);
\\pub const enum_enum_ty = extern enum {
\\pub const enum_enum_ty = extern enum(c_int) {
\\ FOO,
\\ _,
\\};
@ -1104,7 +1104,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const a = @enumToInt(enum_unnamed_1.a);
\\pub const b = @enumToInt(enum_unnamed_1.b);
\\pub const c = @enumToInt(enum_unnamed_1.c);
\\const enum_unnamed_1 = extern enum {
\\const enum_unnamed_1 = extern enum(c_uint) {
\\ a,
\\ b,
\\ c,
@ -1114,7 +1114,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const e = @enumToInt(enum_unnamed_2.e);
\\pub const f = @enumToInt(enum_unnamed_2.f);
\\pub const g = @enumToInt(enum_unnamed_2.g);
\\const enum_unnamed_2 = extern enum {
\\const enum_unnamed_2 = extern enum(c_uint) {
\\ e = 0,
\\ f = 4,
\\ g = 5,
@ -1124,7 +1124,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const i = @enumToInt(enum_unnamed_3.i);
\\pub const j = @enumToInt(enum_unnamed_3.j);
\\pub const k = @enumToInt(enum_unnamed_3.k);
\\const enum_unnamed_3 = extern enum {
\\const enum_unnamed_3 = extern enum(c_uint) {
\\ i,
\\ j,
\\ k,
@ -1137,7 +1137,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const n = @enumToInt(enum_i.n);
\\pub const o = @enumToInt(enum_i.o);
\\pub const p = @enumToInt(enum_i.p);
\\pub const enum_i = extern enum {
\\pub const enum_i = extern enum(c_uint) {
\\ n,
\\ o,
\\ p,
@ -1569,7 +1569,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
, &[_][]const u8{
\\pub const One = @enumToInt(enum_unnamed_1.One);
\\pub const Two = @enumToInt(enum_unnamed_1.Two);
\\const enum_unnamed_1 = extern enum {
\\const enum_unnamed_1 = extern enum(c_uint) {
\\ One,
\\ Two,
\\ _,
@ -1672,7 +1672,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
\\}
, &[_][]const u8{
\\pub const enum_Foo = extern enum {
\\pub const enum_Foo = extern enum(c_uint) {
\\ A,
\\ B,
\\ C,
@ -1718,7 +1718,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ y: c_int,
\\};
,
\\pub const enum_Bar = extern enum {
\\pub const enum_Bar = extern enum(c_uint) {
\\ A,
\\ B,
\\ _,
@ -1982,7 +1982,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return 4;
\\}
, &[_][]const u8{
\\pub const enum_SomeEnum = extern enum {
\\pub const enum_SomeEnum = extern enum(c_uint) {
\\ A,
\\ B,
\\ C,
@ -2424,7 +2424,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const FooA = @enumToInt(enum_Foo.A);
\\pub const FooB = @enumToInt(enum_Foo.B);
\\pub const Foo1 = @enumToInt(enum_Foo.@"1");
\\pub const enum_Foo = extern enum {
\\pub const enum_Foo = extern enum(c_uint) {
\\ A = 2,
\\ B = 5,
\\ @"1" = 6,