Skip to content

Commit f7cfb22

Browse files
committed
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. llvm-svn: 116393
1 parent 2473992 commit f7cfb22

File tree

4 files changed

+108
-51
lines changed

4 files changed

+108
-51
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,8 @@ def err_introducing_special_friend : Error<
444444
"destructor|conversion operator}0 as a friend">;
445445
def err_tagless_friend_type_template : Error<
446446
"friend type templates must use an elaborated type">;
447+
def err_no_matching_local_friend : Error<
448+
"no matching function found in local scope">;
447449

448450
def err_abstract_type_in_decl : Error<
449451
"%select{return|parameter|variable|field}0 type %1 is an abstract class">;

clang/lib/Sema/SemaDecl.cpp

Lines changed: 65 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3153,6 +3153,20 @@ void Sema::AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) {
31533153
}
31543154
}
31553155

3156+
static void DiagnoseInvalidRedeclaration(Sema &S, FunctionDecl *NewFD) {
3157+
LookupResult Prev(S, NewFD->getDeclName(), NewFD->getLocation(),
3158+
Sema::LookupOrdinaryName, Sema::ForRedeclaration);
3159+
S.LookupQualifiedName(Prev, NewFD->getDeclContext());
3160+
assert(!Prev.isAmbiguous() &&
3161+
"Cannot have an ambiguity in previous-declaration lookup");
3162+
for (LookupResult::iterator Func = Prev.begin(), FuncEnd = Prev.end();
3163+
Func != FuncEnd; ++Func) {
3164+
if (isa<FunctionDecl>(*Func) &&
3165+
isNearlyMatchingFunction(S.Context, cast<FunctionDecl>(*Func), NewFD))
3166+
S.Diag((*Func)->getLocation(), diag::note_member_def_close_match);
3167+
}
3168+
}
3169+
31563170
NamedDecl*
31573171
Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
31583172
QualType R, TypeSourceInfo *TInfo,
@@ -3470,13 +3484,6 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
34703484
FilterLookupForScope(*this, Previous, DC, S, NewFD->hasLinkage());
34713485

34723486
if (isFriend) {
3473-
// DC is the namespace in which the function is being declared.
3474-
assert((DC->isFileContext() || !Previous.empty() ||
3475-
(D.getCXXScopeSpec().isSet() &&
3476-
D.getCXXScopeSpec().getScopeRep()->isDependent())) &&
3477-
"previously-undeclared friend function being created "
3478-
"in a non-namespace context");
3479-
34803487
// For now, claim that the objects have no previous declaration.
34813488
if (FunctionTemplate) {
34823489
FunctionTemplate->setObjectOfFriendDecl(false);
@@ -3675,24 +3682,36 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
36753682
: TPC_FunctionTemplate);
36763683
}
36773684

