Repository navigation
fix(cpp): of two overloads alike but for a const, like protobuf's DynamicCastMessage(const MessageLite*) and DynamicCastMessage(MessageLite*), a call reaches the one C++ picks - #2618
Merged
Conversation
…DynamicCastMessage(const MessageLite*)` and `DynamicCastMessage(MessageLite*)`, a call reaches the one C++ picks Overload fit by argument type (cppArgumentTypeChoice) scored a `const T*` parameter and its `T*` twin alike, and the one declared first won: protobuf's `DynamicCastMessage<T>(ptr.get())` on a `std::shared_ptr<Message>` took the `const MessageLite*` overload. - cppConstTwinChoice: of the best-fitting overload's twins, alike but for the `const` of a pointer or reference parameter, the one that adds `const` to the fewest arguments. - cppWrittenTypeFit: a pointer or reference to a non-const type never takes a const object, nor a reference a value a call returns (cppNamesObject tells a named object from a call's result); an object is no pointer, nor a pointer to characters a pointer to another type (or the reverse). None of that for an argument of a template parameter's type, and the pointer and `const` rules not for a parameter the function's own template parameter types (it deduces the `const`). - The argument's type keeps a `const` its declaration writes in front (`const Buffer* b`, `constexpr char key[]`), which the declarator regex leaves out; an array is a pointer to its element; `&ref` is a pointer to the referee; cppLibraryElementOf keeps a `const` in a holder's template argument (`std::shared_ptr<const Message>`). - Template headers are read 8 lines up, not 3 (fmt's `join`). A/B on the cgstd kit against main 0893606: leveldb and redis identical; fmt 14, rocksdb 25 and protobuf 194 calls retargeted, each triaged against C++'s choice (const twins, objects and arrays off the wrong pointer overloads, temporaries to `T&&`). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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.
Summary
Follow-up from #2616, filed as a task chip there.
The bug. Overload fit by argument type scored a
const T*parameter and itsT*twin alike, and the one declared first won. Protobuf'sDynamicCastMessage<T>(ptr.get())on astd::shared_ptr<Message>tookconst T* DynamicCastMessage(const MessageLite*), where C++ picksT* DynamicCastMessage(MessageLite*).The fix.
cppConstTwinChoice(new). Of the best-fitting overload's twins, alike but for theconstof a pointer or reference parameter, it takes the one that addsconstto the fewest arguments. It is a tie-break among twins only: an earlier version penalized everyconst T¶meter, and that moved fmt'sto_string(bigint)off its only viableconst T&overload.cppWrittenTypeFit's hard rules. These all score -3:cppNamesObjecttells a named object from a call's result);Two exceptions:
const S& fmtcould be a pointer);Src* x) is exempt from the pointer andconstrules, because it deduces theconst(rocksdb'sstatic_cast_with_check).Argument types.
const/constexprthat the declarator regex leaves out is kept (const Buffer* b).format_base2e(4, buf, …)).&refis a pointer to the referee.cppLibraryElementOfkeeps aconstinside a holder's template argument (std::shared_ptr<const Message>), which fix(cpp): a call on what a container'sfront()/back()/at()or a holder'sget()/value()returns, like rocksdb'sedits.back()->MarkAtomicGroup(…), calls the element's method #2616's version stripped.Template headers are read up to 8 lines above a declaration, not 3. fmt's
joinspends four lines on its parameters, so itsS&& sepwasn't recognized as dependent.A/B on the C/C++ kit (main
0893606evs this branch)No call was added or removed. Every retarget was compared against C++'s choice, overload pair by overload pair:
DynamicCastMessage/DownCastMessagemove to the pointer and reference twins matching their argument's constness (48 calls), including thestatic_cast<const MessageLite*>(from)forwarders.RefAt(base, …)on aconst void* basetakesconst T& RefAt(const void*, …)(16), andVisitDescriptors(file, proto, …)on a mutableprototakes the mutable overload (5). In rocksdb,GetOffset,GetOffsetAsandAsRawPointeron avoid*take their non-const twins (12). TheGetExtensionSet(m)calls on a constmtake its const overload (3).WithVars(EnumVars(…))reachesWithVars(Map&&), notconst Map*(110), plusGetMiniTable(message)on aPtrOrRaw<T>(3) andMicroString(*new auto(…))(1).appender/iterator arguments offormat_decimal,format_base2e,fill_nandwrite_significand, andprint(wos, …)on astd::wostringstream.toku_unsafe_fetch(m_sto_txnid)→T&, andGetAndRefSuperVersion(cf)on aColumnFamilyId→uint32_t.Char buffer[…]andchar xdigits[…]arguments now reach theChar*overloads. rocksdb'sAdd(level, sst, smallest, largest, …)onconstexpr char smallest[]reaches theconst char*overload. protobuf's python generator'sprinter_->Print(m, template)reachesPrint(const Map&, string_view), because aconstexpr char template[]no longer counts againststring_view.MultiGet(ReadOptions(), keys.size(), …)reaches thenum_keysoverload.Intermediate arms caught three regressions before this version:
const T&penalty that movedto_string(bigint);static_cast_with_check(&other)moved to itsshared_ptroverload, and fmt'sto_string_view(sep)was a coin flip;chararrays no longer counting againstColumnFamilyHandle*, which moved rocksdb'sdb_->Put(opts, key, ts, value).All are fixed, and the final arm shows none of those changes.
Tests
__tests__/cpp-overload-constness.test.ts(5 tests, all failing on0893606e, checked on a detached checkout). It covers protobuf's cast helpers with pointer and reference twins, const and non-const holders, variables, references and a temporary, and class methods with either twin first. It also covers aT* constargument, overloads that differ by more than aconst, an array, a deducedSrc*, arguments of a template parameter's type (a one-line and a four-line header),chararrays against a class pointer and against a deducedT*, a pointer againstconst char*, and aconstexprarray.const/constexpr, array decay, the holder's template-argumentconst, both header windows andcppNamesObject.mcp-status-freshness.test.tstest hit anEBUSYteardown under load; the file passes 4/4 when rerun on its own.🤖 Generated with Claude Code