fix(kernel): C and C++ resolver defects reproduced in the #510 review

Merged
BO
1d ago
+943-67

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 specializes Put<int> no longer links a base class's Put or a namesake; the implicit-this step refuses the reference.
  • Specialization choice spans namespaces (4296873951) and foo.foo<long>() (4296873982): a specialization of a::f no longer removes b::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 #define inside a comment or raw string counts for nothing, and a nested component's #define does not count for the outer one.
  • Static function through a late or disabled include (4296922126): a static function in another source file is reached only through an #include written 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 global lib.
  • Name macro order (4296874017): ROCKSDB_NAMESPACE::DB before the #define, or after an #undef, no longer resolves through the macro. An #undef in an #if 0 branch changes nothing, and an #include after an #undef that defines the macro defines it again (CodeRabbit review 5552142946).
  • Rvalue reference (4296874030): Foo x; Take(x) links Take(const Foo&), not Take(Foo&&). An argument that converts to the parameter's class, such as an int passed to push_back(basic_json&&), still takes the && overload. That case showed up in the nlohmann/json corpus and has its own test.
  • constexpr pointer (4296922223): constexpr Widget* p points at a non-const Widget.
  • Const container element (CodeRabbit review 5551037762): *v.begin() on a const std::vector<Foo>& is a const Foo.
  • Multidimensional array (4296922156): int a[3][4] passed to f takes f(int (*)[4]), not f(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 on e397e414; 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.ts and __tests__/c-*.test.ts plus kernel-golden-dumps: 68 files, 646 tests passed (re-run on ab786c67; 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 trace or Worker exited unexpectedly in stderr.
  • npx tsc --noEmit -p ., npm run check:agent-docs and cargo clippy --release --lib -- -D warnings pass. Each commit was also checked on its own with clippy.
  • Precision (npm run eval:precision), base 2628bfdb against 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&&) to push_back(const basic_json&): operator+=(const basic_json&) and patch_inplace in json.hpp, and the same two in tests/abi/.../json_v3_10_5.hpp. Each passes a basic_json lvalue, 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's const and non-const overloads by the receiver's constness at all. const S* p; p->get() and const S& s; s.get() also link the non-const get(), so this needs a new feature rather than a fix.
  • lib::f(1) with f(int) and f(double) links f(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 in src/resolution/**, and cpp-specializations.ts with 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 specializes Put<int> links nothing instead of a base class's Put; a specialization of a::f no longer removes b::f; template arguments in foo.foo<long>() are read after the method name, not the receiver.
  • C flag detection: an empty build file bounds a component, a #define inside a comment or raw string defines nothing, and a nested component's #define is not the outer one's.
  • A static function in another source file is reached only through an #include written 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 global lib.
  • ROCKSDB_NAMESPACE::DB written before its #define or after an #undef no longer resolves through the macro; an #undef inside #if 0 is ignored, and an #include after an #undef that defines the macro defines it again.
  • Overload selection: Foo x; Take(x) links Take(const Foo&) not Take(Foo&&), constexpr Widget* p points at a non-const Widget, *v.begin() on a const container is const Foo, int a[3][4] takes f(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.