summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/AST/TemplateBase.h6
-rw-r--r--lib/Sema/SemaDecl.cpp10
-rw-r--r--lib/Sema/SemaOverload.cpp1
-rw-r--r--lib/Sema/SemaTemplateInstantiateDecl.cpp84
-rw-r--r--test/SemaTemplate/friend.cpp75
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?!
+}