Repository navigation
Conversation
…ibraries static_rtti's type_id is the address of a local static in a template of the library. Each module has its own copy: always on Windows, and under -fvisibility=hidden on ELF and Mach-O. A class used by several modules then has one type_id per module, initialize() takes the copies for unrelated classes and methods, and a call made in one module on an object created in another lands in the wrong overrider or reads past the end of a v-table. named_static_rtti keeps per-module addresses as type_ids, but points them at the name of the class, cut out of __PRETTY_FUNCTION__ / __FUNCSIG__, and type_index returns that name, so initialize() merges the copies, as std_rtti does with type_info::name(). All modules must be built with the same compiler. A class with internal linkage, or none, may print the same name as a different class, so type_index keys it on its type_id as well, and it is never merged. With C++26 reflection, has_external_linkage decides exactly. Without it, the marks the compilers leave in the name decide: anonymous namespaces, lambdas and unnamed classes are marked by every compiler, local classes only by GCC. Clang prints a local class unqualified and MSVC as if its function were a namespace, so two same-named local classes are merged there; the documentation says so. static_rtti itself is unchanged: it stays correct within one module, local classes included, and its documentation now points to named_static_rtti for programs that share a registry across modules. Tests: test/implicit_shared_libraries/named_static_rtti (hidden visibility, classes in anonymous namespaces on both sides, a v-table layout that makes a mis-merged call land in the wrong overrider) and test_named_static_rtti.cpp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
init_bad_call called Rtti::dynamic_type on every argument, unguarded. The method constructor takes the address of fn_not_implemented and fn_ambiguous, so a registry combining static_rtti or named_static_rtti - which have no dynamic_type - with an error handler did not compile. For a class the rtti policy deems polymorphic, nothing changes. Otherwise, for a virtual_ptr argument, the class is found from the v-table pointer, by scanning the registry's classes on the error path only. The object's own class always matches; if another one does too - v-tables with leading unused slots are pointed to before their start, and may coincide with another's - only the classes deriving from the parameter's class count. If that is still not conclusive, or for any other argument, the parameter's class is reported: less precise, but true. Classes are compared through type_index, so named_static_rtti's per-module copies count as one. Under std_rtti, a non-polymorphic class reached through a virtual_ptr is now reported as the class of the object rather than that of the parameter. Test: test_static_rtti_errors.cpp, with static_rtti and named_static_rtti, each with throw_error_handler. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
std_rtti::type_index keyed on type_info::name(). Two classes in anonymous namespaces in different translation units, both named Helper, have the same name - _GLOBAL__N_1 is the same in every translation unit - so initialize() merged them into one class, and a call aborted with "ambiguous". This happened with GCC, Clang, libc++ and MSVC alike, within a single module. type_index now returns std::type_index again where that is exact: with MSVC, whose type_info equality compares decorated names that carry a hash per translation unit for anonymous namespaces, and with GCC and libstdc++, which compare names except for the classes that GCC marks as having internal linkage. It cannot be used with libc++, which compares the type_info objects of classes with hidden visibility by address - the reason for the switch to names, in boostorg#75 - nor with Clang and libstdc++, since Clang does not emit GCC's mark. There, the key is the mangled name, plus the type_id when the mangling shows internal linkage: an anonymous namespace, a static function or variable in an encoding, or an unnamed type or lambda. A small scanner reads the marks, skipping source names by their length so that identifiers like ZLibStream are not mistaken for them, and skipping the numbers that are not lengths. The key struct of named_static_rtti moves to detail/type_key.hpp, shared by both policies. Tests: test_std_rtti_type_index.cpp checks the scanner on names produced by GCC, Clang and libc++; the default_registry shared-library test gains a class in an anonymous namespace in each module, which must keep its own overrider. Forcing std::type_index on libc++ with hidden visibility reproduces the macOS failure; disabling the scanner on Clang and libstdc++ reproduces the collision. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
An automated preview of the documentation is available at https://127.openmethod.prtest3.cppalliance.org/libs/openmethod/doc/html/index.html If more commits are pushed to the pull request, the docs will rebuild at the same URL. 2026-10-06 23:20:58 UTC |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #127 +/- ##
===========================================
- Coverage 92.34% 91.49% -0.85%
===========================================
Files 27 28 +1
Lines 2234 2352 +118
Branches 679 713 +34
===========================================
+ Hits 2063 2152 +89
- Misses 112 138 +26
- Partials 59 62 +3
... and 1 file with indirect coverage changes Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
This branch has not been deployed
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.
(Written by Claude Code, on behalf of @jll63.)
Three fixes around class identity across shared libraries, and within one program.
named_static_rtti: astatic_rttithat works across shared librariesstatic_rtti'stype_idis the address of a local static in a template of the library. Each module has its own copy: always on Windows, and under-fvisibility=hiddenon ELF and Mach-O.initialize()takes the copies for unrelated classes and methods, and a call made in one module on an object created in another lands in the wrong overrider, or reads past the end of a v-table.named_static_rttikeeps per-module addresses as type ids, but points them at the name of the class, cut out of__PRETTY_FUNCTION__/__FUNCSIG__, and itstype_indexreturns that name, soinitialize()merges the copies. All modules must be built with the same compiler.Classes with internal linkage, or none, may print the same name as a different class, so they are keyed on their
type_idas well and never merged:has_external_linkagedecides, exactly.static_rttiitself is unchanged: it stays correct within one module, and its documentation now points tonamed_static_rtti.bad_callargument types withoutdynamic_typeinit_bad_callcalledRtti::dynamic_typeunguarded, sostatic_rttiornamed_static_rtticombined with any error handler did not compile.virtual_ptr, the class is found from the v-table pointer, by scanning the registry's classes on the error path only. If v-table pointers coincide, only classes deriving from the parameter's class count.std_rtti: same-named classes with internal linkagestd_rtti::type_indexkeyed ontype_info::name(). Two classes namedHelperin anonymous namespaces in different translation units have the same name:_GLOBAL__N_1is the same everywhere.initialize()merged them, and the call aborted with "ambiguous". This happened with GCC, Clang, libc++ and MSVC alike, within a single module.type_indexnow returnsstd::type_indexagain where that is exact: MSVC, and GCC with libstdc++. It cannot be used with libc++, which compares thetype_infoof hidden-visibility classes by address (the reason for the switch to names in #75), nor with Clang and libstdc++, since Clang does not emit GCC's internal-linkage mark.There, the key is the mangled name, plus the
type_idwhen the mangling shows internal linkage. A small scanner reads the marks, skipping source names by their length so that identifiers likeZLibStreamare not mistaken for them.Tests
implicit_shared_libraries/named_static_rtti: hidden visibility, a v-table layout that makes a mis-merged call land in the wrong overrider, and a class in an anonymous namespace in each module.test_named_static_rtti.cpp: names, keys and local-class detection.test_static_rtti_errors.cpp:no_overriderandambiguous_callwith both policies plusthrow_error_handler, checking that the dynamic class is reported.test_std_rtti_type_index.cpp: the scanner on names produced by GCC, Clang and libc++.implicit_shared_libraries/default_registry: gains an anonymous-namespace class in each module.Controls run locally:
static_rttiin the named test reproduces the wrong-overrider landing.std::type_indexon libc++ with hidden visibility reproduces the macOS failure.Verified locally:
-Wextra -Werror)dev/check-flat.sh, Antora🤖 Generated with Claude Code