authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2019-09-23 11:14:36+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-09-23 16:13:28-04:00
logdb988f42a7ac1ce0ec65fde0e7ea94009b1d7238
tree540a7ffaffbb430b9b8e440c06f9ebbcb5fef06e
parent53ae03ebe9e0aa56bca1c9219a47d124b0435258

Fix computation of switch coverage

Closes #3258

3 files changed, 63 insertions(+), 49 deletions(-)

src/range_set.cpp+41-49
...@@ -4,8 +4,12 @@ AstNode *rangeset_add_range(RangeSet *rs, BigInt *first, BigInt *last, AstNode *...@@ -4,8 +4,12 @@ AstNode *rangeset_add_range(RangeSet *rs, BigInt *first, BigInt *last, AstNode *
4 for (size_t i = 0; i < rs->src_range_list.length; i += 1) {4 for (size_t i = 0; i < rs->src_range_list.length; i += 1) {
5 RangeWithSrc *range_with_src = &rs->src_range_list.at(i);5 RangeWithSrc *range_with_src = &rs->src_range_list.at(i);
6 Range *range = &range_with_src->range;6 Range *range = &range_with_src->range;
7 if ((bigint_cmp(first, &range->first) != CmpLT && bigint_cmp(first, &range->last) != CmpGT) ||7 if ((bigint_cmp(first, &range->first) == CmpLT && bigint_cmp(last, &range->first) == CmpLT) ||
8 (bigint_cmp(last, &range->first) != CmpLT && bigint_cmp(last, &range->last) != CmpGT))8 (bigint_cmp(first, &range->last) == CmpGT && bigint_cmp(last, &range->last) == CmpGT))
9 {
10 // first...last is completely before/after `range`
11 }
12 else
9 {13 {
10 return range_with_src->source_node;14 return range_with_src->source_node;
11 }15 }
...@@ -16,64 +20,52 @@ AstNode *rangeset_add_range(RangeSet *rs, BigInt *first, BigInt *last, AstNode *...@@ -16,64 +20,52 @@ AstNode *rangeset_add_range(RangeSet *rs, BigInt *first, BigInt *last, AstNode *
1620
17}21}
1822
19static bool add_range(ZigList<Range> *list, Range *new_range, BigInt *one) {23static int compare_rangeset(const void *a, const void *b) {
20 for (size_t i = 0; i < list->length; i += 1) {24 const Range *r1 = &static_cast<const RangeWithSrc*>(a)->range;
21 Range *range = &list->at(i);25 const Range *r2 = &static_cast<const RangeWithSrc*>(b)->range;
2226 // Assume no two ranges overlap
23 BigInt first_minus_one;27 switch (bigint_cmp(&r1->first, &r2->first)) {
24 bigint_sub(&first_minus_one, &range->first, one);28 case CmpLT: return -1;
2529 case CmpGT: return 1;
26 if (bigint_cmp(&new_range->last, &first_minus_one) == CmpEQ) {30 case CmpEQ: return 0;
27 range->first = new_range->first;31 }
28 return true;32 zig_unreachable();
29 }33}
30
31 BigInt last_plus_one;
32 bigint_add(&last_plus_one, &range->last, one);
3334
34 if (bigint_cmp(&new_range->first, &last_plus_one) == CmpEQ) {35void rangeset_sort(RangeSet *rs) {
35 range->last = new_range->last;36 if (rs->src_range_list.length > 1) {
36 return true;37 qsort(rs->src_range_list.items, rs->src_range_list.length,
37 }38 sizeof(RangeWithSrc), compare_rangeset);
38 }39 }
39 list->append({new_range->first, new_range->last});
40 return false;
41}40}
4241
43bool rangeset_spans(RangeSet *rs, BigInt *first, BigInt *last) {42bool rangeset_spans(RangeSet *rs, BigInt *first, BigInt *last) {
44 ZigList<Range> cur_list_value = {0};43 rangeset_sort(rs);
45 ZigList<Range> other_list_value = {0};
46 ZigList<Range> *cur_list = &cur_list_value;
47 ZigList<Range> *other_list = &other_list_value;
4844
49 for (size_t i = 0; i < rs->src_range_list.length; i += 1) {45 const Range *first_range = &rs->src_range_list.at(0).range;
50 RangeWithSrc *range_with_src = &rs->src_range_list.at(i);46 if (bigint_cmp(&first_range->first, first) != CmpEQ)
51 Range *range = &range_with_src->range;47 return false;
52 cur_list->append({range->first, range->last});48
53 }49 const Range *last_range = &rs->src_range_list.last().range;
50 if (bigint_cmp(&last_range->last, last) != CmpEQ)
51 return false;
5452
55 BigInt one;53 BigInt one;
56 bigint_init_unsigned(&one, 1);54 bigint_init_unsigned(&one, 1);
5755
58 bool changes_made = true;56 // Make sure there are no holes in the first...last range
59 while (changes_made) {57 for (size_t i = 1; i < rs->src_range_list.length; i++) {
60 changes_made = false;58 const Range *range = &rs->src_range_list.at(i).range;
61 for (size_t cur_i = 0; cur_i < cur_list->length; cur_i += 1) {59 const Range *prev_range = &rs->src_range_list.at(i - 1).range;
62 Range *range = &cur_list->at(cur_i);60
63 changes_made = add_range(other_list, range, &one) || changes_made;61 assert(bigint_cmp(&prev_range->last, &range->first) == CmpLT);
64 }62
65 ZigList<Range> *tmp = cur_list;63 BigInt last_plus_one;
66 cur_list = other_list;64 bigint_add(&last_plus_one, &prev_range->last, &one);
67 other_list = tmp;65
68 other_list->resize(0);66 if (bigint_cmp(&last_plus_one, &range->first) != CmpEQ)
67 return false;
69 }68 }
7069
71 if (cur_list->length != 1)
72 return false;
73 Range *range = &cur_list->at(0);
74 if (bigint_cmp(&range->first, first) != CmpEQ)
75 return false;
76 if (bigint_cmp(&range->last, last) != CmpEQ)
77 return false;
78 return true;70 return true;
79}71}
test/compile_errors.zig+13
...@@ -2,6 +2,19 @@ const tests = @import("tests.zig");...@@ -2,6 +2,19 @@ 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.add(
6 "switch with overlapping case ranges",
7 \\export fn entry() void {
8 \\ var q: u8 = 0;
9 \\ switch (q) {
10 \\ 1...2 => {},
11 \\ 0...255 => {},
12 \\ }
13 \\}
14 ,
15 "tmp.zig:5:9: error: duplicate switch value",
16 );
17
5 cases.add(18 cases.add(
6 "attempt to negate a non-integer, non-float or non-vector type",19 "attempt to negate a non-integer, non-float or non-vector type",
7 \\fn foo() anyerror!u32 {20 \\fn foo() anyerror!u32 {
test/stage1/behavior/switch.zig+9
...@@ -425,3 +425,12 @@ test "switch prongs with cases with identical payload types" {...@@ -425,3 +425,12 @@ test "switch prongs with cases with identical payload types" {
425 S.doTheTest();425 S.doTheTest();
426 comptime S.doTheTest();426 comptime S.doTheTest();
427}427}
428
429test "switch with disjoint range" {
430 var q: u8 = 0;
431 switch (q) {
432 0...125 => {},
433 127...255 => {},
434 126...126 => {},
435 }
436}