aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn McCall <rjmccall@apple.com>2010-10-13 05:45:15 +0000
committerJohn McCall <rjmccall@apple.com>2010-10-13 05:45:15 +0000
commit29ae6e5fb001deddd831e66faf143ecd9e50f67c (patch)
treeeef97e606a9b4f6326e5797691f196409321564c
parent3bd5b6c3c2ad1d5a6f88cf21f627e8d4f03c4df4 (diff)
Support friend function declarations in local classes correctly.
Fixes a crash and diagnoses the error condition of an unqualified friend which doesn't resolve to something. I'm still not certain how this is useful. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@116393 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td2
-rw-r--r--lib/Sema/SemaDecl.cpp110
-rw-r--r--lib/Sema/SemaDeclCXX.cpp28
-rw-r--r--test/CXX/class.access/class.friend/p11.cpp19
4 files changed, 108 insertions, 51 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index d003b6d5ed..b6c1515334 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -444,6 +444,8 @@ def err_introducing_special_friend : Error<
"destructor|conversion operator}0 as a friend">;
def err_tagless_friend_type_template : Error<
"friend type templates must use an elaborated type">;
+def err_no_matching_local_friend : Error<
+ "no matching function found in local scope">;
def err_abstract_type_in_decl : Error<
"%select{return|parameter|variable|field}0 type %1 is an abstract class">;
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index cb7656865c..a34d7ecf63 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -3153,6 +3153,20 @@ void Sema::AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) {
}
}
+static void DiagnoseInvalidRedeclaration(Sema &S, FunctionDecl *NewFD) {
+ LookupResult Prev(S, NewFD->getDeclName(), NewFD->getLocation(),
+ Sema::LookupOrdinaryName, Sema::ForRedeclaration);
+ S.LookupQualifiedName(Prev, NewFD->getDeclContext());
+ assert(!Prev.isAmbiguous() &&
+ "Cannot have an ambiguity in previous-declaration lookup");
+ for (LookupResult::iterator Func = Prev.begin(), FuncEnd = Prev.end();
+ Func != FuncEnd; ++Func) {
+ if (isa<FunctionDecl>(*Func) &&
+ isNearlyMatchingFunction(S.Context, cast<FunctionDecl>(*Func), NewFD))
+ S.Diag((*Func)->getLocation(), diag::note_member_def_close_match);
+ }
+}
+
NamedDecl*
Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
QualType R, TypeSourceInfo *TInfo,
@@ -3470,13 +3484,6 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
FilterLookupForScope(*this, Previous, DC, S, NewFD->hasLinkage());
if (isFriend) {
- // DC is the namespace in which the function is being declared.
- assert((DC->isFileContext() || !Previous.empty() ||
- (D.getCXXScopeSpec().isSet() &&
- D.getCXXScopeSpec().getScopeRep()->isDependent())) &&
- "previously-undeclared friend function being created "
- "in a non-namespace context");
-
// For now, claim that the objects have no previous declaration.
if (FunctionTemplate) {
FunctionTemplate->setObjectOfFriendDecl(false);
@@ -3675,24 +3682,36 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
: TPC_FunctionTemplate);
}
- if (D.getCXXScopeSpec().isSet() && !NewFD->isInvalidDecl()) {
- if (isFriend || !CurContext->isRecord()) {
- // Fake up an access specifier if it's supposed to be a class member.
- if (!Redeclaration && isa<CXXRecordDecl>(NewFD->getDeclContext()))
- NewFD->setAccess(AS_public);
-
- // An out-of-line member function declaration must also be a
- // definition (C++ [dcl.meaning]p1).
- // Note that this is not the case for explicit specializations of
- // function templates or member functions of class templates, per
- // C++ [temp.expl.spec]p2. We also allow these declarations as an extension
- // for compatibility with old SWIG code which likes to generate them.
- if (!IsFunctionDefinition && !isFriend &&
- !isFunctionTemplateSpecialization && !isExplicitSpecialization) {
- Diag(NewFD->getLocation(), diag::ext_out_of_line_declaration)
- << D.getCXXScopeSpec().getRange();
- }
- if (!Redeclaration && !(isFriend && CurContext->isDependentContext())) {
+ if (NewFD->isInvalidDecl()) {
+ // Ignore all the rest of this.
+
+ } else if (CurContext->isRecord() && D.getCXXScopeSpec().isSet() &&
+ !isFriend) {
+ // The user provided a superfluous scope specifier inside a class
+ // definition:
+ //
+ // class X {
+ // void X::f();
+ // };
+ Diag(NewFD->getLocation(), diag::warn_member_extra_qualification)
+ << Name << FixItHint::CreateRemoval(D.getCXXScopeSpec().getRange());
+
+ } else if (!Redeclaration) {
+ // Fake up an access specifier if it's supposed to be a class member.
+ if (isa<CXXRecordDecl>(NewFD->getDeclContext()))
+ NewFD->setAccess(AS_public);
+
+ // Qualified decls generally require a previous declaration.
+ if (D.getCXXScopeSpec().isSet()) {
+ // ...with the major exception of dependent friend declarations.
+ // In theory, this condition could be whether the qualifier
+ // is dependent; in practice, the way we nest template parameters
+ // prevents this sort of matching from working, so we have to base it
+ // on the general dependence of the context.
+ if (isFriend && CurContext->isDependentContext()) {
+ // ignore these
+
+ } else {
// The user tried to provide an out-of-line definition for a
// function that is a member of a class or namespace, but there
// was no such member function declared (C++ [class.mfct]p2,
@@ -3711,27 +3730,28 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
<< Name << DC << D.getCXXScopeSpec().getRange();
NewFD->setInvalidDecl();
- LookupResult Prev(*this, Name, D.getIdentifierLoc(), LookupOrdinaryName,
- ForRedeclaration);
- LookupQualifiedName(Prev, DC);
- assert(!Prev.isAmbiguous() &&
- "Cannot have an ambiguity in previous-declaration lookup");
- for (LookupResult::iterator Func = Prev.begin(), FuncEnd = Prev.end();
- Func != FuncEnd; ++Func) {
- if (isa<FunctionDecl>(*Func) &&
- isNearlyMatchingFunction(Context, cast<FunctionDecl>(*Func), NewFD))
- Diag((*Func)->getLocation(), diag::note_member_def_close_match);
- }
+ DiagnoseInvalidRedeclaration(*this, NewFD);
}
- } else {
- // The user provided a superfluous scope specifier inside a class definition:
- //
- // class X {
- // void X::f();
- // };
- Diag(NewFD->getLocation(), diag::warn_member_extra_qualification)
- << Name << FixItHint::CreateRemoval(D.getCXXScopeSpec().getRange());
- }
+
+ // Unqualified local friend declarations are required to resolve
+ // to something.
+ } else if (isFriend && cast<CXXRecordDecl>(CurContext)->isLocalClass()) {
+ Diag(D.getIdentifierLoc(), diag::err_no_matching_local_friend);
+ NewFD->setInvalidDecl();
+ DiagnoseInvalidRedeclaration(*this, NewFD);
+ }
+
+ } else if (!IsFunctionDefinition && D.getCXXScopeSpec().isSet() &&
+ !isFriend && !isFunctionTemplateSpecialization &&
+ !isExplicitSpecialization) {
+ // An out-of-line member function declaration must also be a
+ // definition (C++ [dcl.meaning]p1).
+ // Note that this is not the case for explicit specializations of
+ // function templates or member functions of class templates, per
+ // C++ [temp.expl.spec]p2. We also allow these declarations as an extension
+ // for compatibility with old SWIG code which likes to generate them.
+ Diag(NewFD->getLocation(), diag::ext_out_of_line_declaration)
+ << D.getCXXScopeSpec().getRange();
}
// Handle attributes. We need to have merged decls when handling attributes
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index eb62b6a9db..62ac3bb455 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -6344,12 +6344,26 @@ Decl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition,
// There are four cases here.
// - There's no scope specifier, in which case we just go to the
- // appropriate namespace and create a function or function template
+ // appropriate scope and look for a function or function template
// there as appropriate.
// Recover from invalid scope qualifiers as if they just weren't there.
if (SS.isInvalid() || !SS.isSet()) {
- // Walk out to the nearest namespace scope looking for matches.
-
+ // C++0x [namespace.memdef]p3:
+ // If the name in a friend declaration is neither qualified nor
+ // a template-id and the declaration is a function or an
+ // elaborated-type-specifier, the lookup to determine whether
+ // the entity has been previously declared shall not consider
+ // any scopes outside the innermost enclosing namespace.
+ // C++0x [class.friend]p11:
+ // If a friend declaration appears in a local class and the name
+ // specified is an unqualified name, a prior declaration is
+ // looked up without considering scopes that are outside the
+ // innermost enclosing non-class scope. For a friend function
+ // declaration, if there is no prior declaration, the program is
+ // ill-formed.
+ bool isLocal = cast<CXXRecordDecl>(CurContext)->isLocalClass();
+
+ // Find the appropriate context according to the above.
DC = CurContext;
while (true) {
// Skip class contexts. If someone can cite chapter and verse
@@ -6365,9 +6379,9 @@ Decl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition,
LookupQualifiedName(Previous, DC);
// TODO: decide what we think about using declarations.
- if (!Previous.empty())
+ if (isLocal || !Previous.empty())
break;
-
+
if (DC->isFileContext()) break;
DC = DC->getParent();
}
@@ -6381,6 +6395,8 @@ Decl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition,
&& !getLangOptions().CPlusPlus0x)
Diag(DS.getFriendSpecLoc(), diag::err_friend_is_member);
+ S = getScopeForDeclContext(S, DC);
+
// - There's a non-dependent scope specifier, in which case we
// compute it and do a previous lookup there for a function
// or function template.
@@ -6425,7 +6441,7 @@ Decl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition,
assert(isa<CXXRecordDecl>(DC) && "friend declaration not in class?");
}
- if (DC->isFileContext()) {
+ if (!DC->isRecord()) {
// This implies that it has to be an operator or function.
if (D.getName().getKind() == UnqualifiedId::IK_ConstructorName ||
D.getName().getKind() == UnqualifiedId::IK_DestructorName ||
diff --git a/test/CXX/class.access/class.friend/p11.cpp b/test/CXX/class.access/class.friend/p11.cpp
new file mode 100644
index 0000000000..a05b2d2875
--- /dev/null
+++ b/test/CXX/class.access/class.friend/p11.cpp
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// rdar://problem/8540720
+namespace test0 {
+ void foo() {
+ void bar();
+ class A {
+ friend void bar();
+ };
+ }
+}
+
+namespace test1 {
+ void foo() {
+ class A {
+ friend void bar(); // expected-error {{no matching function found in local scope}}
+ };
+ }
+}