mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 22:35:24 +00:00
require size for non-exhaustive enums
This commit is contained in:
parent
b971c7d0ff
commit
f3d174aa61
@ -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,
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
\\ _,
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user