From: John McCall Date: Tue, 11 Aug 2009 06:59:38 +0000 (+0000) Subject: Argument-dependent lookup for friend declarations. Add a new decl type, X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3f9a8a60614b763785d54ad08821745d03a4af70;p=clang Argument-dependent lookup for friend declarations. Add a new decl type, FriendFunctionDecl, and create instances as appropriate. The design of FriendFunctionDecl is still somewhat up in the air; you can befriend arbitrary types of functions --- methods, constructors, etc. --- and it's not clear that this representation captures that very well. We'll have a better picture when we start consuming this data in access control. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@78653 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index ea782c9a66..0979733644 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -827,6 +827,14 @@ public: /// semantic context via makeDeclVisibleInContext. void addDecl(Decl *D); + /// @brief Add the declaration D to this context without modifying + /// any lookup tables. + /// + /// This is useful for some operations in dependent contexts where + /// the semantic context might not be dependent; this basically + /// only happens with friends. + void addHiddenDecl(Decl *D); + /// lookup_iterator - An iterator that provides access to the results /// of looking up a name within this context. typedef NamedDecl **lookup_iterator; diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 8b601cb810..00e583fa6e 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -1213,6 +1213,36 @@ public: static bool classof(const CXXConversionDecl *D) { return true; } }; +/// FriendFunctionDecl - Represents the declaration (and possibly +/// the definition) of a friend function. +class FriendFunctionDecl : public FunctionDecl { + // Location of the 'friend' specifier. + const SourceLocation FriendLoc; + + FriendFunctionDecl(DeclContext *DC, SourceLocation L, + DeclarationName N, QualType T, + bool isInline, SourceLocation FriendL) + : FunctionDecl(FriendFunction, DC, L, N, T, None, isInline), + FriendLoc(FriendL) + {} + +public: + static FriendFunctionDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation L, DeclarationName N, + QualType T, bool isInline, + SourceLocation FriendL); + + SourceLocation getFriendLoc() const { + return FriendLoc; + } + + // Implement isa/cast/dyncast/etc. + static bool classof(const Decl *D) { + return D->getKind() == FriendFunction; + } + static bool classof(const FriendFunctionDecl *D) { return true; } +}; + /// LinkageSpecDecl - This represents a linkage specification. For example: /// extern "C" void foo(); /// diff --git a/include/clang/AST/DeclNodes.def b/include/clang/AST/DeclNodes.def index 9fac06f8fc..8d5c0681d7 100644 --- a/include/clang/AST/DeclNodes.def +++ b/include/clang/AST/DeclNodes.def @@ -92,6 +92,7 @@ ABSTRACT_DECL(Named, Decl) ABSTRACT_DECL(Value, NamedDecl) DECL(EnumConstant, ValueDecl) DECL(Function, ValueDecl) + DECL(FriendFunction, FunctionDecl) DECL(CXXMethod, FunctionDecl) DECL(CXXConstructor, CXXMethodDecl) DECL(CXXDestructor, CXXMethodDecl) diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 777f75e5a6..cef378f558 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -1143,7 +1143,8 @@ public: /// ActOnFriendDecl - This action is called when a friend declaration is /// encountered. virtual DeclPtrTy ActOnFriendDecl(Scope *S, - llvm::PointerUnion D) { + llvm::PointerUnion D, + bool IsDefinition) { return DeclPtrTy(); } diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 2d276614f2..0f7c4dcc0d 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -198,6 +198,9 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case ObjCProperty: case ObjCCompatibleAlias: return IDNS_Ordinary; + + case FriendFunction: + return IDNS_Friend; case ObjCProtocol: return IDNS_ObjCProtocol; @@ -583,7 +586,7 @@ bool DeclContext::decls_empty() const { return !FirstDecl; } -void DeclContext::addDecl(Decl *D) { +void DeclContext::addHiddenDecl(Decl *D) { assert(D->getLexicalDeclContext() == this && "Decl inserted into wrong lexical context"); assert(!D->getNextDeclInContext() && D != LastDecl && @@ -595,6 +598,10 @@ void DeclContext::addDecl(Decl *D) { } else { FirstDecl = LastDecl = D; } +} + +void DeclContext::addDecl(Decl *D) { + addHiddenDecl(D); if (NamedDecl *ND = dyn_cast(D)) ND->getDeclContext()->makeDeclVisibleInContext(ND); @@ -608,9 +615,12 @@ void DeclContext::buildLookup(DeclContext *DCtx) { for (decl_iterator D = DCtx->decls_begin(), DEnd = DCtx->decls_end(); D != DEnd; ++D) { - // Insert this declaration into the lookup structure + // Insert this declaration into the lookup structure, but only + // if it's semantically in its decl context. During non-lazy + // lookup building, this is implicitly enforced by addDecl. if (NamedDecl *ND = dyn_cast(*D)) - makeDeclVisibleInContextImpl(ND); + if (D->getDeclContext() == DCtx) + makeDeclVisibleInContextImpl(ND); // If this declaration is itself a transparent declaration context, // add its members (recursively). diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index a302fd87f4..f85091e4f5 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -738,6 +738,15 @@ bool OverloadIterator::Equals(const OverloadIterator &Other) const { return !isa(D) || Iter == Other.Iter; } +FriendFunctionDecl *FriendFunctionDecl::Create(ASTContext &C,DeclContext *DC, + SourceLocation L, + DeclarationName N, QualType T, + bool isInline, + SourceLocation FriendL) { + return new (C) FriendFunctionDecl(DC, L, N, T, isInline, FriendL); +} + + LinkageSpecDecl *LinkageSpecDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp index 52a812d4ba..ca9fc322da 100644 --- a/lib/Parse/ParseCXXInlineMethods.cpp +++ b/lib/Parse/ParseCXXInlineMethods.cpp @@ -29,7 +29,7 @@ Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, Declarator &D) { DeclPtrTy FnD; if (D.getDeclSpec().isFriendSpecified()) - FnD = Actions.ActOnFriendDecl(CurScope, &D); + FnD = Actions.ActOnFriendDecl(CurScope, &D, /*IsDefinition*/ true); else FnD = Actions.ActOnCXXMemberDeclarator(CurScope, AS, D, 0, 0); diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 5084d8a831..fd860a4e25 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -954,7 +954,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) { ConsumeToken(); if (DS.isFriendSpecified()) - Actions.ActOnFriendDecl(CurScope, &DS); + Actions.ActOnFriendDecl(CurScope, &DS, /*IsDefinition*/ false); else Actions.ParsedFreeStandingDeclSpec(CurScope, DS); @@ -1060,7 +1060,8 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) { DeclPtrTy ThisDecl; if (DS.isFriendSpecified()) { // TODO: handle initializers, bitfields, 'delete' - ThisDecl = Actions.ActOnFriendDecl(CurScope, &DeclaratorInfo); + ThisDecl = Actions.ActOnFriendDecl(CurScope, &DeclaratorInfo, + /*IsDefinition*/ false); } else ThisDecl = Actions.ActOnCXXMemberDeclarator(CurScope, AS, DeclaratorInfo, diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index b35fe67de6..ddd6d7b571 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2036,7 +2036,8 @@ public: ExprArg AssertMessageExpr); virtual DeclPtrTy ActOnFriendDecl(Scope *S, - llvm::PointerUnion D); + llvm::PointerUnion D, + bool IsDefinition); QualType CheckConstructorDeclarator(Declarator &D, QualType R, FunctionDecl::StorageClass& SC); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index aac79e9fc7..ab5578b378 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2353,6 +2353,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, if (D.getDeclSpec().isThreadSpecified()) Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); + bool isFriend = D.getDeclSpec().isFriendSpecified(); bool isInline = D.getDeclSpec().isInlineSpecified(); bool isVirtual = D.getDeclSpec().isVirtualSpecified(); bool isExplicit = D.getDeclSpec().isExplicitSpecified(); @@ -2382,7 +2383,20 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, bool isVirtualOkay = false; FunctionDecl *NewFD; - if (D.getKind() == Declarator::DK_Constructor) { + if (isFriend) { + // DC is the namespace in which the function is being declared. + assert(DC->isFileContext() || PrevDecl); + + // C++ [class.friend]p5 + // A function can be defined in a friend declaration of a + // class . . . . Such a function is implicitly inline. + isInline |= IsFunctionDefinition; + + NewFD = FriendFunctionDecl::Create(Context, DC, + D.getIdentifierLoc(), Name, R, + isInline, + D.getDeclSpec().getFriendSpecLoc()); + } else if (D.getKind() == Declarator::DK_Constructor) { // This is a C++ constructor declaration. assert(DC->isRecord() && "Constructors can only be declared in a member context"); @@ -2643,7 +2657,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, if (D.getCXXScopeSpec().isSet() && !NewFD->isInvalidDecl()) { // An out-of-line member function declaration must also be a // definition (C++ [dcl.meaning]p1). - if (!IsFunctionDefinition) { + if (!IsFunctionDefinition && !isFriend) { Diag(NewFD->getLocation(), diag::err_out_of_line_declaration) << D.getCXXScopeSpec().getRange(); NewFD->setInvalidDecl(); @@ -4142,7 +4156,7 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // 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()) + while (!SearchDC->isFileContext()) SearchDC = SearchDC->getParent(); // The entity of a decl scope is a DeclContext; see PushDeclContext. diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 20862d4c81..a0bcfb2b78 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -3305,7 +3305,8 @@ Sema::DeclPtrTy Sema::ActOnStaticAssertDeclaration(SourceLocation AssertLoc, } Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S, - llvm::PointerUnion DU) { + llvm::PointerUnion DU, + bool IsDefinition) { Declarator *D = DU.dyn_cast(); const DeclSpec &DS = (D ? D->getDeclSpec() : *DU.get()); @@ -3483,10 +3484,18 @@ Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S, Diag(DS.getFriendSpecLoc(), diag::err_friend_is_member); } + bool Redeclaration = (FD != 0); + + // If we found a match, create a friend function declaration with + // that function as the previous declaration. + if (Redeclaration) { + // Create it in the semantic context of the original declaration. + DC = FD->getDeclContext(); + // 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) { + } else { assert(DC->isFileContext()); // This implies that it has to be an operator or function. @@ -3498,23 +3507,25 @@ Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S, 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(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. + NamedDecl *ND = ActOnFunctionDeclarator(S, *D, DC, T, + /* PrevDecl = */ FD, + MultiTemplateParamsArg(*this), + IsDefinition, + Redeclaration); + FD = cast_or_null(ND); + + // If this is a dependent context, just add the decl to the + // class's decl list and don't both with the lookup tables. This + // doesn't affect lookup because any call that might find this + // function via ADL necessarily has to involve dependently-typed + // arguments and hence can't be resolved until + // template-instantiation anyway. + if (CurContext->isDependentContext()) + CurContext->addHiddenDecl(FD); + else + CurContext->addDecl(FD); return DeclPtrTy::make(FD); } diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index ef105374fa..c600f99a40 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -1756,28 +1756,17 @@ void Sema::ArgumentDependentLookup(DeclarationName Name, // associated classes are visible within their respective // namespaces even if they are not visible during an ordinary // lookup (11.4). - // - // We implement the second clause in the loop below. - DeclContext::lookup_iterator I, E; - for (llvm::tie(I, E) = (*NS)->lookup(Name); I != E; ++I) - CollectFunctionDecl(Functions, *I); - } - - // Look for friend function declarations in associated classes - // which name functions in associated namespaces. - for (AssociatedClassSet::iterator AC = AssociatedClasses.begin(), - ACEnd = AssociatedClasses.end(); - AC != ACEnd; ++AC) { DeclContext::lookup_iterator I, E; - for (llvm::tie(I, E) = (*AC)->lookup(Name); I != E; ++I) { + for (llvm::tie(I, E) = (*NS)->lookup(Name); I != E; ++I) { Decl *D = *I; - if (!D->isInIdentifierNamespace(Decl::IDNS_Friend)) - continue; - - DeclContext *DC = D->getDeclContext(); - if (!AssociatedNamespaces.count(DC)) - continue; - + // Only count friend declarations which were declared in + // associated classes. + if (D->isInIdentifierNamespace(Decl::IDNS_Friend)) { + DeclContext *LexDC = D->getLexicalDeclContext(); + if (!AssociatedClasses.count(cast(LexDC))) + continue; + } + CollectFunctionDecl(Functions, D); } } diff --git a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp new file mode 100644 index 0000000000..8f0bed8789 --- /dev/null +++ b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp @@ -0,0 +1,42 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +namespace A { + class A { + friend void func(A); + friend A operator+(A,A); + }; +} + +namespace B { + class B { + static void func(B); + }; + B operator+(B,B); +} + +namespace D { + class D {}; +} + +namespace C { + class C {}; + void func(C); + C operator+(C,C); + D::D operator+(D::D,D::D); +} + +namespace D { + using namespace C; +} + +namespace Test { + void test() { + func(A::A()); + func(B::B()); // expected-error {{ no matching function for call to 'func' }} + func(C::C()); + A::A() + A::A(); + B::B() + B::B(); + C::C() + C::C(); + D::D() + D::D(); // expected-error {{ invalid operands to binary expression ('D::D' and 'D::D') }} + } +}