authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-01-17 14:26:12-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-01-17 14:26:12-05:00
logb5ac079f88e9098ea9c95356518820a5c3fb42a8
treee4028947b688f53a5387b6b3e0b454090ee240dc
parentd9be6e5dc693fcbcb5f4c343a3d2b0b9fc786e25
parent39f92a9ee4ea109628e1f7d5a65bb53575e53194
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #4191 from Vexu/non-exhaustive-enums

Implement non-exhaustive enums

11 files changed, 330 insertions(+), 131 deletions(-)

doc/langref.html.in+41
......@@ -2893,6 +2893,47 @@ test "switch using enum literals" {
28932893}
28942894 {#code_end#}
28952895 {#header_close#}
2896
2897 {#header_open|Non-exhaustive enum#}
2898 <p>
2899 A Non-exhaustive enum can be created by adding a trailing '_' field.
2900 It must specify a tag type and cannot consume every enumeration value.
2901 </p>
2902 <p>
2903 {#link|@intToEnum#} on a non-exhaustive enum cannot fail.
2904 </p>
2905 <p>
2906 A switch on a non-exhaustive enum can include a '_' prong as an alternative to an {#syntax#}else{#endsyntax#} prong
2907 with the difference being that it makes it a compile error if all the known tag names are not handled by the switch.
2908 </p>
2909 {#code_begin|test#}
2910const std = @import("std");
2911const assert = std.debug.assert;
2912
2913const Number = enum(u8) {
2914 One,
2915 Two,
2916 Three,
2917 _,
2918};
2919
2920test "switch on non-exhaustive enum" {
2921 const number = Number.One;
2922 const result = switch (number) {
2923 .One => true,
2924 .Two,
2925 .Three => false,
2926 _ => false,
2927 };
2928 assert(result);
2929 const is_one = switch (number) {
2930 .One => true,
2931 else => false,
2932 };
2933 assert(is_one);
2934}
2935 {#code_end#}
2936 {#header_close#}
28962937 {#header_close#}
28972938
28982939 {#header_open|union#}
lib/std/builtin.zig+1
......@@ -254,6 +254,7 @@ pub const TypeInfo = union(enum) {
254254 tag_type: type,
255255 fields: []EnumField,
256256 decls: []Declaration,
257 is_exhaustive: bool,
257258 };
258259
259260 /// This data structure is used by the Zig language code generation and
src-self-hosted/translate_c.zig+42-51
......@@ -289,8 +289,7 @@ pub fn translate(
289289 tree.errors = ast.Tree.ErrorList.init(arena);
290290
291291 tree.root_node = try arena.create(ast.Node.Root);
292 tree.root_node.* = ast.Node.Root{
293 .base = ast.Node{ .id = ast.Node.Id.Root },
292 tree.root_node.* = .{
294293 .decls = ast.Node.Root.DeclList.init(arena),
295294 // initialized with the eof token at the end
296295 .eof_token = undefined,
......@@ -440,7 +439,6 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
440439 .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern", .{}),
441440 .Auto => unreachable, // Not legal on functions
442441 .Register => unreachable, // Not legal on functions
443 else => unreachable,
444442 },
445443 };
446444
......@@ -877,25 +875,23 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
877875 // types, while that's not ISO-C compliant many compilers allow this and
878876 // default to the usual integer type used for all the enums.
879877
880 // TODO only emit this tag type if the enum tag type is not the default.
881 // I don't know what the default is, need to figure out how clang is deciding.
882 // it appears to at least be different across gcc/msvc
883 if (int_type.ptr != null and
884 !isCBuiltinType(int_type, .UInt) and
885 !isCBuiltinType(int_type, .Int))
886 {
887 _ = try appendToken(c, .LParen, "(");
888 container_node.init_arg_expr = .{
889 .Type = transQualType(rp, int_type, enum_loc) catch |err| switch (err) {
878 // default to c_int since msvc and gcc default to different types
879 _ = try appendToken(c, .LParen, "(");
880 container_node.init_arg_expr = .{
881 .Type = if (int_type.ptr != null and
882 !isCBuiltinType(int_type, .UInt) and
883 !isCBuiltinType(int_type, .Int))
884 transQualType(rp, int_type, enum_loc) catch |err| switch (err) {
890885 error.UnsupportedType => {
891886 try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
892887 return null;
893888 },
894889 else => |e| return e,
895 },
896 };
897 _ = try appendToken(c, .RParen, ")");
898 }
890 }
891 else
892 try transCreateNodeIdentifier(c, "c_int"),
893 };
894 _ = try appendToken(c, .RParen, ")");
899895
900896 container_node.lbrace_token = try appendToken(c, .LBrace, "{");
901897
......@@ -953,6 +949,19 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
953949 tld_node.semicolon_token = try appendToken(c, .Semicolon, ";");
954950 try addTopLevelDecl(c, field_name, &tld_node.base);
955951 }
952 // make non exhaustive
953 const field_node = try c.a().create(ast.Node.ContainerField);
954 field_node.* = .{
955 .doc_comments = null,
956 .comptime_token = null,
957 .name_token = try appendIdentifier(c, "_"),
958 .type_expr = null,
959 .value_expr = null,
960 .align_expr = null,
961 };
962
963 try container_node.fields_and_decls.push(&field_node.base);
964 _ = try appendToken(c, .Comma, ",");
956965 container_node.rbrace_token = try appendToken(c, .RBrace, "}");
957966
958967 break :blk &container_node.base;
......@@ -1231,18 +1240,6 @@ fn transBinaryOperator(
12311240 op_id = .BitOr;
12321241 op_token = try appendToken(rp.c, .Pipe, "|");
12331242 },
1234 .Assign,
1235 .MulAssign,
1236 .DivAssign,
1237 .RemAssign,
1238 .AddAssign,
1239 .SubAssign,
1240 .ShlAssign,
1241 .ShrAssign,
1242 .AndAssign,
1243 .XorAssign,
1244 .OrAssign,
1245 => unreachable,
12461243 else => unreachable,
12471244 }
12481245
......@@ -1678,7 +1675,6 @@ fn transStringLiteral(
16781675 "TODO: support string literal kind {}",
16791676 .{kind},
16801677 ),
1681 else => unreachable,
16821678 }
16831679}
16841680
......@@ -2206,6 +2202,19 @@ fn transDoWhileLoop(
22062202 .id = .Loop,
22072203 };
22082204
2205 // if (!cond) break;
2206 const if_node = try transCreateNodeIf(rp.c);
2207 var cond_scope = Scope{
2208 .parent = scope,
2209 .id = .Condition,
2210 };
2211 const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
2212 prefix_op.rhs = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);
2213 _ = try appendToken(rp.c, .RParen, ")");
2214 if_node.condition = &prefix_op.base;
2215 if_node.body = &(try transCreateNodeBreak(rp.c, null)).base;
2216 _ = try appendToken(rp.c, .Semicolon, ";");
2217
22092218 const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: {
22102219 // there's already a block in C, so we'll append our condition to it.
22112220 // c: do {
......@@ -2217,10 +2226,7 @@ fn transDoWhileLoop(
22172226 // zig: b;
22182227 // zig: if (!cond) break;
22192228 // zig: }
2220 const body = (try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?;
2221 // if this is used as an expression in Zig it needs to be immediately followed by a semicolon
2222 _ = try appendToken(rp.c, .Semicolon, ";");
2223 break :blk body;
2229 break :blk (try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?;
22242230 } else blk: {
22252231 // the C statement is without a block, so we need to create a block to contain it.
22262232 // c: do
......@@ -2236,19 +2242,6 @@ fn transDoWhileLoop(
22362242 break :blk block;
22372243 };
22382244
2239 // if (!cond) break;
2240 const if_node = try transCreateNodeIf(rp.c);
2241 var cond_scope = Scope{
2242 .parent = scope,
2243 .id = .Condition,
2244 };
2245 const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
2246 prefix_op.rhs = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);
2247 _ = try appendToken(rp.c, .RParen, ")");
2248 if_node.condition = &prefix_op.base;
2249 if_node.body = &(try transCreateNodeBreak(rp.c, null)).base;
2250 _ = try appendToken(rp.c, .Semicolon, ";");
2251
22522245 try body_node.statements.push(&if_node.base);
22532246 if (new)
22542247 body_node.rbrace = try appendToken(rp.c, .RBrace, "}");
......@@ -4783,8 +4776,7 @@ fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex {
47834776fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node {
47844777 const token_index = try appendIdentifier(c, name);
47854778 const identifier = try c.a().create(ast.Node.Identifier);
4786 identifier.* = ast.Node.Identifier{
4787 .base = ast.Node{ .id = ast.Node.Id.Identifier },
4779 identifier.* = .{
47884780 .token = token_index,
47894781 };
47904782 return &identifier.base;
......@@ -4923,8 +4915,7 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u
49234915
49244916 const token_index = try appendToken(c, .Keyword_var, "var");
49254917 const identifier = try c.a().create(ast.Node.Identifier);
4926 identifier.* = ast.Node.Identifier{
4927 .base = ast.Node{ .id = ast.Node.Id.Identifier },
4918 identifier.* = .{
49284919 .token = token_index,
49294920 };
49304921
src/all_types.hpp+2
......@@ -1385,6 +1385,7 @@ struct ZigTypeEnum {
13851385 ContainerLayout layout;
13861386 ResolveStatus resolve_status;
13871387
1388 bool non_exhaustive;
13881389 bool resolve_loop_flag;
13891390};
13901391
......@@ -3669,6 +3670,7 @@ struct IrInstructionCheckSwitchProngs {
36693670 IrInstructionCheckSwitchProngsRange *ranges;
36703671 size_t range_count;
36713672 bool have_else_prong;
3673 bool have_underscore_prong;
36723674};
36733675
36743676struct IrInstructionCheckStatementIsVoid {
src/analyze.cpp+31-7
......@@ -2569,15 +2569,8 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
25692569 return ErrorSemanticAnalyzeFail;
25702570 }
25712571
2572 enum_type->data.enumeration.src_field_count = field_count;
2573 enum_type->data.enumeration.fields = allocate<TypeEnumField>(field_count);
2574 enum_type->data.enumeration.fields_by_name.init(field_count);
2575
25762572 Scope *scope = &enum_type->data.enumeration.decls_scope->base;
25772573
2578 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
2579 occupied_tag_values.init(field_count);
2580
25812574 ZigType *tag_int_type;
25822575 if (enum_type->data.enumeration.layout == ContainerLayoutExtern) {
25832576 tag_int_type = get_c_int_type(g, CIntTypeInt);
......@@ -2619,6 +2612,7 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
26192612 }
26202613 }
26212614
2615 enum_type->data.enumeration.non_exhaustive = false;
26222616 enum_type->data.enumeration.tag_int_type = tag_int_type;
26232617 enum_type->size_in_bits = tag_int_type->size_in_bits;
26242618 enum_type->abi_size = tag_int_type->abi_size;
......@@ -2627,6 +2621,31 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
26272621 BigInt bi_one;
26282622 bigint_init_unsigned(&bi_one, 1);
26292623
2624 AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1);
2625 if (buf_eql_str(last_field_node->data.struct_field.name, "_")) {
2626 field_count -= 1;
2627 if (field_count > 1 && log2_u64(field_count) == enum_type->size_in_bits) {
2628 add_node_error(g, last_field_node, buf_sprintf("non-exhaustive enum specifies every value"));
2629 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2630 }
2631 if (decl_node->data.container_decl.init_arg_expr == nullptr) {
2632 add_node_error(g, last_field_node, buf_sprintf("non-exhaustive enum must specify size"));
2633 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2634 }
2635 if (last_field_node->data.struct_field.value != nullptr) {
2636 add_node_error(g, last_field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum"));
2637 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2638 }
2639 enum_type->data.enumeration.non_exhaustive = true;
2640 }
2641
2642 enum_type->data.enumeration.src_field_count = field_count;
2643 enum_type->data.enumeration.fields = allocate<TypeEnumField>(field_count);
2644 enum_type->data.enumeration.fields_by_name.init(field_count);
2645
2646 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
2647 occupied_tag_values.init(field_count);
2648
26302649 TypeEnumField *last_enum_field = nullptr;
26312650
26322651 for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
......@@ -2648,6 +2667,11 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
26482667 buf_sprintf("consider 'union(enum)' here"));
26492668 }
26502669
2670 if (buf_eql_str(type_enum_field->name, "_")) {
2671 add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last"));
2672 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2673 }
2674
26512675 auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field);
26522676 if (field_entry != nullptr) {
26532677 ErrorMsg *msg = add_node_error(g, field_node,
src/codegen.cpp+6-1
......@@ -3356,7 +3356,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable,
33563356 LLVMValueRef tag_int_value = gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),
33573357 instruction->target->value->type, tag_int_type, target_val);
33583358
3359 if (ir_want_runtime_safety(g, &instruction->base) && wanted_type->data.enumeration.layout != ContainerLayoutExtern) {
3359 if (ir_want_runtime_safety(g, &instruction->base) && !wanted_type->data.enumeration.non_exhaustive) {
33603360 LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue");
33613361 LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue");
33623362 size_t field_count = wanted_type->data.enumeration.src_field_count;
......@@ -5065,6 +5065,11 @@ static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable
50655065{
50665066 ZigType *enum_type = instruction->target->value->type;
50675067 assert(enum_type->id == ZigTypeIdEnum);
5068 if (enum_type->data.enumeration.non_exhaustive) {
5069 add_node_error(g, instruction->base.source_node,
5070 buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991"));
5071 codegen_report_errors_and_exit(g);
5072 }
50685073
50695074 LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type);
50705075
src/ir.cpp+90-33
......@@ -3452,7 +3452,7 @@ static IrInstruction *ir_build_err_to_int(IrBuilder *irb, Scope *scope, AstNode
34523452
34533453static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node,
34543454 IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count,
3455 bool have_else_prong)
3455 bool have_else_prong, bool have_underscore_prong)
34563456{
34573457 IrInstructionCheckSwitchProngs *instruction = ir_build_instruction<IrInstructionCheckSwitchProngs>(
34583458 irb, scope, source_node);
......@@ -3460,6 +3460,7 @@ static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope,
34603460 instruction->ranges = ranges;
34613461 instruction->range_count = range_count;
34623462 instruction->have_else_prong = have_else_prong;
3463 instruction->have_underscore_prong = have_underscore_prong;
34633464
34643465 ir_ref_instruction(target_value, irb->current_basic_block);
34653466 for (size_t i = 0; i < range_count; i += 1) {
......@@ -8092,34 +8093,11 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
80928093 Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
80938094 Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
80948095 AstNode *else_prong = nullptr;
8096 AstNode *underscore_prong = nullptr;
80958097 for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
80968098 AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
80978099 size_t prong_item_count = prong_node->data.switch_prong.items.length;
8098 if (prong_item_count == 0) {
8099 ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
8100 if (else_prong) {
8101 ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
8102 buf_sprintf("multiple else prongs in switch expression"));
8103 add_error_note(irb->codegen, msg, else_prong,
8104 buf_sprintf("previous else prong is here"));
8105 return irb->codegen->invalid_instruction;
8106 }
8107 else_prong = prong_node;
8108
8109 IrBasicBlock *prev_block = irb->current_basic_block;
8110 if (peer_parent->peers.length > 0) {
8111 peer_parent->peers.last()->next_bb = else_block;
8112 }
8113 peer_parent->peers.append(this_peer_result_loc);
8114 ir_set_cursor_at_end_and_append_block(irb, else_block);
8115 if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
8116 is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values,
8117 &switch_else_var, LValNone, &this_peer_result_loc->base))
8118 {
8119 return irb->codegen->invalid_instruction;
8120 }
8121 ir_set_cursor_at_end(irb, prev_block);
8122 } else if (prong_node->data.switch_prong.any_items_are_range) {
8100 if (prong_node->data.switch_prong.any_items_are_range) {
81238101 ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
81248102
81258103 IrInstruction *ok_bit = nullptr;
......@@ -8197,6 +8175,56 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
81978175 }
81988176
81998177 ir_set_cursor_at_end_and_append_block(irb, range_block_no);
8178 } else {
8179 if (prong_item_count == 0) {
8180 if (else_prong) {
8181 ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
8182 buf_sprintf("multiple else prongs in switch expression"));
8183 add_error_note(irb->codegen, msg, else_prong,
8184 buf_sprintf("previous else prong is here"));
8185 return irb->codegen->invalid_instruction;
8186 }
8187 else_prong = prong_node;
8188 } else if (prong_item_count == 1 &&
8189 prong_node->data.switch_prong.items.at(0)->type == NodeTypeSymbol &&
8190 buf_eql_str(prong_node->data.switch_prong.items.at(0)->data.symbol_expr.symbol, "_")) {
8191 if (underscore_prong) {
8192 ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
8193 buf_sprintf("multiple '_' prongs in switch expression"));
8194 add_error_note(irb->codegen, msg, underscore_prong,
8195 buf_sprintf("previous '_' prong is here"));
8196 return irb->codegen->invalid_instruction;
8197 }
8198 underscore_prong = prong_node;
8199 } else {
8200 continue;
8201 }
8202 if (underscore_prong && else_prong) {
8203 ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
8204 buf_sprintf("else and '_' prong in switch expression"));
8205 if (underscore_prong == prong_node)
8206 add_error_note(irb->codegen, msg, else_prong,
8207 buf_sprintf("else prong is here"));
8208 else
8209 add_error_note(irb->codegen, msg, underscore_prong,
8210 buf_sprintf("'_' prong is here"));
8211 return irb->codegen->invalid_instruction;
8212 }
8213 ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
8214
8215 IrBasicBlock *prev_block = irb->current_basic_block;
8216 if (peer_parent->peers.length > 0) {
8217 peer_parent->peers.last()->next_bb = else_block;
8218 }
8219 peer_parent->peers.append(this_peer_result_loc);
8220 ir_set_cursor_at_end_and_append_block(irb, else_block);
8221 if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
8222 is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values,
8223 &switch_else_var, LValNone, &this_peer_result_loc->base))
8224 {
8225 return irb->codegen->invalid_instruction;
8226 }
8227 ir_set_cursor_at_end(irb, prev_block);
82008228 }
82018229 }
82028230
......@@ -8208,6 +8236,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
82088236 continue;
82098237 if (prong_node->data.switch_prong.any_items_are_range)
82108238 continue;
8239 if (underscore_prong == prong_node)
8240 continue;
82118241
82128242 ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
82138243
......@@ -8251,7 +8281,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
82518281 }
82528282
82538283 IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value,
8254 check_ranges.items, check_ranges.length, else_prong != nullptr);
8284 check_ranges.items, check_ranges.length, else_prong != nullptr, underscore_prong != nullptr);
82558285
82568286 IrInstruction *br_instruction;
82578287 if (cases.length == 0) {
......@@ -8271,7 +8301,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
82718301 peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction;
82728302 }
82738303
8274 if (!else_prong) {
8304 if (!else_prong && !underscore_prong) {
82758305 if (peer_parent->peers.length != 0) {
82768306 peer_parent->peers.last()->next_bb = else_block;
82778307 }
......@@ -12792,7 +12822,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour
1279212822 return ira->codegen->invalid_instruction;
1279312823
1279412824 TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint);
12795 if (field == nullptr && wanted_type->data.enumeration.layout != ContainerLayoutExtern) {
12825 if (field == nullptr && !wanted_type->data.enumeration.non_exhaustive) {
1279612826 Buf *val_buf = buf_alloc();
1279712827 bigint_append_buf(val_buf, &val->data.x_bigint, 10);
1279812828 ErrorMsg *msg = ir_add_error(ira, source_instr,
......@@ -22327,6 +22357,11 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns
2232722357 if (instr_is_comptime(target)) {
2232822358 if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown)))
2232922359 return ira->codegen->invalid_instruction;
22360 if (target->value->type->data.enumeration.non_exhaustive) {
22361 add_node_error(ira->codegen, instruction->base.source_node,
22362 buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991"));
22363 return ira->codegen->invalid_instruction;
22364 }
2233022365 TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint);
2233122366 ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee;
2233222367 IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
......@@ -23077,7 +23112,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr
2307723112 result->special = ConstValSpecialStatic;
2307823113 result->type = ir_type_info_get_type(ira, "Enum", nullptr);
2307923114
23080 ZigValue **fields = alloc_const_vals_ptrs(4);
23115 ZigValue **fields = alloc_const_vals_ptrs(5);
2308123116 result->data.x_struct.fields = fields;
2308223117
2308323118 // layout: ContainerLayout
......@@ -23123,6 +23158,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr
2312323158 {
2312423159 return err;
2312523160 }
23161 // is_exhaustive: bool
23162 ensure_field_index(result->type, "is_exhaustive", 4);
23163 fields[4]->special = ConstValSpecialStatic;
23164 fields[4]->type = ira->codegen->builtin_types.entry_bool;
23165 fields[4]->data.x_bool = !type_entry->data.enumeration.non_exhaustive;
2312623166
2312723167 break;
2312823168 }
......@@ -26442,10 +26482,27 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2644226482 bigint_incr(&field_index);
2644326483 }
2644426484 }
26445 if (!instruction->have_else_prong) {
26446 if (switch_type->data.enumeration.layout == ContainerLayoutExtern) {
26485 if (instruction->have_underscore_prong) {
26486 if (!switch_type->data.enumeration.non_exhaustive){
26487 ir_add_error(ira, &instruction->base,
26488 buf_sprintf("switch on non-exhaustive enum has `_` prong"));
26489 }
26490 for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {
26491 TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];
26492 if (buf_eql_str(enum_field->name, "_"))
26493 continue;
26494
26495 auto entry = field_prev_uses.maybe_get(enum_field->value);
26496 if (!entry) {
26497 ir_add_error(ira, &instruction->base,
26498 buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&switch_type->name),
26499 buf_ptr(enum_field->name)));
26500 }
26501 }
26502 } else if (!instruction->have_else_prong) {
26503 if (switch_type->data.enumeration.non_exhaustive) {
2644726504 ir_add_error(ira, &instruction->base,
26448 buf_sprintf("switch on an extern enum must have an else prong"));
26505 buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong"));
2644926506 }
2645026507 for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {
2645126508 TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];
test/compile_errors.zig+50-19
......@@ -2,6 +2,56 @@ const tests = @import("tests.zig");
22const builtin = @import("builtin");
33
44pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.addTest("non-exhaustive enums",
6 \\const A = enum {
7 \\ a,
8 \\ b,
9 \\ _ = 1,
10 \\};
11 \\const B = enum(u1) {
12 \\ a,
13 \\ _,
14 \\ b,
15 \\};
16 \\const C = enum(u1) {
17 \\ a,
18 \\ b,
19 \\ _,
20 \\};
21 \\pub export fn entry() void {
22 \\ _ = A;
23 \\ _ = B;
24 \\ _ = C;
25 \\}
26 , &[_][]const u8{
27 "tmp.zig:4:5: error: non-exhaustive enum must specify size",
28 "error: value assigned to '_' field of non-exhaustive enum",
29 "error: non-exhaustive enum specifies every value",
30 "error: '_' field of non-exhaustive enum must be last",
31 });
32
33 cases.addTest("switching with non-exhaustive enums",
34 \\const E = enum(u8) {
35 \\ a,
36 \\ b,
37 \\ _,
38 \\};
39 \\pub export fn entry() void {
40 \\ var e: E = .b;
41 \\ switch (e) { // error: switch not handling the tag `b`
42 \\ .a => {},
43 \\ _ => {},
44 \\ }
45 \\ switch (e) { // error: switch on non-exhaustive enum must include `else` or `_` prong
46 \\ .a => {},
47 \\ .b => {},
48 \\ }
49 \\}
50 , &[_][]const u8{
51 "tmp.zig:8:5: error: enumeration value 'E.b' not handled in switch",
52 "tmp.zig:12:5: error: switch on non-exhaustive enum must include `else` or `_` prong",
53 });
54
555 cases.addTest("@export with empty name string",
656 \\pub export fn entry() void { }
757 \\comptime {
......@@ -139,25 +189,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
139189 "tmp.zig:2:13: error: pointer type '[*]align(4) u8' requires aligned address",
140190 });
141191
142 cases.add("switch on extern enum missing else prong",
143 \\const i = extern enum {
144 \\ n = 0,
145 \\ o = 2,
146 \\ p = 4,
147 \\ q = 4,
148 \\};
149 \\pub fn main() void {
150 \\ var x = @intToEnum(i, 52);
151 \\ switch (x) {
152 \\ .n,
153 \\ .o,
154 \\ .p => unreachable,
155 \\ }
156 \\}
157 , &[_][]const u8{
158 "tmp.zig:9:5: error: switch on an extern enum must have an else prong",
159 });
160
161192 cases.add("invalid float literal",
162193 \\const std = @import("std");
163194 \\
test/stage1/behavior/cast.zig-1
......@@ -618,7 +618,6 @@ test "peer resolution of string literals" {
618618 .b => "two",
619619 .c => "three",
620620 .d => "four",
621 else => unreachable,
622621 };
623622 expect(mem.eql(u8, cmd, "two"));
624623 }
test/stage1/behavior/enum.zig+46-9
......@@ -11,16 +11,9 @@ test "extern enum" {
1111 };
1212 fn doTheTest(y: c_int) void {
1313 var x = i.o;
14 expect(@enumToInt(x) == 2);
15 x = @intToEnum(i, 12);
16 expect(@enumToInt(x) == 12);
17 x = @intToEnum(i, y);
18 expect(@enumToInt(x) == 52);
1914 switch (x) {
20 .n,
21 .o,
22 .p => unreachable,
23 else => {},
15 .n, .p => unreachable,
16 .o => {},
2417 }
2518 }
2619 };
......@@ -28,6 +21,50 @@ test "extern enum" {
2821 comptime S.doTheTest(52);
2922}
3023
24test "non-exhaustive enum" {
25 const S = struct {
26 const E = enum(u8) {
27 a,
28 b,
29 _,
30 };
31 fn doTheTest(y: u8) void {
32 var e: E = .b;
33 expect(switch (e) {
34 .a => false,
35 .b => true,
36 _ => false,
37 });
38 e = @intToEnum(E, 12);
39 expect(switch (e) {
40 .a => false,
41 .b => false,
42 _ => true,
43 });
44
45 expect(switch (e) {
46 .a => false,
47 .b => false,
48 else => true,
49 });
50 e = .b;
51 expect(switch (e) {
52 .a => false,
53 else => true,
54 });
55
56 expect(@typeInfo(E).Enum.fields.len == 2);
57 e = @intToEnum(E, 12);
58 expect(@enumToInt(e) == 12);
59 e = @intToEnum(E, y);
60 expect(@enumToInt(e) == 52);
61 expect(@typeInfo(E).Enum.is_exhaustive == false);
62 }
63 };
64 S.doTheTest(52);
65 comptime S.doTheTest(52);
66}
67
3168test "enum type" {
3269 const foo1 = Foo{ .One = 13 };
3370 const foo2 = Foo{
test/translate_c.zig+21-10
......@@ -629,6 +629,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
629629 \\ VAL21 = 6917529027641081853,
630630 \\ VAL22 = 0,
631631 \\ VAL23 = -1,
632 \\ _,
632633 \\};
633634 });
634635 }
......@@ -988,8 +989,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
988989 \\enum enum_ty { FOO };
989990 , &[_][]const u8{
990991 \\pub const FOO = @enumToInt(enum_enum_ty.FOO);
991 \\pub const enum_enum_ty = extern enum {
992 \\pub const enum_enum_ty = extern enum(c_int) {
992993 \\ FOO,
994 \\ _,
993995 \\};
994996 \\pub extern var my_enum: enum_enum_ty;
995997 });
......@@ -1102,28 +1104,31 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
11021104 \\pub const a = @enumToInt(enum_unnamed_1.a);
11031105 \\pub const b = @enumToInt(enum_unnamed_1.b);
11041106 \\pub const c = @enumToInt(enum_unnamed_1.c);
1105 \\const enum_unnamed_1 = extern enum {
1107 \\const enum_unnamed_1 = extern enum(c_int) {
11061108 \\ a,
11071109 \\ b,
11081110 \\ c,
1111 \\ _,
11091112 \\};
11101113 \\pub const d = enum_unnamed_1;
11111114 \\pub const e = @enumToInt(enum_unnamed_2.e);
11121115 \\pub const f = @enumToInt(enum_unnamed_2.f);
11131116 \\pub const g = @enumToInt(enum_unnamed_2.g);
1114 \\const enum_unnamed_2 = extern enum {
1117 \\const enum_unnamed_2 = extern enum(c_int) {
11151118 \\ e = 0,
11161119 \\ f = 4,
11171120 \\ g = 5,
1121 \\ _,
11181122 \\};
11191123 \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e);
11201124 \\pub const i = @enumToInt(enum_unnamed_3.i);
11211125 \\pub const j = @enumToInt(enum_unnamed_3.j);
11221126 \\pub const k = @enumToInt(enum_unnamed_3.k);
1123 \\const enum_unnamed_3 = extern enum {
1127 \\const enum_unnamed_3 = extern enum(c_int) {
11241128 \\ i,
11251129 \\ j,
11261130 \\ k,
1131 \\ _,
11271132 \\};
11281133 \\pub const struct_Baz = extern struct {
11291134 \\ l: enum_unnamed_3,
......@@ -1132,10 +1137,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
11321137 \\pub const n = @enumToInt(enum_i.n);
11331138 \\pub const o = @enumToInt(enum_i.o);
11341139 \\pub const p = @enumToInt(enum_i.p);
1135 \\pub const enum_i = extern enum {
1140 \\pub const enum_i = extern enum(c_int) {
11361141 \\ n,
11371142 \\ o,
11381143 \\ p,
1144 \\ _,
11391145 \\};
11401146 ,
11411147 \\pub const Baz = struct_Baz;
......@@ -1563,9 +1569,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
15631569 , &[_][]const u8{
15641570 \\pub const One = @enumToInt(enum_unnamed_1.One);
15651571 \\pub const Two = @enumToInt(enum_unnamed_1.Two);
1566 \\const enum_unnamed_1 = extern enum {
1572 \\const enum_unnamed_1 = extern enum(c_int) {
15671573 \\ One,
15681574 \\ Two,
1575 \\ _,
15691576 \\};
15701577 });
15711578
......@@ -1665,10 +1672,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
16651672 \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
16661673 \\}
16671674 , &[_][]const u8{
1668 \\pub const enum_Foo = extern enum {
1675 \\pub const enum_Foo = extern enum(c_int) {
16691676 \\ A,
16701677 \\ B,
16711678 \\ C,
1679 \\ _,
16721680 \\};
16731681 \\pub const SomeTypedef = c_int;
16741682 \\pub export fn and_or_non_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void) c_int {
......@@ -1710,9 +1718,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
17101718 \\ y: c_int,
17111719 \\};
17121720 ,
1713 \\pub const enum_Bar = extern enum {
1721 \\pub const enum_Bar = extern enum(c_int) {
17141722 \\ A,
17151723 \\ B,
1724 \\ _,
17161725 \\};
17171726 \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
17181727 ,
......@@ -1973,10 +1982,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
19731982 \\ return 4;
19741983 \\}
19751984 , &[_][]const u8{
1976 \\pub const enum_SomeEnum = extern enum {
1985 \\pub const enum_SomeEnum = extern enum(c_int) {
19771986 \\ A,
19781987 \\ B,
19791988 \\ C,
1989 \\ _,
19801990 \\};
19811991 \\pub export fn if_none_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void, arg_d: enum_SomeEnum) c_int {
19821992 \\ var a = arg_a;
......@@ -2414,10 +2424,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
24142424 \\pub const FooA = @enumToInt(enum_Foo.A);
24152425 \\pub const FooB = @enumToInt(enum_Foo.B);
24162426 \\pub const Foo1 = @enumToInt(enum_Foo.@"1");
2417 \\pub const enum_Foo = extern enum {
2427 \\pub const enum_Foo = extern enum(c_int) {
24182428 \\ A = 2,
24192429 \\ B = 5,
24202430 \\ @"1" = 6,
2431 \\ _,
24212432 \\};
24222433 ,
24232434 \\pub const Foo = enum_Foo;