Commit f3d174aa61
Changed files (4)
src/analyze.cpp
@@ -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;
src-self-hosted/translate_c.zig
@@ -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,
};
test/compile_errors.zig
@@ -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,
\\ _,
test/translate_c.zig
@@ -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,