Return insertion point from List::binarySearch() misses#10048
Return insertion point from List::binarySearch() misses#10048cmarcelo wants to merge 2 commits intoshader-slang:masterfrom
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthrough
Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Comment |
There was a problem hiding this comment.
Pull request overview
Updates List::binarySearch() to return a useful insertion-point encoding on misses (bitwise negation of insertion index), aligns an existing caller with the new semantics, and adds coverage to prevent regressions.
Changes:
- Change
List::binarySearch()miss behavior from always-1to~insertionIndex. - Update language-server formatting logic to treat “found” as
>= 0instead of!= -1. - Add unit tests that validate both “found” indices and insertion-point results for default and custom comparers.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
source/core/slang-list.h |
Implements the new “miss returns ~insertionIndex” binarySearch() contract. |
source/slang/slang-language-server-auto-format.cpp |
Fixes a caller that previously assumed “not found” meant -1. |
tools/slang-unit-test/unit-test-list.cpp |
Adds regression tests covering found/miss behavior and insertion usage. |
source/slang/slang-check-decl.cpp |
Uses _compareDeclsInCommonParentByOrderOfDeclaration() for stable decl ordering in the updated area. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
source/slang/slang-check-decl.cpp (1)
12293-12379:⚠️ Potential issue | 🟠 MajorGuard against ancestor/descendant inputs before asserting parentage.
The early return path in
findDeclsLowestCommonAncestor()(line 12277) modifies one of its by-reference parameters without ensuring both parameters remain direct children of the returned ancestor. Specifically, when one declaration is a direct child of another, the function returns the grandparent but leaves one parameter pointing to the child—violating the precondition that bothlhsandrhsare direct children ofcommonParent. This trips the assertions at lines 12303–12304, or causesUNREACHABLEat line 12378.Add a guard after the LCA call to handle cases where the result fails this precondition:
Defensive guard
static int _compareDeclsInCommonParentByOrderOfDeclaration( ContainerDecl* commonParent, Decl* lhs, Decl* rhs) { if (lhs == rhs) return 0; SLANG_ASSERT(commonParent); SLANG_ASSERT(lhs); SLANG_ASSERT(rhs); - SLANG_ASSERT(lhs->parentDecl == commonParent); - SLANG_ASSERT(rhs->parentDecl == commonParent); + if (lhs->parentDecl != commonParent || rhs->parentDecl != commonParent) + { + // One decl is an ancestor of the other or not directly under commonParent. + // Fall back to deterministic comparison by depth and name. + auto depthToParent = [&](Decl* d) + { + Index depth = 0; + while (d && d->parentDecl && d->parentDecl != commonParent) + { + d = d->parentDecl; + ++depth; + } + return depth; + }; + int res = compareThreeWays(depthToParent(lhs), depthToParent(rhs)); + if (res) + return res; + return comparePtrs( + lhs->getName(), + rhs->getName(), + [](Name const& lName, Name const& rName) + { return strcmp(lName.text.begin(), rName.text.begin()); }); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@source/slang/slang-check-decl.cpp` around lines 12293 - 12379, findDeclsLowestCommonAncestor can return a commonParent while one of the by-ref parameters (lhs/rhs) still refers to a descendant rather than a direct child, which breaks the precondition in _compareDeclsInCommonParentByOrderOfDeclaration that both have parentDecl == commonParent; after calling findDeclsLowestCommonAncestor, defend by walking any descendant argument up to its immediate child of commonParent (i.e. while(arg->parentDecl != commonParent) arg = arg->parentDecl;) or otherwise normalize lhs/rhs so that lhs->parentDecl and rhs->parentDecl match commonParent before asserting/entering _compareDeclsInCommonParentByOrderOfDeclaration; ensure you also guard against null and infinite loops by checking parentDecl is non-null.
🤖 Fix all issues with AI agents
Verify each finding against the current code and only fix it if needed.
In `@source/slang/slang-check-decl.cpp`:
- Around line 12293-12379: findDeclsLowestCommonAncestor can return a
commonParent while one of the by-ref parameters (lhs/rhs) still refers to a
descendant rather than a direct child, which breaks the precondition in
_compareDeclsInCommonParentByOrderOfDeclaration that both have parentDecl ==
commonParent; after calling findDeclsLowestCommonAncestor, defend by walking any
descendant argument up to its immediate child of commonParent (i.e.
while(arg->parentDecl != commonParent) arg = arg->parentDecl;) or otherwise
normalize lhs/rhs so that lhs->parentDecl and rhs->parentDecl match commonParent
before asserting/entering _compareDeclsInCommonParentByOrderOfDeclaration;
ensure you also guard against null and infinite loops by checking parentDecl is
non-null.
a0f96e6 to
fa99047
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@source/slang/slang-check-decl.cpp`:
- Around line 3581-3582: The code computes subIndex and supIndex via
ancestor->getMembers().indexOf(subAncestor/supAncestor) but doesn’t handle
indexOf returning -1; if either is <0 the subtraction yields bogus ordering and
triggers the canonical order diagnostic incorrectly. Update the logic around the
indexOf calls (variables subIndex and supIndex) to assert presence or,
preferably, check for <0 and in that case fall back to calling compareDecls(…)
(or otherwise skip the ordering subtraction) so you only use the index
subtraction when both indices are >= 0; ensure the canonical-order diagnostic
path uses the fallback when a member isn’t found.
In `@tools/slang-unit-test/unit-test-list.cpp`:
- Around line 8-89: Add explicit edge-case tests in the SLANG_UNIT_TEST(list)
block to cover empty-list, single-element, and duplicate-value behavior: call
List<T>::binarySearch on an empty List<int> and assert it returns ~0 using
SLANG_CHECK; create a single-element List<int> and verify binarySearch finds the
existing value (>=0) and returns ~0 for a missing value, then insert and
re-check; create a List<int> with duplicate values and verify binarySearch finds
an index within the duplicates and that insert/insertIndex semantics (using
values.insert and ~searchResult) keep the list sorted. Reference the existing
List, binarySearch, insert, and SLANG_CHECK usages to mirror style and
assertions.
| auto subIndex = ancestor->getMembers().indexOf(subAncestor); | ||
| auto supIndex = ancestor->getMembers().indexOf(supAncestor); |
There was a problem hiding this comment.
Guard against indexOf misses before subtracting.
indexOf returns -1 if either ancestor isn’t present in the member list; that collapses ordering (or yields a bogus diff) and can trigger the “canonical order” diagnostic incorrectly. Consider asserting presence or falling back to compareDecls when either index is < 0.
💡 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
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 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); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@source/slang/slang-check-decl.cpp` around lines 3581 - 3582, The code
computes subIndex and supIndex via
ancestor->getMembers().indexOf(subAncestor/supAncestor) but doesn’t handle
indexOf returning -1; if either is <0 the subtraction yields bogus ordering and
triggers the canonical order diagnostic incorrectly. Update the logic around the
indexOf calls (variables subIndex and supIndex) to assert presence or,
preferably, check for <0 and in that case fall back to calling compareDecls(…)
(or otherwise skip the ordering subtraction) so you only use the index
subtraction when both indices are >= 0; ensure the canonical-order diagnostic
path uses the fallback when a member isn’t found.
fa99047 to
f169b79
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tools/slang-unit-test/unit-test-list.cpp`:
- Around line 119-122: The inline comment is wrong: it says "Insert another 20"
but the test actually inserts 15; update the comment to accurately describe the
operation (e.g., "Insert another 15 using the standard insert pattern") so
readers match the intent to the code around Index searchResult =
values.binarySearch(15); and values.insert(~searchResult, 15);.
09e78cc to
dd7bfae
Compare
The binarySearch() was comparing pointer addresses on the member list that is ordered by the declaration position. That may not necessarily work unless is guaranteed that the allocator produces pointers in declaration order and they are not reordered. Use indexOf() instead. See bfae49d ("Mediate access to ContainerDecl members (shader-slang#7242)") for context.
Change binarySearch() to return the bitwise negation of the insertion index when the element is not found, instead of always returning -1. Update call-site that was assuming -1 as the error value.
dd7bfae to
273966b
Compare
Change binarySearch() to return the bitwise negation of the insertion
index when the element is not found, instead of always returning -1.
Update call-site that was assuming -1 as the error value.
On top of #10047
Compiler Impact
This PR updates an internal container utility and its call sites; it affects internal semantic checks and language-server formatting logic but does not change lexer, parser, IR, or codegen.
Compiler stages affected:
source/slang/slang-check-decl.cppupdated to use the newList::binarySearch()semantics (some call-sites switched toindexOfor otherwise adjusted) when computing declaration/member indices for checks (e.g., generic constraint comparisons).source/slang/slang-language-server-auto-format.cppupdated to treat non-foundbinarySearchresults correctly (changed check from!= -1to>= 0).Target backends impacted:
Public API headers:
Changes Summary
source/core/slang-list.hList::binarySearch()now returns the bitwise negation of the insertion index (~imin) when an element is not found, instead of-1. Comments updated to document the behavior.source/slang/slang-check-decl.cppindexOfor updated to handle the new return convention) so computed indices/differences remain correct.source/slang/slang-language-server-auto-format.cppexclusionRangeId != -1toexclusionRangeId >= 0to correctly detect matches under the new convention.tools/slang-unit-test/unit-test-list.cppList::binarySearch()for found results and insertion-point semantics (including custom comparers, empty/single/duplicate-element cases) and verifying insertion behavior using the~resultpattern.Notes