Skip to content

Commit 14f7bd6

Browse files
authored
Reland: [clang] preserve class type sugar when taking pointer to member (#132401)
Original PR: #130537 Originally reverted due to revert of dependent commit. Relanding with no changes. This changes the MemberPointerType representation to use a NestedNameSpecifier instead of a Type to represent the base class. Since the qualifiers are always parsed as nested names, there was an impedance mismatch when converting these back and forth into types, and this led to issues in preserving sugar. The nested names are indeed a better match for these, as the differences which a QualType can represent cannot be expressed syntatically, and they represent the use case more exactly, being either dependent or referring to a CXXRecord, unqualified. This patch also makes the MemberPointerType able to represent sugar for a {up/downcast}cast conversion of the base class, although for now the underlying type is canonical, as preserving the sugar up to that point requires further work. As usual, includes a few drive-by fixes in order to make use of the improvements.
1 parent 4dd7fea commit 14f7bd6

File tree

71 files changed

+1044
-427
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+1044
-427
lines changed

clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,8 +493,7 @@ UseNullptrCheck::UseNullptrCheck(StringRef Name, ClangTidyContext *Context)
493493
: ClangTidyCheck(Name, Context),
494494
NullMacrosStr(Options.get("NullMacros", "NULL")),
495495
IgnoredTypes(utils::options::parseStringList(Options.get(
496-
"IgnoredTypes",
497-
"std::_CmpUnspecifiedParam::;^std::__cmp_cat::__unspec"))) {
496+
"IgnoredTypes", "_CmpUnspecifiedParam;^std::__cmp_cat::__unspec"))) {
498497
StringRef(NullMacrosStr).split(NullMacros, ",");
499498
}
500499

clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,9 @@ bool isFunctionPointerConvertible(QualType From, QualType To) {
178178

179179
// Note: converting Derived::* to Base::* is a different kind of conversion,
180180
// called Pointer-to-member conversion.
181-
return FromMember->getClass() == ToMember->getClass() &&
181+
return FromMember->getQualifier() == ToMember->getQualifier() &&
182+
FromMember->getMostRecentCXXRecordDecl() ==
183+
ToMember->getMostRecentCXXRecordDecl() &&
182184
FromMember->getPointeeType() == ToMember->getPointeeType();
183185
}
184186

