]> granicus.if.org Git - clang/commitdiff
Argument-dependent lookup for friend declarations. Add a new decl type,
authorJohn McCall <rjmccall@apple.com>
Tue, 11 Aug 2009 06:59:38 +0000 (06:59 +0000)
committerJohn McCall <rjmccall@apple.com>
Tue, 11 Aug 2009 06:59:38 +0000 (06:59 +0000)
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

13 files changed:
include/clang/AST/DeclBase.h
include/clang/AST/DeclCXX.h
include/clang/AST/DeclNodes.def
include/clang/Parse/Action.h
lib/AST/DeclBase.cpp
lib/AST/DeclCXX.cpp
lib/Parse/ParseCXXInlineMethods.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Sema/Sema.h
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaLookup.cpp
test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp [new file with mode: 0644]

index ea782c9a66acd6f2a390661f67e7dac4b7e50cfc..0979733644a5f6984d7628842969737fede07b91 100644 (file)
@@ -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;
index 8b601cb810a516fb34318f51793b0ddd4fc0555d..00e583fa6effbed7a7c03d9f2f128fade1b9e40a 100644 (file)
@@ -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();
 ///
index 9fac06f8fc6cca8998225f1c5e7cc0b624d7829b..8d5c0681d78c08e00ee61cc19c32955319fb63fe 100644 (file)
@@ -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)
index 777f75e5a602c89c62706777a766d100b6beffe5..cef378f558c8618254bfaaf31b25442ffb253351 100644 (file)
@@ -1143,7 +1143,8 @@ public:
   /// ActOnFriendDecl - This action is called when a friend declaration is
   /// encountered.
   virtual DeclPtrTy ActOnFriendDecl(Scope *S,
-                        llvm::PointerUnion<const DeclSpec*,Declarator*> D) {
+                        llvm::PointerUnion<const DeclSpec*,Declarator*> D,
+                                    bool IsDefinition) {
     return DeclPtrTy();
   }
 
index 2d276614f2279426be17896c43724ef19651422a..0f7c4dcc0d69d31433c10174193e1513f069b4e7 100644 (file)
@@ -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<NamedDecl>(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<NamedDecl>(*D))
-        makeDeclVisibleInContextImpl(ND);
+        if (D->getDeclContext() == DCtx)
+          makeDeclVisibleInContextImpl(ND);
 
       // If this declaration is itself a transparent declaration context,
       // add its members (recursively).
index a302fd87f448b81c8cea97a61860c1d5da0a1dc5..f85091e4f56b38d052537f75158211775488749d 100644 (file)
@@ -738,6 +738,15 @@ bool OverloadIterator::Equals(const OverloadIterator &Other) const {
   return !isa<OverloadedFunctionDecl>(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,
index 52a812d4ba1f48523f0e909fbea8cb8802bcaa25..ca9fc322da01239c5416cbda14990bbe7cf258d4 100644 (file)
@@ -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);
 
index 5084d8a8315112e9311b58c8470b6670eb03323f..fd860a4e257d1df29fa14203b3e132153dd69b7a 100644 (file)
@@ -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,
index b35fe67de61ab6d34107cbe13550b46ee0e74850..ddd6d7b5712d96209a866cdca612c4fa486b3ff8 100644 (file)
@@ -2036,7 +2036,8 @@ public:
                                                  ExprArg AssertMessageExpr);
   
   virtual DeclPtrTy ActOnFriendDecl(Scope *S,
-                    llvm::PointerUnion<const DeclSpec*,Declarator*> D);
+                          llvm::PointerUnion<const DeclSpec*,Declarator*> D,
+                                    bool IsDefinition);
 
   QualType CheckConstructorDeclarator(Declarator &D, QualType R,
                                       FunctionDecl::StorageClass& SC);
index aac79e9fc702e0620c8c1ba805a8b40f4c74f611..ab5578b378d7919f4948046115a8096ed68a45b0 100644 (file)
@@ -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.
index 20862d4c81f03c7d2b62582f528551d82c034f11..a0bcfb2b78069e6016f2c46096b6f7dd2b091a9e 100644 (file)
@@ -3305,7 +3305,8 @@ Sema::DeclPtrTy Sema::ActOnStaticAssertDeclaration(SourceLocation AssertLoc,
 }
 
 Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S,
-                       llvm::PointerUnion<const DeclSpec*,Declarator*> DU) {
+                       llvm::PointerUnion<const DeclSpec*,Declarator*> DU,
+                                      bool IsDefinition) {
   Declarator *D = DU.dyn_cast<Declarator*>();
   const DeclSpec &DS = (D ? D->getDeclSpec() : *DU.get<const DeclSpec*>());
 
@@ -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<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.
+  NamedDecl *ND = ActOnFunctionDeclarator(S, *D, DC, T,
+                                          /* PrevDecl = */ FD,
+                                          MultiTemplateParamsArg(*this),
+                                          IsDefinition,
+                                          Redeclaration);
+  FD = cast_or_null<FriendFunctionDecl>(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);
 }
index ef105374fac63782b5dd549f8c0dac74f68a9742..c600f99a404652fc2b9e0776ee4ae62d0dd5c8b0 100644 (file)
@@ -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<CXXRecordDecl>(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 (file)
index 0000000..8f0bed8
--- /dev/null
@@ -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') }}
+  }
+}