Skip to content

Commit c4896d2

Browse files
committed
[CIR] Fix RTTI mangling assertion with qualified types
Fixes an assertion failure when creating RTTI descriptors for types with top-level qualifiers (const, volatile, etc.). The Itanium C++ ABI requires that RTTI descriptors be created for unqualified types only, but ClangIR was passing qualified types to the mangling function. The fix strips qualifiers in BuildTypeInfo before mangling, matching the behavior of traditional CodeGen. Also strips qualifiers when getting RTTI descriptors for dynamic_cast operands, consistent with CodeGen's implementation. This fixes std::regex usage and other cases where qualified types are used with typeid or dynamic_cast. ghstack-source-id: 3c9ffe9 Pull-Request: #2002
1 parent 2d3281b commit c4896d2

File tree

2 files changed

+154
-2
lines changed

2 files changed

+154
-2
lines changed

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,6 +1542,10 @@ mlir::Attribute CIRGenItaniumRTTIBuilder::BuildTypeInfo(mlir::Location loc,
15421542
// We want to operate on the canonical type.
15431543
Ty = Ty.getCanonicalType();
15441544

1545+
// RTTI descriptors for qualified types are really just descriptors
1546+
// for the unqualified type, so strip qualifiers.
1547+
Ty = Ty.getUnqualifiedType();
1548+
15451549
// Check if we've already emitted an RTTI descriptor for this type.
15461550
SmallString<256> Name;
15471551
llvm::raw_svector_ostream Out(Name);
@@ -2696,9 +2700,9 @@ static cir::DynamicCastInfoAttr emitDynamicCastInfo(CIRGenFunction &CGF,
26962700
QualType SrcRecordTy,
26972701
QualType DestRecordTy) {
26982702
auto srcRtti = mlir::cast<cir::GlobalViewAttr>(
2699-
CGF.CGM.getAddrOfRTTIDescriptor(Loc, SrcRecordTy));
2703+
CGF.CGM.getAddrOfRTTIDescriptor(Loc, SrcRecordTy.getUnqualifiedType()));
27002704
auto destRtti = mlir::cast<cir::GlobalViewAttr>(
2701-
CGF.CGM.getAddrOfRTTIDescriptor(Loc, DestRecordTy));
2705+
CGF.CGM.getAddrOfRTTIDescriptor(Loc, DestRecordTy.getUnqualifiedType()));
27022706

