[clang] [libcxx] [CodeGen] Added '*' to internal linkage types (PR #220864)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 23 07:22:11 PDT 2026
https://github.com/matthew-j-code updated https://github.com/llvm/llvm-project/pull/220864
>From fc53318a34d9dfceb79dc2b054b7a0754cecd061 Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Tue, 11 Aug 2026 03:13:24 -0700
Subject: [PATCH 1/4] [CodeGen] Added '*' to internal linkage types
RTTI typename for internal-linkage types lacked '*', which broke
type_info::operator== and dynamic_cast with libstdc++
Fixes #34255
---
clang/docs/ReleaseNotes.md | 2 +
clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 20 ++++++--
clang/lib/CodeGen/ItaniumCXXABI.cpp | 23 ++++++---
.../CIR-internal-linkage-typeinfo-name.cpp | 51 +++++++++++++++++++
clang/test/CIR/CodeGenCXX/vtable-linkage.cpp | 12 ++---
.../internal-linkage-typeinfo-name.cpp | 51 +++++++++++++++++++
6 files changed, 142 insertions(+), 17 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/CIR-internal-linkage-typeinfo-name.cpp
create mode 100644 clang/test/CodeGenCXX/internal-linkage-typeinfo-name.cpp
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a49971adef86ff..fdbb12e653da5a 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -511,6 +511,8 @@ features cannot lower the translation-unit ABI level;
- Fixed a crash when declaring a member template within a local class inside an OpenMP region. (#GH216052)
- Fixed a bug where repeated #imports of modular headers in non-modular compilation were translated to #pragma clang module import. (#GH216924)
- Fixed an assertion when `#pragma omp declare simd` or `#pragma omp declare variant` is followed by another OpenMP declarative directive containing a qualified identifier. (#GH217204)
+- Fixed RTTI name for internal-linkage types lacking '*', fixed breaking type_info::operator== and
+dynamic_cast with libstdc++ (#GH34255)
#### Bug Fixes to Compiler Builtins
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 800f20ace0e703..2caabd9292db0e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -1166,12 +1166,20 @@ CIRGenItaniumRTTIBuilder::getAddrOfTypeName(mlir::Location loc, QualType ty,
llvm::raw_svector_ostream out(name);
cgm.getCXXABI().getMangleContext().mangleCXXRTTIName(ty, out);
- // We know that the mangled name of the type starts at index 4 of the
- // mangled name of the typename, so we can just index into it in order to
- // get the mangled name of the type.
+ // RTTI type-name symbol has form "_ZTS<mangled-string>". The string stored in
+ // type_info object excludes the "_ZTS" prefix. So we skip past the first 4
+ // characters. For types that do not have externally visible Clang/C++
+ // linkage, '*' is prepended to the type-name string so that within libstdc++
+ // RTTI names compare correctly via strcmp across translation units. LLVM
+ // internal linkage marks incomplete types as internal linkage, resulting in
+ // '*' being prepended incorrectly, thus Clang/C++ linkage is used, through
+ // isExternallyVisible.
+ SmallString<256> typeName;
+ if (!isExternallyVisible(ty->getLinkage()))
+ typeName += '*';
+ typeName += StringRef(name).substr(4);
mlir::Attribute init = builder.getString(
- name.substr(4), cgm.convertType(cgm.getASTContext().CharTy),
- std::nullopt);
+ typeName, cgm.convertType(cgm.getASTContext().CharTy), std::nullopt);
CharUnits align =
cgm.getASTContext().getTypeAlignInChars(cgm.getASTContext().CharTy);
@@ -1181,6 +1189,8 @@ CIRGenItaniumRTTIBuilder::getAddrOfTypeName(mlir::Location loc, QualType ty,
// So cast Init to a ConstArrayAttr should be safe.
auto initStr = cast<cir::ConstArrayAttr>(init);
+ // RTTI type-name object is still emitted with prefix "_ZTS" in the symbol
+ // name, so that it can be found by the linker.
cir::GlobalOp gv = cgm.createOrReplaceCXXRuntimeVariable(
loc, name, initStr.getType(), linkage, align);
CIRGenModule::setInitializer(gv, init);
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index c17813140b10f7..87a643c6412de3 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -3656,22 +3656,33 @@ llvm::GlobalVariable *ItaniumRTTIBuilder::GetAddrOfTypeName(
llvm::raw_svector_ostream Out(Name);
CGM.getCXXABI().getMangleContext().mangleCXXRTTIName(Ty, Out);
- // We know that the mangled name of the type starts at index 4 of the
- // mangled name of the typename, so we can just index into it in order to
- // get the mangled name of the type.
+ // RTTI type-name symbol has form "_ZTS<mangled-string>". The string stored in
+ // type_info object excludes the "_ZTS" prefix. So we skip past the first 4
+ // characters. For types that do not have externally visible Clang/C++
+ // linkage, '*' is prepended to the type-name string so that within libstdc++
+ // RTTI names compare correctly via strcmp across translation units. LLVM
+ // internal linkage marks incomplete types as internal linkage, resulting in
+ // '*' being prepended incorrectly, thus Clang/C++ linkage is used, through
+ // isExternal
+ SmallString<256> TypeName;
+ if (!isExternallyVisible(Ty->getLinkage()))
+ TypeName += '*';
+ TypeName += StringRef(Name).substr(4);
llvm::Constant *Init;
if (CGM.getTriple().isOSzOS()) {
// On z/OS, typename is stored as 2 encodings: EBCDIC followed by ASCII.
SmallString<256> DualEncodedName;
- llvm::ConverterEBCDIC::convertToEBCDIC(Name.substr(4), DualEncodedName);
+ llvm::ConverterEBCDIC::convertToEBCDIC(TypeName, DualEncodedName);
DualEncodedName += '\0';
- DualEncodedName += Name.substr(4);
+ DualEncodedName += TypeName;
Init = llvm::ConstantDataArray::getString(VMContext, DualEncodedName);
} else
- Init = llvm::ConstantDataArray::getString(VMContext, Name.substr(4));
+ Init = llvm::ConstantDataArray::getString(VMContext, TypeName);
auto Align = CGM.getContext().getTypeAlignInChars(CGM.getContext().CharTy);
+ // RTTI type-name object is still emitted with prefix "_ZTS" in the symbol
+ // name, so that it can be found by the linker.
llvm::GlobalVariable *GV = CGM.CreateOrReplaceCXXRuntimeVariable(
Name, Init->getType(), Linkage, Align.getAsAlign());
diff --git a/clang/test/CIR/CodeGen/CIR-internal-linkage-typeinfo-name.cpp b/clang/test/CIR/CodeGen/CIR-internal-linkage-typeinfo-name.cpp
new file mode 100644
index 00000000000000..3a43ea283ab153
--- /dev/null
+++ b/clang/test/CIR/CodeGen/CIR-internal-linkage-typeinfo-name.cpp
@@ -0,0 +1,51 @@
+//RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 -fclangir -emit-cir -o - %s | FileCheck %s
+
+// Check that RTTI type-name strings use a leading '*' for types that do not have externally visible Clang/C++ linkage,
+// and for it to be omitted from externally visible types.
+
+namespace std { class type_info; }
+
+//The following types have internal linkage, so their typeinfo names should have a leading '*'
+namespace { struct A {}; }
+const std::type_info &t1() { return typeid(A); }
+
+const std::type_info &t2() { struct L {}; return typeid(L); }
+
+const std::type_info &t3() { return typeid(A*); }
+
+template <class T> struct B {};
+const std::type_info &t4() { return typeid(B<A>); }
+
+namespace { struct C { int x; }; }
+const std::type_info &t5() { return typeid(int A::*); }
+
+//Following should not have a '*' prefix in the typeinfo name, since they have external linkage
+struct Ext {};
+
+const std::type_info &t6() { return typeid(Ext); }
+
+const std::type_info &t7() { return typeid(Ext*); }
+
+namespace NS2 { struct D {}; }
+
+const std::type_info &t8() { return typeid(NS2::D); }
+
+struct Fwd;
+const std::type_info &t9() { return typeid(Fwd*); }
+
+// The following checks emitted RTTI type-names. The global name is the ABI-mangled type-name object,
+// while the string constant is the mangled type-name itself. For types without externally visible linkage,
+// Clang emits these as 'internal'. {{.*}} matches for array sizes, which is irrelevant to this test.
+
+// These checks are for types with internal linkage, which should have a '*' prefix in the typeinfo name.
+//CHECK-DAG: cir.global{{.*}}internal{{.*}}@_ZTSN12_GLOBAL__N_11AE = #cir.const_array<"*N12_GLOBAL__N_11AE"{{.*}}>
+//CHECK-DAG: cir.global{{.*}}internal{{.*}}@_ZTSZ2t2vE1L = #cir.const_array<"*Z2t2vE1L"{{.*}}>
+//CHECK-DAG: cir.global{{.*}}internal{{.*}}@_ZTSPN12_GLOBAL__N_11AE = #cir.const_array<"*PN12_GLOBAL__N_11AE"{{.*}}>
+//CHECK-DAG: cir.global{{.*}}internal{{.*}}@_ZTS1BIN12_GLOBAL__N_11AEE = #cir.const_array<"*1BIN12_GLOBAL__N_11AEE"{{.*}}>
+//CHECK-DAG: cir.global{{.*}}internal{{.*}}@_ZTSMN12_GLOBAL__N_11AEi = #cir.const_array<"*MN12_GLOBAL__N_11AEi"{{.*}}>
+
+// These checks are for types with external linkage, which should not have a '*' prefix in the typeinfo name.
+//CHECK-DAG: cir.global{{.*}}@_ZTS3Ext = #cir.const_array<"3Ext"{{.*}}>
+//CHECK-DAG: cir.global{{.*}}@_ZTSP3Ext = #cir.const_array<"P3Ext"{{.*}}>
+//CHECK-DAG: cir.global{{.*}}@_ZTSN3NS21DE = #cir.const_array<"N3NS21DE"{{.*}}>
+//CHECK-DAG: cir.global{{.*}}@_ZTSP3Fwd = #cir.const_array<"P3Fwd"{{.*}}>
\ No newline at end of file
diff --git a/clang/test/CIR/CodeGenCXX/vtable-linkage.cpp b/clang/test/CIR/CodeGenCXX/vtable-linkage.cpp
index 54f6f9c54a69c0..7bdb13252cfc2c 100644
--- a/clang/test/CIR/CodeGenCXX/vtable-linkage.cpp
+++ b/clang/test/CIR/CodeGenCXX/vtable-linkage.cpp
@@ -197,19 +197,19 @@ void use_F() {
// The anonymous struct for e has no linkage, so the vtable should have
// internal linkage.
// CIR-DAG: cir.global "private" constant internal dso_local @_ZTV3$_0 = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI3$_0> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1D1fEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 3>}> : !{{.*}}{alignment = 8 : i64}
-// CIR-DAG: cir.global constant internal dso_local @_ZTS3$_0 = #cir.const_array<"3$_0" : !cir.array<!s8i x 4>, trailing_zeros> : !cir.array<!s8i x 5> {alignment = 1 : i64}
+// CIR-DAG: cir.global constant internal dso_local @_ZTS3$_0 = #cir.const_array<"*3$_0" : !cir.array<!s8i x 5>, trailing_zeros> : !cir.array<!s8i x 6> {alignment = 1 : i64}
// CIR-DAG: cir.global constant internal @_ZTI3$_0 = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv120__si_class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS3$_0> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1D> : !cir.ptr<!u8i>}> : !{{.*}}{alignment = 8 : i64}
-// LLVM-DAG: @"_ZTV3$_0" = internal constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr @"_ZTI3$_0", ptr @_ZN1D1fEv] }, align 8
-// LLVM-DAG: @"_ZTS3$_0" = internal constant [5 x i8] c"3$_0\00", align 1
+// LLVM-DAG: @"_ZTV3$_0" = internal constant {{.*}}{ [3 x ptr] } { [3 x ptr] [ptr null, ptr @"_ZTI3$_0", ptr @_ZN1D1fEv] }, align 8
+// LLVM-DAG: @"_ZTS3$_0" = internal constant {{.*}}[6 x i8] c"*3$_0\00", align 1
// LLVM-DAG: @"_ZTI3$_0" = internal constant { ptr, ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv120__si_class_type_infoE, i64 {{.*}}), ptr @"_ZTS3$_0", ptr @_ZTI1D }, align 8
// The A vtable should have internal linkage since it is inside an anonymous
// namespace.
// CIR-DAG: cir.global "private" constant internal dso_local @_ZTVN12_GLOBAL__N_11AE = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTIN12_GLOBAL__N_11AE> : !cir.ptr<!u8i>, #cir.global_view<@_ZN12_GLOBAL__N_11A1fEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 3>}> : !{{.*}}{alignment = 8 : i64}
-// CIR-DAG: cir.global constant internal dso_local @_ZTSN12_GLOBAL__N_11AE = #cir.const_array<"N12_GLOBAL__N_11AE" : !cir.array<!s8i x 18>, trailing_zeros> : !cir.array<!s8i x 19> {alignment = 1 : i64}
+// CIR-DAG: cir.global constant internal dso_local @_ZTSN12_GLOBAL__N_11AE = #cir.const_array<"*N12_GLOBAL__N_11AE" : !cir.array<!s8i x 19>, trailing_zeros> : !cir.array<!s8i x 20> {alignment = 1 : i64}
// CIR-DAG: cir.global constant internal @_ZTIN12_GLOBAL__N_11AE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTSN12_GLOBAL__N_11AE> : !cir.ptr<!u8i>}> : !{{.*}}{alignment = 8 : i64}
-// LLVM-DAG: @_ZTVN12_GLOBAL__N_11AE = internal constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTIN12_GLOBAL__N_11AE, ptr @_ZN12_GLOBAL__N_11A1fEv] }, align 8
-// LLVM-DAG: @_ZTSN12_GLOBAL__N_11AE = internal constant [19 x i8] c"N12_GLOBAL__N_11AE\00", align 1
+// LLVM-DAG: @_ZTVN12_GLOBAL__N_11AE = internal constant {{.*}}{ [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTIN12_GLOBAL__N_11AE, ptr @_ZN12_GLOBAL__N_11A1fEv] }, align 8
+// LLVM-DAG: @_ZTSN12_GLOBAL__N_11AE = internal constant {{.*}}[20 x i8] c"*N12_GLOBAL__N_11AE\00", align 1
// LLVM-DAG: @_ZTIN12_GLOBAL__N_11AE = internal constant { ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTSN12_GLOBAL__N_11AE }, align 8
// F<char> is an explicit specialization without a key function, so
diff --git a/clang/test/CodeGenCXX/internal-linkage-typeinfo-name.cpp b/clang/test/CodeGenCXX/internal-linkage-typeinfo-name.cpp
new file mode 100644
index 00000000000000..3c8d1c1d0aa62a
--- /dev/null
+++ b/clang/test/CodeGenCXX/internal-linkage-typeinfo-name.cpp
@@ -0,0 +1,51 @@
+//RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 -emit-llvm -o - %s | FileCheck %s
+
+// Check that RTTI type-name strings use a leading '*' for types that do not have externally visible Clang/C++ linkage,
+// and for it to be omitted from externally visible types.
+
+namespace std { class type_info; }
+
+//The following types have internal linkage, so their typeinfo names should have a leading '*'
+namespace { struct A {}; }
+const std::type_info &t1() { return typeid(A); }
+
+const std::type_info &t2() { struct L {}; return typeid(L); }
+
+const std::type_info &t3() { return typeid(A*); }
+
+template <class T> struct B {};
+const std::type_info &t4() { return typeid(B<A>); }
+
+namespace { struct C { int x; }; }
+const std::type_info &t5() { return typeid(int A::*); }
+
+//Following should not have a '*' prefix in the typeinfo name, since they have external linkage
+struct Ext {};
+
+const std::type_info &t6() { return typeid(Ext); }
+
+const std::type_info &t7() { return typeid(Ext*); }
+
+namespace NS2 { struct D {}; }
+
+const std::type_info &t8() { return typeid(NS2::D); }
+
+struct Fwd;
+const std::type_info &t9() { return typeid(Fwd*); }
+
+// The following checks emitted RTTI type-names. The global name is the ABI-mangled type-name object,
+// while the string constant is the mangled type-name itself. For types without externally visible linkage,
+// Clang emits these as 'internal constant'. {{.*}} matches for array sizes, which is irrelevant to this test.
+
+// These checks are for types with internal linkage, which should have a '*' prefix in the typeinfo name.
+//CHECK-DAG: @_ZTSN12_GLOBAL__N_11AE = internal constant {{.*}}c"*N12_GLOBAL__N_11AE\00"
+//CHECK-DAG: @_ZTSZ2t2vE1L = internal constant {{.*}}c"*Z2t2vE1L\00"
+//CHECK-DAG: @_ZTSPN12_GLOBAL__N_11AE = internal constant {{.*}}c"*PN12_GLOBAL__N_11AE\00"
+//CHECK-DAG: @_ZTS1BIN12_GLOBAL__N_11AEE = internal constant {{.*}}c"*1BIN12_GLOBAL__N_11AEE\00"
+//CHECK-DAG: @_ZTSMN12_GLOBAL__N_11AEi = internal constant {{.*}}c"*MN12_GLOBAL__N_11AEi\00"
+
+// These checks are for types with external linkage, which should not have a '*' prefix in the typeinfo name.
+//CHECK-DAG: @_ZTS3Ext = {{.*}}constant {{.*}}c"3Ext\00"
+//CHECK-DAG: @_ZTSP3Ext = {{.*}}constant {{.*}}c"P3Ext\00"
+//CHECK-DAG: @_ZTSN3NS21DE = {{.*}}constant {{.*}}c"N3NS21DE\00"
+//CHECK-DAG: @_ZTSP3Fwd = {{.*}}constant {{.*}}c"P3Fwd\00"
\ No newline at end of file
>From 48e9903af518f3c051a49eb2d3dd6f6582206cee Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Tue, 22 Sep 2026 08:59:28 -0700
Subject: [PATCH 2/4] removed * prefix from rtti name() to match libstdc++,
resulting in no change to libc++. Also added a test to verify this
---
libcxx/include/typeinfo | 3 +-
.../type_info.name.internal_linkage.pass.cpp | 30 +++++++++++++++++++
2 files changed, 32 insertions(+), 1 deletion(-)
create mode 100644 libcxx/test/libcxx/language.support/support.rtti/type.info/type_info.name.internal_linkage.pass.cpp
diff --git a/libcxx/include/typeinfo b/libcxx/include/typeinfo
index aca6b91768d095..58ae6ece281855 100644
--- a/libcxx/include/typeinfo
+++ b/libcxx/include/typeinfo
@@ -309,7 +309,8 @@ protected:
public:
virtual ~type_info();
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const char* name() const _NOEXCEPT {
- return __impl::__type_name_to_string(__type_name);
+ const char* __str = __impl::__type_name_to_string(__type_name);
+ return __str[0] == '*' ? __str + 1 : __str;
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool before(const type_info& __arg) const _NOEXCEPT {
diff --git a/libcxx/test/libcxx/language.support/support.rtti/type.info/type_info.name.internal_linkage.pass.cpp b/libcxx/test/libcxx/language.support/support.rtti/type.info/type_info.name.internal_linkage.pass.cpp
new file mode 100644
index 00000000000000..8d5e487fd598e7
--- /dev/null
+++ b/libcxx/test/libcxx/language.support/support.rtti/type.info/type_info.name.internal_linkage.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: no-rtti
+
+// Compilers may prefix the type_info::name() result with a '*' to indicate that the type has internal linkage. This test checks that libc++ strips the '*' prefix from the name() result.
+
+#include <typeinfo>
+#include <cassert>
+
+namespace {
+struct AnonymousType {};
+} // namespace
+
+const std::type_info& local_type() {
+ struct LocalType {};
+ return typeid(LocalType);
+}
+
+int main(int, char**) {
+ assert(typeid(AnonymousType).name()[0] != '*');
+ assert(typeid(AnonymousType*).name()[0] != '*');
+ assert(local_type().name()[0] != '*');
+ return 0;
+}
>From 4588416e8d62d4ff0fa739fea5980650249bec43 Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Tue, 22 Sep 2026 09:18:51 -0700
Subject: [PATCH 3/4] Moved release notes into ABI changes
---
clang/docs/ReleaseNotes.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index e25586458f23cc..c1fc79c959d396 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -112,6 +112,9 @@ features cannot lower the translation-unit ABI level;
for homogeneous aggregate classification.
`-fclang-abi-compat=23` restores the previous behavior. (#GH218799)
+- Fixed RTTI name for internal-linkage types lacking '*', fixed breaking type_info::operator== and
+ dynamic_cast with libstdc++ (#GH34255)
+
### AST Dumping Potentially Breaking Changes
### Clang Frontend Potentially Breaking Changes
>From 53e2dc8cf4aaccf98ad20ad2bc7df9551bbc4c25 Mon Sep 17 00:00:00 2001
From: "Jones, Matthew (DI SW SIM FRWK PLAT SIMFW HPC)"
<jones.matthew at siemens.com>
Date: Wed, 23 Sep 2026 07:21:49 -0700
Subject: [PATCH 4/4] fixed non_unique_impl incorrectly compared internally
linked types via strcmp, fixed test which incorrectly sets two internally
linked typeinfo names as equal
---
libcxx/include/typeinfo | 13 +++++++++++--
.../type.info/type_info.comparison.unmerged.sh.cpp | 2 +-
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/libcxx/include/typeinfo b/libcxx/include/typeinfo
index 58ae6ece281855..9afec27ea4e6ea 100644
--- a/libcxx/include/typeinfo
+++ b/libcxx/include/typeinfo
@@ -221,10 +221,19 @@ struct __non_unique_impl : __string_impl_base {
return __hash;
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE static bool __eq(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
- return __lhs == __rhs || __builtin_strcmp(__lhs, __rhs) == 0;
+ // Internal linkage types (marked with '*') cannot compare equal via strcmp.
+ // They are only equal if they refer to the same RTTI object.
+ return __lhs == __rhs || (__lhs[0] != '*' && __builtin_strcmp(__lhs, __rhs) == 0);
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE static bool __lt(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
- return __builtin_strcmp(__lhs, __rhs) < 0;
+ int __r = __builtin_strcmp(__lhs, __rhs);
+ if (__r != 0)
+ return __r < 0;
+ // Internal linkage types (marked with '*') are ordered via pointer address.
+ if (__lhs[0] == '*') {
+ return reinterpret_cast<uintptr_t>(__lhs) < reinterpret_cast<uintptr_t>(__rhs);
+ }
+ return false;
}
};
diff --git a/libcxx/test/libcxx/language.support/support.rtti/type.info/type_info.comparison.unmerged.sh.cpp b/libcxx/test/libcxx/language.support/support.rtti/type.info/type_info.comparison.unmerged.sh.cpp
index 9b94fcbc1c92a8..937b5057a1f984 100644
--- a/libcxx/test/libcxx/language.support/support.rtti/type.info/type_info.comparison.unmerged.sh.cpp
+++ b/libcxx/test/libcxx/language.support/support.rtti/type.info/type_info.comparison.unmerged.sh.cpp
@@ -37,7 +37,7 @@ void register2();
register2();
assert(registry.size() == 2);
- assert(registry[0] == registry[1]);
+ assert(registry[0] != registry[1]);
return 0;
}
#else
More information about the cfe-commits
mailing list