From bd2dc746109fdcf4d2885f89872892adb0925cd5 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 17 Oct 2014 20:37:29 +0000 Subject: [PATCH] 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 --- lib/Sema/SemaTemplateInstantiateDecl.cpp | 48 ++++++++++++++---------- test/SemaTemplate/instantiate-scope.cpp | 30 +++++++++++++++ 2 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 test/SemaTemplate/instantiate-scope.cpp diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index ec8c08ddf5..a89bf28813 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -36,14 +36,24 @@ static bool isDeclWithinFunction(const Decl *D) { return false; } -bool TemplateDeclInstantiator::SubstQualifier(const DeclaratorDecl *OldDecl, - DeclaratorDecl *NewDecl) { +template +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(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); diff --git a/test/SemaTemplate/instantiate-scope.cpp b/test/SemaTemplate/instantiate-scope.cpp new file mode 100644 index 0000000000..733105674b --- /dev/null +++ b/test/SemaTemplate/instantiate-scope.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +template struct X { + void f(int); + void f(...); + static int n; +}; + +template using A = T; + +// These definitions are OK, X...> is equivalent to X +// so this defines the member of the primary template. +template +void X...>::f(int) {} // expected-error {{undeclared}} + +template +int X...>::n = 0; // expected-error {{undeclared}} + +struct Y {}; void f(Y); + +void g() { + // OK, substitution succeeds. + X().f(0); + X::n = 1; + + // Error, substitution fails; this should not be treated as a SFINAE-able + // condition, so we don't select X::f(...). + X().f(0); // expected-note {{instantiation of}} + X::n = 1; // expected-note {{instantiation of}} +} -- 2.50.1