Skip to content

[clang] fix deduction of member pointers with dependent named classes #133113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clang/include/clang/AST/NestedNameSpecifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
46 changes: 46 additions & 0 deletions clang/lib/AST/NestedNameSpecifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
26 changes: 3 additions & 23 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
34 changes: 22 additions & 12 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() == 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);
}
Expand Down
43 changes: 43 additions & 0 deletions clang/test/SemaCXX/member-pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 3 additions & 3 deletions clang/test/SemaTemplate/instantiation-backtrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 *>'}}
{ };
Expand All @@ -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 &]}}
}
}
Loading