Skip to content

Commit 839a8fc

Browse files
committed
[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. Other existing potential users will be deduplicated by using this helper in subsequent patches. This regression was introduced here: #130537 and was reported here: #132401 (comment) No release notes, since the regression was never released.
1 parent b0668d8 commit 839a8fc

File tree

5 files changed

+119
-15
lines changed

5 files changed

+119
-15
lines changed

clang/include/clang/AST/NestedNameSpecifier.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,11 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
201201
return nullptr;
202202
}
203203

204+
/// Fully translate this nested name specifier to a type.
205+
/// Unlike getAsType, this will convert this entire nested
206+
/// name specifier chain into its equivalent type.
207+
const Type *translateToType(const ASTContext &Context) const;
208+
204209
NestedNameSpecifierDependence getDependence() const;
205210

206211
/// Whether this nested name specifier refers to a dependent

clang/lib/AST/NestedNameSpecifier.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,52 @@ bool NestedNameSpecifier::containsErrors() const {
245245
return getDependence() & NestedNameSpecifierDependence::Error;
246246
}
247247

248+
const Type *
249+
NestedNameSpecifier::translateToType(const ASTContext &Context) const {
250+
NestedNameSpecifier *Prefix = getPrefix();
251+
switch (getKind()) {
252+
case SpecifierKind::Identifier:
253+
return Context
254+
.getDependentNameType(ElaboratedTypeKeyword::None, Prefix,
255+
getAsIdentifier())
256+
.getTypePtr();
257+
case SpecifierKind::TypeSpec:
258+
case SpecifierKind::TypeSpecWithTemplate: {
259+
const Type *T = getAsType();
260+
switch (T->getTypeClass()) {
261+
case Type::DependentTemplateSpecialization: {
262+
const auto *DT = cast<DependentTemplateSpecializationType>(T);
263+
// FIXME: The type node can't represent the template keyword.
264+
return Context
265+
.getDependentTemplateSpecializationType(ElaboratedTypeKeyword::None,
266+
Prefix, DT->getIdentifier(),
267+
DT->template_arguments())
268+
.getTypePtr();
269+
}
270+
case Type::Record:
271+
case Type::TemplateSpecialization:
272+
case Type::Using:
273+
case Type::Enum:
274+
case Type::Typedef:
275+
case Type::UnresolvedUsing:
276+
return Context
277+
.getElaboratedType(ElaboratedTypeKeyword::None, Prefix,
278+
QualType(T, 0))
279+
.getTypePtr();
280+
default:
281+
assert(Prefix == nullptr && "unexpected type with elaboration");
282+
return T;
283+
}
284+
}
285+
case SpecifierKind::Global:
286+
case SpecifierKind::Namespace:
287+
case SpecifierKind::NamespaceAlias:
288+
case SpecifierKind::Super:
289+
// These are not representable as types.
290+
return nullptr;
291+
}
292+
}
293+
248294
/// Print this nested name specifier to the given output
249295
/// stream.
250296
void NestedNameSpecifier::print(raw_ostream &OS, const PrintingPolicy &Policy,

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2127,19 +2127,29 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
21272127
/*DeducedFromArrayBound=*/false, HasDeducedAnyParam);
21282128
Result != TemplateDeductionResult::Success)
21292129
return Result;
2130-
const Type *QP = MPP->getQualifier()->getAsType(),
2131-
*QA = MPA->getQualifier()->getAsType();
2132-
CXXRecordDecl *ClsP = MPP->getMostRecentCXXRecordDecl(),
2133-
*ClsA = MPA->getMostRecentCXXRecordDecl();
2134-
// FIXME: Don't drop the rest of the prefixes here.
2135-
QualType P = !ClsP || declaresSameEntity(QP->getAsCXXRecordDecl(), ClsP)
2136-
? QualType(QP, 0)
2137-
: S.Context.getTypeDeclType(ClsP);
2138-
QualType A = !ClsA || declaresSameEntity(QA->getAsCXXRecordDecl(), ClsA)
2139-
? QualType(QA, 0)
2140-
: S.Context.getTypeDeclType(ClsA);
2130+
2131+
QualType TP;
2132+
if (MPP->isSugared()) {
2133+
TP = S.Context.getTypeDeclType(MPP->getMostRecentCXXRecordDecl());
2134+
} else {
2135+
NestedNameSpecifier *QP = MPP->getQualifier();
2136+
if (QP->getKind() == clang::NestedNameSpecifier::Identifier)
2137+
// Skip translation if it's a non-deduced context anyway.
2138+
return TemplateDeductionResult::Success;
2139+
TP = QualType(QP->translateToType(S.Context), 0);
2140+
}
2141+
assert(!TP.isNull() && "member pointer with non-type class");
2142+
2143+
QualType TA;
2144+
if (MPA->isSugared()) {
2145+
TA = S.Context.getTypeDeclType(MPA->getMostRecentCXXRecordDecl());
2146+
} else {
2147+
NestedNameSpecifier *QA = MPA->getQualifier();
2148+
TA = QualType(QA->translateToType(S.Context), 0);
2149+
}
2150+
assert(!TA.isNull() && "member pointer with non-type class");
21412151
return DeduceTemplateArgumentsByTypeMatch(
2142-
S, TemplateParams, P, A, Info, Deduced, SubTDF,
2152+
S, TemplateParams, TP, TA, Info, Deduced, SubTDF,
21432153
degradeCallPartialOrderingKind(POK),
21442154
/*DeducedFromArrayBound=*/false, HasDeducedAnyParam);
21452155
}

