-
Notifications
You must be signed in to change notification settings - Fork 436
Fix fragile List::binarySearch() usage #10047
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 | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3715,8 +3715,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); | ||||||||||||||||
| auto supIndex = ancestor->getMembers().indexOf(supAncestor); | ||||||||||||||||
|
Comment on lines
+3718
to
+3719
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. 🧩 Analysis chain🏁 Script executed: # First, locate and examine the List class definition and indexOf method
rg -B2 -A10 'indexOf\(' --type cpp --type hpp | head -100Repository: shader-slang/slang Length of output: 89 🏁 Script executed: # Check Index type definition
rg 'typedef.*Index|using.*Index' --type hpp | head -30Repository: shader-slang/slang Length of output: 89 🏁 Script executed: # Examine the context at lines 3581-3582
sed -n '3570,3600p' source/slang/slang-check-decl.cppRepository: shader-slang/slang Length of output: 1031 🏁 Script executed: # Search for GenericDecl definition and its member structure
rg -B2 -A15 'class GenericDecl' --type hpp | head -80Repository: shader-slang/slang Length of output: 89 🏁 Script executed: # Check ContainerDecl::getMembers implementation
rg -B2 -A10 'getMembers\(\)' --type hpp --type cpp | head -100Repository: shader-slang/slang Length of output: 89 🏁 Script executed: # Search for List class and indexOf method definition
rg 'indexOf' -A5 -B2 --type c --type h | head -150Repository: shader-slang/slang Length of output: 7585 🏁 Script executed: # Check Index type definition
rg 'typedef.*Index|using.*Index' --type h | head -50Repository: shader-slang/slang Length of output: 3685 🏁 Script executed: # Search for GenericDecl class definition
rg -A20 'class GenericDecl' --type h | head -100Repository: shader-slang/slang Length of output: 5103 🏁 Script executed: # Check ContainerDecl implementation
rg -A10 'class ContainerDecl' --type h | head -150Repository: shader-slang/slang Length of output: 2891 🏁 Script executed: # Search for getMembers in headers
rg -B2 -A8 'getMembers\(' --type h --type c | head -100Repository: shader-slang/slang Length of output: 1361 🏁 Script executed: # Get complete indexOf implementation from slang-list.h
sed -n '1,/template.*indexOf/p' source/core/slang-list.h | tail -40Repository: shader-slang/slang Length of output: 891 🏁 Script executed: # Find the full indexOf method with return statement
rg -B2 -A15 'template.*indexOf\(const T2' source/core/slang-list.hRepository: shader-slang/slang Length of output: 44 🏁 Script executed: # Search for getDirectMemberDecls implementation
rg -B2 -A15 'getDirectMemberDecls' --type c --type h | head -150Repository: shader-slang/slang Length of output: 9271 🏁 Script executed: # Check if FIDDLE() marked fields are included in member lists
rg -B5 -A10 'FIDDLE.*inner' source/slang/slang-ast-decl.hRepository: shader-slang/slang Length of output: 532 🏁 Script executed: # Look for how GenericDecl members are enumerated
rg -B10 -A10 'class GenericDecl' source/slang/slang-ast-decl.hRepository: shader-slang/slang Length of output: 602 🏁 Script executed: # Find getDirectMemberDecls implementation in cpp files
rg -B2 -A20 'getDirectMemberDecls\(\)' --type c | head -100Repository: shader-slang/slang Length of output: 6349 🏁 Script executed: # Search for how GenericDecl members are handled in the codebase
rg 'GenericDecl.*inner' --type c --type h | head -50Repository: shader-slang/slang Length of output: 44 🏁 Script executed: # Find the complete indexOf implementation with return statement
rg -B2 -A8 'Index indexOf' source/core/slang-list.hRepository: shader-slang/slang Length of output: 284 🏁 Script executed: # Search for getDirectMemberDecls implementation in cpp
rg -B3 -A15 'List<Decl\*> const& ContainerDecl::getDirectMemberDecls' --type cRepository: shader-slang/slang Length of output: 44 🏁 Script executed: # Also check slang-ast-decl.cpp for implementation
grep -n 'getDirectMemberDecls' source/slang/slang-ast-decl.cpp | head -20Repository: shader-slang/slang Length of output: 196 🏁 Script executed: # Look at what fields are included for GenericDecl member enumeration
rg -B5 -A20 'class GenericDecl' source/slang/slang-ast-decl.cppRepository: shader-slang/slang Length of output: 44 🏁 Script executed: # Check how FIDDLE macro works - does it include fields in member lists?
rg -B2 -A5 'FIDDLE\(\)' source/slang/slang-ast-decl.h | grep -A5 'GenericDecl'Repository: shader-slang/slang Length of output: 385 🏁 Script executed: # Read the implementation at line 304
sed -n '300,330p' source/slang/slang-ast-decl.cppRepository: shader-slang/slang Length of output: 687 🏁 Script executed: # Look for how _directMemberDecls is populated for GenericDecl
rg '_directMemberDecls' --type c --type h | grep -i genericRepository: shader-slang/slang Length of output: 44 🏁 Script executed: # Search for how GenericDecl handles its inner decl in member operations
rg -B5 -A10 'GenericDecl.*addMember\|addDirectMemberDecl.*GenericDecl' --type cRepository: shader-slang/slang Length of output: 44 🏁 Script executed: # Check if there's special handling for GenericDecl's inner in the context of checkGenericTypeEqualityConstraintSubType
sed -n '3550,3600p' source/slang/slang-check-decl.cppRepository: shader-slang/slang Length of output: 1938 🏁 Script executed: # Look for tests or usages that show expected behavior when ancestor is GenericDecl
rg -B10 -A10 'findDeclsLowestCommonAncestor.*GenericDecl\|GenericDecl.*indexOf' --type cRepository: shader-slang/slang Length of output: 44 🏁 Script executed: # Check the actual field structure - is inner a direct member or a special field?
rg -B10 -A30 'class GenericDecl' source/slang/slang-ast-decl.h | head -60Repository: shader-slang/slang Length of output: 1027 🏁 Script executed: # Search for how GenericTypeConstraintDecl relates to GenericDecl
rg -B5 -A15 'class GenericTypeConstraintDecl' source/slang/slang-ast-decl.hRepository: shader-slang/slang Length of output: 819 🏁 Script executed: # Look for where generic parameters are added as members
rg -B5 -A10 'addMember.*GenericTypeParam\|GenericTypeParam.*addMember' --type c | head -50Repository: shader-slang/slang Length of output: 44 🏁 Script executed: # Check if there's defensive code elsewhere that handles indexOf returning -1
rg -B3 -A3 'indexOf.*!= -1\|indexOf.*>= 0\|indexOf.*< 0' --type c --type h | head -80Repository: shader-slang/slang Length of output: 44 🏁 Script executed: # Let me look at the broader context of checkGenericTypeEqualityConstraintSubType
sed -n '3550,3620p' source/slang/slang-check-decl.cppRepository: shader-slang/slang Length of output: 2569 🏁 Script executed: # Check findDeclsLowestCommonAncestor to understand what it returns
rg -B5 -A20 'findDeclsLowestCommonAncestor' --type c --type h | head -100Repository: shader-slang/slang Length of output: 44 🏁 Script executed: # Look for safeguards elsewhere in the codebase when using indexOf result
rg -B2 -A5 'indexOf.*-1' source/core/slang-list.hRepository: shader-slang/slang Length of output: 237 🏁 Script executed: # Search for where GenericTypeParamDecl instances are added as members
rg -B10 -A10 'GenericTypeParamDecl.*addMember\|addDirectMemberDecl.*Param' --type c | head -50Repository: shader-slang/slang Length of output: 44 🏁 Script executed: # Look at the broader generic decl creation flow
rg -B5 -A15 'new GenericTypeParamDecl\|GenericTypeParamDecl\(' --type c | head -80Repository: shader-slang/slang Length of output: 44 🏁 Script executed: # See if there's any documentation about what getMembers returns for GenericDecl
rg -B3 -A3 'GenericDecl.*getMembers\|getMembers.*GenericDecl' --type c --type hRepository: shader-slang/slang Length of output: 44 🏁 Script executed: # Check if there are any assertions or checks before using indexOf result elsewhere
rg -B2 -A2 'subIndex.*supIndex\|supIndex.*subIndex' --type cRepository: shader-slang/slang Length of output: 44 🏁 Script executed: # Final check: confirm Index(-1) is the standard sentinel by looking at contains patterns
rg 'indexOf.*!= -1\|indexOf.*== -1\|!= Index\(-1\)' source/core/slang-list.h source/core/slang-array.hRepository: shader-slang/slang Length of output: 44 Add guards against At lines 3581-3582, Suggested guard auto subIndex = ancestor->getMembers().indexOf(subAncestor);
auto supIndex = ancestor->getMembers().indexOf(supAncestor);
+ if (subIndex == Index(-1) || supIndex == Index(-1))
+ return compareDecls(subAncestor, supAncestor);
return int(supIndex - subIndex);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Contributor
Author
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. Would someone help verify if it is applicable, is it a valid concern? My own understanding here is that we expect both to be found.
Collaborator
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. I think it wouldn't hurt to have
Contributor
Author
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. Added and appended to the commit message
Contributor
Author
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. I think I've found a case where the asserts added here don't hold, will update the PR and add the test that can trigger it.
Contributor
Author
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. Adding the assert would've failed on cases where one of the sides is the ancestor. I've posted a test in #10447 that would trigger such assertion. Without the assert this case gets a correct diagnostic by slang. So I've removed the asserts from here now.
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.
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. Ahh, #10447. I'm half-blind. |
||||||||||||||||
|
|
||||||||||||||||
| return int(supIndex - subIndex); | ||||||||||||||||
|
Comment on lines
+3718
to
3721
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.
(I see this was already discussed, and the author found a real case where the assumption doesn't hold.) Suggestion: Consider replacing this block with the existing if (ContainerDecl* lca = findDeclsLowestCommonAncestor(lLCAChild, rLCAChild))
{
res = _compareDeclsInCommonParentByOrderOfDeclaration(lca, lLCAChild, rLCAChild);
}Note that
Contributor
Author
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. I think the current code (and the new version here) will just give "an ordering" and the proper diagnostic will already happen later -- I can definitely add such new case but would like some developer familiar with the code to make the call here.
Comment on lines
+3718
to
3721
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. Missing defensive assertions & existing helperTwo concerns with this 1. No guard for SLANG_ASSERT(subIndex >= 0);
SLANG_ASSERT(supIndex >= 0);2. Consider reusing This is exactly the pattern if (ContainerDecl* lca = findDeclsLowestCommonAncestor(lLCAChild, rLCAChild))
res = _compareDeclsInCommonParentByOrderOfDeclaration(lca, lLCAChild, rLCAChild);Replacing 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.
🟡 Gap: No regression test for sibling-parameter equality constraint ordering
The code comments at lines 3706-3710 describe the exact scenario this fixes:
__generic<A : IA, B : IB> where A::T == B::T— two sibling parameters in the sameGenericDeclwith an associated-type equality constraint. However, no existing test exercises this specific path. The closest tests (type-equality-cononical.slang,type-equality-ancestor-ordering.slang) use interface self-types or nesting scenarios, not sibling parameters within the same generic.Since the old
binarySearchbug could silently produce wrong ordering depending on allocator behavior (non-deterministic), a targeted test would guard against future regressions in this path.Suggestion: Consider adding a test like:
This exercises the
findDeclsLowestCommonAncestor→indexOfpath with both forward and reverse orderings of sibling generic parameters.