diff --git a/src/ir.cpp b/src/ir.cpp index 44df69aeed..8395cf0a3e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -28700,6 +28700,10 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, buf_ptr(enum_field->name))); } } + } else if(!switch_type->data.enumeration.non_exhaustive && switch_type->data.enumeration.src_field_count == instruction->range_count) { + ir_add_error_node(ira, instruction->else_prong, + buf_sprintf("unreachable else prong, all cases already handled")); + return ira->codegen->invalid_inst_gen; } } else if (switch_type->id == ZigTypeIdErrorSet) { if (!resolve_inferred_error_set(ira->codegen, switch_type, target_value->base.source_node)) { @@ -28808,16 +28812,20 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, return ira->codegen->invalid_inst_gen; } } - if (instruction->else_prong == nullptr) { + BigInt min_val; eval_min_max_value_int(ira->codegen, switch_type, &min_val, false); BigInt max_val; eval_min_max_value_int(ira->codegen, switch_type, &max_val, true); - if (!rangeset_spans(&rs, &min_val, &max_val)) { + bool handles_all_cases = rangeset_spans(&rs, &min_val, &max_val); + if (!handles_all_cases && instruction->else_prong == nullptr) { ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities")); return ira->codegen->invalid_inst_gen; + } else if(handles_all_cases && instruction->else_prong != nullptr) { + ir_add_error_node(ira, instruction->else_prong, + buf_sprintf("unreachable else prong, all cases already handled")); + return ira->codegen->invalid_inst_gen; } - } } else if (switch_type->id == ZigTypeIdBool) { int seenTrue = 0; int seenFalse = 0; @@ -28851,6 +28859,12 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities")); return ira->codegen->invalid_inst_gen; } + + if(seenTrue == 1 && seenFalse == 1 && instruction->else_prong != nullptr) { + ir_add_error_node(ira, instruction->else_prong, + buf_sprintf("unreachable else prong, all cases already handled")); + return ira->codegen->invalid_inst_gen; + } } else if (instruction->else_prong == nullptr) { ir_add_error(ira, &instruction->base.base, buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name))); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 3f898cc337..29ff994cd3 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -468,6 +468,102 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:12:5: error: switch on non-exhaustive enum must include `else` or `_` prong", }); + cases.add("switch expression - unreachable else prong (bool)", + \\fn foo(x: bool) void { + \\ switch (x) { + \\ true => {}, + \\ false => {}, + \\ else => {}, + \\ } + \\} + \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + , &[_][]const u8{ + "tmp.zig:5:9: error: unreachable else prong, all cases already handled", + }); + + cases.add("switch expression - unreachable else prong (u1)", + \\fn foo(x: u1) void { + \\ switch (x) { + \\ 0 => {}, + \\ 1 => {}, + \\ else => {}, + \\ } + \\} + \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + , &[_][]const u8{ + "tmp.zig:5:9: error: unreachable else prong, all cases already handled", + }); + + cases.add("switch expression - unreachable else prong (u2)", + \\fn foo(x: u2) void { + \\ switch (x) { + \\ 0 => {}, + \\ 1 => {}, + \\ 2 => {}, + \\ 3 => {}, + \\ else => {}, + \\ } + \\} + \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + , &[_][]const u8{ + "tmp.zig:7:9: error: unreachable else prong, all cases already handled", + }); + + cases.add("switch expression - unreachable else prong (range u8)", + \\fn foo(x: u8) void { + \\ switch (x) { + \\ 0 => {}, + \\ 1 => {}, + \\ 2 => {}, + \\ 3 => {}, + \\ 4...255 => {}, + \\ else => {}, + \\ } + \\} + \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + , &[_][]const u8{ + "tmp.zig:8:9: error: unreachable else prong, all cases already handled", + }); + + cases.add("switch expression - unreachable else prong (range i8)", + \\fn foo(x: i8) void { + \\ switch (x) { + \\ -128...0 => {}, + \\ 1 => {}, + \\ 2 => {}, + \\ 3 => {}, + \\ 4...127 => {}, + \\ else => {}, + \\ } + \\} + \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + , &[_][]const u8{ + "tmp.zig:8:9: error: unreachable else prong, all cases already handled", + }); + + cases.add("switch expression - unreachable else prong (enum)", + \\const TestEnum = enum{ T1, T2 }; + \\ + \\fn err(x: u8) TestEnum { + \\ switch (x) { + \\ 0 => return TestEnum.T1, + \\ else => return TestEnum.T2, + \\ } + \\} + \\ + \\fn foo(x: u8) void { + \\ switch (err(x)) { + \\ TestEnum.T1 => {}, + \\ TestEnum.T2 => {}, + \\ else => {}, + \\ } + \\} + \\ + \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + , &[_][]const u8{ + "tmp.zig:14:9: error: unreachable else prong, all cases already handled", + }); + cases.addTest("@export with empty name string", \\pub export fn entry() void { } \\comptime {