mirror of https://github.com/microsoft/clang.git
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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@220085 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
355e8c8de8
commit
bd2dc74610
|
@ -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);
|
||||
|
||||
|
|
|
@ -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}}
|
||||
}
|
Loading…
Reference in New Issue