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