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
signaturebadge-question-mark 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" {...@@ -2893,6 +2893,47 @@ test "switch using enum literals" {
2893}2893}
2894 {#code_end#}2894 {#code_end#}
2895 {#header_close#}2895 {#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#}
2896 {#header_close#}2937 {#header_close#}
28972938
2898 {#header_open|union#}2939 {#header_open|union#}
lib/std/builtin.zig+1
...@@ -254,6 +254,7 @@ pub const TypeInfo = union(enum) {...@@ -254,6 +254,7 @@ pub const TypeInfo = union(enum) {
254 tag_type: type,254 tag_type: type,
255 fields: []EnumField,255 fields: []EnumField,
256 decls: []Declaration,256 decls: []Declaration,
257 is_exhaustive: bool,
257 };258 };
258259
259 /// This data structure is used by the Zig language code generation and260 /// 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(...@@ -289,8 +289,7 @@ pub fn translate(
289 tree.errors = ast.Tree.ErrorList.init(arena);289 tree.errors = ast.Tree.ErrorList.init(arena);
290290
291 tree.root_node = try arena.create(ast.Node.Root);291 tree.root_node = try arena.create(ast.Node.Root);
292 tree.root_node.* = ast.Node.Root{292 tree.root_node.* = .{
293 .base = ast.Node{ .id = ast.Node.Id.Root },
294 .decls = ast.Node.Root.DeclList.init(arena),293 .decls = ast.Node.Root.DeclList.init(arena),
295 // initialized with the eof token at the end294 // initialized with the eof token at the end
296 .eof_token = undefined,295 .eof_token = undefined,
...@@ -440,7 +439,6 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {...@@ -440,7 +439,6 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
440 .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern", .{}),439 .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern", .{}),
441 .Auto => unreachable, // Not legal on functions440 .Auto => unreachable, // Not legal on functions
442 .Register => unreachable, // Not legal on functions441 .Register => unreachable, // Not legal on functions
443 else => unreachable,
444 },442 },
445 };443 };
446444
...@@ -877,25 +875,23 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No...@@ -877,25 +875,23 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
877 // types, while that's not ISO-C compliant many compilers allow this and875 // types, while that's not ISO-C compliant many compilers allow this and
878 // default to the usual integer type used for all the enums.876 // 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.878 // default to c_int since msvc and gcc default to different types
881 // I don't know what the default is, need to figure out how clang is deciding.879 _ = try appendToken(c, .LParen, "(");
882 // it appears to at least be different across gcc/msvc880 container_node.init_arg_expr = .{
883 if (int_type.ptr != null and881 .Type = if (int_type.ptr != null and
884 !isCBuiltinType(int_type, .UInt) and882 !isCBuiltinType(int_type, .UInt) and
885 !isCBuiltinType(int_type, .Int))883 !isCBuiltinType(int_type, .Int))
886 {884 transQualType(rp, int_type, enum_loc) catch |err| switch (err) {
887 _ = try appendToken(c, .LParen, "(");
888 container_node.init_arg_expr = .{
889 .Type = transQualType(rp, int_type, enum_loc) catch |err| switch (err) {
890 error.UnsupportedType => {885 error.UnsupportedType => {
891 try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});886 try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
892 return null;887 return null;
893 },888 },
894 else => |e| return e,889 else => |e| return e,
895 },890 }
896 };891 else
897 _ = try appendToken(c, .RParen, ")");892 try transCreateNodeIdentifier(c, "c_int"),
898 }893 };
894 _ = try appendToken(c, .RParen, ")");
899895
900 container_node.lbrace_token = try appendToken(c, .LBrace, "{");896 container_node.lbrace_token = try appendToken(c, .LBrace, "{");
901897
...@@ -953,6 +949,19 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No...@@ -953,6 +949,19 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
953 tld_node.semicolon_token = try appendToken(c, .Semicolon, ";");949 tld_node.semicolon_token = try appendToken(c, .Semicolon, ";");
954 try addTopLevelDecl(c, field_name, &tld_node.base);950 try addTopLevelDecl(c, field_name, &tld_node.base);
955 }951 }
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, ",");
956 container_node.rbrace_token = try appendToken(c, .RBrace, "}");965 container_node.rbrace_token = try appendToken(c, .RBrace, "}");
957966
958 break :blk &container_node.base;967 break :blk &container_node.base;
...@@ -1231,18 +1240,6 @@ fn transBinaryOperator(...@@ -1231,18 +1240,6 @@ fn transBinaryOperator(
1231 op_id = .BitOr;1240 op_id = .BitOr;
1232 op_token = try appendToken(rp.c, .Pipe, "|");1241 op_token = try appendToken(rp.c, .Pipe, "|");
1233 },1242 },
1234 .Assign,
1235 .MulAssign,
1236 .DivAssign,
1237 .RemAssign,
1238 .AddAssign,
1239 .SubAssign,
1240 .ShlAssign,
1241 .ShrAssign,
1242 .AndAssign,
1243 .XorAssign,
1244 .OrAssign,
1245 => unreachable,
1246 else => unreachable,1243 else => unreachable,
1247 }1244 }
12481245
...@@ -1678,7 +1675,6 @@ fn transStringLiteral(...@@ -1678,7 +1675,6 @@ fn transStringLiteral(
1678 "TODO: support string literal kind {}",1675 "TODO: support string literal kind {}",
1679 .{kind},1676 .{kind},
1680 ),1677 ),
1681 else => unreachable,
1682 }1678 }
1683}1679}
16841680
...@@ -2206,6 +2202,19 @@ fn transDoWhileLoop(...@@ -2206,6 +2202,19 @@ fn transDoWhileLoop(
2206 .id = .Loop,2202 .id = .Loop,
2207 };2203 };
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
2209 const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: {2218 const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: {
2210 // there's already a block in C, so we'll append our condition to it.2219 // there's already a block in C, so we'll append our condition to it.
2211 // c: do {2220 // c: do {
...@@ -2217,10 +2226,7 @@ fn transDoWhileLoop(...@@ -2217,10 +2226,7 @@ fn transDoWhileLoop(
2217 // zig: b;2226 // zig: b;
2218 // zig: if (!cond) break;2227 // zig: if (!cond) break;
2219 // zig: }2228 // zig: }
2220 const body = (try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?;2229 break :blk (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;
2224 } else blk: {2230 } else blk: {
2225 // the C statement is without a block, so we need to create a block to contain it.2231 // the C statement is without a block, so we need to create a block to contain it.
2226 // c: do2232 // c: do
...@@ -2236,19 +2242,6 @@ fn transDoWhileLoop(...@@ -2236,19 +2242,6 @@ fn transDoWhileLoop(
2236 break :blk block;2242 break :blk block;
2237 };2243 };
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
2252 try body_node.statements.push(&if_node.base);2245 try body_node.statements.push(&if_node.base);
2253 if (new)2246 if (new)
2254 body_node.rbrace = try appendToken(rp.c, .RBrace, "}");2247 body_node.rbrace = try appendToken(rp.c, .RBrace, "}");
...@@ -4783,8 +4776,7 @@ fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex {...@@ -4783,8 +4776,7 @@ fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex {
4783fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node {4776fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node {
4784 const token_index = try appendIdentifier(c, name);4777 const token_index = try appendIdentifier(c, name);
4785 const identifier = try c.a().create(ast.Node.Identifier);4778 const identifier = try c.a().create(ast.Node.Identifier);
4786 identifier.* = ast.Node.Identifier{4779 identifier.* = .{
4787 .base = ast.Node{ .id = ast.Node.Id.Identifier },
4788 .token = token_index,4780 .token = token_index,
4789 };4781 };
4790 return &identifier.base;4782 return &identifier.base;
...@@ -4923,8 +4915,7 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u...@@ -4923,8 +4915,7 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u
49234915
4924 const token_index = try appendToken(c, .Keyword_var, "var");4916 const token_index = try appendToken(c, .Keyword_var, "var");
4925 const identifier = try c.a().create(ast.Node.Identifier);4917 const identifier = try c.a().create(ast.Node.Identifier);
4926 identifier.* = ast.Node.Identifier{4918 identifier.* = .{
4927 .base = ast.Node{ .id = ast.Node.Id.Identifier },
4928 .token = token_index,4919 .token = token_index,
4929 };4920 };
49304921
src/all_types.hpp+2
...@@ -1385,6 +1385,7 @@ struct ZigTypeEnum {...@@ -1385,6 +1385,7 @@ struct ZigTypeEnum {
1385 ContainerLayout layout;1385 ContainerLayout layout;
1386 ResolveStatus resolve_status;1386 ResolveStatus resolve_status;
13871387
1388 bool non_exhaustive;
1388 bool resolve_loop_flag;1389 bool resolve_loop_flag;
1389};1390};
13901391
...@@ -3669,6 +3670,7 @@ struct IrInstructionCheckSwitchProngs {...@@ -3669,6 +3670,7 @@ struct IrInstructionCheckSwitchProngs {
3669 IrInstructionCheckSwitchProngsRange *ranges;3670 IrInstructionCheckSwitchProngsRange *ranges;
3670 size_t range_count;3671 size_t range_count;
3671 bool have_else_prong;3672 bool have_else_prong;
3673 bool have_underscore_prong;
3672};3674};
36733675
3674struct IrInstructionCheckStatementIsVoid {3676struct IrInstructionCheckStatementIsVoid {
src/analyze.cpp+31-7
...@@ -2569,15 +2569,8 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -2569,15 +2569,8 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
2569 return ErrorSemanticAnalyzeFail;2569 return ErrorSemanticAnalyzeFail;
2570 }2570 }
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
2576 Scope *scope = &enum_type->data.enumeration.decls_scope->base;2572 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
2581 ZigType *tag_int_type;2574 ZigType *tag_int_type;
2582 if (enum_type->data.enumeration.layout == ContainerLayoutExtern) {2575 if (enum_type->data.enumeration.layout == ContainerLayoutExtern) {
2583 tag_int_type = get_c_int_type(g, CIntTypeInt);2576 tag_int_type = get_c_int_type(g, CIntTypeInt);
...@@ -2619,6 +2612,7 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -2619,6 +2612,7 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
2619 }2612 }
2620 }2613 }
26212614
2615 enum_type->data.enumeration.non_exhaustive = false;
2622 enum_type->data.enumeration.tag_int_type = tag_int_type;2616 enum_type->data.enumeration.tag_int_type = tag_int_type;
2623 enum_type->size_in_bits = tag_int_type->size_in_bits;2617 enum_type->size_in_bits = tag_int_type->size_in_bits;
2624 enum_type->abi_size = tag_int_type->abi_size;2618 enum_type->abi_size = tag_int_type->abi_size;
...@@ -2627,6 +2621,31 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -2627,6 +2621,31 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
2627 BigInt bi_one;2621 BigInt bi_one;
2628 bigint_init_unsigned(&bi_one, 1);2622 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
2630 TypeEnumField *last_enum_field = nullptr;2649 TypeEnumField *last_enum_field = nullptr;
26312650
2632 for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {2651 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) {...@@ -2648,6 +2667,11 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
2648 buf_sprintf("consider 'union(enum)' here"));2667 buf_sprintf("consider 'union(enum)' here"));
2649 }2668 }
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
2651 auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field);2675 auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field);
2652 if (field_entry != nullptr) {2676 if (field_entry != nullptr) {
2653 ErrorMsg *msg = add_node_error(g, field_node,2677 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,...@@ -3356,7 +3356,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable,
3356 LLVMValueRef tag_int_value = gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),3356 LLVMValueRef tag_int_value = gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),
3357 instruction->target->value->type, tag_int_type, target_val);3357 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) {
3360 LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue");3360 LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue");
3361 LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue");3361 LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue");
3362 size_t field_count = wanted_type->data.enumeration.src_field_count;3362 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...@@ -5065,6 +5065,11 @@ static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable
5065{5065{
5066 ZigType *enum_type = instruction->target->value->type;5066 ZigType *enum_type = instruction->target->value->type;
5067 assert(enum_type->id == ZigTypeIdEnum);5067 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
5069 LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type);5074 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...@@ -3452,7 +3452,7 @@ static IrInstruction *ir_build_err_to_int(IrBuilder *irb, Scope *scope, AstNode
34523452
3453static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node,3453static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node,
3454 IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count,3454 IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count,
3455 bool have_else_prong)3455 bool have_else_prong, bool have_underscore_prong)
3456{3456{
3457 IrInstructionCheckSwitchProngs *instruction = ir_build_instruction<IrInstructionCheckSwitchProngs>(3457 IrInstructionCheckSwitchProngs *instruction = ir_build_instruction<IrInstructionCheckSwitchProngs>(
3458 irb, scope, source_node);3458 irb, scope, source_node);
...@@ -3460,6 +3460,7 @@ static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope,...@@ -3460,6 +3460,7 @@ static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope,
3460 instruction->ranges = ranges;3460 instruction->ranges = ranges;
3461 instruction->range_count = range_count;3461 instruction->range_count = range_count;
3462 instruction->have_else_prong = have_else_prong;3462 instruction->have_else_prong = have_else_prong;
3463 instruction->have_underscore_prong = have_underscore_prong;
34633464
3464 ir_ref_instruction(target_value, irb->current_basic_block);3465 ir_ref_instruction(target_value, irb->current_basic_block);
3465 for (size_t i = 0; i < range_count; i += 1) {3466 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 *...@@ -8092,34 +8093,11 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
8092 Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);8093 Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
8093 Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);8094 Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
8094 AstNode *else_prong = nullptr;8095 AstNode *else_prong = nullptr;
8096 AstNode *underscore_prong = nullptr;
8095 for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {8097 for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
8096 AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);8098 AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
8097 size_t prong_item_count = prong_node->data.switch_prong.items.length;8099 size_t prong_item_count = prong_node->data.switch_prong.items.length;
8098 if (prong_item_count == 0) {8100 if (prong_node->data.switch_prong.any_items_are_range) {
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) {
8123 ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);8101 ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
81248102
8125 IrInstruction *ok_bit = nullptr;8103 IrInstruction *ok_bit = nullptr;
...@@ -8197,6 +8175,56 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *...@@ -8197,6 +8175,56 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
8197 }8175 }
81988176
8199 ir_set_cursor_at_end_and_append_block(irb, range_block_no);8177 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);
8200 }8228 }
8201 }8229 }
82028230
...@@ -8208,6 +8236,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *...@@ -8208,6 +8236,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
8208 continue;8236 continue;
8209 if (prong_node->data.switch_prong.any_items_are_range)8237 if (prong_node->data.switch_prong.any_items_are_range)
8210 continue;8238 continue;
8239 if (underscore_prong == prong_node)
8240 continue;
82118241
8212 ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);8242 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 *...@@ -8251,7 +8281,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
8251 }8281 }
82528282
8253 IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value,8283 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
8256 IrInstruction *br_instruction;8286 IrInstruction *br_instruction;
8257 if (cases.length == 0) {8287 if (cases.length == 0) {
...@@ -8271,7 +8301,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *...@@ -8271,7 +8301,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
8271 peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction;8301 peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction;
8272 }8302 }
82738303
8274 if (!else_prong) {8304 if (!else_prong && !underscore_prong) {
8275 if (peer_parent->peers.length != 0) {8305 if (peer_parent->peers.length != 0) {
8276 peer_parent->peers.last()->next_bb = else_block;8306 peer_parent->peers.last()->next_bb = else_block;
8277 }8307 }
...@@ -12792,7 +12822,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour...@@ -12792,7 +12822,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour
12792 return ira->codegen->invalid_instruction;12822 return ira->codegen->invalid_instruction;
1279312823
12794 TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint);12824 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) {
12796 Buf *val_buf = buf_alloc();12826 Buf *val_buf = buf_alloc();
12797 bigint_append_buf(val_buf, &val->data.x_bigint, 10);12827 bigint_append_buf(val_buf, &val->data.x_bigint, 10);
12798 ErrorMsg *msg = ir_add_error(ira, source_instr,12828 ErrorMsg *msg = ir_add_error(ira, source_instr,
...@@ -22327,6 +22357,11 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns...@@ -22327,6 +22357,11 @@ static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIns
22327 if (instr_is_comptime(target)) {22357 if (instr_is_comptime(target)) {
22328 if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown)))22358 if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown)))
22329 return ira->codegen->invalid_instruction;22359 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 }
22330 TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint);22365 TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint);
22331 ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee;22366 ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee;
22332 IrInstruction *result = ir_const(ira, &instruction->base, nullptr);22367 IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
...@@ -23077,7 +23112,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr...@@ -23077,7 +23112,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr
23077 result->special = ConstValSpecialStatic;23112 result->special = ConstValSpecialStatic;
23078 result->type = ir_type_info_get_type(ira, "Enum", nullptr);23113 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);
23081 result->data.x_struct.fields = fields;23116 result->data.x_struct.fields = fields;
2308223117
23083 // layout: ContainerLayout23118 // layout: ContainerLayout
...@@ -23123,6 +23158,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr...@@ -23123,6 +23158,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr
23123 {23158 {
23124 return err;23159 return err;
23125 }23160 }
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
23127 break;23167 break;
23128 }23168 }
...@@ -26442,10 +26482,27 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -26442,10 +26482,27 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
26442 bigint_incr(&field_index);26482 bigint_incr(&field_index);
26443 }26483 }
26444 }26484 }
26445 if (!instruction->have_else_prong) {26485 if (instruction->have_underscore_prong) {
26446 if (switch_type->data.enumeration.layout == ContainerLayoutExtern) {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) {
26447 ir_add_error(ira, &instruction->base,26504 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"));
26449 }26506 }
26450 for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {26507 for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {
26451 TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];26508 TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];
test/compile_errors.zig+50-19
...@@ -2,6 +2,56 @@ const tests = @import("tests.zig");...@@ -2,6 +2,56 @@ const tests = @import("tests.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub 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
5 cases.addTest("@export with empty name string",55 cases.addTest("@export with empty name string",
6 \\pub export fn entry() void { }56 \\pub export fn entry() void { }
7 \\comptime {57 \\comptime {
...@@ -139,25 +189,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -139,25 +189,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
139 "tmp.zig:2:13: error: pointer type '[*]align(4) u8' requires aligned address",189 "tmp.zig:2:13: error: pointer type '[*]align(4) u8' requires aligned address",
140 });190 });
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
161 cases.add("invalid float literal",192 cases.add("invalid float literal",
162 \\const std = @import("std");193 \\const std = @import("std");
163 \\194 \\
test/stage1/behavior/cast.zig-1
...@@ -618,7 +618,6 @@ test "peer resolution of string literals" {...@@ -618,7 +618,6 @@ test "peer resolution of string literals" {
618 .b => "two",618 .b => "two",
619 .c => "three",619 .c => "three",
620 .d => "four",620 .d => "four",
621 else => unreachable,
622 };621 };
623 expect(mem.eql(u8, cmd, "two"));622 expect(mem.eql(u8, cmd, "two"));
624 }623 }
test/stage1/behavior/enum.zig+46-9
...@@ -11,16 +11,9 @@ test "extern enum" {...@@ -11,16 +11,9 @@ test "extern enum" {
11 };11 };
12 fn doTheTest(y: c_int) void {12 fn doTheTest(y: c_int) void {
13 var x = i.o;13 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);
19 switch (x) {14 switch (x) {
20 .n,15 .n, .p => unreachable,
21 .o,16 .o => {},
22 .p => unreachable,
23 else => {},
24 }17 }
25 }18 }
26 };19 };
...@@ -28,6 +21,50 @@ test "extern enum" {...@@ -28,6 +21,50 @@ test "extern enum" {
28 comptime S.doTheTest(52);21 comptime S.doTheTest(52);
29}22}
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
31test "enum type" {68test "enum type" {
32 const foo1 = Foo{ .One = 13 };69 const foo1 = Foo{ .One = 13 };
33 const foo2 = Foo{70 const foo2 = Foo{
test/translate_c.zig+21-10
...@@ -629,6 +629,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -629,6 +629,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
629 \\ VAL21 = 6917529027641081853,629 \\ VAL21 = 6917529027641081853,
630 \\ VAL22 = 0,630 \\ VAL22 = 0,
631 \\ VAL23 = -1,631 \\ VAL23 = -1,
632 \\ _,
632 \\};633 \\};
633 });634 });
634 }635 }
...@@ -988,8 +989,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -988,8 +989,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
988 \\enum enum_ty { FOO };989 \\enum enum_ty { FOO };
989 , &[_][]const u8{990 , &[_][]const u8{
990 \\pub const FOO = @enumToInt(enum_enum_ty.FOO);991 \\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) {
992 \\ FOO,993 \\ FOO,
994 \\ _,
993 \\};995 \\};
994 \\pub extern var my_enum: enum_enum_ty;996 \\pub extern var my_enum: enum_enum_ty;
995 });997 });
...@@ -1102,28 +1104,31 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1102,28 +1104,31 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1102 \\pub const a = @enumToInt(enum_unnamed_1.a);1104 \\pub const a = @enumToInt(enum_unnamed_1.a);
1103 \\pub const b = @enumToInt(enum_unnamed_1.b);1105 \\pub const b = @enumToInt(enum_unnamed_1.b);
1104 \\pub const c = @enumToInt(enum_unnamed_1.c);1106 \\pub const c = @enumToInt(enum_unnamed_1.c);
1105 \\const enum_unnamed_1 = extern enum {1107 \\const enum_unnamed_1 = extern enum(c_int) {
1106 \\ a,1108 \\ a,
1107 \\ b,1109 \\ b,
1108 \\ c,1110 \\ c,
1111 \\ _,
1109 \\};1112 \\};
1110 \\pub const d = enum_unnamed_1;1113 \\pub const d = enum_unnamed_1;
1111 \\pub const e = @enumToInt(enum_unnamed_2.e);1114 \\pub const e = @enumToInt(enum_unnamed_2.e);
1112 \\pub const f = @enumToInt(enum_unnamed_2.f);1115 \\pub const f = @enumToInt(enum_unnamed_2.f);
1113 \\pub const g = @enumToInt(enum_unnamed_2.g);1116 \\pub const g = @enumToInt(enum_unnamed_2.g);
1114 \\const enum_unnamed_2 = extern enum {1117 \\const enum_unnamed_2 = extern enum(c_int) {
1115 \\ e = 0,1118 \\ e = 0,
1116 \\ f = 4,1119 \\ f = 4,
1117 \\ g = 5,1120 \\ g = 5,
1121 \\ _,
1118 \\};1122 \\};
1119 \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e);1123 \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e);
1120 \\pub const i = @enumToInt(enum_unnamed_3.i);1124 \\pub const i = @enumToInt(enum_unnamed_3.i);
1121 \\pub const j = @enumToInt(enum_unnamed_3.j);1125 \\pub const j = @enumToInt(enum_unnamed_3.j);
1122 \\pub const k = @enumToInt(enum_unnamed_3.k);1126 \\pub const k = @enumToInt(enum_unnamed_3.k);
1123 \\const enum_unnamed_3 = extern enum {1127 \\const enum_unnamed_3 = extern enum(c_int) {
1124 \\ i,1128 \\ i,
1125 \\ j,1129 \\ j,
1126 \\ k,1130 \\ k,
1131 \\ _,
1127 \\};1132 \\};
1128 \\pub const struct_Baz = extern struct {1133 \\pub const struct_Baz = extern struct {
1129 \\ l: enum_unnamed_3,1134 \\ l: enum_unnamed_3,
...@@ -1132,10 +1137,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1132,10 +1137,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1132 \\pub const n = @enumToInt(enum_i.n);1137 \\pub const n = @enumToInt(enum_i.n);
1133 \\pub const o = @enumToInt(enum_i.o);1138 \\pub const o = @enumToInt(enum_i.o);
1134 \\pub const p = @enumToInt(enum_i.p);1139 \\pub const p = @enumToInt(enum_i.p);
1135 \\pub const enum_i = extern enum {1140 \\pub const enum_i = extern enum(c_int) {
1136 \\ n,1141 \\ n,
1137 \\ o,1142 \\ o,
1138 \\ p,1143 \\ p,
1144 \\ _,
1139 \\};1145 \\};
1140 ,1146 ,
1141 \\pub const Baz = struct_Baz;1147 \\pub const Baz = struct_Baz;
...@@ -1563,9 +1569,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1563,9 +1569,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1563 , &[_][]const u8{1569 , &[_][]const u8{
1564 \\pub const One = @enumToInt(enum_unnamed_1.One);1570 \\pub const One = @enumToInt(enum_unnamed_1.One);
1565 \\pub const Two = @enumToInt(enum_unnamed_1.Two);1571 \\pub const Two = @enumToInt(enum_unnamed_1.Two);
1566 \\const enum_unnamed_1 = extern enum {1572 \\const enum_unnamed_1 = extern enum(c_int) {
1567 \\ One,1573 \\ One,
1568 \\ Two,1574 \\ Two,
1575 \\ _,
1569 \\};1576 \\};
1570 });1577 });
15711578
...@@ -1665,10 +1672,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1665,10 +1672,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1665 \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);1672 \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
1666 \\}1673 \\}
1667 , &[_][]const u8{1674 , &[_][]const u8{
1668 \\pub const enum_Foo = extern enum {1675 \\pub const enum_Foo = extern enum(c_int) {
1669 \\ A,1676 \\ A,
1670 \\ B,1677 \\ B,
1671 \\ C,1678 \\ C,
1679 \\ _,
1672 \\};1680 \\};
1673 \\pub const SomeTypedef = c_int;1681 \\pub const SomeTypedef = c_int;
1674 \\pub export fn and_or_non_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void) c_int {1682 \\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 {...@@ -1710,9 +1718,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1710 \\ y: c_int,1718 \\ y: c_int,
1711 \\};1719 \\};
1712 ,1720 ,
1713 \\pub const enum_Bar = extern enum {1721 \\pub const enum_Bar = extern enum(c_int) {
1714 \\ A,1722 \\ A,
1715 \\ B,1723 \\ B,
1724 \\ _,
1716 \\};1725 \\};
1717 \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;1726 \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
1718 ,1727 ,
...@@ -1973,10 +1982,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1973,10 +1982,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1973 \\ return 4;1982 \\ return 4;
1974 \\}1983 \\}
1975 , &[_][]const u8{1984 , &[_][]const u8{
1976 \\pub const enum_SomeEnum = extern enum {1985 \\pub const enum_SomeEnum = extern enum(c_int) {
1977 \\ A,1986 \\ A,
1978 \\ B,1987 \\ B,
1979 \\ C,1988 \\ C,
1989 \\ _,
1980 \\};1990 \\};
1981 \\pub export fn if_none_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void, arg_d: enum_SomeEnum) c_int {1991 \\pub export fn if_none_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void, arg_d: enum_SomeEnum) c_int {
1982 \\ var a = arg_a;1992 \\ var a = arg_a;
...@@ -2414,10 +2424,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2414,10 +2424,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2414 \\pub const FooA = @enumToInt(enum_Foo.A);2424 \\pub const FooA = @enumToInt(enum_Foo.A);
2415 \\pub const FooB = @enumToInt(enum_Foo.B);2425 \\pub const FooB = @enumToInt(enum_Foo.B);
2416 \\pub const Foo1 = @enumToInt(enum_Foo.@"1");2426 \\pub const Foo1 = @enumToInt(enum_Foo.@"1");
2417 \\pub const enum_Foo = extern enum {2427 \\pub const enum_Foo = extern enum(c_int) {
2418 \\ A = 2,2428 \\ A = 2,
2419 \\ B = 5,2429 \\ B = 5,
2420 \\ @"1" = 6,2430 \\ @"1" = 6,
2431 \\ _,
2421 \\};2432 \\};
2422 ,2433 ,
2423 \\pub const Foo = enum_Foo;2434 \\pub const Foo = enum_Foo;