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)" <[email protected]> Date: Tue, 11 Aug 2026 03:13:24 -0700 Subject: [PATCH] [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 a49971adef86f..fdbb12e653da5 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 800f20ace0e70..2caabd9292db0 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 c17813140b10f..87a643c6412de 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 0000000000000..3a43ea283ab15 --- /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 54f6f9c54a69c..7bdb13252cfc2 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 0000000000000..3c8d1c1d0aa62 --- /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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