clang/test/SemaCXX/member-pointer.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,3 +355,46 @@ namespace GH132401 {
355355
};
356356
template struct CallableHelper<void (QIODevice::*)()>;
357357
} // namespace GH132401
358+
359+
namespace deduction1 {
360+
template <typename> struct RunCallImpl;
361+
362+
template <typename Derived>
363+
struct RunCallImpl<int (Derived::Info::*)(Derived *)> {};
364+
365+
template <typename d>
366+
void RunCall(d) {
367+
RunCallImpl<d>();
368+
}
369+
370+
struct Filter {
371+
virtual void MakeCall();
372+
virtual ~Filter() = default;
373+
};
374+
375+
template <typename Derived>
376+
struct ImplementFilter : Filter {
377+
void MakeCall() { RunCall(&Derived::Info::OnStuffHandler); }
378+
};
379+
380+
struct FoobarFilter : ImplementFilter<FoobarFilter> {
381+
struct Info {
382+
int OnStuffHandler(FoobarFilter *);
383+
};
384+
};
385+
} // namespace deduction1
386+
387+
namespace deduction2 {
388+
template <typename> struct A;
389+
template <typename T>
390+
struct A<void (T::C::*)(int &, T *)> {};
391+
template <typename T> void e(T) {
392+
A<T> f;
393+
}
394+
struct S {
395+
struct C {
396+
void h(int &, S *);
397+
};
398+
void i() { e(&C::h); }
399+
};
400+
} // namespace deduction2

clang/test/SemaTemplate/instantiation-backtrace.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ void g() {
2222
(void)sizeof(B<X>); // expected-note{{in instantiation of template class 'B<X>' requested here}}
2323
}
2424

25-
template<typename T>
25+
template<typename T>
2626
struct G : A<T>, // expected-error{{implicit instantiation of undefined template 'A<int>'}}
2727
A<T*> // expected-error{{implicit instantiation of undefined template 'A<int *>'}}
2828
{ };
@@ -39,13 +39,13 @@ namespace PR13365 {
3939
template <class T1, class T2>
4040
typename ResultTy<T2>::error Deduce( void (T1::*member)(T2) ) {} // \
4141
// expected-note {{instantiation of template class 'PR13365::ResultTy<int &>'}} \
42-
// expected-note {{substitution failure [with T1 = PR13365::Cls, T2 = int &]}}
42+
// expected-note {{substitution failure [with T1 = Cls, T2 = int &]}}
4343

4444
struct Cls {
4545
void method(int&);
4646
};
4747
void test() {
4848
Deduce(&Cls::method); // expected-error {{no matching function}} \
49-
// expected-note {{substituting deduced template arguments into function template 'Deduce' [with T1 = PR13365::Cls, T2 = int &]}}
49+
// expected-note {{substituting deduced template arguments into function template 'Deduce' [with T1 = Cls, T2 = int &]}}
5050
}
5151
}

0 commit comments

Comments
 (0)