27032707
auto runtimeFuncOp = getItaniumDynamicCastFn(CGF);
27042708
auto badCastFuncOp = getBadCastFn(CGF);
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
3+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
4+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
5+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.og.ll
6+
// RUN: FileCheck --input-file=%t.og.ll %s --check-prefix=OGCG
7+
8+
namespace std {
9+
class type_info {
10+
public:
11+
virtual ~type_info();
12+
const char* name() const { return __name; }
13+
protected:
14+
const char *__name;
15+
};
16+
}
17+
18+
// Test RTTI with qualified types
19+
// This tests the fix for the bug where RTTI descriptors were being created
20+
// with top-level qualifiers, violating the Itanium ABI assertion:
21+
// "RTTI info cannot have top-level qualifiers"
22+
23+
struct Simple {
24+
int x;
25+
};
26+
27+
// Test 1: typeid with const-qualified type
28+
void test_const_type(const std::type_info*& out) {
29+
// CIR-LABEL: cir.func {{.*}}@_Z15test_const_type
30+
out = &typeid(const int);
31+
// CIR: cir.get_global @_ZTIi
32+
// Note: Should use unqualified type's RTTI (_ZTIi, not _ZTIKi)
33+
34+
// LLVM-LABEL: define {{.*}}@_Z15test_const_type
35+
// LLVM: store ptr @_ZTIi
36+
37+
// OGCG-LABEL: define {{.*}}@_Z15test_const_type
38+
// OGCG: store ptr @_ZTIi
39+
}
40+
41+
// Test 2: typeid with volatile-qualified type
42+
void test_volatile_type(const std::type_info*& out) {
43+
// CIR-LABEL: cir.func {{.*}}@_Z18test_volatile_type
44+
out = &typeid(volatile int);
45+
// CIR: cir.get_global @_ZTIi
46+
// Note: Should use unqualified type's RTTI
47+
48+
// LLVM-LABEL: define {{.*}}@_Z18test_volatile_type
49+
// LLVM: store ptr @_ZTIi
50+
51+
// OGCG-LABEL: define {{.*}}@_Z18test_volatile_type
52+
// OGCG: store ptr @_ZTIi
53+
}
54+
55+
// Test 3: typeid with const-volatile-qualified type
56+
void test_const_volatile_type(const std::type_info*& out) {
57+
// CIR-LABEL: cir.func {{.*}}@_Z24test_const_volatile_type
58+
out = &typeid(const volatile int);
59+
// CIR: cir.get_global @_ZTIi
60+
// Note: Should use unqualified type's RTTI
61+
62+
// LLVM-LABEL: define {{.*}}@_Z24test_const_volatile_type
63+
// LLVM: store ptr @_ZTIi
64+
65+
// OGCG-LABEL: define {{.*}}@_Z24test_const_volatile_type
66+
// OGCG: store ptr @_ZTIi
67+
}
68+
69+
// Test 4: typeid with const pointer (the pointer itself is const)
70+
void test_const_pointer(const std::type_info*& out) {
71+
// CIR-LABEL: cir.func {{.*}}@_Z18test_const_pointer
72+
int* const ptr = nullptr;
73+
out = &typeid(ptr);
74+
// CIR: cir.get_global @_ZTIPi
75+
// Note: typeid of a const pointer should use unqualified pointer type RTTI
76+
77+
// LLVM-LABEL: define {{.*}}@_Z18test_const_pointer
78+
// LLVM: store ptr @_ZTIPi
79+
80+
// OGCG-LABEL: define {{.*}}@_Z18test_const_pointer
81+
// OGCG: store ptr @_ZTIPi
82+
}
83+
84+
// Test 5: typeid with pointer to const (pointee is const)
85+
void test_pointer_to_const(const std::type_info*& out) {
86+
// CIR-LABEL: cir.func {{.*}}@_Z21test_pointer_to_const
87+
const int* ptr = nullptr;
88+
out = &typeid(ptr);
89+
// CIR: cir.get_global @_ZTIPKi
90+
// Note: Pointer to const int has different RTTI (qualifiers on pointee matter)
91+
92+
// LLVM-LABEL: define {{.*}}@_Z21test_pointer_to_const
93+
// LLVM: store ptr @_ZTIPKi
94+
95+
// OGCG-LABEL: define {{.*}}@_Z21test_pointer_to_const
96+
// OGCG: store ptr @_ZTIPKi
97+
}
98+
99+
// Test 6: typeid with qualified struct type
100+
void test_const_struct(const std::type_info*& out) {
101+
// CIR-LABEL: cir.func {{.*}}@_Z17test_const_struct
102+
out = &typeid(const Simple);
103+
// CIR: cir.get_global @_ZTI6Simple
104+
// Note: Should use unqualified struct's RTTI
105+
106+
// LLVM-LABEL: define {{.*}}@_Z17test_const_struct
107+
// LLVM: store ptr @_ZTI6Simple
108+
109+
// OGCG-LABEL: define {{.*}}@_Z17test_const_struct
110+
// OGCG: store ptr @_ZTI6Simple
111+
}
112+
113+
// Test 7: typeid with qualified expression
114+
void test_const_expr(const std::type_info*& out) {
115+
// CIR-LABEL: cir.func {{.*}}@_Z15test_const_expr
116+
const int x = 42;
117+
out = &typeid(x);
118+
// CIR: cir.get_global @_ZTIi
119+
// Note: Should use unqualified type's RTTI
120+
121+
// LLVM-LABEL: define {{.*}}@_Z15test_const_expr
122+
// LLVM: store ptr @_ZTIi
123+
124+
// OGCG-LABEL: define {{.*}}@_Z15test_const_expr
125+
// OGCG: store ptr @_ZTIi
126+
}
127+
128+
// Test 8: dynamic_cast with qualified types
129+
struct PolyBase {
130+
virtual ~PolyBase() = default;
131+
};
132+
133+
struct PolyDerived : PolyBase {
134+
int value;
135+
};
136+
137+
void test_dynamic_cast_qualified(const PolyBase* b, const PolyDerived*& out) {
138+
// CIR-LABEL: cir.func {{.*}}@_Z27test_dynamic_cast_qualified
139+
out = dynamic_cast<const PolyDerived*>(b);
140+
// CIR: cir.call @__dynamic_cast
141+
// Note: Should use unqualified RTTI descriptors for Base and Derived
142+
143+
// LLVM-LABEL: define {{.*}}@_Z27test_dynamic_cast_qualified
144+
// LLVM: call ptr @__dynamic_cast
145+
146+
// OGCG-LABEL: define {{.*}}@_Z27test_dynamic_cast_qualified
147+
// OGCG: call ptr @__dynamic_cast
148+
}

0 commit comments

Comments
 (0)