feat(cpp): standard-conversion-sequence ranking for overload resolution#1606
Conversation
…ution (abhigyanpatwari#1578) Introduce `ConversionRankFn` abstraction and `cppConversionRank` implementation to disambiguate C++ overloaded calls by argument-to-parameter conversion cost. Exact type match (rank 0) beats standard arithmetic conversion (rank 2), which beats non-viable mismatch (Infinity). Thread the rank function through `narrowOverloadCandidates`, `pickImplicitThisOverload`, `pickOverload`, and `pickUniqueGlobalCallable` via the `ScopeResolver.conversionRankFn` contract. Add `findAllCallableBindingsInScope` scope walker for collecting all overloads at the first binding scope. Guard against false ambiguity suppression when candidates span different files (local-shadows-import preservation).
|
@azizur100389 is attempting to deploy a commit to the NexusCore Team on Vercel. A member of the Team first needs to authorize it. |
CI Report✅ All checks passed Pipeline Status
Test Results
✅ All 9088 tests passed 1 test(s) skipped — expand for details
Code CoverageTests
📋 View full run · Generated by CI |
Keep both expected-failure entries: conversion-rank scoring (abhigyanpatwari#1578) and ADL reference-arg negative fixtures (abhigyanpatwari#1598).
|
@azizur100389 Can you please merge origin/main and resolve the conflicts? Please be careful during the resolution as it might be non-trivial. Anyway, tests are there to protect. :) |
Adapt conversion-rank narrowing (abhigyanpatwari#1578) to the new ADL architecture introduced in abhigyanpatwari#1599. Key resolution decisions: - adl.ts: remove internal narrowing (moved to caller per abhigyanpatwari#1599) - free-call-fallback.ts: thread conversionRankFn through both the ordinary-only and merged ordinary+ADL narrowing paths; add same-file guard to prevent false ambiguity suppression on cross-file shadowing - scope-resolver.ts: adopt upstream's merged using-named + ADL candidate array pattern - walkers.ts: drop duplicate findAllCallableBindingsInScope (upstream abhigyanpatwari#1599 added an identical version) - helpers.ts: merge expected-failure entries from both branches All 216 C++ tests pass in registry-primary mode (216/216). Legacy mode: 186 passed, 30 skipped (expected).
|
Claude finished @magyargergo's task in 9m 36s —— View job Adversarial Production-Readiness Review — PR #1606Review bar for this PRDoD.md §2.7 (tests cover the real changed path), §2.1 (correct on real runtime path), and §2.3 (comments explain intent without overpromising). The PR-specific bar adds: ranker must not emit wrong CALLS edges for multi-argument overloads, conversion-rank path must be reachable by the test suite, and char/bool promotion limitations must be documented or tested. Current PR stateCI is green (9074/9074 passing, 1 unrelated skip). TypeScript, format, and lint clean. 192 C++ registry-primary tests pass; 25 legacy-parity skipped as expected. The Vercel deployment request is irrelevant to resolver correctness. Branch hygiene assessmentClassification: merge-from-main commit present but harmless and merge-safe The local Understanding of the changeThe PR introduces The key design choice: argument types are inferred from the tree-sitter AST at capture time ( FindingsFinding 1 — Integration tests do not exercise the conversion rankerSeverity: HIGH Evidence:
Risk: The new code path ( Recommended fix: Add a test case that requires the ranker to resolve. The canonical trigger is a variable (not a literal) argument: Alternatively, add unit tests directly for Blocks merge: yes. DoD §2.7: "Tests would fail if behavior, wiring, or contracts were broken." Removing the ranker does not break any test. Finding 2 — Multi-argument total-score tie picks first-match in non-ADL and receiver-bound pathsSeverity: HIGH Evidence:
Both score
C++ would report this call as ambiguous. GitNexus emits a false CALLS edge to the first-registered overload. The ADL merged-candidate path is correct: when
Risk: Any C++ codebase with overloaded functions using cross-argument type tradeoffs (e.g., Recommended fix: In the non-ADL fallback (after conversion ranking), when Blocks merge: yes. The review criteria is explicit: "Block if a simple summed rank can emit wrong CALLS edges for common multi-argument overloads." Finding 3 — char/bool promotions misranked as standard conversions; not testedSeverity: MEDIUM Evidence:
Consequence for No test covers Risk: False silence (missing CALLS edges) for calls using Recommended fix: Either (a) add rank-1 special cases for Blocks merge: maybe. The omission creates missing edges (false silences), not false positives. It becomes yes if the PR description or test names continue claiming "standard-conversion-sequence ranking" as equivalent to ISO C++ overload resolution. Finding 4 — Documentation contradiction: rank-1 promotions listed but not implementedSeverity: MEDIUM Evidence: The top-level comment of Risk: Future contributors reading the comment will believe promotions are already ranked correctly and won't add the missing cases. Alternatively they'll read the second table and be confused about which table the implementation actually follows. The test name "standard-conversion-sequence ranking" further overstates the approximation boundary. Recommended fix: Remove the ISO C++ ranking table from the top of the comment, or clearly label it "C++ standard (not implemented)" and keep only the post-normalization table ("what this function actually implements"). Rename the PR / test suite description to "GitNexus normalized arithmetic conversion ranking (approximation)" to set honest expectations. Blocks merge: maybe. Non-blocking on its own but compounds Finding 3's impact by creating misleading documentation that will slow down future fixes. Finding 5 — Same-file ambiguity guard is a brittle proxy for overload-set identitySeverity: LOW Evidence: In the non-ADL free-call path ( C++ allows a single overload set to span header declarations and multiple Risk: Multi-file overload sets (split definitions, template specializations in separate TUs) can produce a false CALLS edge where suppression is the correct behaviour. Recommended fix: For the V1 approximation, document this limitation explicitly in a comment. Longer term, use overload-set identity (e.g., same qualified name, same owner, same namespace scope) rather than file path. Blocks merge: maybe. The fixture's overloads are all in the same file, so the test doesn't catch this. The pattern of header-declared + multiple-TU-defined overloads is not common enough to block, but warrants a comment. Finding 6 — Unicode in source files is visible punctuation; no bidi controls foundSeverity: INFO The non-ASCII characters in changed files are Blocks merge: no. PR-specific assessment sectionsC++ standard-conversion ranking compliance and approximation boundary The ranker is a deliberately simplified 3-tier approximation (0/2/∞) over normalized type strings. This is architecturally sound and correctly isolated behind cppConversionRank correctness The function's ARITHMETIC set ( narrowOverloadCandidates ranking and ambiguity behavior Step 4b is correctly unreachable when exact-type filter succeeds. The conversion ranking correctly filters Infinity candidates. The ambiguity detection after ranking ( Free-call fallback behavior Local-shadows-import semantics are preserved: ADL merged-candidate interaction and #1599 dependency #1599 is confirmed in Receiver-bound call behavior Case 0.5 (explicit Shared ScopeResolver contract and cross-language safety
helpers.ts legacy DAG skip correctness The three new skip entries exactly match the test names in
The Tests and CI coverage 192/192 C++ registry-primary tests pass. Three new tests pass. However, all three new tests are resolved via the exact-type filter before the conversion ranker is reached (see Finding 1). No unit tests exist for Hidden Unicode/hygiene No bidi controls. Visible arrows/em dashes in comments only. Clean. Final verdictnot production-ready The integration tests pass but do so via the pre-existing exact-type filter — none of the three new test cases exercise |
Finding 1 (HIGH): add tests that exercise the conversion ranker.
- p('a') with p(int)/p(double): char→int promotion (rank 1) beats
char→double conversion (rank 2), forcing step 4b in
narrowOverloadCandidates. Exact-type filter misses both overloads.
- h(42, 2.5) with h(int,int)/h(double,double): multi-arg tied total
score forces the ranker, both candidates score 2 → suppressed.
Finding 2 (HIGH): unify multi-candidate suppression across all paths.
- Non-ADL free-call: suppress when narrowed.length > 1 (same-file
guard), mirroring ADL merged-candidate behavior.
- ADL ordinary-only: same pattern.
- pickOverload: return OVERLOAD_AMBIGUOUS when candidates.length > 1
after normalized-ambiguity check.
- Case 0.5 (this receiver): set ambiguous=true when narrowed > 1.
Finding 3+4 (MEDIUM): implement rank-1 integral promotions.
- char→int and bool→int now return rank 1 (ISO C++ [conv.prom]).
- Updated comment to remove misleading ISO table header; document
only the post-normalization ranking that is actually implemented.
- Updated ConversionRankFn JSDoc in overload-narrowing.ts.
218/218 C++ tests pass (registry-primary). Legacy: 186+32.
|
Claude finished @magyargergo's task in 12m 36s —— View job Adversarial Production-Readiness Review — PR #1606Tasks
Review bar for this PRDoD §2.7 (tests cover the real changed path), §2.1 (correct on real runtime path), and §2.3 (comments explain intent without overpromising). PR-specific bar (from trigger): ranker must not emit wrong CALLS edges for multi-argument overloads, conversion-rank path must be reachable by the test suite, char/bool promotion limitations must be documented or tested, multi-arg incomparable candidates must suppress rather than pick by summed cost. Current PR stateThis review is based on the current HEAD ( The previous review found six findings. The author has addressed five of the six. One finding persists and is merge-blocking. Branch hygiene assessmentClassification: merge-from-main commit present but harmless and merge-safe The branch contains merge commits from Understanding of the change
FindingsFinding 1 — Summed-cost ranking emits wrong CALLS edges for asymmetric multi-arg overloadsSeverity: HIGH Evidence:
Concrete trigger using the existing fixture types ( ISO C++ check for the same call:
Neither overload is "not worse for all arguments" → ISO C++ calls this ambiguous. The correct result is zero CALLS edges. GitNexus emits a false edge to This affects all five call paths that invoke
The trigger requires mixed literal/promotion arguments — Risk: Any C++ codebase with arithmetic overloads and mixed literal arguments (e.g. Recommended fix: Two options: (A) Document and test the limitation: Add a test asserting that (B) Implement pairwise comparison: Replace the summed-cost loop with a pairwise filter: for each candidate F1, check whether any other candidate F2 is "not worse for all args and better for at least one" (i.e., F2 dominates F1). Return only non-dominated candidates. If more than one remains, they are genuinely ambiguous. This is the ISO-correct fix. Blocks merge: yes. DoD criterion 5 and final verdict rule: "If ranking can emit a wrong CALLS edge for multi-argument incomparable overloads, verdict must be not production-ready." Finding 2 — Non-ADL cross-file path still emits first-match when narrowed.length > 1 and !sameFileSeverity: LOW Evidence:
} else if (narrowed.length > 1) {
const sameFile = narrowed.every((d) => d.filePath === narrowed[0]!.filePath);
if (sameFile) { handledSites.add(...); continue; }
}
// narrowed.length === 0: keep first-match fnDefWhen However, for C++ — the only language currently setting Risk: Low for current codebase. Potential footgun for future language adopters. Recommended fix: Document that the non-ADL path suppresses for sameFile but falls back to first-match for cross-file, or unify the behavior with the ADL path (always suppress when Blocks merge: no. Dead code for current C++ usage. PR-specific assessment sectionsISO C++ overload-resolution rule complianceThe implementation is honestly bounded. However, the summed-cost vs. pairwise divergence (Finding 1) makes the approximation incorrect for the asymmetric multi-arg case, which is within the stated scope of "arithmetic primitive conversion ranking." GitNexus approximation boundaryThe approximation is correctly documented: The gap is Finding 1: within the claimed arithmetic primitive scope, the asymmetric multi-arg case produces a false positive. This is not a limitation at the boundary but a bug within the stated scope. cppConversionRank correctness
The function itself is correct. The bug is in how narrowOverloadCandidates ranking and ambiguity behaviorStep 4b correctly runs only when the exact-type filter returns empty. The ∞ exclusion (viable=false on any ∞ slot) is correct. The The issue is in Free-call fallback behaviorLocal-shadows-import semantics preserved ✓. ADL merged-candidate interaction and #1599 baseline#1599 is confirmed in Receiver-bound call behaviorCase 0.5 (
Shared ScopeResolver contract and cross-language safety
helpers.ts legacy DAG skip correctnessFive new conversion-rank entries exactly match the test names in
Skip comments reference Tests and CI coverageThe
Gap: No test for the asymmetric case ( Existing f(2.5) and f(42) tests are still resolved via exact-type filter (step 4, not step 4b) — they prove the outcome but not the ranker path. The promotion and tied-total tests now cover step 4b. Target identity verified in: f(double) via Hidden Unicode/hygieneChecked Progress since previous review
Final verdictnot production-ready The The remaining blocking issue is that |
|
@azizur100389 can you please implement option B for the 1st finding? 🙏 |
Replace the summed per-slot conversion cost with ISO C++-aligned
pairwise dominance comparison ([over.ics.rank]). F1 is better than
F2 only when F1 is not worse for every argument and strictly better
for at least one. Non-dominated candidates are returned; if multiple
remain they are genuinely ambiguous.
This fixes false CALLS edges for asymmetric multi-arg overloads:
h('a', 2.5) against h(int,int) / h(double,double) — the old summed
cost picked h(double,double) (cost 2 < 3), but ISO C++ considers
the call ambiguous because h(int,int) is better at arg 0 via char
promotion. The pairwise check correctly finds neither dominates.
Add h('a', 2.5) test case asserting zero CALLS edges alongside
the existing h(42, 2.5) symmetric-tie test.
218/218 C++ tests pass (registry-primary). Legacy: 186+32.
Keep both conversion-rank skip entries (abhigyanpatwari#1578) and inline-namespace ambiguity skip entries (abhigyanpatwari#1600) from the two branches.
|
@azizur100389 Thank you 🙏 |
…ith abhigyanpatwari#1606 conversion-rank scoring
…on (abhigyanpatwari#1606) * feat(cpp): add standard-conversion-sequence ranking to overload resolution (abhigyanpatwari#1578) Introduce `ConversionRankFn` abstraction and `cppConversionRank` implementation to disambiguate C++ overloaded calls by argument-to-parameter conversion cost. Exact type match (rank 0) beats standard arithmetic conversion (rank 2), which beats non-viable mismatch (Infinity). Thread the rank function through `narrowOverloadCandidates`, `pickImplicitThisOverload`, `pickOverload`, and `pickUniqueGlobalCallable` via the `ScopeResolver.conversionRankFn` contract. Add `findAllCallableBindingsInScope` scope walker for collecting all overloads at the first binding scope. Guard against false ambiguity suppression when candidates span different files (local-shadows-import preservation). * fix: address Claude review findings on conversion-rank PR Finding 1 (HIGH): add tests that exercise the conversion ranker. - p('a') with p(int)/p(double): char→int promotion (rank 1) beats char→double conversion (rank 2), forcing step 4b in narrowOverloadCandidates. Exact-type filter misses both overloads. - h(42, 2.5) with h(int,int)/h(double,double): multi-arg tied total score forces the ranker, both candidates score 2 → suppressed. Finding 2 (HIGH): unify multi-candidate suppression across all paths. - Non-ADL free-call: suppress when narrowed.length > 1 (same-file guard), mirroring ADL merged-candidate behavior. - ADL ordinary-only: same pattern. - pickOverload: return OVERLOAD_AMBIGUOUS when candidates.length > 1 after normalized-ambiguity check. - Case 0.5 (this receiver): set ambiguous=true when narrowed > 1. Finding 3+4 (MEDIUM): implement rank-1 integral promotions. - char→int and bool→int now return rank 1 (ISO C++ [conv.prom]). - Updated comment to remove misleading ISO table header; document only the post-normalization ranking that is actually implemented. - Updated ConversionRankFn JSDoc in overload-narrowing.ts. 218/218 C++ tests pass (registry-primary). Legacy: 186+32. * fix: implement pairwise dominance comparison for overload ranking Replace the summed per-slot conversion cost with ISO C++-aligned pairwise dominance comparison ([over.ics.rank]). F1 is better than F2 only when F1 is not worse for every argument and strictly better for at least one. Non-dominated candidates are returned; if multiple remain they are genuinely ambiguous. This fixes false CALLS edges for asymmetric multi-arg overloads: h('a', 2.5) against h(int,int) / h(double,double) — the old summed cost picked h(double,double) (cost 2 < 3), but ISO C++ considers the call ambiguous because h(int,int) is better at arg 0 via char promotion. The pairwise check correctly finds neither dominates. Add h('a', 2.5) test case asserting zero CALLS edges alongside the existing h(42, 2.5) symmetric-tie test. 218/218 C++ tests pass (registry-primary). Legacy: 186+32. * docs: update step 4b JSDoc to reflect pairwise dominance --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Summary
Closes #1578.
ConversionRankFntype andcppConversionRankimplementation to score argument-to-parameter conversions: exact match (0) beats standard arithmetic conversion (2) beats non-viable mismatch (∞)narrowOverloadCandidates,pickImplicitThisOverload,pickOverload, receiver-bound Case 0.5, ADL, andpickUniqueGlobalCallablevia theScopeResolver.conversionRankFncontract propertyfindAllCallableBindingsInScopescope walker to collect all callable overloads at the first binding scope (needed for rank-based disambiguation vs. first-match)Test plan
f(2.5) → f(double)— exact match beats standard conversion (double → int)f(42) → f(int)— exact match beats standard conversion (int → double)g(42) → 0 edges—int/longnormalize to same type, ambiguous after normalizationtsc --noEmitclean