| author | |
| committer | |
| log | b5ac079f88e9098ea9c95356518820a5c3fb42a8 |
| tree | e4028947b688f53a5387b6b3e0b454090ee240dc |
| parent | d9be6e5dc693fcbcb5f4c343a3d2b0b9fc786e25 |
| parent | 39f92a9ee4ea109628e1f7d5a65bb53575e53194 |
| signature |
Implement non-exhaustive enums11 files changed, 330 insertions(+), 131 deletions(-)
doc/langref.html.in+41| ... | ... | @@ -2893,6 +2893,47 @@ test "switch using enum literals" { |
| 2893 | 2893 | } |
| 2894 | 2894 | {#code_end#} |
| 2895 | 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#} | |
| 2910 | const std = @import("std"); | |
| 2911 | const assert = std.debug.assert; | |
| 2912 | ||
| 2913 | const Number = enum(u8) { | |
| 2914 | One, | |
| 2915 | Two, | |
| 2916 | Three, | |
| 2917 | _, | |
| 2918 | }; | |
| 2919 | ||
| 2920 | test "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 | 2937 | {#header_close#} |
| 2897 | 2938 | |
| 2898 | 2939 | {#header_open|union#} |
lib/std/builtin.zig+1| ... | ... | @@ -254,6 +254,7 @@ pub const TypeInfo = union(enum) { |
| 254 | 254 | tag_type: type, |
| 255 | 255 | fields: []EnumField, |
| 256 | 256 | decls: []Declaration, |
| 257 | is_exhaustive: bool, | |
| 257 | 258 | }; |
| 258 | 259 | |
| 259 | 260 | /// 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 | 289 | tree.errors = ast.Tree.ErrorList.init(arena); |
| 290 | 290 | |
| 291 | 291 | 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.* = .{ | |
| 294 | 293 | .decls = ast.Node.Root.DeclList.init(arena), |
| 295 | 294 | // initialized with the eof token at the end |
| 296 | 295 | .eof_token = undefined, |
| ... | ... | @@ -440,7 +439,6 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { |
| 440 | 439 | .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern", .{}), |
| 441 | 440 | .Auto => unreachable, // Not legal on functions |
| 442 | 441 | .Register => unreachable, // Not legal on functions |
| 443 | else => unreachable, | |
| 444 | 442 | }, |
| 445 | 443 | }; |
| 446 | 444 | |
| ... | ... | @@ -877,25 +875,23 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No |
| 877 | 875 | // types, while that's not ISO-C compliant many compilers allow this and |
| 878 | 876 | // default to the usual integer type used for all the enums. |
| 879 | 877 | |
| 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) { | |
| 890 | 885 | error.UnsupportedType => { |
| 891 | 886 | try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{}); |
| 892 | 887 | return null; |
| 893 | 888 | }, |
| 894 | 889 | 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, ")"); | |
| 899 | 895 | |
| 900 | 896 | container_node.lbrace_token = try appendToken(c, .LBrace, "{"); |
| 901 | 897 | |
| ... | ... | @@ -953,6 +949,19 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No |
| 953 | 949 | tld_node.semicolon_token = try appendToken(c, .Semicolon, ";"); |
| 954 | 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 | 965 | container_node.rbrace_token = try appendToken(c, .RBrace, "}"); |
| 957 | 966 | |
| 958 | 967 | break :blk &container_node.base; |
| ... | ... | @@ -1231,18 +1240,6 @@ fn transBinaryOperator( |
| 1231 | 1240 | op_id = .BitOr; |
| 1232 | 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 | 1243 | else => unreachable, |
| 1247 | 1244 | } |
| 1248 | 1245 | |
| ... | ... | @@ -1678,7 +1675,6 @@ fn transStringLiteral( |
| 1678 | 1675 | "TODO: support string literal kind {}", |
| 1679 | 1676 | .{kind}, |
| 1680 | 1677 | ), |
| 1681 | else => unreachable, | |
| 1682 | 1678 | } |
| 1683 | 1679 | } |
| 1684 | 1680 | |
| ... | ... | @@ -2206,6 +2202,19 @@ fn transDoWhileLoop( |
| 2206 | 2202 | .id = .Loop, |
| 2207 | 2203 | }; |
| 2208 | 2204 | |
| 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 | 2218 | const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: { |
| 2210 | 2219 | // there's already a block in C, so we'll append our condition to it. |
| 2211 | 2220 | // c: do { |
| ... | ... | @@ -2217,10 +2226,7 @@ fn transDoWhileLoop( |
| 2217 | 2226 | // zig: b; |
| 2218 | 2227 | // zig: if (!cond) break; |
| 2219 | 2228 | // 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).?; | |
| 2224 | 2230 | } else blk: { |
| 2225 | 2231 | // the C statement is without a block, so we need to create a block to contain it. |
| 2226 | 2232 | // c: do |
| ... | ... | @@ -2236,19 +2242,6 @@ fn transDoWhileLoop( |
| 2236 | 2242 | break :blk block; |
| 2237 | 2243 | }; |
| 2238 | 2244 | |
| 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 | 2245 | try body_node.statements.push(&if_node.base); |
| 2253 | 2246 | if (new) |
| 2254 | 2247 | body_node.rbrace = try appendToken(rp.c, .RBrace, "}"); |
| ... | ... | @@ -4783,8 +4776,7 @@ fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex { |
| 4783 | 4776 | fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node { |
| 4784 | 4777 | const token_index = try appendIdentifier(c, name); |
| 4785 | 4778 | 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.* = .{ | |
| 4788 | 4780 | .token = token_index, |
| 4789 | 4781 | }; |
| 4790 | 4782 | return &identifier.base; |
| ... | ... | @@ -4923,8 +4915,7 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u |
| 4923 | 4915 | |
| 4924 | 4916 | const token_index = try appendToken(c, .Keyword_var, "var"); |
| 4925 | 4917 | 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.* = .{ | |
| 4928 | 4919 | .token = token_index, |
| 4929 | 4920 | }; |
| 4930 | 4921 |
src/all_types.hpp+2| ... | ... | @@ -1385,6 +1385,7 @@ struct ZigTypeEnum { |
| 1385 | 1385 | ContainerLayout layout; |
| 1386 | 1386 | ResolveStatus resolve_status; |
| 1387 | 1387 | |
| 1388 | bool non_exhaustive; | |
| 1388 | 1389 | bool resolve_loop_flag; |
| 1389 | 1390 | }; |
| 1390 | 1391 | |
| ... | ... | @@ -3669,6 +3670,7 @@ struct IrInstructionCheckSwitchProngs { |
| 3669 | 3670 | IrInstructionCheckSwitchProngsRange *ranges; |
| 3670 | 3671 | size_t range_count; |
| 3671 | 3672 | bool have_else_prong; |
| 3673 | bool have_underscore_prong; | |
| 3672 | 3674 | }; |
| 3673 | 3675 | |
| 3674 | 3676 | struct IrInstructionCheckStatementIsVoid { |
src/analyze.cpp+31-7| ... | ... | @@ -2569,15 +2569,8 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { |
| 2569 | 2569 | return ErrorSemanticAnalyzeFail; |
| 2570 | 2570 | } |
| 2571 | 2571 | |
| 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 | 2572 | Scope *scope = &enum_type->data.enumeration.decls_scope->base; |
| 2577 | 2573 | |
| 2578 | HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {}; | |
| 2579 | occupied_tag_values.init(field_count); | |
| 2580 | ||
| 2581 | 2574 | ZigType *tag_int_type; |
| 2582 | 2575 | if (enum_type->data.enumeration.layout == ContainerLayoutExtern) { |
| 2583 | 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 | 2612 | } |
| 2620 | 2613 | } |
| 2621 | 2614 | |
| 2615 | enum_type->data.enumeration.non_exhaustive = false; | |
| 2622 | 2616 | enum_type->data.enumeration.tag_int_type = tag_int_type; |
| 2623 | 2617 | enum_type->size_in_bits = tag_int_type->size_in_bits; |
| 2624 | 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 | 2621 | BigInt bi_one; |
| 2628 | 2622 | bigint_init_unsigned(&bi_one, 1); |
| 2629 | 2623 | |
| 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 | 2649 | TypeEnumField *last_enum_field = nullptr; |
| 2631 | 2650 | |
| 2632 | 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 | 2667 | buf_sprintf("consider 'union(enum)' here")); |
| 2649 | 2668 | } |
| 2650 | 2669 | |
| 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 | 2675 | auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field); |
| 2652 | 2676 | if (field_entry != nullptr) { |
| 2653 | 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 | 3356 | LLVMValueRef tag_int_value = gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), |
| 3357 | 3357 | instruction->target->value->type, tag_int_type, target_val); |
| 3358 | 3358 | |
| 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 | 3360 | LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue"); |
| 3361 | 3361 | LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue"); |
| 3362 | 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 | 5065 | { |
| 5066 | 5066 | ZigType *enum_type = instruction->target->value->type; |
| 5067 | 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 | } | |
| 5068 | 5073 | |
| 5069 | 5074 | LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type); |
| 5070 | 5075 |
src/ir.cpp+90-33| ... | ... | @@ -3452,7 +3452,7 @@ static IrInstruction *ir_build_err_to_int(IrBuilder *irb, Scope *scope, AstNode |
| 3452 | 3452 | |
| 3453 | 3453 | static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 3454 | 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 | 3457 | IrInstructionCheckSwitchProngs *instruction = ir_build_instruction<IrInstructionCheckSwitchProngs>( |
| 3458 | 3458 | irb, scope, source_node); |
| ... | ... | @@ -3460,6 +3460,7 @@ static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, |
| 3460 | 3460 | instruction->ranges = ranges; |
| 3461 | 3461 | instruction->range_count = range_count; |
| 3462 | 3462 | instruction->have_else_prong = have_else_prong; |
| 3463 | instruction->have_underscore_prong = have_underscore_prong; | |
| 3463 | 3464 | |
| 3464 | 3465 | ir_ref_instruction(target_value, irb->current_basic_block); |
| 3465 | 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 | 8093 | Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); |
| 8093 | 8094 | Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); |
| 8094 | 8095 | AstNode *else_prong = nullptr; |
| 8096 | AstNode *underscore_prong = nullptr; | |
| 8095 | 8097 | for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) { |
| 8096 | 8098 | AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); |
| 8097 | 8099 | 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) { | |
| 8123 | 8101 | ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); |
| 8124 | 8102 | |
| 8125 | 8103 | IrInstruction *ok_bit = nullptr; |
| ... | ... | @@ -8197,6 +8175,56 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 8197 | 8175 | } |
| 8198 | 8176 | |
| 8199 | 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 | } |
| 8202 | 8230 | |
| ... | ... | @@ -8208,6 +8236,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 8208 | 8236 | continue; |
| 8209 | 8237 | if (prong_node->data.switch_prong.any_items_are_range) |
| 8210 | 8238 | continue; |
| 8239 | if (underscore_prong == prong_node) | |
| 8240 | continue; | |
| 8211 | 8241 | |
| 8212 | 8242 | ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); |
| 8213 | 8243 | |
| ... | ... | @@ -8251,7 +8281,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 8251 | 8281 | } |
| 8252 | 8282 | |
| 8253 | 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); | |
| 8255 | 8285 | |
| 8256 | 8286 | IrInstruction *br_instruction; |
| 8257 | 8287 | if (cases.length == 0) { |
| ... | ... | @@ -8271,7 +8301,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 8271 | 8301 | peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction; |
| 8272 | 8302 | } |
| 8273 | 8303 | |
| 8274 | if (!else_prong) { | |
| 8304 | if (!else_prong && !underscore_prong) { | |
| 8275 | 8305 | if (peer_parent->peers.length != 0) { |
| 8276 | 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 | 12822 | return ira->codegen->invalid_instruction; |
| 12793 | 12823 | |
| 12794 | 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 | 12826 | Buf *val_buf = buf_alloc(); |
| 12797 | 12827 | bigint_append_buf(val_buf, &val->data.x_bigint, 10); |
| 12798 | 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 | 22357 | if (instr_is_comptime(target)) { |
| 22328 | 22358 | if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown))) |
| 22329 | 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 | 22365 | TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint); |
| 22331 | 22366 | ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee; |
| 22332 | 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 | 23112 | result->special = ConstValSpecialStatic; |
| 23078 | 23113 | result->type = ir_type_info_get_type(ira, "Enum", nullptr); |
| 23079 | 23114 | |
| 23080 | ZigValue **fields = alloc_const_vals_ptrs(4); | |
| 23115 | ZigValue **fields = alloc_const_vals_ptrs(5); | |
| 23081 | 23116 | result->data.x_struct.fields = fields; |
| 23082 | 23117 | |
| 23083 | 23118 | // layout: ContainerLayout |
| ... | ... | @@ -23123,6 +23158,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr |
| 23123 | 23158 | { |
| 23124 | 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; | |
| 23126 | 23166 | |
| 23127 | 23167 | break; |
| 23128 | 23168 | } |
| ... | ... | @@ -26442,10 +26482,27 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, |
| 26442 | 26482 | bigint_incr(&field_index); |
| 26443 | 26483 | } |
| 26444 | 26484 | } |
| 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) { | |
| 26447 | 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 | 26507 | for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { |
| 26451 | 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 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | |
| 4 | 4 | pub 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 | 55 | cases.addTest("@export with empty name string", |
| 6 | 56 | \\pub export fn entry() void { } |
| 7 | 57 | \\comptime { |
| ... | ... | @@ -139,25 +189,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 139 | 189 | "tmp.zig:2:13: error: pointer type '[*]align(4) u8' requires aligned address", |
| 140 | 190 | }); |
| 141 | 191 | |
| 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 | 192 | cases.add("invalid float literal", |
| 162 | 193 | \\const std = @import("std"); |
| 163 | 194 | \\ |
test/stage1/behavior/cast.zig-1| ... | ... | @@ -618,7 +618,6 @@ test "peer resolution of string literals" { |
| 618 | 618 | .b => "two", |
| 619 | 619 | .c => "three", |
| 620 | 620 | .d => "four", |
| 621 | else => unreachable, | |
| 622 | 621 | }; |
| 623 | 622 | expect(mem.eql(u8, cmd, "two")); |
| 624 | 623 | } |
test/stage1/behavior/enum.zig+46-9| ... | ... | @@ -11,16 +11,9 @@ test "extern enum" { |
| 11 | 11 | }; |
| 12 | 12 | fn doTheTest(y: c_int) void { |
| 13 | 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 | 14 | switch (x) { |
| 20 | .n, | |
| 21 | .o, | |
| 22 | .p => unreachable, | |
| 23 | else => {}, | |
| 15 | .n, .p => unreachable, | |
| 16 | .o => {}, | |
| 24 | 17 | } |
| 25 | 18 | } |
| 26 | 19 | }; |
| ... | ... | @@ -28,6 +21,50 @@ test "extern enum" { |
| 28 | 21 | comptime S.doTheTest(52); |
| 29 | 22 | } |
| 30 | 23 | |
| 24 | test "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 | ||
| 31 | 68 | test "enum type" { |
| 32 | 69 | const foo1 = Foo{ .One = 13 }; |
| 33 | 70 | const foo2 = Foo{ |
test/translate_c.zig+21-10| ... | ... | @@ -629,6 +629,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 629 | 629 | \\ VAL21 = 6917529027641081853, |
| 630 | 630 | \\ VAL22 = 0, |
| 631 | 631 | \\ VAL23 = -1, |
| 632 | \\ _, | |
| 632 | 633 | \\}; |
| 633 | 634 | }); |
| 634 | 635 | } |
| ... | ... | @@ -988,8 +989,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 988 | 989 | \\enum enum_ty { FOO }; |
| 989 | 990 | , &[_][]const u8{ |
| 990 | 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 | 993 | \\ FOO, |
| 994 | \\ _, | |
| 993 | 995 | \\}; |
| 994 | 996 | \\pub extern var my_enum: enum_enum_ty; |
| 995 | 997 | }); |
| ... | ... | @@ -1102,28 +1104,31 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1102 | 1104 | \\pub const a = @enumToInt(enum_unnamed_1.a); |
| 1103 | 1105 | \\pub const b = @enumToInt(enum_unnamed_1.b); |
| 1104 | 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 | 1108 | \\ a, |
| 1107 | 1109 | \\ b, |
| 1108 | 1110 | \\ c, |
| 1111 | \\ _, | |
| 1109 | 1112 | \\}; |
| 1110 | 1113 | \\pub const d = enum_unnamed_1; |
| 1111 | 1114 | \\pub const e = @enumToInt(enum_unnamed_2.e); |
| 1112 | 1115 | \\pub const f = @enumToInt(enum_unnamed_2.f); |
| 1113 | 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 | 1118 | \\ e = 0, |
| 1116 | 1119 | \\ f = 4, |
| 1117 | 1120 | \\ g = 5, |
| 1121 | \\ _, | |
| 1118 | 1122 | \\}; |
| 1119 | 1123 | \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e); |
| 1120 | 1124 | \\pub const i = @enumToInt(enum_unnamed_3.i); |
| 1121 | 1125 | \\pub const j = @enumToInt(enum_unnamed_3.j); |
| 1122 | 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 | 1128 | \\ i, |
| 1125 | 1129 | \\ j, |
| 1126 | 1130 | \\ k, |
| 1131 | \\ _, | |
| 1127 | 1132 | \\}; |
| 1128 | 1133 | \\pub const struct_Baz = extern struct { |
| 1129 | 1134 | \\ l: enum_unnamed_3, |
| ... | ... | @@ -1132,10 +1137,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1132 | 1137 | \\pub const n = @enumToInt(enum_i.n); |
| 1133 | 1138 | \\pub const o = @enumToInt(enum_i.o); |
| 1134 | 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 | 1141 | \\ n, |
| 1137 | 1142 | \\ o, |
| 1138 | 1143 | \\ p, |
| 1144 | \\ _, | |
| 1139 | 1145 | \\}; |
| 1140 | 1146 | , |
| 1141 | 1147 | \\pub const Baz = struct_Baz; |
| ... | ... | @@ -1563,9 +1569,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1563 | 1569 | , &[_][]const u8{ |
| 1564 | 1570 | \\pub const One = @enumToInt(enum_unnamed_1.One); |
| 1565 | 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 | 1573 | \\ One, |
| 1568 | 1574 | \\ Two, |
| 1575 | \\ _, | |
| 1569 | 1576 | \\}; |
| 1570 | 1577 | }); |
| 1571 | 1578 | |
| ... | ... | @@ -1665,10 +1672,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1665 | 1672 | \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p); |
| 1666 | 1673 | \\} |
| 1667 | 1674 | , &[_][]const u8{ |
| 1668 | \\pub const enum_Foo = extern enum { | |
| 1675 | \\pub const enum_Foo = extern enum(c_int) { | |
| 1669 | 1676 | \\ A, |
| 1670 | 1677 | \\ B, |
| 1671 | 1678 | \\ C, |
| 1679 | \\ _, | |
| 1672 | 1680 | \\}; |
| 1673 | 1681 | \\pub const SomeTypedef = c_int; |
| 1674 | 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 | 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 | 1722 | \\ A, |
| 1715 | 1723 | \\ B, |
| 1724 | \\ _, | |
| 1716 | 1725 | \\}; |
| 1717 | 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 | 1982 | \\ return 4; |
| 1974 | 1983 | \\} |
| 1975 | 1984 | , &[_][]const u8{ |
| 1976 | \\pub const enum_SomeEnum = extern enum { | |
| 1985 | \\pub const enum_SomeEnum = extern enum(c_int) { | |
| 1977 | 1986 | \\ A, |
| 1978 | 1987 | \\ B, |
| 1979 | 1988 | \\ C, |
| 1989 | \\ _, | |
| 1980 | 1990 | \\}; |
| 1981 | 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 | 1992 | \\ var a = arg_a; |
| ... | ... | @@ -2414,10 +2424,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2414 | 2424 | \\pub const FooA = @enumToInt(enum_Foo.A); |
| 2415 | 2425 | \\pub const FooB = @enumToInt(enum_Foo.B); |
| 2416 | 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 | 2428 | \\ A = 2, |
| 2419 | 2429 | \\ B = 5, |
| 2420 | 2430 | \\ @"1" = 6, |
| 2431 | \\ _, | |
| 2421 | 2432 | \\}; |
| 2422 | 2433 | , |
| 2423 | 2434 | \\pub const Foo = enum_Foo; |