mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-16 16:56:35 +00:00
[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: 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 this regression was never released.
This commit is contained in:
parent
6d1184d05f
commit
a942d7f810
@ -201,6 +201,11 @@ public:
|
||||
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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 &]}}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user