fix(kernel): C and C++ resolver defects reproduced in the #510 review
C and C++ calls that the native resolver linked to the wrong overload, the wrong namespace or a function the call cannot see now link to the right target or to nothing. Each fix has a test that fails without it. The defects were reproduced during the review of #510.
Fixed
- Member template only specialized for other arguments (cubic 4296873889):
Put<long>()in a class that only specializesPut<int>no longer links a base class'sPutor a namesake; the implicit-thisstep refuses the reference. - Specialization choice spans namespaces (4296873951) and
foo.foo<long>()(4296873982): a specialization ofa::fno longer removesb::f, and template arguments are read after the method name, not after a receiver of the same name. - C flag detection (4296874003, 4296874009, 4296922106): an empty build file bounds a component, a
#defineinside a comment or raw string counts for nothing, and a nested component's#definedoes not count for the outer one. - Static function through a late or disabled include (4296922126): a
staticfunction in another source file is reached only through an#includewritten before the call and not under#if 0. - Namespace alias scope (4296873977) and global alias target (4296874024): an alias applies from its line on and only inside the block that declares it, and
namespace a = ::lib;names the globallib. - Name macro order (4296874017):
ROCKSDB_NAMESPACE::DBbefore the#define, or after an#undef, no longer resolves through the macro. An#undefin an#if 0branch changes nothing, and an#includeafter an#undefthat defines the macro defines it again (CodeRabbit review 5552142946). - Rvalue reference (4296874030):
Foo x; Take(x)linksTake(const Foo&), notTake(Foo&&). An argument that converts to the parameter's class, such as anintpassed topush_back(basic_json&&), still takes the&&overload. That case showed up in the nlohmann/json corpus and has its own test. constexprpointer (4296922223):constexpr Widget* ppoints at a non-constWidget.- Const container element (CodeRabbit review 5551037762):
*v.begin()on aconst std::vector<Foo>&is aconst Foo. - Multidimensional array (4296922156):
int a[3][4]passed toftakesf(int (*)[4]), notf(int*). - Top-fit tie (4296922134): when several overloads fit a call's argument types equally well, the argument-type choice no longer takes the first of them. This applies only to calls resolved by the qualified-name step, such as a call into a namespace a macro opens. A plain written-out namespace is resolved by the name step, which still picks by where the overloads are, as upstream does.
Verification
- 19 new tests in 10 blocks. The 15 that exercise a defect failed on
2628bfdb; the 2 added for the review of this PR (directives in disabled branches, include after#undef) failed one397e414; the 2 controls (an include before the call, a call naming its specialization) passed there and still pass. The tie test's final fixture and the converted-argument case were checked against a kernel built without their fix. - All
__tests__/cpp-*.test.tsand__tests__/c-*.test.tspluskernel-golden-dumps: 68 files, 646 tests passed (re-run onab786c67; precision, clippy and tsc re-run there too, results unchanged). The golden dumps did not change. - Full suite: 742 files, 9,240 passed and 39 skipped. No
Native stack traceorWorker exited unexpectedlyin stderr. npx tsc --noEmit -p .,npm run check:agent-docsandcargo clippy --release --lib -- -D warningspass. Each commit was also checked on its own with clippy.- Precision (
npm run eval:precision), base2628bfdbagainst this branch, compared edge by edge:- jq (C): 7,784 edges both times, no edge differs.
- nlohmann/json (C++): 28,803 edges both times, absent cases 11/11 and present cases 7/7 held. Four calls moved from
push_back(basic_json&&)topush_back(const basic_json&):operator+=(const basic_json&)andpatch_inplaceinjson.hpp, and the same two intests/abi/.../json_v3_10_5.hpp. Each passes abasic_jsonlvalue, so the new targets are correct.
README rows checked: "About this fork" name-resolution row, the C/C++ paragraphs, and the C++ differences list (typedef and alias scope, -> calls, receiver types, namespace aliases, object creation). None is made wrong by this change; the namespace-alias row still holds and is now stricter. The precision-replay row is unaffected.
Not covered
const_iterator->get()(cubic 4296922187): not fixed. The fork does not choose between a method'sconstand non-const overloads by the receiver's constness at all.const S* p; p->get()andconst S& s; s.get()also link the non-constget(), so this needs a new feature rather than a fix.lib::f(1)withf(int)andf(double)linksf(double): not fixed. Both overloads score alike and the name step picks by location; choosing the exact match over a conversion needs overload ranking that the fork does not have. The top-fit tie change does not reach it.- Out of scope for this PR:
src/extraction/**, the TypeScript insrc/resolution/**, andcpp-specializations.tswith its Rust scanner mirror (cubic 4296873943 on digit separators and 4296922172 on a return type that repeats the function's name): these belong to separate owners.- Upstream's tests.
- Review findings that did not reproduce.
Summary by cubic
Fixes C and C++ calls the native resolver linked to the wrong overload, the wrong namespace, or a function the call cannot see. The defects were reproduced during the review of #510; each fix has a test that fails without it.
Bug Fixes
- Member template calls:
Put<long>()in a class that only specializesPut<int>links nothing instead of a base class'sPut; a specialization ofa::fno longer removesb::f; template arguments infoo.foo<long>()are read after the method name, not the receiver. - C flag detection: an empty build file bounds a component, a
#defineinside a comment or raw string defines nothing, and a nested component's#defineis not the outer one's. - A
staticfunction in another source file is reached only through an#includewritten before the call, not one under#if 0. - Namespace aliases apply from their declaration line on and only inside the block that declares them;
namespace a = ::lib;names the globallib. ROCKSDB_NAMESPACE::DBwritten before its#defineor after an#undefno longer resolves through the macro; an#undefinside#if 0is ignored, and an#includeafter an#undefthat defines the macro defines it again.- Overload selection:
Foo x; Take(x)linksTake(const Foo&)notTake(Foo&&),constexpr Widget* ppoints at a non-constWidget,*v.begin()on a const container isconst Foo,int a[3][4]takesf(int (*)[4]), and when several overloads fit equally well the qualified-name step links none of them.
Migration
- Re-index C and C++ projects after upgrading.
Summary by CodeRabbit
- Bug Fixes
- Improved C and C++ call resolution for overloaded functions, including cases involving argument types, arrays, constness, templates, and specializations.
- Corrected namespace alias and macro lookup to respect declaration scope, source order, and active includes.
- Refined visibility of static functions and C feature-flag definitions based on include order, component boundaries, and actual preprocessor directives.
- Prevented unresolved member-template calls from linking to unrelated functions.
- Upgrade Note
- Re-index affected C and C++ projects to apply these fixes.