diff options
-rw-r--r-- | include/clang/AST/TemplateBase.h | 6 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 10 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/SemaTemplateInstantiateDecl.cpp | 84 | ||||
-rw-r--r-- | test/SemaTemplate/friend.cpp | 75 |
5 files changed, 161 insertions, 15 deletions
diff --git a/include/clang/AST/TemplateBase.h b/include/clang/AST/TemplateBase.h index 01bdc0df1a..e3a773b4e4 100644 --- a/include/clang/AST/TemplateBase.h +++ b/include/clang/AST/TemplateBase.h @@ -620,13 +620,17 @@ public: /// The number of template arguments in TemplateArgs. unsigned NumTemplateArgs; + SourceLocation getLAngleLoc() const { return LAngleLoc; } + SourceLocation getRAngleLoc() const { return RAngleLoc; } + /// Retrieve the template arguments const TemplateArgumentLoc *getTemplateArgs() const { return getTrailingObjects<TemplateArgumentLoc>(); } + unsigned getNumTemplateArgs() const { return NumTemplateArgs; } llvm::ArrayRef<TemplateArgumentLoc> arguments() const { - return llvm::makeArrayRef(getTemplateArgs(), NumTemplateArgs); + return llvm::makeArrayRef(getTemplateArgs(), getNumTemplateArgs()); } const TemplateArgumentLoc &operator[](unsigned I) const { diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 2b88955285..f77aeb64b3 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -9030,10 +9030,14 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // selecting a friend based on a dependent factor. But there // are situations where these conditions don't apply and we // can actually do this check immediately. + // + // Unless the scope is dependent, it's always an error if qualified + // redeclaration lookup found nothing at all. Diagnose that now; + // nothing will diagnose that error later. if (isFriend && - (TemplateParamLists.size() || - D.getCXXScopeSpec().getScopeRep()->isDependent() || - CurContext->isDependentContext())) { + (D.getCXXScopeSpec().getScopeRep()->isDependent() || + (!Previous.empty() && (TemplateParamLists.size() || + CurContext->isDependentContext())))) { // ignore these } else { // The user tried to provide an out-of-line definition for a diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index d54e905061..52be0598fb 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -1057,6 +1057,7 @@ Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old, // third bullet. If the type of the friend is dependent, skip this lookup // until instantiation. if (New->getFriendObjectKind() && New->getQualifier() && + !New->getDependentSpecializationInfo() && !New->getType()->isDependentType()) { LookupResult TemplateSpecResult(LookupResult::Temporary, Old); TemplateSpecResult.addAllDecls(Old); diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 31353e45ba..fad3c065e8 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1747,10 +1747,13 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation); } + if (isFriend) + Function->setObjectOfFriendDecl(); + if (InitFunctionInstantiation(Function, D)) Function->setInvalidDecl(); - bool isExplicitSpecialization = false; + bool IsExplicitSpecialization = false; LookupResult Previous( SemaRef, Function->getDeclName(), SourceLocation(), @@ -1763,9 +1766,6 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, = D->getDependentSpecializationInfo()) { assert(isFriend && "non-friend has dependent specialization info?"); - // This needs to be set now for future sanity. - Function->setObjectOfFriendDecl(); - // Instantiate the explicit template arguments. TemplateArgumentListInfo ExplicitArgs(Info->getLAngleLoc(), Info->getRAngleLoc()); @@ -1788,8 +1788,25 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, Previous)) Function->setInvalidDecl(); - isExplicitSpecialization = true; + IsExplicitSpecialization = true; + } else if (const ASTTemplateArgumentListInfo *Info = + D->getTemplateSpecializationArgsAsWritten()) { + // The name of this function was written as a template-id. + SemaRef.LookupQualifiedName(Previous, DC); + // Instantiate the explicit template arguments. + TemplateArgumentListInfo ExplicitArgs(Info->getLAngleLoc(), + Info->getRAngleLoc()); + if (SemaRef.Subst(Info->getTemplateArgs(), Info->getNumTemplateArgs(), + ExplicitArgs, TemplateArgs)) + return nullptr; + + if (SemaRef.CheckFunctionTemplateSpecialization(Function, + &ExplicitArgs, + Previous)) + Function->setInvalidDecl(); + + IsExplicitSpecialization = true; } else if (TemplateParams || !FunctionTemplate) { // Look only into the namespace where the friend would be declared to // find a previous declaration. This is the innermost enclosing namespace, @@ -1804,11 +1821,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, Previous.clear(); } - if (isFriend) - Function->setObjectOfFriendDecl(); - SemaRef.CheckFunctionDeclaration(/*Scope*/ nullptr, Function, Previous, - isExplicitSpecialization); + IsExplicitSpecialization); NamedDecl *PrincipalDecl = (TemplateParams ? cast<NamedDecl>(FunctionTemplate) @@ -2055,7 +2069,54 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, LookupResult Previous(SemaRef, NameInfo, Sema::LookupOrdinaryName, Sema::ForExternalRedeclaration); - if (!FunctionTemplate || TemplateParams || isFriend) { + bool IsExplicitSpecialization = false; + + // If the name of this function was written as a template-id, instantiate + // the explicit template arguments. + if (DependentFunctionTemplateSpecializationInfo *Info + = D->getDependentSpecializationInfo()) { + assert(isFriend && "non-friend has dependent specialization info?"); + + // Instantiate the explicit template arguments. + TemplateArgumentListInfo ExplicitArgs(Info->getLAngleLoc(), + Info->getRAngleLoc()); + if (SemaRef.Subst(Info->getTemplateArgs(), Info->getNumTemplateArgs(), + ExplicitArgs, TemplateArgs)) + return nullptr; + + // Map the candidate templates to their instantiations. + for (unsigned I = 0, E = Info->getNumTemplates(); I != E; ++I) { + Decl *Temp = SemaRef.FindInstantiatedDecl(D->getLocation(), + Info->getTemplate(I), + TemplateArgs); + if (!Temp) return nullptr; + + Previous.addDecl(cast<FunctionTemplateDecl>(Temp)); + } + + if (SemaRef.CheckFunctionTemplateSpecialization(Method, + &ExplicitArgs, + Previous)) + Method->setInvalidDecl(); + + IsExplicitSpecialization = true; + } else if (const ASTTemplateArgumentListInfo *Info = + D->getTemplateSpecializationArgsAsWritten()) { + SemaRef.LookupQualifiedName(Previous, DC); + + TemplateArgumentListInfo ExplicitArgs(Info->getLAngleLoc(), + Info->getRAngleLoc()); + if (SemaRef.Subst(Info->getTemplateArgs(), Info->getNumTemplateArgs(), + ExplicitArgs, TemplateArgs)) + return nullptr; + + if (SemaRef.CheckFunctionTemplateSpecialization(Method, + &ExplicitArgs, + Previous)) + Method->setInvalidDecl(); + + IsExplicitSpecialization = true; + } else if (!FunctionTemplate || TemplateParams || isFriend) { SemaRef.LookupQualifiedName(Previous, Record); // In C++, the previous declaration we find might be a tag type @@ -2067,7 +2128,8 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, } if (!IsClassScopeSpecialization) - SemaRef.CheckFunctionDeclaration(nullptr, Method, Previous, false); + SemaRef.CheckFunctionDeclaration(nullptr, Method, Previous, + IsExplicitSpecialization); if (D->isPure()) SemaRef.CheckPureMethod(Method, SourceRange()); diff --git a/test/SemaTemplate/friend.cpp b/test/SemaTemplate/friend.cpp index ef1aed50e6..777682be3f 100644 --- a/test/SemaTemplate/friend.cpp +++ b/test/SemaTemplate/friend.cpp @@ -47,3 +47,78 @@ inline void foo() {} inline void bar() {} C<int> c; } + +namespace qualified_friend { + void f(int); // expected-note 2{{type mismatch at 1st parameter}} + template<typename T> void f(T*); // expected-note 2{{could not match 'type-parameter-0-0 *' against 'double'}} + template<typename T> void nondep(); + + template<typename> struct X1 { + friend void qualified_friend::f(double); // expected-error {{friend declaration of 'f' does not match any declaration in namespace 'qualified_friend'}} + friend void qualified_friend::g(); // expected-error {{friend declaration of 'g' does not match any declaration in namespace 'qualified_friend'}} + }; + template<typename T> struct X2 { + friend void qualified_friend::f(T); // expected-error {{friend declaration of 'f' does not match any declaration in namespace 'qualified_friend'}} + }; + X1<int> xi; + X2<double> xd; // expected-note {{in instantiation of}} + X2<int> x2i; + + struct Y { + void f(int); // expected-note 2{{type mismatch at 1st parameter}} + template<typename T> void f(T*); // expected-note 2{{could not match 'type-parameter-0-0 *' against 'double'}} + template<typename T> void nondep(); + }; + + template<typename> struct Z1 { + friend void Y::f(double); // expected-error {{friend declaration of 'f' does not match any declaration in 'qualified_friend::Y'}} + friend void Y::g(); // expected-error {{friend declaration of 'g' does not match any declaration in 'qualified_friend::Y'}} + }; + template<typename T> struct Z2 { + friend void Y::f(T); // expected-error {{friend declaration of 'f' does not match any declaration in 'qualified_friend::Y'}} + }; + Z1<int> zi; + Z2<double> zd; // expected-note {{in instantiation of}} + Z2<int> z2i; + + template<typename T> + struct OK { + friend void qualified_friend::f(int); + friend void qualified_friend::f(int*); + friend void qualified_friend::f(T*); + friend void qualified_friend::f<T>(T*); + friend void qualified_friend::nondep<int>(); + friend void qualified_friend::nondep<T>(); + + friend void Y::f(int); + friend void Y::f(int*); + friend void Y::f(T*); + friend void Y::f<T>(T*); + friend void Y::nondep<int>(); + friend void Y::nondep<T>(); + }; + OK<float> ok; +} + +namespace qualified_friend_finds_nothing { + // FIXME: The status of this example is unclear. For now, we diagnose if the + // qualified declaration has nothing it can redeclare, but allow qualified + // lookup to find later-declared function templates during instantiation. + // + // This matches the behavior of GCC, EDG, ICC, and MSVC (except that GCC and + // ICC bizarrely accept the instantiation of B<float>). + namespace N {} + + template<typename T> struct A { + friend void N::f(T); // expected-error {{friend declaration of 'f' does not match}} + }; + namespace N { void f(); } // expected-note {{different number of parameters}} + + template<typename T> struct B { + friend void N::f(T); // expected-error {{friend declaration of 'f' does not match}} + }; + B<float> bf; // expected-note {{in instantiation of}} + + namespace N { void f(int); } + B<int> bi; // ok?! +} |