return dyn_cast<FunctionDecl>(getDeclContext());
}
+ FunctionDecl *isLocalClass() {
+ return const_cast<FunctionDecl*>(
+ const_cast<const CXXRecordDecl*>(this)->isLocalClass());
+ }
+
/// \brief Determine whether this dependent class is a current instantiation,
/// when viewed from within the given context.
bool isCurrentInstantiation(const DeclContext *CurContext) const;
def err_member_name_of_class : Error<"member %0 has the same name as its class">;
def err_member_def_undefined_record : Error<
"out-of-line definition of %0 from class %1 without definition">;
-def err_member_def_does_not_match : Error<
- "out-of-line definition of %0 does not match any declaration in %1">;
+def err_member_decl_does_not_match : Error<
+ "out-of-line %select{declaration|definition}2 of %0 "
+ "does not match any declaration in %1">;
def err_friend_decl_with_def_arg_must_be_def : Error<
"friend declaration specifying a default argument must be a definition">;
def err_friend_decl_with_def_arg_redeclared : Error<
"friend declaration specifying a default argument must be the only declaration">;
def err_friend_decl_does_not_match : Error<
"friend declaration of %0 does not match any declaration in %1">;
-def err_member_def_does_not_match_suggest : Error<
- "out-of-line definition of %0 does not match any declaration in %1; "
- "did you mean %2?">;
+def err_member_decl_does_not_match_suggest : Error<
+ "out-of-line %select{declaration|definition}3 of %0 "
+ "does not match any declaration in %1; did you mean %2?">;
def err_member_def_does_not_match_ret_type : Error<
"out-of-line definition of %q0 differs from the declaration in the return type">;
def err_nonstatic_member_out_of_line : Error<
def note_member_def_close_param_match : Note<
"type of %ordinal0 parameter of member declaration does not match definition"
"%diff{ ($ vs $)|}1,2">;
+def note_local_decl_close_match : Note<"local declaration nearly matches">;
+def note_local_decl_close_param_match : Note<
+ "type of %ordinal0 parameter of local declaration does not match definition"
+ "%diff{ ($ vs $)|}1,2">;
def err_typecheck_ivar_variable_size : Error<
"instance variables must have a constant size">;
def err_ivar_reference_type : Error<
/// are outside of the current scope unless they have linkage. See
/// C99 6.2.2p4-5 and C++ [basic.link]p6.
LookupRedeclarationWithLinkage,
+ /// Look up a friend of a local class. This lookup does not look
+ /// outside the innermost non-class scope. See C++11 [class.friend]p11.
+ LookupLocalFriendName,
/// Look up the name of an Objective-C protocol.
LookupObjCProtocolName,
/// Look up implicit 'self' parameter of an objective-c method.
assert(this == getPrimaryContext() && "expected a primary DC");
// Skip declarations within functions.
- // FIXME: We shouldn't need to build lookup tables for function declarations
- // ever, and we can't do so correctly because we can't model the nesting of
- // scopes which occurs within functions. We use "qualified" lookup into
- // function declarations when handling friend declarations inside nested
- // classes, and consequently accept the following invalid code:
- //
- // void f() { void g(); { int g; struct S { friend void g(); }; } }
- if (isFunctionOrMethod() && !isa<FunctionDecl>(D))
+ if (isFunctionOrMethod())
return;
// Skip declarations which should be invisible to name lookup.
// the template out-of-line.
if (!D.getCXXScopeSpec().isInvalid() && !Invalid &&
!PrevVarTemplate) {
- Diag(D.getIdentifierLoc(), diag::err_member_def_does_not_match)
- << Name << DC << D.getCXXScopeSpec().getRange();
+ Diag(D.getIdentifierLoc(), diag::err_member_decl_does_not_match)
+ << Name << DC << /*IsDefinition*/true
+ << D.getCXXScopeSpec().getRange();
Invalid = true;
}
}
///
/// Returns a NamedDecl iff typo correction was performed and substituting in
/// the new declaration name does not cause new errors.
-static NamedDecl* DiagnoseInvalidRedeclaration(
+static NamedDecl *DiagnoseInvalidRedeclaration(
Sema &SemaRef, LookupResult &Previous, FunctionDecl *NewFD,
- ActOnFDArgs &ExtraArgs) {
+ ActOnFDArgs &ExtraArgs, bool IsLocalFriend, Scope *S) {
NamedDecl *Result = NULL;
DeclarationName Name = NewFD->getDeclName();
DeclContext *NewDC = NewFD->getDeclContext();
- LookupResult Prev(SemaRef, Name, NewFD->getLocation(),
- Sema::LookupOrdinaryName, Sema::ForRedeclaration);
SmallVector<unsigned, 1> MismatchedParams;
SmallVector<std::pair<FunctionDecl *, unsigned>, 1> NearMatches;
TypoCorrection Correction;
- bool isFriendDecl = (SemaRef.getLangOpts().CPlusPlus &&
- ExtraArgs.D.getDeclSpec().isFriendSpecified());
- unsigned DiagMsg = isFriendDecl ? diag::err_no_matching_local_friend
- : diag::err_member_def_does_not_match;
+ unsigned DiagMsg = IsLocalFriend ? diag::err_no_matching_local_friend
+ : diag::err_member_decl_does_not_match;
+ LookupResult Prev(SemaRef, Name, NewFD->getLocation(),
+ IsLocalFriend ? Sema::LookupLocalFriendName
+ : Sema::LookupOrdinaryName,
+ Sema::ForRedeclaration);
NewFD->setInvalidDecl();
- SemaRef.LookupQualifiedName(Prev, NewDC);
+ if (IsLocalFriend)
+ SemaRef.LookupName(Prev, S);
+ else
+ SemaRef.LookupQualifiedName(Prev, NewDC);
assert(!Prev.isAmbiguous() &&
"Cannot have an ambiguity in previous-declaration lookup");
CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewFD);
}
}
// If the qualified name lookup yielded nothing, try typo correction
- } else if ((Correction = SemaRef.CorrectTypo(Prev.getLookupNameInfo(),
- Prev.getLookupKind(), 0, 0,
- Validator, NewDC))) {
+ } else if ((Correction = SemaRef.CorrectTypo(
+ Prev.getLookupNameInfo(), Prev.getLookupKind(), S, 0,
+ Validator, IsLocalFriend ? 0 : NewDC))) {
// Trap errors.
Sema::SFINAETrap Trap(SemaRef);
// Ignore the correction if it didn't yield any close FunctionDecl matches
Correction = TypoCorrection();
} else {
- DiagMsg = isFriendDecl ? diag::err_no_matching_local_friend_suggest
- : diag::err_member_def_does_not_match_suggest;
+ DiagMsg = IsLocalFriend ? diag::err_no_matching_local_friend_suggest
+ : diag::err_member_decl_does_not_match_suggest;
}
}
+ bool IsDefinition = ExtraArgs.D.isFunctionDefinition();
if (Correction) {
// FIXME: use Correction.getCorrectionRange() instead of computing the range
// here. This requires passing in the CXXScopeSpec to CorrectTypo which in
FixItLoc.setBegin(SS.getBeginLoc());
SemaRef.Diag(NewFD->getLocStart(), DiagMsg)
<< Name << NewDC << Correction.getQuoted(SemaRef.getLangOpts())
+ << IsDefinition
<< FixItHint::CreateReplacement(
FixItLoc, Correction.getAsString(SemaRef.getLangOpts()));
} else {
SemaRef.Diag(NewFD->getLocation(), DiagMsg)
- << Name << NewDC << NewFD->getLocation();
+ << Name << NewDC << IsDefinition << NewFD->getLocation();
}
bool NewFDisConst = false;
NearMatch = NearMatches.begin(), NearMatchEnd = NearMatches.end();
NearMatch != NearMatchEnd; ++NearMatch) {
FunctionDecl *FD = NearMatch->first;
- bool FDisConst = false;
- if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD))
- FDisConst = MD->isConst();
+ CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
+ bool FDisConst = MD && MD->isConst();
+ bool IsMember = MD || !IsLocalFriend;
if (unsigned Idx = NearMatch->second) {
ParmVarDecl *FDParam = FD->getParamDecl(Idx-1);
SourceLocation Loc = FDParam->getTypeSpecStartLoc();
if (Loc.isInvalid()) Loc = FD->getLocation();
- SemaRef.Diag(Loc, diag::note_member_def_close_param_match)
- << Idx << FDParam->getType() << NewFD->getParamDecl(Idx-1)->getType();
+ SemaRef.Diag(Loc, IsMember ? diag::note_member_def_close_param_match
+ : diag::note_local_decl_close_param_match)
+ << Idx << FDParam->getType()
+ << NewFD->getParamDecl(Idx - 1)->getType();
} else if (Correction) {
SemaRef.Diag(FD->getLocation(), diag::note_previous_decl)
<< Correction.getQuoted(SemaRef.getLangOpts());
SemaRef.Diag(FD->getLocation(), diag::note_member_def_close_const_match)
<< NewFDisConst << FD->getSourceRange().getEnd();
} else
- SemaRef.Diag(FD->getLocation(), diag::note_member_def_close_match);
+ SemaRef.Diag(FD->getLocation(),
+ IsMember ? diag::note_member_def_close_match
+ : diag::note_local_decl_close_match);
}
return Result;
}
// matches (e.g., those that differ only in cv-qualifiers and
// whether the parameter types are references).
- if (NamedDecl *Result = DiagnoseInvalidRedeclaration(*this, Previous,
- NewFD,
- ExtraArgs)) {
+ if (NamedDecl *Result = DiagnoseInvalidRedeclaration(
+ *this, Previous, NewFD, ExtraArgs, false, 0)) {
AddToScope = ExtraArgs.AddToScope;
return Result;
}
// Unqualified local friend declarations are required to resolve
// to something.
} else if (isFriend && cast<CXXRecordDecl>(CurContext)->isLocalClass()) {
- if (NamedDecl *Result = DiagnoseInvalidRedeclaration(*this, Previous,
- NewFD,
- ExtraArgs)) {
+ if (NamedDecl *Result = DiagnoseInvalidRedeclaration(
+ *this, Previous, NewFD, ExtraArgs, true, S)) {
AddToScope = ExtraArgs.AddToScope;
return Result;
}
LookupResult Previous(*this, NameInfo, LookupOrdinaryName,
ForRedeclaration);
- // FIXME: there are different rules in local classes
+ // There are five cases here.
+ // - There's no scope specifier and we're in a local class. Only look
+ // for functions declared in the immediately-enclosing block scope.
+ // We recover from invalid scope qualifiers as if they just weren't there.
+ FunctionDecl *FunctionContainingLocalClass = 0;
+ if ((SS.isInvalid() || !SS.isSet()) &&
+ (FunctionContainingLocalClass =
+ cast<CXXRecordDecl>(CurContext)->isLocalClass())) {
+ // C++11 [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.
+
+ // Find the innermost enclosing non-class scope. This is the block
+ // scope containing the local class definition (or for a nested class,
+ // the outer local class).
+ DCScope = S->getFnParent();
+
+ // Look up the function name in the scope.
+ Previous.clear(LookupLocalFriendName);
+ LookupName(Previous, S, /*AllowBuiltinCreation*/false);
+
+ if (!Previous.empty()) {
+ // All possible previous declarations must have the same context:
+ // either they were declared at block scope or they are members of
+ // one of the enclosing local classes.
+ DC = Previous.getRepresentativeDecl()->getDeclContext();
+ } else {
+ // This is ill-formed, but provide the context that we would have
+ // declared the function in, if we were permitted to, for error recovery.
+ DC = FunctionContainingLocalClass;
+ }
+
+ // C++ [class.friend]p6:
+ // A function can be defined in a friend declaration of a class if and
+ // only if the class is a non-local class (9.8), the function name is
+ // unqualified, and the function has namespace scope.
+ if (D.isFunctionDefinition()) {
+ Diag(NameInfo.getBeginLoc(), diag::err_friend_def_in_local_class);
+ }
- // There are four cases here.
// - There's no scope specifier, in which case we just go to the
// 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()) {
- // C++0x [namespace.memdef]p3:
+ } else if (SS.isInvalid() || !SS.isSet()) {
+ // C++11 [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();
bool isTemplateId = D.getName().getKind() == UnqualifiedId::IK_TemplateId;
// Find the appropriate context according to the above.
while (true) {
LookupQualifiedName(Previous, LookupDC);
- // TODO: decide what we think about using declarations.
- if (isLocal)
- break;
-
if (!Previous.empty()) {
DC = LookupDC;
break;
}
DCScope = getScopeForDeclContext(S, DC);
-
- // C++ [class.friend]p6:
- // A function can be defined in a friend declaration of a class if and
- // only if the class is a non-local class (9.8), the function name is
- // unqualified, and the function has namespace scope.
- if (isLocal && D.isFunctionDefinition()) {
- Diag(NameInfo.getBeginLoc(), diag::err_friend_def_in_local_class);
- }
-
+
// - 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.
FakeDCScope.setEntity(DC);
DCScope = &FakeDCScope;
}
-
+
bool AddToScope = true;
NamedDecl *ND = ActOnFunctionDeclarator(DCScope, D, DC, TInfo, Previous,
TemplateParams, AddToScope);
if (!ND) return 0;
- assert(ND->getDeclContext() == DC);
assert(ND->getLexicalDeclContext() == CurContext);
+ // If we performed typo correction, we might have added a scope specifier
+ // and changed the decl context.
+ DC = ND->getDeclContext();
+
// Add the function declaration to the appropriate lookup tables,
// adjusting the redeclarations list as necessary. We don't
// want to do this yet if the friending class is dependent.
case Sema::LookupObjCImplicitSelfParam:
case Sema::LookupOrdinaryName:
case Sema::LookupRedeclarationWithLinkage:
+ case Sema::LookupLocalFriendName:
IDNS = Decl::IDNS_Ordinary;
if (CPlusPlus) {
IDNS |= Decl::IDNS_Tag | Decl::IDNS_Member | Decl::IDNS_Namespace;
return true;
}
+ if (R.getLookupKind() == LookupLocalFriendName && !S->isClassScope()) {
+ // C++11 [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.
+ return false;
+ }
+
if (!Ctx && S->isTemplateParamScope() && OutsideOfTemplateParamDC &&
S->getParent() && !S->getParent()->isTemplateParamScope()) {
// We've just searched the last template parameter scope and
case LookupOrdinaryName:
case LookupMemberName:
case LookupRedeclarationWithLinkage:
+ case LookupLocalFriendName:
BaseCallback = &CXXRecordDecl::FindOrdinaryMember;
break;
// template out-of-line.
if (!SS.isInvalid() && !Invalid && !PrevClassTemplate) {
Diag(NameLoc, TUK == TUK_Friend ? diag::err_friend_decl_does_not_match
- : diag::err_member_def_does_not_match)
- << Name << SemanticContext << SS.getRange();
+ : diag::err_member_decl_does_not_match)
+ << Name << SemanticContext << /*IsDefinition*/true << SS.getRange();
Invalid = true;
}
}
};
}
}
+
+namespace test2 {
+ void bar(); // expected-note {{'::test2::bar' declared here}}
+
+ void foo() { // expected-note {{'::test2::foo' declared here}}
+ struct S1 {
+ friend void foo(); // expected-error {{no matching function 'foo' found in local scope; did you mean '::test2::foo'?}}
+ };
+
+ void foo(); // expected-note {{local declaration nearly matches}}
+ struct S2 {
+ friend void foo();
+ };
+
+ {
+ struct S2 {
+ friend void foo(); // expected-error {{no matching function found in local scope}}
+ };
+ }
+
+ {
+ int foo;
+ struct S3 {
+ friend void foo(); // expected-error {{no matching function found in local scope}}
+ };
+ }
+
+ struct S4 {
+ friend void bar(); // expected-error {{no matching function 'bar' found in local scope; did you mean '::test2::bar'?}}
+ };
+
+ { void bar(); }
+ struct S5 {
+ friend void bar(); // expected-error {{no matching function found in local scope}}
+ };
+
+ {
+ void bar();
+ struct S6 {
+ friend void bar();
+ };
+ }
+
+ struct S7 {
+ void bar() { Inner::f(); }
+ struct Inner {
+ friend void bar();
+ static void f() {}
+ };
+ };
+
+ void bar(); // expected-note {{'bar' declared here}}
+ struct S8 {
+ struct Inner {
+ friend void bar();
+ };
+ };
+
+ struct S9 {
+ struct Inner {
+ friend void baz(); // expected-error {{no matching function 'baz' found in local scope; did you mean 'bar'?}}
+ };
+ };
+
+ struct S10 {
+ void quux() {}
+ void foo() {
+ struct Inner1 {
+ friend void bar(); // expected-error {{no matching function found in local scope}}
+ friend void quux(); // expected-error {{no matching function found in local scope}}
+ };
+
+ void bar();
+ struct Inner2 {
+ friend void bar();
+ };
+ }
+ };
+ }
+}
struct NoMove11 : NonTrivialMoveAssignVBase {}; // expected-note {{'const DR1402::NoMove11 &'}}
struct Test {
- friend NoMove1::NoMove1(NoMove1 &&); // expected-error {{no matching function}}
- friend NoMove2::NoMove2(NoMove2 &&); // expected-error {{no matching function}}
- friend NoMove3::NoMove3(NoMove3 &&); // expected-error {{no matching function}}
- friend NoMove4::NoMove4(NoMove4 &&); // expected-error {{no matching function}}
- friend NoMove5::NoMove5(NoMove5 &&); // expected-error {{no matching function}}
- friend NoMove6::NoMove6(NoMove6 &&); // expected-error {{no matching function}}
- friend NoMove7::NoMove7(NoMove7 &&); // expected-error {{no matching function}}
- friend NoMove8::NoMove8(NoMove8 &&); // expected-error {{no matching function}}
+ friend NoMove1::NoMove1(NoMove1 &&); // expected-error {{does not match}}
+ friend NoMove2::NoMove2(NoMove2 &&); // expected-error {{does not match}}
+ friend NoMove3::NoMove3(NoMove3 &&); // expected-error {{does not match}}
+ friend NoMove4::NoMove4(NoMove4 &&); // expected-error {{does not match}}
+ friend NoMove5::NoMove5(NoMove5 &&); // expected-error {{does not match}}
+ friend NoMove6::NoMove6(NoMove6 &&); // expected-error {{does not match}}
+ friend NoMove7::NoMove7(NoMove7 &&); // expected-error {{does not match}}
+ friend NoMove8::NoMove8(NoMove8 &&); // expected-error {{does not match}}
friend NoMove9::NoMove9(NoMove9 &&);
friend NoMove10::NoMove10(NoMove10 &&);
friend NoMove11::NoMove11(NoMove11 &&);
- friend NoMove1 &NoMove1::operator=(NoMove1 &&); // expected-error {{no matching function}}
- friend NoMove2 &NoMove2::operator=(NoMove2 &&); // expected-error {{no matching function}}
- friend NoMove3 &NoMove3::operator=(NoMove3 &&); // expected-error {{no matching function}}
- friend NoMove4 &NoMove4::operator=(NoMove4 &&); // expected-error {{no matching function}}
- friend NoMove5 &NoMove5::operator=(NoMove5 &&); // expected-error {{no matching function}}
- friend NoMove6 &NoMove6::operator=(NoMove6 &&); // expected-error {{no matching function}}
- friend NoMove7 &NoMove7::operator=(NoMove7 &&); // expected-error {{no matching function}}
- friend NoMove8 &NoMove8::operator=(NoMove8 &&); // expected-error {{no matching function}}
+ friend NoMove1 &NoMove1::operator=(NoMove1 &&); // expected-error {{does not match}}
+ friend NoMove2 &NoMove2::operator=(NoMove2 &&); // expected-error {{does not match}}
+ friend NoMove3 &NoMove3::operator=(NoMove3 &&); // expected-error {{does not match}}
+ friend NoMove4 &NoMove4::operator=(NoMove4 &&); // expected-error {{does not match}}
+ friend NoMove5 &NoMove5::operator=(NoMove5 &&); // expected-error {{does not match}}
+ friend NoMove6 &NoMove6::operator=(NoMove6 &&); // expected-error {{does not match}}
+ friend NoMove7 &NoMove7::operator=(NoMove7 &&); // expected-error {{does not match}}
+ friend NoMove8 &NoMove8::operator=(NoMove8 &&); // expected-error {{does not match}}
friend NoMove9 &NoMove9::operator=(NoMove9 &&);
- friend NoMove10 &NoMove10::operator=(NoMove10 &&); // expected-error {{no matching function}}
- friend NoMove11 &NoMove11::operator=(NoMove11 &&); // expected-error {{no matching function}}
+ friend NoMove10 &NoMove10::operator=(NoMove10 &&); // expected-error {{does not match}}
+ friend NoMove11 &NoMove11::operator=(NoMove11 &&); // expected-error {{does not match}}
};
}
using namespace A::B;
// FIXME: Adding '::PR12297::' is not needed as removing 'A::' is sufficient
- T A::global(); // expected-error {{out-of-line definition of 'global' does not match any declaration in namespace 'PR12297::A'; did you mean '::PR12297::global'?}}
+ T A::global(); // expected-error {{out-of-line declaration of 'global' does not match any declaration in namespace 'PR12297::A'; did you mean '::PR12297::global'?}}
}