From 6fd0dddf186f6435f422f2992f44ec9a35e09f20 Mon Sep 17 00:00:00 2001
From: Vexu
Date: Wed, 15 Jan 2020 20:23:49 +0200
Subject: implement non-exhaustive enums
---
src/analyze.cpp | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index b7838003c8..a62e0414e0 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -2572,6 +2572,7 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
enum_type->data.enumeration.src_field_count = field_count;
enum_type->data.enumeration.fields = allocate(field_count);
enum_type->data.enumeration.fields_by_name.init(field_count);
+ enum_type->data.enumeration.non_exhaustive = false;
Scope *scope = &enum_type->data.enumeration.decls_scope->base;
@@ -2648,6 +2649,21 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
buf_sprintf("consider 'union(enum)' here"));
}
+ AstNode *tag_value = field_node->data.struct_field.value;
+
+ if (buf_eql_str(type_enum_field->name, "_")) {
+ 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;
+ }
+ if (tag_value != nullptr) {
+ add_node_error(g, field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum"));
+ enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
+ }
+ enum_type->data.enumeration.non_exhaustive = true;
+ continue;
+ }
+
auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field);
if (field_entry != nullptr) {
ErrorMsg *msg = add_node_error(g, field_node,
@@ -2657,8 +2673,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
continue;
}
- AstNode *tag_value = field_node->data.struct_field.value;
-
if (tag_value != nullptr) {
// A user-specified value is available
ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type,
--
cgit v1.2.3
From f3d174aa616401117927988dfc499a1762db01a3 Mon Sep 17 00:00:00 2001
From: Vexu
Date: Wed, 15 Jan 2020 21:38:11 +0200
Subject: require size for non-exhaustive enums
---
src-self-hosted/translate_c.zig | 63 +++++++++++++++++------------------------
src/analyze.cpp | 8 ++++++
test/compile_errors.zig | 25 +++++++++++++++-
test/translate_c.zig | 20 ++++++-------
4 files changed, 68 insertions(+), 48 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig
index ad58b6a916..c726bf8cbf 100644
--- a/src-self-hosted/translate_c.zig
+++ b/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,
};
diff --git a/src/analyze.cpp b/src/analyze.cpp
index a62e0414e0..0b2b6ddeaa 100644
--- a/src/analyze.cpp
+++ b/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;
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index e7ef53452a..9a76319af5 100644
--- a/test/compile_errors.zig
+++ b/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,
\\ _,
diff --git a/test/translate_c.zig b/test/translate_c.zig
index 13779cb2fa..adee4c9a4d 100644
--- a/test/translate_c.zig
+++ 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,
--
cgit v1.2.3
From 5c2238fc4ad1d10f0620c931d369005b53742eb7 Mon Sep 17 00:00:00 2001
From: Vexu
Date: Wed, 15 Jan 2020 22:09:19 +0200
Subject: small fixes
* error for '_' prong on exhaustive enum
* todo panic for `@tagName` on non-exhaustive enum
* don't require '_' field on tagged unions
---
src/analyze.cpp | 2 +-
src/codegen.cpp | 2 ++
src/ir.cpp | 8 +++++++-
3 files changed, 10 insertions(+), 2 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 0b2b6ddeaa..7669e0890b 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -3293,7 +3293,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
} else if (enum_type_node != nullptr) {
for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) {
TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i];
- if (!covered_enum_fields[i]) {
+ if (!covered_enum_fields[i] && !buf_eql_str(enum_field->name, "_")) {
AstNode *enum_decl_node = tag_type->data.enumeration.decl_node;
AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i);
ErrorMsg *msg = add_node_error(g, decl_node,
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 0dc820be51..42fd188824 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -5065,6 +5065,8 @@ static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable
{
ZigType *enum_type = instruction->target->value->type;
assert(enum_type->id == ZigTypeIdEnum);
+ if (enum_type->data.enumeration.non_exhaustive)
+ zig_panic("TODO @tagName on non-exhaustive enum");
LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type);
diff --git a/src/ir.cpp b/src/ir.cpp
index 08f80e0647..7aa3243c34 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -22357,6 +22357,8 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns
if (instr_is_comptime(target)) {
if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
+ if (target->value->type->data.enumeration.non_exhaustive)
+ zig_panic("TODO @tagName on non-exhaustive enum");
TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint);
ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee;
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
@@ -26471,7 +26473,11 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
bigint_incr(&field_index);
}
}
- if (switch_type->data.enumeration.non_exhaustive && instruction->have_underscore_prong) {
+ if (instruction->have_underscore_prong) {
+ if (!switch_type->data.enumeration.non_exhaustive){
+ ir_add_error(ira, &instruction->base,
+ buf_sprintf("switch on non-exhaustive enum has `_` prong"));
+ }
for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {
TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];
if (buf_eql_str(enum_field->name, "_"))
--
cgit v1.2.3
From d84569895c80136d9b081a319301a737f342d251 Mon Sep 17 00:00:00 2001
From: Vexu
Date: Thu, 16 Jan 2020 09:04:11 +0200
Subject: turn panics into compile errors, require at least 1 field in
non-exhaustive enum
---
doc/langref.html.in | 7 ++-----
src/analyze.cpp | 3 ++-
src/codegen.cpp | 7 +++++--
src/ir.cpp | 32 ++++++++++++++++----------------
4 files changed, 25 insertions(+), 24 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/doc/langref.html.in b/doc/langref.html.in
index dbe98ce708..cac01c5686 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -2903,11 +2903,8 @@ test "switch using enum literals" {
{#link|@intToEnum#} on a non-exhaustive enum cannot fail.
- A switch on a non-exhaustive enum can include a '_' prong with the following properties:
-
- - makes it a compile error if all the known tag names are not handled by the switch
- - allows omitting {#syntax#}else{#endsyntax#}
-
+ A switch on a non-exhaustive enum can include a '_' prong as an alternative to an {#syntax#}else{#endsyntax#} prong
+ with the difference being that it makes it a compile error if all the known tag names are not handled by the switch.
{#code_begin|test#}
const std = @import("std");
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 7669e0890b..a9aaf74a85 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -2560,7 +2560,8 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
assert(!enum_type->data.enumeration.fields);
uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length;
- if (field_count == 0) {
+ if (field_count == 0 || (field_count == 1 &&
+ buf_eql_str(decl_node->data.container_decl.fields.at(0)->data.struct_field.name, "_"))) {
add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields"));
enum_type->data.enumeration.src_field_count = field_count;
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 42fd188824..030e892d45 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -5065,8 +5065,11 @@ static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable
{
ZigType *enum_type = instruction->target->value->type;
assert(enum_type->id == ZigTypeIdEnum);
- if (enum_type->data.enumeration.non_exhaustive)
- zig_panic("TODO @tagName on non-exhaustive enum");
+ if (enum_type->data.enumeration.non_exhaustive) {
+ add_node_error(g, instruction->base.source_node,
+ buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991"));
+ codegen_report_errors_and_exit(g);
+ }
LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type);
diff --git a/src/ir.cpp b/src/ir.cpp
index 7aa3243c34..43ca01113b 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -8183,13 +8183,6 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
return irb->codegen->invalid_instruction;
}
else_prong = prong_node;
- if (underscore_prong) {
- ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
- buf_sprintf("else and '_' prong in switch expression"));
- add_error_note(irb->codegen, msg, underscore_prong,
- buf_sprintf("'_' prong is here"));
- return irb->codegen->invalid_instruction;
- }
} else if (prong_item_count == 1 &&
prong_node->data.switch_prong.items.at(0)->type == NodeTypeSymbol &&
buf_eql_str(prong_node->data.switch_prong.items.at(0)->data.symbol_expr.symbol, "_")) {
@@ -8201,16 +8194,20 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
return irb->codegen->invalid_instruction;
}
underscore_prong = prong_node;
- if (else_prong) {
- ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
- buf_sprintf("else and '_' prong in switch expression"));
- add_error_note(irb->codegen, msg, else_prong,
- buf_sprintf("else prong is here"));
- return irb->codegen->invalid_instruction;
- }
} else {
continue;
}
+ if (underscore_prong && else_prong) {
+ ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
+ buf_sprintf("else and '_' prong in switch expression"));
+ if (underscore_prong == prong_node)
+ add_error_note(irb->codegen, msg, else_prong,
+ buf_sprintf("else prong is here"));
+ else
+ add_error_note(irb->codegen, msg, underscore_prong,
+ buf_sprintf("'_' prong is here"));
+ return irb->codegen->invalid_instruction;
+ }
ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
IrBasicBlock *prev_block = irb->current_basic_block;
@@ -22357,8 +22354,11 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns
if (instr_is_comptime(target)) {
if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
- if (target->value->type->data.enumeration.non_exhaustive)
- zig_panic("TODO @tagName on non-exhaustive enum");
+ if (target->value->type->data.enumeration.non_exhaustive) {
+ add_node_error(ira->codegen, instruction->base.source_node,
+ buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991"));
+ return ira->codegen->invalid_instruction;
+ }
TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint);
ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee;
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
--
cgit v1.2.3
From cb257b4e11768ccce93d7cf11c416fa8e745f206 Mon Sep 17 00:00:00 2001
From: Vexu
Date: Thu, 16 Jan 2020 09:23:26 +0200
Subject: allow non-exhaustive enums with no fields
---
src/analyze.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index a9aaf74a85..18dc7e032c 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -2560,8 +2560,7 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
assert(!enum_type->data.enumeration.fields);
uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length;
- if (field_count == 0 || (field_count == 1 &&
- buf_eql_str(decl_node->data.container_decl.fields.at(0)->data.struct_field.name, "_"))) {
+ if (field_count == 0) {
add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields"));
enum_type->data.enumeration.src_field_count = field_count;
@@ -2657,7 +2656,7 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
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) {
+ if (field_count > 1 && 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;
}
--
cgit v1.2.3
From 6c8f01dcde03380806338758d489c3f2a78e5b5b Mon Sep 17 00:00:00 2001
From: Vexu
Date: Thu, 16 Jan 2020 22:48:01 +0200
Subject: correct field count
---
src/analyze.cpp | 60 ++++++++++++++++++++++---------------------
test/compile_errors.zig | 6 +++++
test/stage1/behavior/enum.zig | 1 +
3 files changed, 38 insertions(+), 29 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 18dc7e032c..e064677a09 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -2569,16 +2569,8 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
return ErrorSemanticAnalyzeFail;
}
- enum_type->data.enumeration.src_field_count = field_count;
- enum_type->data.enumeration.fields = allocate(field_count);
- enum_type->data.enumeration.fields_by_name.init(field_count);
- enum_type->data.enumeration.non_exhaustive = false;
-
Scope *scope = &enum_type->data.enumeration.decls_scope->base;
- HashMap occupied_tag_values = {};
- occupied_tag_values.init(field_count);
-
ZigType *tag_int_type;
if (enum_type->data.enumeration.layout == ContainerLayoutExtern) {
tag_int_type = get_c_int_type(g, CIntTypeInt);
@@ -2620,6 +2612,7 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
}
}
+ enum_type->data.enumeration.non_exhaustive = false;
enum_type->data.enumeration.tag_int_type = tag_int_type;
enum_type->size_in_bits = tag_int_type->size_in_bits;
enum_type->abi_size = tag_int_type->abi_size;
@@ -2628,6 +2621,31 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
BigInt bi_one;
bigint_init_unsigned(&bi_one, 1);
+ AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1);
+ if (buf_eql_str(last_field_node->data.struct_field.name, "_")) {
+ field_count -= 1;
+ if (field_count > 1 && log2_u64(field_count) == enum_type->size_in_bits) {
+ add_node_error(g, last_field_node, buf_sprintf("non-exhaustive enum specifies every value"));
+ enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
+ }
+ if (decl_node->data.container_decl.init_arg_expr == nullptr) {
+ add_node_error(g, last_field_node, buf_sprintf("non-exhaustive enum must specify size"));
+ enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
+ }
+ if (last_field_node->data.struct_field.value != nullptr) {
+ add_node_error(g, last_field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum"));
+ enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
+ }
+ enum_type->data.enumeration.non_exhaustive = true;
+ }
+
+ enum_type->data.enumeration.src_field_count = field_count;
+ enum_type->data.enumeration.fields = allocate(field_count);
+ enum_type->data.enumeration.fields_by_name.init(field_count);
+
+ HashMap occupied_tag_values = {};
+ occupied_tag_values.init(field_count);
+
TypeEnumField *last_enum_field = nullptr;
for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
@@ -2649,27 +2667,9 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
buf_sprintf("consider 'union(enum)' here"));
}
- 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 (field_count > 1 && 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;
- }
- if (tag_value != nullptr) {
- add_node_error(g, field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum"));
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- }
- enum_type->data.enumeration.non_exhaustive = true;
- continue;
+ add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last"));
+ enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
}
auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field);
@@ -2681,6 +2681,8 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
continue;
}
+ AstNode *tag_value = field_node->data.struct_field.value;
+
if (tag_value != nullptr) {
// A user-specified value is available
ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type,
@@ -3293,7 +3295,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
} else if (enum_type_node != nullptr) {
for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) {
TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i];
- if (!covered_enum_fields[i] && !buf_eql_str(enum_field->name, "_")) {
+ if (!covered_enum_fields[i]) {
AstNode *enum_decl_node = tag_type->data.enumeration.decl_node;
AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i);
ErrorMsg *msg = add_node_error(g, decl_node,
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 702cc76524..be2a40d74d 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -13,9 +13,15 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _,
\\ b,
\\};
+ \\const C = enum(u1) {
+ \\ a,
+ \\ b,
+ \\ _,
+ \\};
\\pub export fn entry() void {
\\ _ = A;
\\ _ = B;
+ \\ _ = C;
\\}
, &[_][]const u8{
"tmp.zig:4:5: error: non-exhaustive enum must specify size",
diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig
index 82e57a3a38..62b7d51c26 100644
--- a/test/stage1/behavior/enum.zig
+++ b/test/stage1/behavior/enum.zig
@@ -41,6 +41,7 @@ test "non-exhaustive enum" {
.b => {},
else => {},
}
+ expect(@typeInfo(E).Enum.fields.len == 2);
expect(@enumToInt(e) == 1);
e = @intToEnum(E, 12);
expect(@enumToInt(e) == 12);
--
cgit v1.2.3
From c53d94e5127a8dcfefd906c5be0e6b81eaf3d22c Mon Sep 17 00:00:00 2001
From: LemonBoy
Date: Sat, 18 Jan 2020 15:13:21 +0100
Subject: Prevent crash with empty non-exhaustive enum
---
src/analyze.cpp | 2 +-
src/ir.cpp | 2 +-
test/stage1/behavior/enum.zig | 20 ++++++++++++++++++++
3 files changed, 22 insertions(+), 2 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index e064677a09..0bbec66a9b 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -8312,7 +8312,7 @@ static void resolve_llvm_types_enum(CodeGen *g, ZigType *enum_type, ResolveStatu
uint32_t field_count = enum_type->data.enumeration.src_field_count;
- assert(enum_type->data.enumeration.fields);
+ assert(field_count == 0 || enum_type->data.enumeration.fields != nullptr);
ZigLLVMDIEnumerator **di_enumerators = allocate(field_count);
for (uint32_t i = 0; i < field_count; i += 1) {
diff --git a/src/ir.cpp b/src/ir.cpp
index 88b4c1a832..ae02ac9cae 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -21631,7 +21631,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira,
case ZigTypeIdEnum: {
if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
- if (target_type->data.enumeration.src_field_count < 2) {
+ if (target_type->data.enumeration.src_field_count == 1) {
TypeEnumField *only_field = &target_type->data.enumeration.fields[0];
IrInstruction *result = ir_const(ira, &switch_target_instruction->base, target_type);
bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value);
diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig
index 83ad76b72c..a478729003 100644
--- a/test/stage1/behavior/enum.zig
+++ b/test/stage1/behavior/enum.zig
@@ -65,6 +65,26 @@ test "non-exhaustive enum" {
comptime S.doTheTest(52);
}
+test "empty non-exhaustive enum" {
+ const S = struct {
+ const E = enum(u8) {
+ _,
+ };
+ fn doTheTest(y: u8) void {
+ var e = @intToEnum(E, y);
+ expect(switch (e) {
+ _ => true,
+ });
+ expect(@enumToInt(e) == y);
+
+ expect(@typeInfo(E).Enum.fields.len == 0);
+ expect(@typeInfo(E).Enum.is_exhaustive == false);
+ }
+ };
+ S.doTheTest(42);
+ comptime S.doTheTest(42);
+}
+
test "enum type" {
const foo1 = Foo{ .One = 13 };
const foo2 = Foo{
--
cgit v1.2.3
From 7a1cde7206263c8bb3265c225ed4213d1b7bdb58 Mon Sep 17 00:00:00 2001
From: LemonBoy
Date: Sun, 19 Jan 2020 10:06:48 +0100
Subject: Fix wrong error code being returned in enum analisys
Fixes the assertion failure seen in #4233
---
src/analyze.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 0bbec66a9b..638b0b03b0 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -2605,7 +2605,7 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
buf_ptr(&wanted_tag_int_type->name)));
add_error_note(g, msg, decl_node->data.container_decl.init_arg_expr,
buf_sprintf("any integral type of size 8, 16, 32, 64 or 128 bit is valid"));
- return ErrorNone;
+ return ErrorSemanticAnalyzeFail;
}
}
tag_int_type = wanted_tag_int_type;
--
cgit v1.2.3