Skip to content

Commit a942d7f

Browse files
authored
[clang] fix deduction of member pointers with dependent named classes (#133113)
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: #130537 and was reported here: #132401 (comment) No release notes, since this regression was never released.
1 parent 6d1184d commit a942d7f

File tree

6 files changed

+122
-38
lines changed

6 files changed

+122
-38
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/SemaExprCXX.cpp

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -60,30 +60,10 @@ ParsedType Sema::getInheritingConstructorName(CXXScopeSpec &SS,
6060
SourceLocation NameLoc,
6161
const IdentifierInfo &Name) {
6262
NestedNameSpecifier *NNS = SS.getScopeRep();
63+
if (const IdentifierInfo *II = NNS->getAsIdentifier())
64+
assert(II == &Name && "not a constructor name");
6365

64-
// Convert the nested-name-specifier into a type.
65-
QualType Type;
66-
switch (NNS->getKind()) {
67-
case NestedNameSpecifier::TypeSpec:
68-
case NestedNameSpecifier::TypeSpecWithTemplate:
69-
Type = QualType(NNS->getAsType(), 0);
70-
break;
71-
72-
case NestedNameSpecifier::Identifier:
73-
// Strip off the last layer of the nested-name-specifier and build a
74-
// typename type for it.
75-
assert(NNS->getAsIdentifier() == &Name && "not a constructor name");
76-
Type = Context.getDependentNameType(
77-
ElaboratedTypeKeyword::None, NNS->getPrefix(), NNS->getAsIdentifier());
78-
break;
79-
80-
case NestedNameSpecifier::Global:
81-
case NestedNameSpecifier::Super:
82-
case NestedNameSpecifier::Namespace:
83-
case NestedNameSpecifier::NamespaceAlias:
84-
llvm_unreachable("Nested name specifier is not a type for inheriting ctor");
85-
}
86-
66+
QualType Type(NNS->translateToType(Context), 0);
8767
// This reference to the type is located entirely at the location of the
8868
// final identifier in the qualified-id.
8969
return CreateParsedType(Type,

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() == 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
@@ -365,3 +365,46 @@ namespace adl_dependent_class {
365365
void f(A);
366366
void g() { f(d<C>); }
367367
} // namespace adl_dependent_class
368+
369+
namespace deduction1 {
370+
template <typename> struct RunCallImpl;
371+
372+
template <typename Derived>
373+
struct RunCallImpl<int (Derived::Info::*)(Derived *)> {};
374+
375+
template <typename d>
376+
void RunCall(d) {
377+
RunCallImpl<d>();
378+
}
379+
380+
struct Filter {
381+
virtual void MakeCall();
382+
virtual ~Filter() = default;
383+
};
384+
385+
template <typename Derived>
386+
struct ImplementFilter : Filter {
387+
void MakeCall() { RunCall(&Derived::Info::OnStuffHandler); }
388+
};
389+
390+
struct FoobarFilter : ImplementFilter<FoobarFilter> {
391+
struct Info {
392+
int OnStuffHandler(FoobarFilter *);
393+
};
394+
};
395+
} // namespace deduction1
396+
397+
namespace deduction2 {
398+
template <typename> struct A;
399+
template <typename T>
400+
struct A<void (T::C::*)(int &, T *)> {};
401+
template <typename T> void e(T) {
402+
A<T> f;
403+
}
404+
struct S {
405+
struct C {
406+
void h(int &, S *);
407+
};
408+
void i() { e(&C::h); }
409+
};
410+
} // 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)