-
Notifications
You must be signed in to change notification settings - Fork 437
Return insertion point from List::binarySearch() misses #10048
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -576,10 +576,8 @@ class List | |||||||||
| else | ||||||||||
| imax = imid - 1; | ||||||||||
| } | ||||||||||
| // TODO: The return value on a failed search should be | ||||||||||
| // the bitwise negation of the index where `obj` should | ||||||||||
| // be inserted to be in the proper sorted location. | ||||||||||
| return -1; | ||||||||||
| // If not found, return the bitwise negation of the insertion index. | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: The inline comment is accurate but minimal for a contract-breaking change to a core utility. Consider adding a doc comment above the method signature documenting the full return value semantics, since the bitwise complement convention is non-obvious to anyone who hasn't seen it in Java/C#:
Suggested change
Comment on lines
578
to
+579
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Consider expanding the comment slightly to document the full return-value contract at the method level, since callers need to know how to interpret both success and failure cases. Something like: This is minor — the existing comment is accurate, and the unit tests serve as living documentation for the convention. |
||||||||||
| return ~imin; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| template<typename T2> | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3689,8 +3689,8 @@ void SemanticsDeclHeaderVisitor::checkGenericTypeEqualityConstraintSubType( | |||||||||||||||||
| return compareDecls(subAncestor, supAncestor); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| auto subIndex = ancestor->getMembers().binarySearch(subAncestor); | ||||||||||||||||||
| auto supIndex = ancestor->getMembers().binarySearch(supAncestor); | ||||||||||||||||||
| auto subIndex = ancestor->getMembers().indexOf(subAncestor); | ||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good fix. This was a latent bug: Switching to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. The old code was using
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good fix. The original code was using |
||||||||||||||||||
| auto supIndex = ancestor->getMembers().indexOf(supAncestor); | ||||||||||||||||||
|
Comment on lines
+3692
to
+3693
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard against
💡 Suggested defensive fallback- auto subIndex = ancestor->getMembers().indexOf(subAncestor);
- auto supIndex = ancestor->getMembers().indexOf(supAncestor);
+ auto subIndex = ancestor->getMembers().indexOf(subAncestor);
+ auto supIndex = ancestor->getMembers().indexOf(supAncestor);
+ if (subIndex < 0 || supIndex < 0)
+ {
+ return compareDecls(subAncestor, supAncestor);
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
|
|
||||||||||||||||||
| return int(supIndex - subIndex); | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -332,7 +332,7 @@ List<Edit> formatSource( | |
| return 1; | ||
| return 0; | ||
| }); | ||
| if (exclusionRangeId != -1) | ||
| if (exclusionRangeId >= 0) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. Under the old semantics
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct update. With the new |
||
| continue; | ||
| pos += lengthStr.getLength(); | ||
| if (pos < line.getLength() && line[pos] == '\'') | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| // unit-test-list.cpp | ||
|
|
||
| #include "core/slang-basic.h" | ||
| #include "unit-test/slang-unit-test.h" | ||
|
|
||
| using namespace Slang; | ||
|
|
||
| namespace | ||
| { | ||
| template<typename T, typename GetValueFunc> | ||
| void checkListIsSortedBy(const List<T>& list, const GetValueFunc& getValue) | ||
| { | ||
| for (Index i = 1; i < list.getCount(); i++) | ||
| SLANG_CHECK(getValue(list[i - 1]) <= getValue(list[i])); | ||
| } | ||
|
|
||
| template<typename T> | ||
| void checkListIsSorted(const List<T>& list) | ||
| { | ||
| checkListIsSortedBy(list, [](const T& value) { return value; }); | ||
| } | ||
| } // namespace | ||
|
|
||
| SLANG_UNIT_TEST(list) | ||
| { | ||
| { | ||
| List<int> values = {10, 20, 30, 40}; | ||
|
|
||
| SLANG_CHECK(values.binarySearch(5) == ~0); | ||
| SLANG_CHECK(values.binarySearch(10) == 0); | ||
| SLANG_CHECK(values.binarySearch(25) == ~2); | ||
| SLANG_CHECK(values.binarySearch(30) == 2); | ||
| SLANG_CHECK(values.binarySearch(50) == ~4); | ||
|
|
||
| auto insertAndCheck = [&](int queryValue, Index expectedInsertIndex) | ||
| { | ||
| Index searchResult = values.binarySearch(queryValue); | ||
| SLANG_CHECK(searchResult == ~expectedInsertIndex); | ||
|
|
||
| Index insertIndex = ~searchResult; | ||
| values.insert(insertIndex, queryValue); | ||
|
|
||
| checkListIsSorted(values); | ||
|
|
||
| Index foundIndex = values.binarySearch(queryValue); | ||
| SLANG_CHECK(foundIndex >= 0); | ||
| SLANG_CHECK(values[foundIndex] == queryValue); | ||
| }; | ||
|
|
||
| insertAndCheck(5, 0); | ||
| insertAndCheck(25, 3); | ||
| insertAndCheck(50, 6); | ||
| } | ||
|
|
||
| { | ||
| struct Entry | ||
| { | ||
| int key; | ||
| }; | ||
|
|
||
| List<Entry> entries; | ||
| entries.add(Entry{1}); | ||
| entries.add(Entry{4}); | ||
| entries.add(Entry{9}); | ||
|
|
||
| auto comparer = [](const Entry& entry, int key) | ||
| { | ||
| if (entry.key < key) | ||
| return -1; | ||
| if (entry.key > key) | ||
| return 1; | ||
| return 0; | ||
| }; | ||
|
|
||
| auto insertAndCheck = [&](int queryKey, Index expectedInsertIndex) | ||
| { | ||
| Index searchResult = entries.binarySearch(queryKey, comparer); | ||
| SLANG_CHECK(searchResult == ~expectedInsertIndex); | ||
|
|
||
| Index insertIndex = ~searchResult; | ||
| entries.insert(insertIndex, Entry{queryKey}); | ||
|
|
||
| checkListIsSortedBy(entries, [](const Entry& entry) { return entry.key; }); | ||
|
|
||
| Index foundIndex = entries.binarySearch(queryKey, comparer); | ||
| SLANG_CHECK(foundIndex >= 0); | ||
| SLANG_CHECK(entries[foundIndex].key == queryKey); | ||
| }; | ||
|
|
||
| SLANG_CHECK(entries.binarySearch(0, comparer) == ~0); | ||
| SLANG_CHECK(entries.binarySearch(4, comparer) == 1); | ||
| SLANG_CHECK(entries.binarySearch(6, comparer) == ~2); | ||
| SLANG_CHECK(entries.binarySearch(10, comparer) == ~3); | ||
|
|
||
| insertAndCheck(0, 0); | ||
| insertAndCheck(6, 3); | ||
| insertAndCheck(10, 5); | ||
| } | ||
|
|
||
| // Empty list | ||
| { | ||
| List<int> values; | ||
| SLANG_CHECK(values.binarySearch(42) == ~0); | ||
| } | ||
|
|
||
| // Single element | ||
| { | ||
| List<int> values = {10}; | ||
| SLANG_CHECK(values.binarySearch(10) >= 0); | ||
| SLANG_CHECK(values.binarySearch(5) == ~0); | ||
| SLANG_CHECK(values.binarySearch(15) == ~1); | ||
|
|
||
| Index searchResult = values.binarySearch(5); | ||
| values.insert(~searchResult, 5); | ||
| SLANG_CHECK(values.getCount() == 2); | ||
| checkListIsSorted(values); | ||
| SLANG_CHECK(values[0] == 5); | ||
| SLANG_CHECK(values[1] == 10); | ||
| SLANG_CHECK(values.binarySearch(5) >= 0); | ||
| } | ||
|
|
||
| // Duplicate values | ||
| { | ||
| List<int> values = {10, 20, 20, 30}; | ||
|
|
||
| Index foundIndex = values.binarySearch(20); | ||
| SLANG_CHECK(foundIndex >= 0); | ||
| SLANG_CHECK(values[foundIndex] == 20); | ||
|
|
||
| Index searchResult = values.binarySearch(15); | ||
| SLANG_CHECK(searchResult < 0); | ||
| values.insert(~searchResult, 15); | ||
|
|
||
| checkListIsSorted(values); | ||
|
|
||
| SLANG_CHECK(values.binarySearch(15) >= 0); | ||
| } | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
Comment on lines
+1
to
+138
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Excellent test coverage. The tests exercise:
The |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Consider adding a brief doc comment above the method signature to document the return-value contract for future callers unfamiliar with the bitwise complement convention. Something like: