authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-07 16:39:10-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-07 16:39:10-07:00
log8f28e26e7a4f770f8d8e700386e2ade111948891
treed96d35bdf5e3e530b0dd42fd30114af79b916fd5
parentccdba774c84e2b3c7f650e27f430c9a3eed78fd3

Sema: implement switch validation for enums


3 files changed, 238 insertions(+), 25 deletions(-)

src/Sema.zig+169-24
...@@ -2729,6 +2729,8 @@ fn analyzeSwitch(...@@ -2729,6 +2729,8 @@ fn analyzeSwitch(
2729 src_node_offset: i32,2729 src_node_offset: i32,
2730) InnerError!*Inst {2730) InnerError!*Inst {
2731 const gpa = sema.gpa;2731 const gpa = sema.gpa;
2732 const mod = sema.mod;
2733
2732 const special: struct { body: []const zir.Inst.Index, end: usize } = switch (special_prong) {2734 const special: struct { body: []const zir.Inst.Index, end: usize } = switch (special_prong) {
2733 .none => .{ .body = &.{}, .end = extra_end },2735 .none => .{ .body = &.{}, .end = extra_end },
2734 .under, .@"else" => blk: {2736 .under, .@"else" => blk: {
...@@ -2748,14 +2750,14 @@ fn analyzeSwitch(...@@ -2748,14 +2750,14 @@ fn analyzeSwitch(
2748 // Validate usage of '_' prongs.2750 // Validate usage of '_' prongs.
2749 if (special_prong == .under and !operand.ty.isExhaustiveEnum()) {2751 if (special_prong == .under and !operand.ty.isExhaustiveEnum()) {
2750 const msg = msg: {2752 const msg = msg: {
2751 const msg = try sema.mod.errMsg(2753 const msg = try mod.errMsg(
2752 &block.base,2754 &block.base,
2753 src,2755 src,
2754 "'_' prong only allowed when switching on non-exhaustive enums",2756 "'_' prong only allowed when switching on non-exhaustive enums",
2755 .{},2757 .{},
2756 );2758 );
2757 errdefer msg.destroy(gpa);2759 errdefer msg.destroy(gpa);
2758 try sema.mod.errNote(2760 try mod.errNote(
2759 &block.base,2761 &block.base,
2760 special_prong_src,2762 special_prong_src,
2761 msg,2763 msg,
...@@ -2764,14 +2766,121 @@ fn analyzeSwitch(...@@ -2764,14 +2766,121 @@ fn analyzeSwitch(
2764 );2766 );
2765 break :msg msg;2767 break :msg msg;
2766 };2768 };
2767 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);2769 return mod.failWithOwnedErrorMsg(&block.base, msg);
2768 }2770 }
27692771
2770 // Validate for duplicate items, missing else prong, and invalid range.2772 // Validate for duplicate items, missing else prong, and invalid range.
2771 switch (operand.ty.zigTypeTag()) {2773 switch (operand.ty.zigTypeTag()) {
2772 .Enum => return sema.mod.fail(&block.base, src, "TODO validate switch .Enum", .{}),2774 .Enum => {
2773 .ErrorSet => return sema.mod.fail(&block.base, src, "TODO validate switch .ErrorSet", .{}),2775 var seen_fields = try gpa.alloc(?AstGen.SwitchProngSrc, operand.ty.enumFieldCount());
2774 .Union => return sema.mod.fail(&block.base, src, "TODO validate switch .Union", .{}),2776 defer gpa.free(seen_fields);
2777
2778 var extra_index: usize = special.end;
2779 {
2780 var scalar_i: u32 = 0;
2781 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2782 const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
2783 extra_index += 1;
2784 const body_len = sema.code.extra[extra_index];
2785 extra_index += 1;
2786 const body = sema.code.extra[extra_index..][0..body_len];
2787 extra_index += body_len;
2788
2789 try sema.validateSwitchItemEnum(
2790 block,
2791 seen_fields,
2792 item_ref,
2793 src_node_offset,
2794 .{ .scalar = scalar_i },
2795 );
2796 }
2797 }
2798 {
2799 var multi_i: u32 = 0;
2800 while (multi_i < multi_cases_len) : (multi_i += 1) {
2801 const items_len = sema.code.extra[extra_index];
2802 extra_index += 1;
2803 const ranges_len = sema.code.extra[extra_index];
2804 extra_index += 1;
2805 const body_len = sema.code.extra[extra_index];
2806 extra_index += 1;
2807 const items = sema.code.refSlice(extra_index, items_len);
2808 extra_index += items_len + body_len;
2809
2810 for (items) |item_ref, item_i| {
2811 try sema.validateSwitchItemEnum(
2812 block,
2813 seen_fields,
2814 item_ref,
2815 src_node_offset,
2816 .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
2817 );
2818 }
2819
2820 try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset);
2821 }
2822 }
2823 const all_tags_handled = for (seen_fields) |seen_src| {
2824 if (seen_src == null) break false;
2825 } else true;
2826
2827 switch (special_prong) {
2828 .none => {
2829 if (!all_tags_handled) {
2830 const msg = msg: {
2831 const msg = try mod.errMsg(
2832 &block.base,
2833 src,
2834 "switch must handle all possibilities",
2835 .{},
2836 );
2837 errdefer msg.destroy(sema.gpa);
2838 try mod.errNoteNonLazy(
2839 operand.ty.declSrcLoc(),
2840 msg,
2841 "enum '{}' declared here",
2842 .{operand.ty},
2843 );
2844 for (seen_fields) |seen_src, i| {
2845 if (seen_src != null) continue;
2846
2847 const field_name = operand.ty.enumFieldName(i);
2848
2849 // TODO have this point to the tag decl instead of here
2850 try mod.errNote(
2851 &block.base,
2852 src,
2853 msg,
2854 "unhandled enumeration value: '{s}",
2855 .{field_name},
2856 );
2857 }
2858 break :msg msg;
2859 };
2860 return mod.failWithOwnedErrorMsg(&block.base, msg);
2861 }
2862 },
2863 .under => {
2864 if (all_tags_handled) return mod.fail(
2865 &block.base,
2866 special_prong_src,
2867 "unreachable '_' prong; all cases already handled",
2868 .{},
2869 );
2870 },
2871 .@"else" => {
2872 if (all_tags_handled) return mod.fail(
2873 &block.base,
2874 special_prong_src,
2875 "unreachable else prong; all cases already handled",
2876 .{},
2877 );
2878 },
2879 }
2880 },
2881
2882 .ErrorSet => return mod.fail(&block.base, src, "TODO validate switch .ErrorSet", .{}),
2883 .Union => return mod.fail(&block.base, src, "TODO validate switch .Union", .{}),
2775 .Int, .ComptimeInt => {2884 .Int, .ComptimeInt => {
2776 var range_set = RangeSet.init(gpa);2885 var range_set = RangeSet.init(gpa);
2777 defer range_set.deinit();2886 defer range_set.deinit();
...@@ -2844,11 +2953,11 @@ fn analyzeSwitch(...@@ -2844,11 +2953,11 @@ fn analyzeSwitch(
2844 var arena = std.heap.ArenaAllocator.init(gpa);2953 var arena = std.heap.ArenaAllocator.init(gpa);
2845 defer arena.deinit();2954 defer arena.deinit();
28462955
2847 const min_int = try operand.ty.minInt(&arena, sema.mod.getTarget());2956 const min_int = try operand.ty.minInt(&arena, mod.getTarget());
2848 const max_int = try operand.ty.maxInt(&arena, sema.mod.getTarget());2957 const max_int = try operand.ty.maxInt(&arena, mod.getTarget());
2849 if (try range_set.spans(min_int, max_int)) {2958 if (try range_set.spans(min_int, max_int)) {
2850 if (special_prong == .@"else") {2959 if (special_prong == .@"else") {
2851 return sema.mod.fail(2960 return mod.fail(
2852 &block.base,2961 &block.base,
2853 special_prong_src,2962 special_prong_src,
2854 "unreachable else prong; all cases already handled",2963 "unreachable else prong; all cases already handled",
...@@ -2859,7 +2968,7 @@ fn analyzeSwitch(...@@ -2859,7 +2968,7 @@ fn analyzeSwitch(
2859 }2968 }
2860 }2969 }
2861 if (special_prong != .@"else") {2970 if (special_prong != .@"else") {
2862 return sema.mod.fail(2971 return mod.fail(
2863 &block.base,2972 &block.base,
2864 src,2973 src,
2865 "switch must handle all possibilities",2974 "switch must handle all possibilities",
...@@ -2922,7 +3031,7 @@ fn analyzeSwitch(...@@ -2922,7 +3031,7 @@ fn analyzeSwitch(
2922 switch (special_prong) {3031 switch (special_prong) {
2923 .@"else" => {3032 .@"else" => {
2924 if (true_count + false_count == 2) {3033 if (true_count + false_count == 2) {
2925 return sema.mod.fail(3034 return mod.fail(
2926 &block.base,3035 &block.base,
2927 src,3036 src,
2928 "unreachable else prong; all cases already handled",3037 "unreachable else prong; all cases already handled",
...@@ -2932,7 +3041,7 @@ fn analyzeSwitch(...@@ -2932,7 +3041,7 @@ fn analyzeSwitch(
2932 },3041 },
2933 .under, .none => {3042 .under, .none => {
2934 if (true_count + false_count < 2) {3043 if (true_count + false_count < 2) {
2935 return sema.mod.fail(3044 return mod.fail(
2936 &block.base,3045 &block.base,
2937 src,3046 src,
2938 "switch must handle all possibilities",3047 "switch must handle all possibilities",
...@@ -2944,7 +3053,7 @@ fn analyzeSwitch(...@@ -2944,7 +3053,7 @@ fn analyzeSwitch(
2944 },3053 },
2945 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {3054 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
2946 if (special_prong != .@"else") {3055 if (special_prong != .@"else") {
2947 return sema.mod.fail(3056 return mod.fail(
2948 &block.base,3057 &block.base,
2949 src,3058 src,
2950 "else prong required when switching on type '{}'",3059 "else prong required when switching on type '{}'",
...@@ -3016,7 +3125,7 @@ fn analyzeSwitch(...@@ -3016,7 +3125,7 @@ fn analyzeSwitch(
3016 .AnyFrame,3125 .AnyFrame,
3017 .ComptimeFloat,3126 .ComptimeFloat,
3018 .Float,3127 .Float,
3019 => return sema.mod.fail(&block.base, operand_src, "invalid switch operand type '{}'", .{3128 => return mod.fail(&block.base, operand_src, "invalid switch operand type '{}'", .{
3020 operand.ty,3129 operand.ty,
3021 }),3130 }),
3022 }3131 }
...@@ -3291,7 +3400,7 @@ fn resolveSwitchItemVal(...@@ -3291,7 +3400,7 @@ fn resolveSwitchItemVal(
3291 switch_node_offset: i32,3400 switch_node_offset: i32,
3292 switch_prong_src: AstGen.SwitchProngSrc,3401 switch_prong_src: AstGen.SwitchProngSrc,
3293 range_expand: AstGen.SwitchProngSrc.RangeExpand,3402 range_expand: AstGen.SwitchProngSrc.RangeExpand,
3294) InnerError!Value {3403) InnerError!TypedValue {
3295 const item = try sema.resolveInst(item_ref);3404 const item = try sema.resolveInst(item_ref);
3296 // We have to avoid the other helper functions here because we cannot construct a LazySrcLoc3405 // We have to avoid the other helper functions here because we cannot construct a LazySrcLoc
3297 // because we only have the switch AST node. Only if we know for sure we need to report3406 // because we only have the switch AST node. Only if we know for sure we need to report
...@@ -3301,7 +3410,7 @@ fn resolveSwitchItemVal(...@@ -3301,7 +3410,7 @@ fn resolveSwitchItemVal(
3301 const src = switch_prong_src.resolve(block.src_decl, switch_node_offset, range_expand);3410 const src = switch_prong_src.resolve(block.src_decl, switch_node_offset, range_expand);
3302 return sema.failWithUseOfUndef(block, src);3411 return sema.failWithUseOfUndef(block, src);
3303 }3412 }
3304 return val;3413 return TypedValue{ .ty = item.ty, .val = val };
3305 }3414 }
3306 const src = switch_prong_src.resolve(block.src_decl, switch_node_offset, range_expand);3415 const src = switch_prong_src.resolve(block.src_decl, switch_node_offset, range_expand);
3307 return sema.failWithNeededComptime(block, src);3416 return sema.failWithNeededComptime(block, src);
...@@ -3316,8 +3425,8 @@ fn validateSwitchRange(...@@ -3316,8 +3425,8 @@ fn validateSwitchRange(
3316 src_node_offset: i32,3425 src_node_offset: i32,
3317 switch_prong_src: AstGen.SwitchProngSrc,3426 switch_prong_src: AstGen.SwitchProngSrc,
3318) InnerError!void {3427) InnerError!void {
3319 const first_val = try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first);3428 const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val;
3320 const last_val = try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last);3429 const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val;
3321 const maybe_prev_src = try range_set.add(first_val, last_val, switch_prong_src);3430 const maybe_prev_src = try range_set.add(first_val, last_val, switch_prong_src);
3322 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);3431 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
3323}3432}
...@@ -3330,11 +3439,46 @@ fn validateSwitchItem(...@@ -3330,11 +3439,46 @@ fn validateSwitchItem(
3330 src_node_offset: i32,3439 src_node_offset: i32,
3331 switch_prong_src: AstGen.SwitchProngSrc,3440 switch_prong_src: AstGen.SwitchProngSrc,
3332) InnerError!void {3441) InnerError!void {
3333 const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);3442 const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
3334 const maybe_prev_src = try range_set.add(item_val, item_val, switch_prong_src);3443 const maybe_prev_src = try range_set.add(item_val, item_val, switch_prong_src);
3335 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);3444 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
3336}3445}
33373446
3447fn validateSwitchItemEnum(
3448 sema: *Sema,
3449 block: *Scope.Block,
3450 seen_fields: []?AstGen.SwitchProngSrc,
3451 item_ref: zir.Inst.Ref,
3452 src_node_offset: i32,
3453 switch_prong_src: AstGen.SwitchProngSrc,
3454) InnerError!void {
3455 const mod = sema.mod;
3456 const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
3457 const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val) orelse {
3458 const msg = msg: {
3459 const src = switch_prong_src.resolve(block.src_decl, src_node_offset, .none);
3460 const msg = try mod.errMsg(
3461 &block.base,
3462 src,
3463 "enum '{}' has no tag with value '{}'",
3464 .{ item_tv.ty, item_tv.val },
3465 );
3466 errdefer msg.destroy(sema.gpa);
3467 try mod.errNoteNonLazy(
3468 item_tv.ty.declSrcLoc(),
3469 msg,
3470 "enum declared here",
3471 .{},
3472 );
3473 break :msg msg;
3474 };
3475 return mod.failWithOwnedErrorMsg(&block.base, msg);
3476 };
3477 const maybe_prev_src = seen_fields[field_index];
3478 seen_fields[field_index] = switch_prong_src;
3479 return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
3480}
3481
3338fn validateSwitchDupe(3482fn validateSwitchDupe(
3339 sema: *Sema,3483 sema: *Sema,
3340 block: *Scope.Block,3484 block: *Scope.Block,
...@@ -3343,17 +3487,18 @@ fn validateSwitchDupe(...@@ -3343,17 +3487,18 @@ fn validateSwitchDupe(
3343 src_node_offset: i32,3487 src_node_offset: i32,
3344) InnerError!void {3488) InnerError!void {
3345 const prev_prong_src = maybe_prev_src orelse return;3489 const prev_prong_src = maybe_prev_src orelse return;
3490 const mod = sema.mod;
3346 const src = switch_prong_src.resolve(block.src_decl, src_node_offset, .none);3491 const src = switch_prong_src.resolve(block.src_decl, src_node_offset, .none);
3347 const prev_src = prev_prong_src.resolve(block.src_decl, src_node_offset, .none);3492 const prev_src = prev_prong_src.resolve(block.src_decl, src_node_offset, .none);
3348 const msg = msg: {3493 const msg = msg: {
3349 const msg = try sema.mod.errMsg(3494 const msg = try mod.errMsg(
3350 &block.base,3495 &block.base,
3351 src,3496 src,
3352 "duplicate switch value",3497 "duplicate switch value",
3353 .{},3498 .{},
3354 );3499 );
3355 errdefer msg.destroy(sema.gpa);3500 errdefer msg.destroy(sema.gpa);
3356 try sema.mod.errNote(3501 try mod.errNote(
3357 &block.base,3502 &block.base,
3358 prev_src,3503 prev_src,
3359 msg,3504 msg,
...@@ -3362,7 +3507,7 @@ fn validateSwitchDupe(...@@ -3362,7 +3507,7 @@ fn validateSwitchDupe(
3362 );3507 );
3363 break :msg msg;3508 break :msg msg;
3364 };3509 };
3365 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);3510 return mod.failWithOwnedErrorMsg(&block.base, msg);
3366}3511}
33673512
3368fn validateSwitchItemBool(3513fn validateSwitchItemBool(
...@@ -3374,7 +3519,7 @@ fn validateSwitchItemBool(...@@ -3374,7 +3519,7 @@ fn validateSwitchItemBool(
3374 src_node_offset: i32,3519 src_node_offset: i32,
3375 switch_prong_src: AstGen.SwitchProngSrc,3520 switch_prong_src: AstGen.SwitchProngSrc,
3376) InnerError!void {3521) InnerError!void {
3377 const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);3522 const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
3378 if (item_val.toBool()) {3523 if (item_val.toBool()) {
3379 true_count.* += 1;3524 true_count.* += 1;
3380 } else {3525 } else {
...@@ -3396,7 +3541,7 @@ fn validateSwitchItemSparse(...@@ -3396,7 +3541,7 @@ fn validateSwitchItemSparse(
3396 src_node_offset: i32,3541 src_node_offset: i32,
3397 switch_prong_src: AstGen.SwitchProngSrc,3542 switch_prong_src: AstGen.SwitchProngSrc,
3398) InnerError!void {3543) InnerError!void {
3399 const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);3544 const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
3400 const entry = (try seen_values.fetchPut(item_val, switch_prong_src)) orelse return;3545 const entry = (try seen_values.fetchPut(item_val, switch_prong_src)) orelse return;
3401 return sema.validateSwitchDupe(block, entry.value, switch_prong_src, src_node_offset);3546 return sema.validateSwitchDupe(block, entry.value, switch_prong_src, src_node_offset);
3402}3547}
src/type.zig+64
...@@ -2126,6 +2126,34 @@ pub const Type = extern union {...@@ -2126,6 +2126,34 @@ pub const Type = extern union {
2126 };2126 };
2127 }2127 }
21282128
2129 pub fn enumFieldCount(ty: Type) usize {
2130 switch (ty.tag()) {
2131 .enum_full, .enum_nonexhaustive => {
2132 const enum_full = ty.cast(Payload.EnumFull).?.data;
2133 return enum_full.fields.count();
2134 },
2135 .enum_simple => {
2136 const enum_simple = ty.castTag(.enum_simple).?.data;
2137 return enum_simple.fields.count();
2138 },
2139 else => unreachable,
2140 }
2141 }
2142
2143 pub fn enumFieldName(ty: Type, field_index: usize) []const u8 {
2144 switch (ty.tag()) {
2145 .enum_full, .enum_nonexhaustive => {
2146 const enum_full = ty.cast(Payload.EnumFull).?.data;
2147 return enum_full.fields.entries.items[field_index].key;
2148 },
2149 .enum_simple => {
2150 const enum_simple = ty.castTag(.enum_simple).?.data;
2151 return enum_simple.fields.entries.items[field_index].key;
2152 },
2153 else => unreachable,
2154 }
2155 }
2156
2129 pub fn enumFieldIndex(ty: Type, field_name: []const u8) ?usize {2157 pub fn enumFieldIndex(ty: Type, field_name: []const u8) ?usize {
2130 switch (ty.tag()) {2158 switch (ty.tag()) {
2131 .enum_full, .enum_nonexhaustive => {2159 .enum_full, .enum_nonexhaustive => {
...@@ -2140,6 +2168,42 @@ pub const Type = extern union {...@@ -2140,6 +2168,42 @@ pub const Type = extern union {
2140 }2168 }
2141 }2169 }
21422170
2171 /// Asserts `ty` is an enum. `enum_tag` can either be `enum_field_index` or
2172 /// an integer which represents the enum value. Returns the field index in
2173 /// declaration order, or `null` if `enum_tag` does not match any field.
2174 pub fn enumTagFieldIndex(ty: Type, enum_tag: Value) ?usize {
2175 if (enum_tag.castTag(.enum_field_index)) |payload| {
2176 return @as(usize, payload.data);
2177 }
2178 const S = struct {
2179 fn fieldWithRange(int_val: Value, end: usize) ?usize {
2180 if (int_val.compareWithZero(.lt)) return null;
2181 var end_payload: Value.Payload.U64 = .{
2182 .base = .{ .tag = .int_u64 },
2183 .data = end,
2184 };
2185 const end_val = Value.initPayload(&end_payload.base);
2186 if (int_val.compare(.gte, end_val)) return null;
2187 return int_val.toUnsignedInt();
2188 }
2189 };
2190 switch (ty.tag()) {
2191 .enum_full, .enum_nonexhaustive => {
2192 const enum_full = ty.cast(Payload.EnumFull).?.data;
2193 if (enum_full.values.count() == 0) {
2194 return S.fieldWithRange(enum_tag, enum_full.fields.count());
2195 } else {
2196 return enum_full.values.getIndex(enum_tag);
2197 }
2198 },
2199 .enum_simple => {
2200 const enum_simple = ty.castTag(.enum_simple).?.data;
2201 return S.fieldWithRange(enum_tag, enum_simple.fields.count());
2202 },
2203 else => unreachable,
2204 }
2205 }
2206
2143 pub fn declSrcLoc(ty: Type) Module.SrcLoc {2207 pub fn declSrcLoc(ty: Type) Module.SrcLoc {
2144 switch (ty.tag()) {2208 switch (ty.tag()) {
2145 .enum_full, .enum_nonexhaustive => {2209 .enum_full, .enum_nonexhaustive => {
test/stage2/cbe.zig+5-1
...@@ -552,7 +552,11 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -552,7 +552,11 @@ pub fn addCases(ctx: *TestContext) !void {
552 \\ if (@enumToInt(number3) != 2) return 1;552 \\ if (@enumToInt(number3) != 2) return 1;
553 \\ var x: Number = .Two;553 \\ var x: Number = .Two;
554 \\ if (number2 != x) return 1;554 \\ if (number2 != x) return 1;
555 \\ return 0;555 \\ switch (x) {
556 \\ .One => return 1,
557 \\ .Two => return 0,
558 \\ number3 => return 2,
559 \\ }
556 \\}560 \\}
557 , "");561 , "");
558 }562 }