[cfe-commits] r78274 - in /cfe/trunk: include/clang/Basic/ include/clang/Parse/ lib/Parse/ lib/Sema/ test/CXX/basic/basic.lookup/basic.lookup.unqual/ test/CXX/class/class.friend/ test/Parser/
John McCall
rjmccall at apple.com
Wed Aug 5 19:15:44 PDT 2009
Author: rjmccall
Date: Wed Aug 5 21:15:43 2009
New Revision: 78274
URL: http://llvm.org/viewvc/llvm-project?rev=78274&view=rev
Log:
First pass at friend semantics.
Added:
cfe/trunk/test/CXX/class/class.friend/
cfe/trunk/test/CXX/class/class.friend/p1.cpp
cfe/trunk/test/CXX/class/class.friend/p2.cpp
cfe/trunk/test/CXX/class/class.friend/p6.cpp
Modified:
cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/include/clang/Parse/Action.h
cfe/trunk/include/clang/Parse/Parser.h
cfe/trunk/lib/Parse/DeclSpec.cpp
cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp
cfe/trunk/lib/Parse/ParseDecl.cpp
cfe/trunk/lib/Parse/ParseDeclCXX.cpp
cfe/trunk/lib/Sema/Sema.h
cfe/trunk/lib/Sema/SemaDecl.cpp
cfe/trunk/lib/Sema/SemaDeclCXX.cpp
cfe/trunk/lib/Sema/SemaLookup.cpp
cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp
cfe/trunk/test/Parser/cxx-friend.cpp
Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=78274&r1=78273&r2=78274&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Wed Aug 5 21:15:43 2009
@@ -35,6 +35,7 @@
def err_invalid_long_spec : Error<"'long %0' is invalid">;
def err_invalid_longlong_spec : Error<"'long long %0' is invalid">;
def err_invalid_complex_spec : Error<"'_Complex %0' is invalid">;
+def err_friend_storage_spec : Error<"'%0' is invalid in friend declarations">;
def ext_ident_list_in_param : Extension<
"type-less parameter names in function declaration">;
@@ -152,6 +153,8 @@
"type name does not allow function specifier to be specified">;
def err_invalid_decl_spec_combination : Error<
"cannot combine with previous '%0' declaration specifier">;
+def err_friend_invalid_in_context : Error<
+ "'friend' used outside of class">;
def err_unknown_typename : Error<
"unknown type name %0">;
def err_use_of_tag_name_without_tag : Error<
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=78274&r1=78273&r2=78274&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Aug 5 21:15:43 2009
@@ -293,8 +293,20 @@
"static_assert expression is not an integral constant expression">;
def err_static_assert_failed : Error<"static_assert failed \"%0\"">;
-def err_friend_decl_outside_class : Error<
- "'friend' used outside of class">;
+def err_friend_decl_defines_class : Error<
+ "cannot define a type in a friend declaration">;
+def err_unexpected_friend : Error<
+ "friends can only be classes or functions">;
+def err_friend_is_member : Error<
+ "friends cannot be members of the declaring class">;
+def err_unelaborated_friend_type : Error<
+ "must specify '%select{class|union}0' in a friend "
+ "%select{class|union}0 declaration">;
+def err_qualified_friend_not_found : Error<
+ "no function named %0 with type %1 was found in the specified scope">;
+def err_introducing_special_friend : Error<
+ "must use a qualified name when declaring a %select{constructor|"
+ "destructor|conversion operator}0 as a friend">;
def err_abstract_type_in_decl : Error<
"%select{return|parameter|variable|field}1 type %0 is an abstract class">;
Modified: cfe/trunk/include/clang/Parse/Action.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Action.h?rev=78274&r1=78273&r2=78274&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Action.h (original)
+++ cfe/trunk/include/clang/Parse/Action.h Wed Aug 5 21:15:43 2009
@@ -22,6 +22,7 @@
#include "clang/Parse/DeclSpec.h"
#include "clang/Parse/Ownership.h"
#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/ADT/PointerUnion.h"
namespace clang {
// Semantic.
@@ -413,7 +414,8 @@
enum TagUseKind {
TUK_Reference, // Reference to a tag: 'struct foo *X;'
TUK_Declaration, // Fwd decl of a tag: 'struct foo;'
- TUK_Definition // Definition of a tag: 'struct foo { int X; } Y;'
+ TUK_Definition, // Definition of a tag: 'struct foo { int X; } Y;'
+ TUK_Friend // Friend declaration: 'friend struct foo;'
};
/// \brief The parser has encountered a tag (e.g., "class X") that should be
@@ -1109,12 +1111,11 @@
}
/// ActOnFriendDecl - This action is called when a friend declaration is
- /// encountered. Returns false on success.
- virtual bool ActOnFriendDecl(Scope *S, SourceLocation FriendLoc,
- DeclPtrTy Dcl) {
- return false;
+ /// encountered.
+ virtual DeclPtrTy ActOnFriendDecl(Scope *S,
+ llvm::PointerUnion<const DeclSpec*,Declarator*> D) {
+ return DeclPtrTy();
}
-
//===------------------------- C++ Expressions --------------------------===//
Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=78274&r1=78273&r2=78274&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Wed Aug 5 21:15:43 2009
@@ -906,6 +906,14 @@
//===--------------------------------------------------------------------===//
// C99 6.7: Declarations.
+
+ /// A context for parsing declaration specifiers. TODO: flesh this
+ /// out, there are other significant restrictions on specifiers than
+ /// would be best implemented in the parser.
+ enum DeclSpecContext {
+ DSC_normal, // normal context
+ DSC_class // class context, enables 'friend'
+ };
DeclGroupPtrTy ParseDeclaration(unsigned Context, SourceLocation &DeclEnd);
DeclGroupPtrTy ParseSimpleDeclaration(unsigned Context,
@@ -922,7 +930,8 @@
AccessSpecifier AS);
void ParseDeclarationSpecifiers(DeclSpec &DS,
const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(),
- AccessSpecifier AS = AS_none);
+ AccessSpecifier AS = AS_none,
+ DeclSpecContext DSC = DSC_normal);
bool ParseOptionalTypeSpecifier(DeclSpec &DS, bool &isInvalid,
const char *&PrevSpec,
unsigned &DiagID,
Modified: cfe/trunk/lib/Parse/DeclSpec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/DeclSpec.cpp?rev=78274&r1=78273&r2=78274&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/DeclSpec.cpp (original)
+++ cfe/trunk/lib/Parse/DeclSpec.cpp Wed Aug 5 21:15:43 2009
@@ -398,6 +398,24 @@
}
}
+ // C++ [class.friend]p6:
+ // No storage-class-specifier shall appear in the decl-specifier-seq
+ // of a friend declaration.
+ if (isFriendSpecified() && getStorageClassSpec()) {
+ DeclSpec::SCS SC = getStorageClassSpec();
+ const char *SpecName = getSpecifierName(SC);
+
+ SourceLocation SCLoc = getStorageClassSpecLoc();
+ SourceLocation SCEndLoc = SCLoc.getFileLocWithOffset(strlen(SpecName));
+
+ Diag(D, SCLoc, SrcMgr, diag::err_friend_storage_spec)
+ << SpecName
+ << CodeModificationHint::CreateRemoval(SourceRange(SCLoc, SCEndLoc));
+
+ ClearStorageClassSpecs();
+ }
+
+
// Okay, now we can infer the real type.
// TODO: return "auto function" and other bad things based on the real type.
Modified: cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp?rev=78274&r1=78273&r2=78274&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp (original)
+++ cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp Wed Aug 5 21:15:43 2009
@@ -27,7 +27,11 @@
assert((Tok.is(tok::l_brace) || Tok.is(tok::colon) || Tok.is(tok::kw_try)) &&
"Current token not a '{', ':' or 'try'!");
- DeclPtrTy FnD = Actions.ActOnCXXMemberDeclarator(CurScope, AS, D, 0, 0);
+ DeclPtrTy FnD;
+ if (D.getDeclSpec().isFriendSpecified())
+ FnD = Actions.ActOnFriendDecl(CurScope, &D);
+ else
+ FnD = Actions.ActOnCXXMemberDeclarator(CurScope, AS, D, 0, 0);
HandleMemberFunctionDefaultArgs(D, FnD);
Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=78274&r1=78273&r2=78274&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Wed Aug 5 21:15:43 2009
@@ -708,7 +708,8 @@
///
void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
const ParsedTemplateInfo &TemplateInfo,
- AccessSpecifier AS) {
+ AccessSpecifier AS,
+ DeclSpecContext DSContext) {
DS.SetRangeStart(Tok.getLocation());
while (1) {
bool isInvalid = false;
@@ -968,7 +969,13 @@
// friend
case tok::kw_friend:
- isInvalid = DS.SetFriendSpec(Loc, PrevSpec, DiagID);
+ if (DSContext == DSC_class)
+ isInvalid = DS.SetFriendSpec(Loc, PrevSpec, DiagID);
+ else {
+ PrevSpec = ""; // not actually used by the diagnostic
+ DiagID = diag::err_friend_invalid_in_context;
+ isInvalid = true;
+ }
break;
// type-specifier
Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=78274&r1=78273&r2=78274&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Wed Aug 5 21:15:43 2009
@@ -567,15 +567,16 @@
}
}
- // There are three options here. If we have 'struct foo;', then
- // this is a forward declaration. If we have 'struct foo {...' or
+ // There are four options here. If we have 'struct foo;', then this
+ // is either a forward declaration or a friend declaration, which
+ // have to be treated differently. If we have 'struct foo {...' or
// 'struct foo :...' then this is a definition. Otherwise we have
// something like 'struct foo xyz', a reference.
Action::TagUseKind TUK;
if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon)))
TUK = Action::TUK_Definition;
- else if (Tok.is(tok::semi) && !DS.isFriendSpecified())
- TUK = Action::TUK_Declaration;
+ else if (Tok.is(tok::semi))
+ TUK = DS.isFriendSpecified() ? Action::TUK_Friend : Action::TUK_Declaration;
else
TUK = Action::TUK_Reference;
@@ -600,7 +601,7 @@
// to turn that template-id into a type.
bool Owned = false;
- if (TemplateId && TUK != Action::TUK_Reference) {
+ if (TemplateId && TUK != Action::TUK_Reference && TUK != Action::TUK_Friend) {
// Explicit specialization, class template partial specialization,
// or explicit instantiation.
ASTTemplateArgsPtr TemplateArgsPtr(Actions,
@@ -727,10 +728,6 @@
if (DS.SetTypeSpecType(TagType, StartLoc, PrevSpec, DiagID,
TagOrTempResult.get().getAs<void>(), Owned))
Diag(StartLoc, DiagID) << PrevSpec;
-
- if (DS.isFriendSpecified())
- Actions.ActOnFriendDecl(CurScope, DS.getFriendSpecLoc(),
- TagOrTempResult.get());
}
/// ParseBaseClause - Parse the base-clause of a C++ class [C++ class.derived].
@@ -951,24 +948,17 @@
// decl-specifier-seq:
// Parse the common declaration-specifiers piece.
DeclSpec DS;
- ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS);
+ ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC_class);
if (Tok.is(tok::semi)) {
ConsumeToken();
- // C++ 9.2p7: The member-declarator-list can be omitted only after a
- // class-specifier or an enum-specifier or in a friend declaration.
- // FIXME: Friend declarations.
- switch (DS.getTypeSpecType()) {
- case DeclSpec::TST_struct:
- case DeclSpec::TST_union:
- case DeclSpec::TST_class:
- case DeclSpec::TST_enum:
+
+ if (DS.isFriendSpecified())
+ Actions.ActOnFriendDecl(CurScope, &DS);
+ else
Actions.ParsedFreeStandingDeclSpec(CurScope, DS);
- return;
- default:
- Diag(DSStart, diag::err_no_declarators);
- return;
- }
+
+ return;
}
Declarator DeclaratorInfo(DS, Declarator::MemberContext);
@@ -1066,11 +1056,17 @@
// NOTE: If Sema is the Action module and declarator is an instance field,
// this call will *not* return the created decl; It will return null.
// See Sema::ActOnCXXMemberDeclarator for details.
- DeclPtrTy ThisDecl = Actions.ActOnCXXMemberDeclarator(CurScope, AS,
- DeclaratorInfo,
- BitfieldSize.release(),
- Init.release(),
- Deleted);
+
+ DeclPtrTy ThisDecl;
+ if (DS.isFriendSpecified()) {
+ // TODO: handle initializers, bitfields, 'delete'
+ ThisDecl = Actions.ActOnFriendDecl(CurScope, &DeclaratorInfo);
+ } else
+ ThisDecl = Actions.ActOnCXXMemberDeclarator(CurScope, AS,
+ DeclaratorInfo,
+ BitfieldSize.release(),
+ Init.release(),
+ Deleted);
if (ThisDecl)
DeclsInGroup.push_back(ThisDecl);
Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=78274&r1=78273&r2=78274&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Wed Aug 5 21:15:43 2009
@@ -1192,6 +1192,9 @@
LookupResult LookupQualifiedName(DeclContext *LookupCtx, DeclarationName Name,
LookupNameKind NameKind,
bool RedeclarationOnly = false);
+ Decl *LookupQualifiedNameWithType(DeclContext *LookupCtx,
+ DeclarationName Name,
+ QualType T);
LookupResult LookupParsedName(Scope *S, const CXXScopeSpec *SS,
DeclarationName Name,
LookupNameKind NameKind,
@@ -2001,8 +2004,8 @@
ExprArg AssertExpr,
ExprArg AssertMessageExpr);
- virtual bool ActOnFriendDecl(Scope *S, SourceLocation FriendLoc,
- DeclPtrTy Dcl);
+ virtual DeclPtrTy ActOnFriendDecl(Scope *S,
+ llvm::PointerUnion<const DeclSpec*,Declarator*> D);
QualType CheckConstructorDeclarator(Declarator &D, QualType R,
FunctionDecl::StorageClass& SC);
Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=78274&r1=78273&r2=78274&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed Aug 5 21:15:43 2009
@@ -1275,6 +1275,9 @@
if (!DS.getTypeRep()) // We probably had an error
return DeclPtrTy();
+ // Note that the above type specs guarantee that the
+ // type rep is a Decl, whereas in many of the others
+ // it's a Type.
Tag = dyn_cast<TagDecl>(static_cast<Decl *>(DS.getTypeRep()));
}
@@ -3973,7 +3976,8 @@
// If this is a use of a previous tag, or if the tag is already declared
// in the same scope (so that the definition/declaration completes or
// rementions the tag), reuse the decl.
- if (TUK == TUK_Reference || isDeclInScope(PrevDecl, SearchDC, S)) {
+ if (TUK == TUK_Reference || TUK == TUK_Friend ||
+ isDeclInScope(PrevDecl, SearchDC, S)) {
// Make sure that this wasn't declared as an enum and now used as a
// struct or something similar.
if (!isAcceptableTagRedeclaration(PrevTagDecl, Kind, KWLoc, *Name)) {
@@ -4009,6 +4013,11 @@
if (TUK == TUK_Reference)
return DeclPtrTy::make(PrevDecl);
+ // If this is a friend, make sure we create the new
+ // declaration in the appropriate semantic context.
+ if (TUK == TUK_Friend)
+ SearchDC = PrevDecl->getDeclContext();
+
// Diagnose attempts to redefine a tag.
if (TUK == TUK_Definition) {
if (TagDecl *Def = PrevTagDecl->getDefinition(Context)) {
@@ -4039,7 +4048,8 @@
}
}
// If we get here we have (another) forward declaration or we
- // have a definition. Just create a new decl.
+ // have a definition. Just create a new decl.
+
} else {
// If we get here, this is a definition of a new tag type in a nested
// scope, e.g. "struct foo; void bar() { struct foo; }", just create a
@@ -4102,6 +4112,18 @@
(S->getEntity() &&
((DeclContext *)S->getEntity())->isTransparentContext()))
S = S->getParent();
+
+ } else if (TUK == TUK_Friend && SS.isEmpty() && Name) {
+ // C++ [namespace.memdef]p3:
+ // If a friend declaration in a non-local class first declares a
+ // class or function, the friend class or function is a member of
+ // the innermost enclosing namespace.
+ while (!SearchDC->isNamespace() && !SearchDC->isTranslationUnit())
+ SearchDC = SearchDC->getParent();
+
+ // The entity of a decl scope is a DeclContext; see PushDeclContext.
+ while (S->getEntity() != SearchDC)
+ S = S->getParent();
}
CreateNewDecl:
@@ -4195,14 +4217,14 @@
New->setLexicalDeclContext(CurContext);
// Set the access specifier.
- if (!Invalid)
+ if (!Invalid && TUK != TUK_Friend)
SetMemberAccessSpecifier(New, PrevDecl, AS);
if (TUK == TUK_Definition)
New->startDefinition();
// If this has an identifier, add it to the scope stack.
- if (Name) {
+ if (Name && TUK != TUK_Friend) {
S = getNonFieldDeclScope(S);
PushOnScopeChains(New, S);
} else {
Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=78274&r1=78273&r2=78274&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Aug 5 21:15:43 2009
@@ -550,6 +550,8 @@
bool isFunc = D.isFunctionDeclarator();
+ assert(!DS.isFriendSpecified());
+
// C++ 9.2p6: A member shall not be declared to have automatic storage
// duration (auto, register) or with the extern storage-class-specifier.
// C++ 7.1.1p8: The mutable specifier can be applied only to names of class
@@ -3302,13 +3304,208 @@
return DeclPtrTy::make(Decl);
}
-bool Sema::ActOnFriendDecl(Scope *S, SourceLocation FriendLoc, DeclPtrTy Dcl) {
- if (!(S->getFlags() & Scope::ClassScope)) {
- Diag(FriendLoc, diag::err_friend_decl_outside_class);
- return true;
+Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S,
+ llvm::PointerUnion<const DeclSpec*,Declarator*> DU) {
+ Declarator *D = DU.dyn_cast<Declarator*>();
+ const DeclSpec &DS = (D ? D->getDeclSpec() : *DU.get<const DeclSpec*>());
+
+ assert(DS.isFriendSpecified());
+ assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified);
+
+ // If there's no declarator, then this can only be a friend class
+ // declaration (or else it's just invalid).
+ if (!D) {
+
+ // C++ [class.friend]p2:
+ // An elaborated-type-specifier shall be used in a friend declaration
+ // for a class.*
+ // * The class-key of the elaborated-type-specifier is required.
+ CXXRecordDecl *RD = 0;
+
+ switch (DS.getTypeSpecType()) {
+ case DeclSpec::TST_class:
+ case DeclSpec::TST_struct:
+ case DeclSpec::TST_union:
+ RD = dyn_cast_or_null<CXXRecordDecl>(static_cast<Decl*>(DS.getTypeRep()));
+ if (!RD) return DeclPtrTy();
+ break;
+
+ case DeclSpec::TST_typename:
+ if (const RecordType *RT =
+ ((const Type*) DS.getTypeRep())->getAs<RecordType>())
+ RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
+ // fallthrough
+ default:
+ if (RD) {
+ Diag(DS.getFriendSpecLoc(), diag::err_unelaborated_friend_type)
+ << (RD->isUnion())
+ << CodeModificationHint::CreateInsertion(DS.getTypeSpecTypeLoc(),
+ RD->isUnion() ? " union" : " class");
+ return DeclPtrTy::make(RD);
+ }
+
+ Diag(DS.getFriendSpecLoc(), diag::err_unexpected_friend)
+ << DS.getSourceRange();
+ return DeclPtrTy();
+ }
+
+ // The record declaration we get from friend declarations is not
+ // canonicalized; see ActOnTag.
+ assert(RD);
+
+ // C++ [class.friend]p2: A class shall not be defined inside
+ // a friend declaration.
+ if (RD->isDefinition())
+ Diag(DS.getFriendSpecLoc(), diag::err_friend_decl_defines_class)
+ << RD->getSourceRange();
+
+ // C++ [class.friend]p1: A friend of a class is a function or
+ // class that is not a member of the class . . .
+ // Definitions currently get treated in a way that causes this
+ // error, so only report it if we didn't see a definition.
+ else if (RD->getDeclContext() == CurContext)
+ Diag(DS.getFriendSpecLoc(), diag::err_friend_is_member);
+
+ return DeclPtrTy::make(RD);
+ }
+
+ // We have a declarator.
+ assert(D);
+
+ SourceLocation Loc = D->getIdentifierLoc();
+ QualType T = GetTypeForDeclarator(*D, S);
+
+ // C++ [class.friend]p1
+ // A friend of a class is a function or class....
+ // Note that this sees through typedefs, which is intended.
+ if (!T->isFunctionType()) {
+ Diag(Loc, diag::err_unexpected_friend);
+
+ // It might be worthwhile to try to recover by creating an
+ // appropriate declaration.
+ return DeclPtrTy();
}
-
- return false;
+
+ // C++ [namespace.memdef]p3
+ // - If a friend declaration in a non-local class first declares a
+ // class or function, the friend class or function is a member
+ // of the innermost enclosing namespace.
+ // - The name of the friend is not found by simple name lookup
+ // until a matching declaration is provided in that namespace
+ // scope (either before or after the class declaration granting
+ // friendship).
+ // - If a friend function is called, its name may be found by the
+ // name lookup that considers functions from namespaces and
+ // classes associated with the types of the function arguments.
+ // - When looking for a prior declaration of a class or a function
+ // declared as a friend, scopes outside the innermost enclosing
+ // namespace scope are not considered.
+
+ CXXScopeSpec &ScopeQual = D->getCXXScopeSpec();
+ DeclarationName Name = GetNameForDeclarator(*D);
+ assert(Name);
+
+ // The existing declaration we found.
+ FunctionDecl *FD = NULL;
+
+ // The context we found the declaration in, or in which we should
+ // create the declaration.
+ DeclContext *DC;
+
+ // FIXME: handle local classes
+
+ // Recover from invalid scope qualifiers as if they just weren't there.
+ if (!ScopeQual.isInvalid() && ScopeQual.isSet()) {
+ DC = computeDeclContext(ScopeQual);
+
+ // FIXME: handle dependent contexts
+ if (!DC) return DeclPtrTy();
+
+ Decl *Dec = LookupQualifiedNameWithType(DC, Name, T);
+
+ // If searching in that context implicitly found a declaration in
+ // a different context, treat it like it wasn't found at all.
+ // TODO: better diagnostics for this case. Suggesting the right
+ // qualified scope would be nice...
+ if (!Dec || Dec->getDeclContext() != DC) {
+ D->setInvalidType();
+ Diag(Loc, diag::err_qualified_friend_not_found) << Name << T;
+ return DeclPtrTy();
+ }
+
+ // C++ [class.friend]p1: A friend of a class is a function or
+ // class that is not a member of the class . . .
+ if (DC == CurContext)
+ Diag(DS.getFriendSpecLoc(), diag::err_friend_is_member);
+
+ FD = cast<FunctionDecl>(Dec);
+
+ // Otherwise walk out to the nearest namespace scope looking for matches.
+ } else {
+ // TODO: handle local class contexts.
+
+ DC = CurContext;
+ while (true) {
+ // Skip class contexts. If someone can cite chapter and verse
+ // for this behavior, that would be nice --- it's what GCC and
+ // EDG do, and it seems like a reasonable intent, but the spec
+ // really only says that checks for unqualified existing
+ // declarations should stop at the nearest enclosing namespace,
+ // not that they should only consider the nearest enclosing
+ // namespace.
+ while (DC->isRecord()) DC = DC->getParent();
+
+ Decl *Dec = LookupQualifiedNameWithType(DC, Name, T);
+
+ // TODO: decide what we think about using declarations.
+ if (Dec) {
+ FD = cast<FunctionDecl>(Dec);
+ break;
+ }
+ if (DC->isFileContext()) break;
+ DC = DC->getParent();
+ }
+
+ // C++ [class.friend]p1: A friend of a class is a function or
+ // class that is not a member of the class . . .
+ if (FD && DC == CurContext)
+ Diag(DS.getFriendSpecLoc(), diag::err_friend_is_member);
+ }
+
+ // If we didn't find something matching the type exactly, create
+ // a declaration. This declaration should only be findable via
+ // argument-dependent lookup.
+ if (!FD) {
+ assert(DC->isFileContext());
+
+ // This implies that it has to be an operator or function.
+ if (D->getKind() == Declarator::DK_Constructor ||
+ D->getKind() == Declarator::DK_Destructor ||
+ D->getKind() == Declarator::DK_Conversion) {
+ Diag(Loc, diag::err_introducing_special_friend) <<
+ (D->getKind() == Declarator::DK_Constructor ? 0 :
+ D->getKind() == Declarator::DK_Destructor ? 1 : 2);
+ return DeclPtrTy();
+ }
+
+ bool Redeclaration = false;
+ NamedDecl *ND = ActOnFunctionDeclarator(S, *D, DC, T,
+ /* PrevDecl = */ NULL,
+ MultiTemplateParamsArg(*this),
+ /* isFunctionDef */ false,
+ Redeclaration);
+
+ FD = cast_or_null<FunctionDecl>(ND);
+
+ // Note that we're creating a declaration but *not* pushing
+ // it onto the scope chains.
+
+ // TODO: make accessible via argument-dependent lookup.
+ }
+
+ // TODO: actually register the function as a friend.
+
+ return DeclPtrTy::make(FD);
}
void Sema::SetDeclDeleted(DeclPtrTy dcl, SourceLocation DelLoc) {
Modified: cfe/trunk/lib/Sema/SemaLookup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLookup.cpp?rev=78274&r1=78273&r2=78274&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLookup.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLookup.cpp Wed Aug 5 21:15:43 2009
@@ -1677,6 +1677,26 @@
return cast_or_null<ObjCCategoryImplDecl>(D);
}
+// Attempts to find a declaration in the given declaration context
+// with exactly the given type. Returns null if no such declaration
+// was found.
+Decl *Sema::LookupQualifiedNameWithType(DeclContext *DC,
+ DeclarationName Name,
+ QualType T) {
+ LookupResult result =
+ LookupQualifiedName(DC, Name, LookupOrdinaryName, true);
+
+ CanQualType CQT = Context.getCanonicalType(T);
+
+ for (LookupResult::iterator ir = result.begin(), ie = result.end();
+ ir != ie; ++ir)
+ if (FunctionDecl *CurFD = dyn_cast<FunctionDecl>(*ir))
+ if (Context.getCanonicalType(CurFD->getType()) == CQT)
+ return CurFD;
+
+ return NULL;
+}
+
void Sema::LookupOverloadedOperatorName(OverloadedOperatorKind Op, Scope *S,
QualType T1, QualType T2,
FunctionSet &Functions) {
Modified: cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp?rev=78274&r1=78273&r2=78274&view=diff
==============================================================================
--- cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp (original)
+++ cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp Wed Aug 5 21:15:43 2009
@@ -1,9 +1,7 @@
// RUN: clang-cc -fsyntax-only -verify %s
-// XFAIL
-// FIXME: This part is here to demonstrate the failure in looking up 'f', it can
-// be removed once the whole test passes.
typedef int f;
+
namespace N0 {
struct A {
friend void f();
Added: cfe/trunk/test/CXX/class/class.friend/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class/class.friend/p1.cpp?rev=78274&view=auto
==============================================================================
--- cfe/trunk/test/CXX/class/class.friend/p1.cpp (added)
+++ cfe/trunk/test/CXX/class/class.friend/p1.cpp Wed Aug 5 21:15:43 2009
@@ -0,0 +1,59 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+struct Outer {
+ struct Inner {
+ int intfield;
+ };
+};
+
+struct Base {
+ void base_member();
+
+ typedef int Int;
+ Int typedeffed_member();
+};
+
+struct Derived : public Base {
+};
+
+int myglobal;
+
+class A {
+ class AInner {
+ };
+
+ friend class PreDeclared;
+ friend class Outer::Inner;
+ friend int Outer::Inner::intfield; // expected-error {{ friends can only be classes or functions }}
+ friend int Outer::Inner::missing_field; //expected-error {{ friends can only be classes or functions }}
+ friend int myoperation(float); // okay
+ friend int myglobal; // expected-error {{ friends can only be classes or functions }}
+
+ void a_member();
+ friend void A::a_member(); // expected-error {{ friends cannot be members of the declaring class }}
+ friend void a_member(); // okay (because we ignore class scopes when looking up friends)
+ friend class A::AInner; // expected-error {{ friends cannot be members of the declaring class }}
+ friend class AInner; // expected-error {{ friends cannot be members of the declaring class }}
+
+ friend void Derived::missing_member(); // expected-error {{ no function named 'missing_member' with type 'void ()' was found in the specified scope }}
+
+ friend void Derived::base_member(); // expected-error {{ no function named 'base_member' with type 'void ()' was found in the specified scope }}
+
+ friend int Base::typedeffed_member(); // okay: should look through typedef
+
+ // These test that the friend is properly not being treated as a
+ // member function.
+ friend A operator|(const A& l, const A& r); // okay
+ friend A operator|(const A& r); // expected-error {{ overloaded 'operator|' must be a binary operator (has 1 parameter) }}
+
+ friend operator bool() const; // expected-error {{ must use a qualified name when declaring a conversion operator as a friend }}
+
+ typedef void ftypedef();
+ friend ftypedef typedeffed_function; // okay (because it's not declared as a member)
+};
+
+class PreDeclared;
+
+int myoperation(float f) {
+ return (int) f;
+}
Added: cfe/trunk/test/CXX/class/class.friend/p2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class/class.friend/p2.cpp?rev=78274&view=auto
==============================================================================
--- cfe/trunk/test/CXX/class/class.friend/p2.cpp (added)
+++ cfe/trunk/test/CXX/class/class.friend/p2.cpp Wed Aug 5 21:15:43 2009
@@ -0,0 +1,8 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+class A {
+ friend class B {}; // expected-error {{cannot define a type in a friend declaration}}
+ friend int; // expected-error {{friends can only be classes or functions}}
+ friend B; // expected-error {{must specify 'class' in a friend class declaration}}
+ friend class C; // okay
+};
Added: cfe/trunk/test/CXX/class/class.friend/p6.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class/class.friend/p6.cpp?rev=78274&view=auto
==============================================================================
--- cfe/trunk/test/CXX/class/class.friend/p6.cpp (added)
+++ cfe/trunk/test/CXX/class/class.friend/p6.cpp Wed Aug 5 21:15:43 2009
@@ -0,0 +1,10 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+class A {
+ friend static class B; // expected-error {{'static' is invalid in friend declarations}}
+ friend extern class C; // expected-error {{'extern' is invalid in friend declarations}}
+ friend auto class D; // expected-error {{'auto' is invalid in friend declarations}}
+ friend register class E; // expected-error {{'register' is invalid in friend declarations}}
+ friend mutable class F; // expected-error {{'mutable' is invalid in friend declarations}}
+ friend typedef class G; // expected-error {{'typedef' is invalid in friend declarations}}
+};
Modified: cfe/trunk/test/Parser/cxx-friend.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx-friend.cpp?rev=78274&r1=78273&r2=78274&view=diff
==============================================================================
--- cfe/trunk/test/Parser/cxx-friend.cpp (original)
+++ cfe/trunk/test/Parser/cxx-friend.cpp Wed Aug 5 21:15:43 2009
@@ -1,4 +1,4 @@
-// RUN: clang-cc -fsyntax-only %s
+// RUN: clang-cc -fsyntax-only -verify %s
class C {
friend class D;
@@ -9,9 +9,24 @@
void f();
};
+friend int x; // expected-error {{'friend' used outside of class}}
+
+friend class D {}; // expected-error {{'friend' used outside of class}}
+
+union U {
+ int u1;
+};
+
class B {
// 'A' here should refer to the declaration above.
friend class A;
- void f(A *a) { a->f(); }
+ friend C; // expected-error {{must specify 'class' in a friend class declaration}}
+ friend U; // expected-error {{must specify 'union' in a friend union declaration}}
+ friend int; // expected-error {{friends can only be classes or functions}}
+
+ friend void myfunc();
+
+ void f(A *a) { a->f(); }
};
+
More information about the cfe-commits
mailing list