Repository navigation
fix(registry): break suffix_match ties by kind, C# stub, then QN order, not depth - #2578
Merged
Merged
Conversation
…r, not depth 1a34255 made suffix_match ties deterministic by preferring the least nested QN, then the smaller QN. Determinism was the point, and it stays. But depth says nothing about which same-named definition a call means. An audit of main against v0.11.0 (2026-10-10) traced three resolution regressions to that one rule: - django: about 5,600 `Model.objects.X(...)` calls tie between `QuerySet.X` and same-named methods elsewhere. `Library.filter`, `EngineHandler.all` and `HttpResponseBase.get` are each one segment shallower, so the QuerySet share fell from 57 % to 24 %. - Kotlin / Java: a call went to a property or field that merely shares the callee's name. Kotlin `where(...)` went to `var where`, and Java `esr.indexMode()` to a field whose QN lacks its class segment. Kotlin CALLS edges on Variable/Field targets went 785 -> 1,648. - C#: .NET reference assemblies (`src/libraries/<Asm>/ref/<Asm>.cs`, `throw null` bodies) are shallower than the implementation, and "ref" sorts before "src". 14.1 % of dotnet/runtime's CALLS landed on such stubs (4.9 % in v0.11.0). A tie now goes to: 1. the more plainly callable candidate: Function/Method, then any other definition, then an unknown label, then Variable/Field; 2. for C# only, an implementation over a reference-assembly stub; 3. the lexicographically smaller QN. This is still a pure function of the candidate set (O9). The kernel cases behind 1a34255 keep their answers: `dev_name` is the only Function among the fields, and include/linux sorts before tools/virtio. Measured on the bench corpora (macOS arm64, main cb8b641 vs this change, one cold full index each): - django: `objects.X` -> QuerySet 23.7 % -> 41.4 %; CALLS -> Variable 1,022 -> 741 - Kotlin (Exposed): CALLS -> Variable 1,648 -> 480 - Java (elasticsearch): CALLS -> Variable/Field 62,313 -> 42,957 - C# (dotnet/runtime): CALLS -> ref/ stubs 143,799 -> 52,000; CALLS -> Variable/Field 33,779 -> 28,662 - TypeScript: CALLS -> Variable 7,978 -> 7,341 The rest of the django gap needs Manager -> QuerySet typing, which no tie order can supply. Tests: - registry_tie_prefers_callable_over_property - registry_tie_ignores_nesting_depth - registry_tie_prefers_csharp_implementation_over_ref_stub All three are RED on main and RED again with the registry change reverted. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
When several same-named definitions tie for a
suffix_matchcall, the winner is now chosen in this order:src/libraries/<Asm>/ref/<Asm>.cs,throw nullbodies).This replaces "least nested QN, then smaller QN" (1a34255). That change made ties deterministic, and determinism stays: the rule is still a pure function of the candidate set. But nesting depth says nothing about which definition a call means.
Why
An audit of main against v0.11.0 (2026-10-10) found three resolution regressions. Each was traced by bisect to that one tie rule:
Model.objects.X(...)→QuerySet.X.Library.filter,EngineHandler.allandHttpResponseBase.getare each one segment shallower, so they won.where(...)went to the propertyvar where.ref/stubs. The stubs are shallower, andrefsorts beforesrc.Measured
macOS arm64, one cold full index per corpus, current main cb8b641 vs this PR:
objects.X→ QuerySetref/stubsThe rest of the django gap needs Manager → QuerySet typing, which no tie order can supply.
The kernel cases behind 1a34255 keep their answers:
dev_nameis the only Function among the struct fields, and include/linux sorts before tools/virtio. Two kernel indexes with this PR are identical: 8,504,091 nodes, 1,844,907 CALLS, and the same CALLS-set hash.Tests
registry_tie_prefers_callable_over_propertyregistry_tie_ignores_nesting_depthregistry_tie_prefers_csharp_implementation_over_ref_stub(it also guards that the stub rule is C#-only)All three are RED on main and RED again with the registry change reverted.
Local CI on this exact tree:
The 8 Windows guard checks are green.
make lint-ciis clean.