[clang] [clang] fix deduction of member pointers with dependent named classes (PR #133113)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 26 11:41:25 PDT 2025
https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/133113
>From d1831e84e62f5a100de193619cbfdf9a1d403674 Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Wed, 26 Mar 2025 12:26:40 -0300
Subject: [PATCH 1/2] [clang] fix deduction of member pointers with dependent
named classes
This fixes a regression when interpreting a nested name specifier
for deduction purposes in member pointers.
This introduces a helper for fully translating a nested name specifier into a
type.
This regression was introduced here: https://github.com/llvm/llvm-project/pull/130537
and was reported here: https://github.com/llvm/llvm-project/pull/132401#issuecomment-2751489581
No release notes, since the regression was never released.
---
clang/include/clang/AST/NestedNameSpecifier.h | 5 ++
clang/lib/AST/NestedNameSpecifier.cpp | 46 +++++++++++++++++++
clang/lib/Sema/SemaExprCXX.cpp | 26 ++---------
clang/lib/Sema/SemaTemplateDeduction.cpp | 34 +++++++++-----
clang/test/SemaCXX/member-pointer.cpp | 43 +++++++++++++++++
.../SemaTemplate/instantiation-backtrace.cpp | 6 +--
6 files changed, 122 insertions(+), 38 deletions(-)
diff --git a/clang/include/clang/AST/NestedNameSpecifier.h b/clang/include/clang/AST/NestedNameSpecifier.h
index 051d632f1cdf9..273e73e7c1e95 100644
--- a/clang/include/clang/AST/NestedNameSpecifier.h
+++ b/clang/include/clang/AST/NestedNameSpecifier.h
@@ -201,6 +201,11 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
return nullptr;
}
+ /// Fully translate this nested name specifier to a type.
+ /// Unlike getAsType, this will convert this entire nested
+ /// name specifier chain into its equivalent type.
+ const Type *translateToType(const ASTContext &Context) const;
+
NestedNameSpecifierDependence getDependence() const;
/// Whether this nested name specifier refers to a dependent
diff --git a/clang/lib/AST/NestedNameSpecifier.cpp b/clang/lib/AST/NestedNameSpecifier.cpp
index a256a87695afc..206e462a58a79 100644
--- a/clang/lib/AST/NestedNameSpecifier.cpp
+++ b/clang/lib/AST/NestedNameSpecifier.cpp
@@ -245,6 +245,52 @@ bool NestedNameSpecifier::containsErrors() const {
return getDependence() & NestedNameSpecifierDependence::Error;
}
+const Type *
+NestedNameSpecifier::translateToType(const ASTContext &Context) const {
+ NestedNameSpecifier *Prefix = getPrefix();
+ switch (getKind()) {
+ case SpecifierKind::Identifier:
+ return Context
+ .getDependentNameType(ElaboratedTypeKeyword::None, Prefix,
+ getAsIdentifier())
+ .getTypePtr();
+ case SpecifierKind::TypeSpec:
+ case SpecifierKind::TypeSpecWithTemplate: {
+ const Type *T = getAsType();
+ switch (T->getTypeClass()) {
+ case Type::DependentTemplateSpecialization: {
+ const auto *DT = cast<DependentTemplateSpecializationType>(T);
+ // FIXME: The type node can't represent the template keyword.
+ return Context
+ .getDependentTemplateSpecializationType(ElaboratedTypeKeyword::None,
+ Prefix, DT->getIdentifier(),
+ DT->template_arguments())
+ .getTypePtr();
+ }
+ case Type::Record:
+ case Type::TemplateSpecialization:
+ case Type::Using:
+ case Type::Enum:
+ case Type::Typedef:
+ case Type::UnresolvedUsing:
+ return Context
+ .getElaboratedType(ElaboratedTypeKeyword::None, Prefix,
+ QualType(T, 0))
+ .getTypePtr();
+ default:
+ assert(Prefix == nullptr && "unexpected type with elaboration");
+ return T;
+ }
+ }
+ case SpecifierKind::Global:
+ case SpecifierKind::Namespace:
+ case SpecifierKind::NamespaceAlias:
+ case SpecifierKind::Super:
+ // These are not representable as types.
+ return nullptr;
+ }
+}
+
/// Print this nested name specifier to the given output
/// stream.
void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy,
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 856b505e92214..b2310628adc64 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -60,30 +60,10 @@ ParsedType Sema::getInheritingConstructorName(CXXScopeSpec &SS,
SourceLocation NameLoc,
const IdentifierInfo &Name) {
NestedNameSpecifier *NNS = SS.getScopeRep();
+ if (const IdentifierInfo *II = NNS->getAsIdentifier())
+ assert(II == &Name && "not a constructor name");
- // Convert the nested-name-specifier into a type.
- QualType Type;
- switch (NNS->getKind()) {
- case NestedNameSpecifier::TypeSpec:
- case NestedNameSpecifier::TypeSpecWithTemplate:
- Type = QualType(NNS->getAsType(), 0);
- break;
-
- case NestedNameSpecifier::Identifier:
- // Strip off the last layer of the nested-name-specifier and build a
- // typename type for it.
- assert(NNS->getAsIdentifier() == &Name && "not a constructor name");
- Type = Context.getDependentNameType(
- ElaboratedTypeKeyword::None, NNS->getPrefix(), NNS->getAsIdentifier());
- break;
-
- case NestedNameSpecifier::Global:
- case NestedNameSpecifier::Super:
- case NestedNameSpecifier::Namespace:
- case NestedNameSpecifier::NamespaceAlias:
- llvm_unreachable("Nested name specifier is not a type for inheriting ctor");
- }
-
+ QualType Type(NNS->translateToType(Context), 0);
// This reference to the type is located entirely at the location of the
// final identifier in the qualified-id.
return CreateParsedType(Type,
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 410b5a2c83e8d..4f6383a40f457 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2127,19 +2127,29 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
/*DeducedFromArrayBound=*/false, HasDeducedAnyParam);
Result != TemplateDeductionResult::Success)
return Result;
- const Type *QP = MPP->getQualifier()->getAsType(),
- *QA = MPA->getQualifier()->getAsType();
- CXXRecordDecl *ClsP = MPP->getMostRecentCXXRecordDecl(),
- *ClsA = MPA->getMostRecentCXXRecordDecl();
- // FIXME: Don't drop the rest of the prefixes here.
- QualType P = !ClsP || declaresSameEntity(QP->getAsCXXRecordDecl(), ClsP)
- ? QualType(QP, 0)
- : S.Context.getTypeDeclType(ClsP);
- QualType A = !ClsA || declaresSameEntity(QA->getAsCXXRecordDecl(), ClsA)
- ? QualType(QA, 0)
- : S.Context.getTypeDeclType(ClsA);
+
+ QualType TP;
+ if (MPP->isSugared()) {
+ TP = S.Context.getTypeDeclType(MPP->getMostRecentCXXRecordDecl());
+ } else {
+ NestedNameSpecifier *QP = MPP->getQualifier();
+ if (QP->getKind() == clang::NestedNameSpecifier::Identifier)
+ // Skip translation if it's a non-deduced context anyway.
+ return TemplateDeductionResult::Success;
+ TP = QualType(QP->translateToType(S.Context), 0);
+ }
+ assert(!TP.isNull() && "member pointer with non-type class");
+
+ QualType TA;
+ if (MPA->isSugared()) {
+ TA = S.Context.getTypeDeclType(MPA->getMostRecentCXXRecordDecl());
+ } else {
+ NestedNameSpecifier *QA = MPA->getQualifier();
+ TA = QualType(QA->translateToType(S.Context), 0);
+ }
+ assert(!TA.isNull() && "member pointer with non-type class");
return DeduceTemplateArgumentsByTypeMatch(
- S, TemplateParams, P, A, Info, Deduced, SubTDF,
+ S, TemplateParams, TP, TA, Info, Deduced, SubTDF,
degradeCallPartialOrderingKind(POK),
/*DeducedFromArrayBound=*/false, HasDeducedAnyParam);
}
diff --git a/clang/test/SemaCXX/member-pointer.cpp b/clang/test/SemaCXX/member-pointer.cpp
index fa3e6a5a4bcf7..c94c26fbcbb28 100644
--- a/clang/test/SemaCXX/member-pointer.cpp
+++ b/clang/test/SemaCXX/member-pointer.cpp
@@ -365,3 +365,46 @@ namespace adl_dependent_class {
void f(A);
void g() { f(d<C>); }
} // namespace adl_dependent_class
+
+namespace deduction1 {
+ template <typename> struct RunCallImpl;
+
+ template <typename Derived>
+ struct RunCallImpl<int (Derived::Info::*)(Derived *)> {};
+
+ template <typename d>
+ void RunCall(d) {
+ RunCallImpl<d>();
+ }
+
+ struct Filter {
+ virtual void MakeCall();
+ virtual ~Filter() = default;
+ };
+
+ template <typename Derived>
+ struct ImplementFilter : Filter {
+ void MakeCall() { RunCall(&Derived::Info::OnStuffHandler); }
+ };
+
+ struct FoobarFilter : ImplementFilter<FoobarFilter> {
+ struct Info {
+ int OnStuffHandler(FoobarFilter *);
+ };
+ };
+} // namespace deduction1
+
+namespace deduction2 {
+ template <typename> struct A;
+ template <typename T>
+ struct A<void (T::C::*)(int &, T *)> {};
+ template <typename T> void e(T) {
+ A<T> f;
+ }
+ struct S {
+ struct C {
+ void h(int &, S *);
+ };
+ void i() { e(&C::h); }
+ };
+} // namespace deduction2
diff --git a/clang/test/SemaTemplate/instantiation-backtrace.cpp b/clang/test/SemaTemplate/instantiation-backtrace.cpp
index 39dfb0b32a2fb..6b51d0eba7979 100644
--- a/clang/test/SemaTemplate/instantiation-backtrace.cpp
+++ b/clang/test/SemaTemplate/instantiation-backtrace.cpp
@@ -22,7 +22,7 @@ void g() {
(void)sizeof(B<X>); // expected-note{{in instantiation of template class 'B<X>' requested here}}
}
-template<typename T>
+template<typename T>
struct G : A<T>, // expected-error{{implicit instantiation of undefined template 'A<int>'}}
A<T*> // expected-error{{implicit instantiation of undefined template 'A<int *>'}}
{ };
@@ -39,13 +39,13 @@ namespace PR13365 {
template <class T1, class T2>
typename ResultTy<T2>::error Deduce( void (T1::*member)(T2) ) {} // \
// expected-note {{instantiation of template class 'PR13365::ResultTy<int &>'}} \
- // expected-note {{substitution failure [with T1 = PR13365::Cls, T2 = int &]}}
+ // expected-note {{substitution failure [with T1 = Cls, T2 = int &]}}
struct Cls {
void method(int&);
};
void test() {
Deduce(&Cls::method); // expected-error {{no matching function}} \
- // expected-note {{substituting deduced template arguments into function template 'Deduce' [with T1 = PR13365::Cls, T2 = int &]}}
+ // expected-note {{substituting deduced template arguments into function template 'Deduce' [with T1 = Cls, T2 = int &]}}
}
}
>From d97302abc8bc50ac517fa3551419db59b2ca869d Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Wed, 26 Mar 2025 15:41:03 -0300
Subject: [PATCH 2/2] fixup! [clang] fix deduction of member pointers with
dependent named classes
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 4f6383a40f457..740a7a1513975 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2133,7 +2133,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
TP = S.Context.getTypeDeclType(MPP->getMostRecentCXXRecordDecl());
} else {
NestedNameSpecifier *QP = MPP->getQualifier();
- if (QP->getKind() == clang::NestedNameSpecifier::Identifier)
+ if (QP->getKind() == NestedNameSpecifier::Identifier)
// Skip translation if it's a non-deduced context anyway.
return TemplateDeductionResult::Success;
TP = QualType(QP->translateToType(S.Context), 0);
More information about the cfe-commits
mailing list