3678-
if (D.getCXXScopeSpec().isSet() && !NewFD->isInvalidDecl()) {
3679-
if (isFriend || !CurContext->isRecord()) {
3680-
// Fake up an access specifier if it's supposed to be a class member.
3681-
if (!Redeclaration && isa<CXXRecordDecl>(NewFD->getDeclContext()))
3682-
NewFD->setAccess(AS_public);
3683-
3684-
// An out-of-line member function declaration must also be a
3685-
// definition (C++ [dcl.meaning]p1).
3686-
// Note that this is not the case for explicit specializations of
3687-
// function templates or member functions of class templates, per
3688-
// C++ [temp.expl.spec]p2. We also allow these declarations as an extension
3689-
// for compatibility with old SWIG code which likes to generate them.
3690-
if (!IsFunctionDefinition && !isFriend &&
3691-
!isFunctionTemplateSpecialization && !isExplicitSpecialization) {
3692-
Diag(NewFD->getLocation(), diag::ext_out_of_line_declaration)
3693-
<< D.getCXXScopeSpec().getRange();
3694-
}
3695-
if (!Redeclaration && !(isFriend && CurContext->isDependentContext())) {
3685+
if (NewFD->isInvalidDecl()) {
3686+
// Ignore all the rest of this.
3687+
3688+
} else if (CurContext->isRecord() && D.getCXXScopeSpec().isSet() &&
3689+
!isFriend) {
3690+
// The user provided a superfluous scope specifier inside a class
3691+
// definition:
3692+
//
3693+
// class X {
3694+
// void X::f();
3695+
// };
3696+
Diag(NewFD->getLocation(), diag::warn_member_extra_qualification)
3697+
<< Name << FixItHint::CreateRemoval(D.getCXXScopeSpec().getRange());
3698+
3699+
} else if (!Redeclaration) {
3700+
// Fake up an access specifier if it's supposed to be a class member.
3701+
if (isa<CXXRecordDecl>(NewFD->getDeclContext()))
3702+
NewFD->setAccess(AS_public);
3703+
3704+
// Qualified decls generally require a previous declaration.
3705+
if (D.getCXXScopeSpec().isSet()) {
3706+
// ...with the major exception of dependent friend declarations.
3707+
// In theory, this condition could be whether the qualifier
3708+
// is dependent; in practice, the way we nest template parameters
3709+
// prevents this sort of matching from working, so we have to base it
3710+
// on the general dependence of the context.
3711+
if (isFriend && CurContext->isDependentContext()) {
3712+
// ignore these
3713+
3714+
} else {
36963715
// The user tried to provide an out-of-line definition for a
36973716
// function that is a member of a class or namespace, but there
36983717
// was no such member function declared (C++ [class.mfct]p2,
@@ -3711,27 +3730,28 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
37113730
<< Name << DC << D.getCXXScopeSpec().getRange();
37123731
NewFD->setInvalidDecl();
37133732

3714-
LookupResult Prev(*this, Name, D.getIdentifierLoc(), LookupOrdinaryName,
3715-
ForRedeclaration);
3716-
LookupQualifiedName(Prev, DC);
3717-
assert(!Prev.isAmbiguous() &&
3718-
"Cannot have an ambiguity in previous-declaration lookup");
3719-
for (LookupResult::iterator Func = Prev.begin(), FuncEnd = Prev.end();
3720-
Func != FuncEnd; ++Func) {
3721-
if (isa<FunctionDecl>(*Func) &&
3722-
isNearlyMatchingFunction(Context, cast<FunctionDecl>(*Func), NewFD))
3723-
Diag((*Func)->getLocation(), diag::note_member_def_close_match);
3724-
}
3733+
DiagnoseInvalidRedeclaration(*this, NewFD);
37253734
}
3726-
} else {
3727-
// The user provided a superfluous scope specifier inside a class definition:
3728-
//
3729-
// class X {
3730-
// void X::f();
3731-
// };
3732-
Diag(NewFD->getLocation(), diag::warn_member_extra_qualification)
3733-
<< Name << FixItHint::CreateRemoval(D.getCXXScopeSpec().getRange());
3734-
}
3735+
3736+
// Unqualified local friend declarations are required to resolve
3737+
// to something.
3738+
} else if (isFriend && cast<CXXRecordDecl>(CurContext)->isLocalClass()) {
3739+
Diag(D.getIdentifierLoc(), diag::err_no_matching_local_friend);
3740+
NewFD->setInvalidDecl();
3741+
DiagnoseInvalidRedeclaration(*this, NewFD);
3742+
}
3743+
3744+
} else if (!IsFunctionDefinition && D.getCXXScopeSpec().isSet() &&
3745+
!isFriend && !isFunctionTemplateSpecialization &&
3746+
!isExplicitSpecialization) {
3747+
// An out-of-line member function declaration must also be a
3748+
// definition (C++ [dcl.meaning]p1).
3749+
// Note that this is not the case for explicit specializations of
3750+
// function templates or member functions of class templates, per
3751+
// C++ [temp.expl.spec]p2. We also allow these declarations as an extension
3752+
// for compatibility with old SWIG code which likes to generate them.
3753+
Diag(NewFD->getLocation(), diag::ext_out_of_line_declaration)
3754+
<< D.getCXXScopeSpec().getRange();
37353755
}
37363756

37373757
// Handle attributes. We need to have merged decls when handling attributes

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6344,12 +6344,26 @@ Decl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition,
63446344

63456345
// There are four cases here.
63466346
// - There's no scope specifier, in which case we just go to the
6347-
// appropriate namespace and create a function or function template
6347+
// appropriate scope and look for a function or function template
63486348
// there as appropriate.
63496349
// Recover from invalid scope qualifiers as if they just weren't there.
63506350
if (SS.isInvalid() || !SS.isSet()) {
6351-
// Walk out to the nearest namespace scope looking for matches.
6352-
6351+
// C++0x [namespace.memdef]p3:
6352+
// If the name in a friend declaration is neither qualified nor
6353+
// a template-id and the declaration is a function or an
6354+
// elaborated-type-specifier, the lookup to determine whether
6355+
// the entity has been previously declared shall not consider
6356+
// any scopes outside the innermost enclosing namespace.
6357+
// C++0x [class.friend]p11:
6358+
// If a friend declaration appears in a local class and the name
6359+
// specified is an unqualified name, a prior declaration is
6360+
// looked up without considering scopes that are outside the
6361+
// innermost enclosing non-class scope. For a friend function
6362+
// declaration, if there is no prior declaration, the program is
6363+
// ill-formed.
6364+
bool isLocal = cast<CXXRecordDecl>(CurContext)->isLocalClass();
6365+
6366+
// Find the appropriate context according to the above.
63536367
DC = CurContext;
63546368
while (true) {
63556369
// Skip class contexts. If someone can cite chapter and verse
@@ -6365,9 +6379,9 @@ Decl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition,
63656379
LookupQualifiedName(Previous, DC);
63666380

63676381
// TODO: decide what we think about using declarations.
6368-
if (!Previous.empty())
6382+
if (isLocal || !Previous.empty())
63696383
break;
6370-
6384+
63716385
if (DC->isFileContext()) break;
63726386
DC = DC->getParent();
63736387
}
@@ -6381,6 +6395,8 @@ Decl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition,
63816395
&& !getLangOptions().CPlusPlus0x)
63826396
Diag(DS.getFriendSpecLoc(), diag::err_friend_is_member);
63836397

6398+
S = getScopeForDeclContext(S, DC);
6399+
63846400
// - There's a non-dependent scope specifier, in which case we
63856401
// compute it and do a previous lookup there for a function
63866402
// or function template.
@@ -6425,7 +6441,7 @@ Decl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition,
64256441
assert(isa<CXXRecordDecl>(DC) && "friend declaration not in class?");
64266442
}
64276443

6428-
if (DC->isFileContext()) {
6444+
if (!DC->isRecord()) {
64296445
// This implies that it has to be an operator or function.
64306446
if (D.getName().getKind() == UnqualifiedId::IK_ConstructorName ||
64316447
D.getName().getKind() == UnqualifiedId::IK_DestructorName ||
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
3+
// rdar://problem/8540720
4+
namespace test0 {
5+
void foo() {
6+
void bar();
7+
class A {
8+
friend void bar();
9+
};
10+
}
11+
}
12+
13+
namespace test1 {
14+
void foo() {
15+
class A {
16+
friend void bar(); // expected-error {{no matching function found in local scope}}
17+
};
18+
}
19+
}

0 commit comments

Comments
 (0)