Don't forget to substitute into the qualifier when instantiating the definition

of a member function of a class template that is defined outside the template.
This substitution can actually fail in some weird cases.

llvm-svn: 220085
This commit is contained in:
Richard Smith 2014-10-17 20:37:29 +00:00
parent ce80084446
commit cc92866e0c
2 changed files with 59 additions and 19 deletions

View File

@ -36,14 +36,24 @@ static bool isDeclWithinFunction(const Decl *D) {
return false;
}
bool TemplateDeclInstantiator::SubstQualifier(const DeclaratorDecl *OldDecl,
DeclaratorDecl *NewDecl) {
template<typename DeclT>
static bool SubstQualifier(Sema &SemaRef, const DeclT *OldDecl, DeclT *NewDecl,
const MultiLevelTemplateArgumentList &TemplateArgs) {
if (!OldDecl->getQualifierLoc())
return false;
assert((NewDecl->getFriendObjectKind() ||
!OldDecl->getLexicalDeclContext()->isDependentContext()) &&
"non-friend with qualified name defined in dependent context");
Sema::ContextRAII SavedContext(
SemaRef,
const_cast<DeclContext *>(NewDecl->getFriendObjectKind()
? NewDecl->getLexicalDeclContext()
: OldDecl->getLexicalDeclContext()));
NestedNameSpecifierLoc NewQualifierLoc
= SemaRef.SubstNestedNameSpecifierLoc(OldDecl->getQualifierLoc(),
TemplateArgs);
= SemaRef.SubstNestedNameSpecifierLoc(OldDecl->getQualifierLoc(),
TemplateArgs);
if (!NewQualifierLoc)
return true;
@ -52,20 +62,14 @@ bool TemplateDeclInstantiator::SubstQualifier(const DeclaratorDecl *OldDecl,
return false;
}
bool TemplateDeclInstantiator::SubstQualifier(const DeclaratorDecl *OldDecl,
DeclaratorDecl *NewDecl) {
return ::SubstQualifier(SemaRef, OldDecl, NewDecl, TemplateArgs);
}
bool TemplateDeclInstantiator::SubstQualifier(const TagDecl *OldDecl,
TagDecl *NewDecl) {
if (!OldDecl->getQualifierLoc())
return false;
NestedNameSpecifierLoc NewQualifierLoc
= SemaRef.SubstNestedNameSpecifierLoc(OldDecl->getQualifierLoc(),
TemplateArgs);
if (!NewQualifierLoc)
return true;
NewDecl->setQualifierInfo(NewQualifierLoc);
return false;
return ::SubstQualifier(SemaRef, OldDecl, NewDecl, TemplateArgs);
}
// Include attribute instantiation code.
@ -3497,15 +3501,21 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
if (PatternDecl->isDefaulted())
SetDeclDefaulted(Function, PatternDecl->getLocation());
else {
MultiLevelTemplateArgumentList TemplateArgs =
getTemplateInstantiationArgs(Function, nullptr, false, PatternDecl);
// Substitute into the qualifier; we can get a substitution failure here
// through evil use of alias templates.
// FIXME: Is CurContext correct for this? Should we go to the (instantiation
// of the) lexical context of the pattern?
SubstQualifier(*this, PatternDecl, Function, TemplateArgs);
ActOnStartOfFunctionDef(nullptr, Function);
// Enter the scope of this instantiation. We don't use
// PushDeclContext because we don't have a scope.
Sema::ContextRAII savedContext(*this, Function);
MultiLevelTemplateArgumentList TemplateArgs =
getTemplateInstantiationArgs(Function, nullptr, false, PatternDecl);
addInstantiatedParametersToScope(*this, Function, PatternDecl, Scope,
TemplateArgs);

View File

@ -0,0 +1,30 @@
// RUN: %clang_cc1 -std=c++11 -verify %s
template<typename ...T> struct X {
void f(int);
void f(...);
static int n;
};
template<typename T, typename U> using A = T;
// These definitions are OK, X<A<T, decltype(...)>...> is equivalent to X<T...>
// so this defines the member of the primary template.
template<typename ...T>
void X<A<T, decltype(f(T()))>...>::f(int) {} // expected-error {{undeclared}}
template<typename ...T>
int X<A<T, decltype(f(T()))>...>::n = 0; // expected-error {{undeclared}}
struct Y {}; void f(Y);
void g() {
// OK, substitution succeeds.
X<Y>().f(0);
X<Y>::n = 1;
// Error, substitution fails; this should not be treated as a SFINAE-able
// condition, so we don't select X<void>::f(...).
X<void>().f(0); // expected-note {{instantiation of}}
X<void>::n = 1; // expected-note {{instantiation of}}
}