clang-tools-extra/clangd/unittests/FindTargetTests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1489,7 +1489,7 @@ TEST_F(FindExplicitReferencesTest, AllRefsInFoo) {
14891489
"4: targets = {a}\n"
14901490
"5: targets = {a::b}, qualifier = 'a::'\n"
14911491
"6: targets = {a::b::S}\n"
1492-
"7: targets = {a::b::S::type}, qualifier = 'struct S::'\n"
1492+
"7: targets = {a::b::S::type}, qualifier = 'S::'\n"
14931493
"8: targets = {y}, decl\n"},
14941494
{R"cpp(
14951495
void foo() {

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ Improvements to Clang's diagnostics
267267
under the subgroup ``-Wunsafe-buffer-usage-in-libc-call``.
268268
- Diagnostics on chained comparisons (``a < b < c``) are now an error by default. This can be disabled with
269269
``-Wno-error=parentheses``.
270+
- Clang now better preserves the sugared types of pointers to member.
270271
- The ``-Wshift-bool`` warning has been added to warn about shifting a boolean. (#GH28334)
271272
- Fixed diagnostics adding a trailing ``::`` when printing some source code
272273
constructs, like base classes.

clang/include/clang/AST/ASTContext.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,10 +1558,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
15581558
QualType getRValueReferenceType(QualType T) const;
15591559

15601560
/// Return the uniqued reference to the type for a member pointer to
1561-
/// the specified type in the specified class.
1562-
///
1563-
/// The class \p Cls is a \c Type because it could be a dependent name.
1564-
QualType getMemberPointerType(QualType T, const Type *Cls) const;
1561+
/// the specified type in the specified nested name.
1562+
QualType getMemberPointerType(QualType T, NestedNameSpecifier *Qualifier,
1563+
const CXXRecordDecl *Cls) const;
15651564

15661565
/// Return a non-unique reference to the type for a variable array of
15671566
/// the specified element type.

clang/include/clang/AST/ASTNodeTraverser.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,9 @@ class ASTNodeTraverser
393393
Visit(T->getPointeeType());
394394
}
395395
void VisitMemberPointerType(const MemberPointerType *T) {
396-
Visit(T->getClass());
396+
// FIXME: Provide a NestedNameSpecifier visitor.
397+
Visit(T->getQualifier()->getAsType());
398+
Visit(T->getMostRecentCXXRecordDecl());
397399
Visit(T->getPointeeType());
398400
}
399401
void VisitArrayType(const ArrayType *T) { Visit(T->getElementType()); }
@@ -485,7 +487,8 @@ class ASTNodeTraverser
485487
}
486488
}
487489
void VisitMemberPointerTypeLoc(MemberPointerTypeLoc TL) {
488-
Visit(TL.getClassTInfo()->getTypeLoc());
490+
// FIXME: Provide NestedNamespecifierLoc visitor.
491+
Visit(TL.getQualifierLoc().getTypeLoc());
489492
}
490493
void VisitVariableArrayTypeLoc(VariableArrayTypeLoc TL) {
491494
Visit(TL.getSizeExpr());

clang/include/clang/AST/CanonicalType.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ template<>
453453
struct CanProxyAdaptor<MemberPointerType>
454454
: public CanProxyBase<MemberPointerType> {
455455
LLVM_CLANG_CANPROXY_TYPE_ACCESSOR(getPointeeType)
456-
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(const Type *, getClass)
456+
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(NestedNameSpecifier *, getQualifier)
457457
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(const CXXRecordDecl *,
458458
getMostRecentCXXRecordDecl)
459459
};

clang/include/clang/AST/RecursiveASTVisitor.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,7 +1004,8 @@ DEF_TRAVERSE_TYPE(RValueReferenceType,
10041004
{ TRY_TO(TraverseType(T->getPointeeType())); })
10051005

10061006
DEF_TRAVERSE_TYPE(MemberPointerType, {
1007-
TRY_TO(TraverseType(QualType(T->getClass(), 0)));
1007+
TRY_TO(TraverseNestedNameSpecifier(T->getQualifier()));
1008+
TRY_TO(TraverseDecl(T->getMostRecentCXXRecordDecl()));
10081009
TRY_TO(TraverseType(T->getPointeeType()));
10091010
})
10101011

@@ -1269,10 +1270,10 @@ DEF_TRAVERSE_TYPELOC(RValueReferenceType,
12691270
// We traverse this in the type case as well, but how is it not reached through
12701271
// the pointee type?
12711272
DEF_TRAVERSE_TYPELOC(MemberPointerType, {
1272-
if (auto *TSI = TL.getClassTInfo())
1273-
TRY_TO(TraverseTypeLoc(TSI->getTypeLoc()));
1273+
if (NestedNameSpecifierLoc QL = TL.getQualifierLoc())
1274+
TRY_TO(TraverseNestedNameSpecifierLoc(QL));
12741275
else
1275-
TRY_TO(TraverseType(QualType(TL.getTypePtr()->getClass(), 0)));
1276+
TRY_TO(TraverseNestedNameSpecifier(TL.getTypePtr()->getQualifier()));
12761277
TRY_TO(TraverseTypeLoc(TL.getPointeeLoc()));
12771278
})
12781279

clang/include/clang/AST/Type.h

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3527,14 +3527,16 @@ class MemberPointerType : public Type, public llvm::FoldingSetNode {
35273527
QualType PointeeType;
35283528

35293529
/// The class of which the pointee is a member. Must ultimately be a
3530-
/// RecordType, but could be a typedef or a template parameter too.
3531-
const Type *Class;
3530+
/// CXXRecordType, but could be a typedef or a template parameter too.
3531+
NestedNameSpecifier *Qualifier;
35323532

3533-
MemberPointerType(QualType Pointee, const Type *Cls, QualType CanonicalPtr)
3533+
MemberPointerType(QualType Pointee, NestedNameSpecifier *Qualifier,
3534+
QualType CanonicalPtr)
35343535
: Type(MemberPointer, CanonicalPtr,
3535-
(Cls->getDependence() & ~TypeDependence::VariablyModified) |
3536+
(toTypeDependence(Qualifier->getDependence()) &
3537+
~TypeDependence::VariablyModified) |
35363538
Pointee->getDependence()),
3537-
PointeeType(Pointee), Class(Cls) {}
3539+
PointeeType(Pointee), Qualifier(Qualifier) {}
35383540

35393541
public:
35403542
QualType getPointeeType() const { return PointeeType; }
@@ -3551,21 +3553,21 @@ class MemberPointerType : public Type, public llvm::FoldingSetNode {
35513553
return !PointeeType->isFunctionProtoType();
35523554
}
35533555

3554-
const Type *getClass() const { return Class; }
3556+
NestedNameSpecifier *getQualifier() const { return Qualifier; }
35553557
CXXRecordDecl *getMostRecentCXXRecordDecl() const;
35563558

3557-
bool isSugared() const { return false; }
3558-
QualType desugar() const { return QualType(this, 0); }
3559+
bool isSugared() const;
3560+
QualType desugar() const {
3561+
return isSugared() ? getCanonicalTypeInternal() : QualType(this, 0);
3562+
}
35593563

35603564
void Profile(llvm::FoldingSetNodeID &ID) {
3561-
Profile(ID, getPointeeType(), getClass());
3565+
Profile(ID, getPointeeType(), getQualifier(), getMostRecentCXXRecordDecl());
35623566
}
35633567

35643568
static void Profile(llvm::FoldingSetNodeID &ID, QualType Pointee,
3565-
const Type *Class) {
3566-
ID.AddPointer(Pointee.getAsOpaquePtr());
3567-
ID.AddPointer(Class);
3568-
}
3569+
const NestedNameSpecifier *Qualifier,
3570+
const CXXRecordDecl *Cls);
35693571

35703572
static bool classof(const Type *T) {
35713573
return T->getTypeClass() == MemberPointer;

clang/include/clang/AST/TypeLoc.h

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class TypeLoc {
139139
}
140140

141141
/// Get the pointer where source information is stored.
142+
// FIXME: This should provide a type-safe interface.
142143
void *getOpaqueData() const {
143144
return Data;
144145
}
@@ -1355,7 +1356,7 @@ class BlockPointerTypeLoc : public PointerLikeTypeLoc<BlockPointerTypeLoc,
13551356
};
13561357

13571358
struct MemberPointerLocInfo : public PointerLikeLocInfo {
1358-
TypeSourceInfo *ClassTInfo;
1359+
void *QualifierData = nullptr;
13591360
};
13601361

13611362
/// Wrapper for source info for member pointers.
@@ -1371,28 +1372,32 @@ class MemberPointerTypeLoc : public PointerLikeTypeLoc<MemberPointerTypeLoc,
13711372
setSigilLoc(Loc);
13721373
}
13731374

1374-
const Type *getClass() const {
1375-
return getTypePtr()->getClass();
1376-
}
1377-
1378-
TypeSourceInfo *getClassTInfo() const {
1379-
return getLocalData()->ClassTInfo;
1375+
NestedNameSpecifierLoc getQualifierLoc() const {
1376+
return NestedNameSpecifierLoc(getTypePtr()->getQualifier(),
1377+
getLocalData()->QualifierData);
13801378
}
13811379

1382-
void setClassTInfo(TypeSourceInfo* TI) {
1383-
getLocalData()->ClassTInfo = TI;
1380+
void setQualifierLoc(NestedNameSpecifierLoc QualifierLoc) {
1381+
assert(QualifierLoc.getNestedNameSpecifier() ==
1382+
getTypePtr()->getQualifier() &&
1383+
"Inconsistent nested-name-specifier pointer");
1384+
getLocalData()->QualifierData = QualifierLoc.getOpaqueData();
13841385
}
13851386

13861387
void initializeLocal(ASTContext &Context, SourceLocation Loc) {
13871388
setSigilLoc(Loc);
1388-
setClassTInfo(nullptr);
1389+
if (auto *Qualifier = getTypePtr()->getQualifier()) {
1390+
NestedNameSpecifierLocBuilder Builder;
1391+
Builder.MakeTrivial(Context, Qualifier, Loc);
1392+
setQualifierLoc(Builder.getWithLocInContext(Context));
1393+
} else
1394+
getLocalData()->QualifierData = nullptr;
13891395
}
13901396

13911397
SourceRange getLocalSourceRange() const {
1392-
if (TypeSourceInfo *TI = getClassTInfo())
1393-
return SourceRange(TI->getTypeLoc().getBeginLoc(), getStarLoc());
1394-
else
1395-
return SourceRange(getStarLoc());
1398+
if (NestedNameSpecifierLoc QL = getQualifierLoc())
1399+
return SourceRange(QL.getBeginLoc(), getStarLoc());
1400+
return SourceRange(getStarLoc());
13961401
}
13971402
};
13981403

clang/include/clang/AST/TypeProperties.td

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,15 @@ let Class = MemberPointerType in {
100100
def : Property<"pointeeType", QualType> {
101101
let Read = [{ node->getPointeeType() }];
102102
}
103-
def : Property<"baseType", QualType> {
104-
let Read = [{ QualType(node->getClass(), 0) }];
103+
def : Property<"Qualifier", NestedNameSpecifier> {
104+
let Read = [{ node->getQualifier() }];
105+
}
106+
def : Property<"Cls", DeclRef> {
107+
let Read = [{ node->getMostRecentCXXRecordDecl() }];
105108
}
106109

107110
def : Creator<[{
108-
return ctx.getMemberPointerType(pointeeType, baseType.getTypePtr());
111+
return ctx.getMemberPointerType(pointeeType, Qualifier, cast_or_null<CXXRecordDecl>(Cls));
109112
}]>;
110113
}
111114

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6975,10 +6975,8 @@ def err_illegal_decl_mempointer_to_reference : Error<
69756975
"'%0' declared as a member pointer to a reference of type %1">;
69766976
def err_illegal_decl_mempointer_to_void : Error<
69776977
"'%0' declared as a member pointer to void">;
6978-
def err_illegal_decl_mempointer_in_nonclass : Error<
6979-
"'%0' does not point into a class">;
6980-
def err_mempointer_in_nonclass_type : Error<
6981-
"member pointer refers into non-class type %0">;
6978+
def err_illegal_decl_mempointer_in_nonclass
6979+
: Error<"'%0' does not point into a class">;
69826980
def err_reference_to_void : Error<"cannot form a reference to 'void'">;
69836981
def err_nonfunction_block_type : Error<
69846982
"block pointer to non-function type is invalid">;

clang/include/clang/Sema/Sema.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,12 @@ class Sema final : public SemaBase {
13481348
unsigned DiagID, bool ForceCheck = false,
13491349
bool ForceUnprivileged = false);
13501350

1351+
AccessResult CheckBaseClassAccess(
1352+
SourceLocation AccessLoc, CXXRecordDecl *Base, CXXRecordDecl *Derived,
1353+
const CXXBasePath &Path, unsigned DiagID,
1354+
llvm::function_ref<void(PartialDiagnostic &PD)> SetupPDiag,
1355+
bool ForceCheck = false, bool ForceUnprivileged = false);
1356+
13511357
/// Checks access to all the declarations in the given result set.
13521358
void CheckLookupAccess(const LookupResult &R);
13531359

@@ -14879,8 +14885,9 @@ class Sema final : public SemaBase {
1487914885
///
1488014886
/// \returns a member pointer type, if successful, or a NULL type if there was
1488114887
/// an error.
14882-
QualType BuildMemberPointerType(QualType T, QualType Class,
14883-
SourceLocation Loc, DeclarationName Entity);
14888+
QualType BuildMemberPointerType(QualType T, NestedNameSpecifier *Qualifier,
14889+
CXXRecordDecl *Cls, SourceLocation Loc,
14890+
DeclarationName Entity);
1488414891

1488514892
/// Build a block pointer type.
1488614893
///

clang/lib/AST/ASTContext.cpp

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3322,7 +3322,8 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx,
33223322
case Type::MemberPointer: {
33233323
OS << "M";
33243324
const auto *MPT = T->castAs<MemberPointerType>();
3325-
encodeTypeForFunctionPointerAuth(Ctx, OS, QualType(MPT->getClass(), 0));
3325+
encodeTypeForFunctionPointerAuth(
3326+
Ctx, OS, QualType(MPT->getQualifier()->getAsType(), 0));
33263327
encodeTypeForFunctionPointerAuth(Ctx, OS, MPT->getPointeeType());
33273328
return;
33283329
}
@@ -3511,7 +3512,8 @@ uint16_t ASTContext::getPointerAuthTypeDiscriminator(QualType T) {
35113512
if (PointeeType->castAs<FunctionProtoType>()->getExceptionSpecType() !=
35123513
EST_None) {
35133514
QualType FT = getFunctionTypeWithExceptionSpec(PointeeType, EST_None);
3514-
T = getMemberPointerType(FT, MPT->getClass());
3515+
T = getMemberPointerType(FT, MPT->getQualifier(),
3516+
MPT->getMostRecentCXXRecordDecl());
35153517
}
35163518
}
35173519
std::unique_ptr<MangleContext> MC(createMangleContext());
@@ -4025,32 +4027,50 @@ QualType ASTContext::getRValueReferenceType(QualType T) const {
40254027
return QualType(New, 0);
40264028
}
40274029

4028-
/// getMemberPointerType - Return the uniqued reference to the type for a
4029-
/// member pointer to the specified type, in the specified class.
4030-
QualType ASTContext::getMemberPointerType(QualType T, const Type *Cls) const {
4030+
QualType ASTContext::getMemberPointerType(QualType T,
4031+
NestedNameSpecifier *Qualifier,
4032+
const CXXRecordDecl *Cls) const {
4033+
if (!Qualifier) {
4034+
assert(Cls && "At least one of Qualifier or Cls must be provided");
4035+
Qualifier = NestedNameSpecifier::Create(*this, /*Prefix=*/nullptr,
4036+
/*Template=*/false,
4037+
getTypeDeclType(Cls).getTypePtr());
4038+
} else if (!Cls) {
4039+
Cls = Qualifier->getAsRecordDecl();
4040+
}
40314041
// Unique pointers, to guarantee there is only one pointer of a particular
40324042
// structure.
40334043
llvm::FoldingSetNodeID ID;
4034-
MemberPointerType::Profile(ID, T, Cls);
4044+
MemberPointerType::Profile(ID, T, Qualifier, Cls);
40354045

40364046
void *InsertPos = nullptr;
40374047
if (MemberPointerType *PT =
40384048
MemberPointerTypes.FindNodeOrInsertPos(ID, InsertPos))
40394049
return QualType(PT, 0);
40404050

4051+
NestedNameSpecifier *CanonicalQualifier = [&] {
4052+
if (!Cls)
4053+
return getCanonicalNestedNameSpecifier(Qualifier);
4054+
NestedNameSpecifier *R = NestedNameSpecifier::Create(
4055+
*this, /*Prefix=*/nullptr, /*Template=*/false,
4056+
Cls->getCanonicalDecl()->getTypeForDecl());
4057+
assert(R == getCanonicalNestedNameSpecifier(R));
4058+
return R;
4059+
}();
40414060
// If the pointee or class type isn't canonical, this won't be a canonical
40424061
// type either, so fill in the canonical type field.
40434062
QualType Canonical;
4044-
if (!T.isCanonical() || !Cls->isCanonicalUnqualified()) {
4045-
Canonical = getMemberPointerType(getCanonicalType(T),getCanonicalType(Cls));
4046-
4063+
if (!T.isCanonical() || Qualifier != CanonicalQualifier) {
4064+
Canonical =
4065+
getMemberPointerType(getCanonicalType(T), CanonicalQualifier, Cls);
4066+
assert(!cast<MemberPointerType>(Canonical)->isSugared());
40474067
// Get the new insert position for the node we care about.
4048-
MemberPointerType *NewIP =
4049-
MemberPointerTypes.FindNodeOrInsertPos(ID, InsertPos);
4050-
assert(!NewIP && "Shouldn't be in the map!"); (void)NewIP;
4068+
[[maybe_unused]] MemberPointerType *NewIP =
4069+
MemberPointerTypes.FindNodeOrInsertPos(ID, InsertPos);
4070+
assert(!NewIP && "Shouldn't be in the map!");
40514071
}
40524072
auto *New = new (*this, alignof(MemberPointerType))
4053-
MemberPointerType(T, Cls, Canonical);
4073+
MemberPointerType(T, Qualifier, Canonical);
40544074
Types.push_back(New);
40554075
MemberPointerTypes.InsertNode(New, InsertPos);
40564076
return QualType(New, 0);
@@ -6812,11 +6832,16 @@ bool ASTContext::UnwrapSimilarTypes(QualType &T1, QualType &T2,
68126832
return true;
68136833
}
68146834

6815-
const auto *T1MPType = T1->getAs<MemberPointerType>();
6816-
const auto *T2MPType = T2->getAs<MemberPointerType>();
6817-
if (T1MPType && T2MPType &&
6818-
hasSameUnqualifiedType(QualType(T1MPType->getClass(), 0),
6819-
QualType(T2MPType->getClass(), 0))) {
6835+
if (const auto *T1MPType = T1->getAs<MemberPointerType>(),
6836+
*T2MPType = T2->getAs<MemberPointerType>();
6837+
T1MPType && T2MPType) {
6838+
if (auto *RD1 = T1MPType->getMostRecentCXXRecordDecl(),
6839+
*RD2 = T2MPType->getMostRecentCXXRecordDecl();
6840+
RD1 != RD2 && RD1->getCanonicalDecl() != RD2->getCanonicalDecl())
6841+
return false;
6842+
if (getCanonicalNestedNameSpecifier(T1MPType->getQualifier()) !=
6843+
getCanonicalNestedNameSpecifier(T2MPType->getQualifier()))
6844+
return false;
68206845
T1 = T1MPType->getPointeeType();
68216846
T2 = T2MPType->getPointeeType();
68226847
return true;
@@ -13857,11 +13882,12 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X,
1385713882
case Type::MemberPointer: {
1385813883
const auto *PX = cast<MemberPointerType>(X),
1385913884
*PY = cast<MemberPointerType>(Y);
13885+
assert(declaresSameEntity(PX->getMostRecentCXXRecordDecl(),
13886+
PY->getMostRecentCXXRecordDecl()));
1386013887
return Ctx.getMemberPointerType(
1386113888
getCommonPointeeType(Ctx, PX, PY),
13862-
Ctx.getCommonSugaredType(QualType(PX->getClass(), 0),
13863-
QualType(PY->getClass(), 0))
13864-
.getTypePtr());
13889+
getCommonQualifier(Ctx, PX, PY, /*IsSame=*/true),
13890+
PX->getMostRecentCXXRecordDecl());
1386513891
}
1386613892
case Type::LValueReference: {
1386713893
const auto *PX = cast<LValueReferenceType>(X),

0 commit comments

Comments
 (0)