]> granicus.if.org Git - clang/commitdiff
Omnibus friend decl refactoring. Instead of cloning AST classes for friend
authorJohn McCall <rjmccall@apple.com>
Fri, 28 Aug 2009 07:59:38 +0000 (07:59 +0000)
committerJohn McCall <rjmccall@apple.com>
Fri, 28 Aug 2009 07:59:38 +0000 (07:59 +0000)
declarations of same, introduce a single AST class and add appropriate bits
(encoded in the namespace) for whether a decl is "real" or not.  Much hackery
about previously-declared / not-previously-declared, but it's essentially
mandated by the standard that friends alter lookup, and this is at least
fairly non-intrusive.

Refactor the Sema methods specific to friends for cleaner flow and less nesting.

Incidentally solve a few bugs, but I remain confident that we can put them back.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@80353 91177308-0d34-0410-b5e6-96231b3b80d8

12 files changed:
include/clang/AST/DeclBase.h
include/clang/AST/DeclCXX.h
include/clang/AST/DeclNodes.def
lib/AST/DeclBase.cpp
lib/AST/DeclCXX.cpp
lib/Sema/Sema.h
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaLookup.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
test/CXX/class/class.friend/p1.cpp
test/CXX/temp/temp.decls/temp.friend/p1.cpp

index e43f15d79a7c947edce6216e72c1f5777aca8972..9aea17bd1bf30e68348b1e182f058a7fb74e2657 100644 (file)
@@ -79,7 +79,9 @@ public:
   /// namespaces, labels, tags, members and ordinary
   /// identifiers. These are meant as bitmasks, so that searches in
   /// C++ can look into the "tag" namespace during ordinary lookup. We
-  /// use additional namespaces for Objective-C entities.
+  /// use additional namespaces for Objective-C entities.  We also
+  /// put C++ friend declarations (of previously-undeclared entities) in
+  /// shadow namespaces.
   enum IdentifierNamespace {
     IDNS_Label = 0x1,
     IDNS_Tag = 0x2,
@@ -88,7 +90,8 @@ public:
     IDNS_ObjCProtocol = 0x10,
     IDNS_ObjCImplementation = 0x20,
     IDNS_ObjCCategoryImpl = 0x40,
-    IDNS_Friend = 0x80
+    IDNS_OrdinaryFriend = 0x80,
+    IDNS_TagFriend = 0x100
   };
   
   /// ObjCDeclQualifier - Qualifier used on types in method declarations
@@ -164,7 +167,7 @@ private:
 
 protected:
   /// IdentifierNamespace - This specifies what IDNS_* namespace this lives in.
-  unsigned IdentifierNamespace : 8;
+  unsigned IdentifierNamespace : 16;
   
 private:
 #ifndef NDEBUG
@@ -406,6 +409,42 @@ public:
 
   /// \brief Whether this declaration is a function or function template.
   bool isFunctionOrFunctionTemplate() const;
+
+  /// \brief Changes the namespace of this declaration to reflect that it's
+  /// the object of a friend declaration.
+  ///
+  /// These declarations appear in the lexical context of the friending
+  /// class, but in the semantic context of the actual entity.  This property
+  /// applies only to a specific decl object;  other redeclarations of the
+  /// same entity may not (and probably don't) share this property.
+  void setObjectOfFriendDecl(bool PreviouslyDeclared) {
+    unsigned OldNS = IdentifierNamespace;
+    assert((OldNS == IDNS_Tag || OldNS == IDNS_Ordinary)
+           && "unsupported namespace for undeclared friend");
+    if (!PreviouslyDeclared) IdentifierNamespace = 0;
+
+    if (OldNS == IDNS_Tag)
+      IdentifierNamespace |= IDNS_TagFriend;
+    else
+      IdentifierNamespace |= IDNS_OrdinaryFriend;
+  }
+
+  enum FriendObjectKind {
+    FOK_None, // not a friend object
+    FOK_Declared, // a friend of a previously-declared entity
+    FOK_Undeclared // a friend of a previously-undeclared entity
+  };
+
+  /// \brief Determines whether this declaration is the object of a
+  /// friend declaration and, if so, what kind.
+  ///
+  /// There is currently no direct way to find the associated FriendDecl.
+  FriendObjectKind getFriendObjectKind() const {
+    unsigned mask
+      = (IdentifierNamespace & (IDNS_TagFriend | IDNS_OrdinaryFriend));
+    if (!mask) return FOK_None;
+    return (mask & (IDNS_Tag | IDNS_Ordinary) ? FOK_Declared : FOK_Undeclared);
+  }
   
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *) { return true; }
index 2d16557d95478f0a5fa2ea7794498c3dbd83cb8e..45e85c0a49f2bb0e519bbd30d70e6e7a95796e8c 100644 (file)
@@ -1280,83 +1280,68 @@ public:
   static bool classof(const CXXConversionDecl *D) { return true; }
 };
 
-/// FriendFunctionDecl - Represents the declaration (and possibly
-/// the definition) of a friend function.  For example:
+/// FriendDecl - Represents the declaration of a friend entity,
+/// which can be a function, a type, or a templated function or type.
+//  For example:
 ///
 /// @code
-/// class A {
-///   friend int foo(int);
+/// template <typename T> class A {
+///   friend int foo(T);
+///   friend class B;
+///   friend T; // only in C++0x
+///   template <typename U> friend class C;
+///   template <typename U> friend A& operator+=(A&, const U&) { ... }
 /// };
 /// @endcode
-class FriendFunctionDecl : public FunctionDecl {
-  // Location of the 'friend' specifier.
-  const SourceLocation FriendLoc;
-
-  FriendFunctionDecl(DeclContext *DC, SourceLocation L,
-                     DeclarationName N, QualType T, DeclaratorInfo *DInfo,
-                     bool isInline, SourceLocation FriendL)
-    : FunctionDecl(FriendFunction, DC, L, N, T, DInfo, None, isInline),
-      FriendLoc(FriendL)
-  {}
-
+///
+/// The semantic context of a friend decl is its declaring class.
+class FriendDecl : public Decl {
 public:
-  static FriendFunctionDecl *Create(ASTContext &C, DeclContext *DC,
-                                    SourceLocation L, DeclarationName N,
-                                    QualType T, DeclaratorInfo *DInfo,
-                                    bool isInline, SourceLocation FriendL);
-
-  SourceLocation getFriendLoc() const {
-    return FriendLoc;
-  }
+  typedef llvm::PointerUnion<NamedDecl*,Type*> FriendUnion;
 
-  // Implement isa/cast/dyncast/etc.
-  static bool classof(const Decl *D) { 
-    return D->getKind() == FriendFunction;
-  }
-  static bool classof(const FriendFunctionDecl *D) { return true; }
-};
-  
-/// FriendClassDecl - Represents the declaration of a friend class.
-/// For example:
-/// 
-/// @code
-/// class X {
-///   friend class Y;
-/// };
-/// @endcode
-class FriendClassDecl : public Decl {
-  // The friended type.  In C++0x, this can be an arbitrary type,
-  // which we simply ignore if it's not a record type.
-  QualType FriendType;
+private:
+  // The declaration that's a friend of this class.
+  FriendUnion Friend;
 
   // Location of the 'friend' specifier.
   SourceLocation FriendLoc;
 
-  FriendClassDecl(DeclContext *DC, SourceLocation L,
-                  QualType T, SourceLocation FriendL)
-    : Decl(FriendClass, DC, L),
-      FriendType(T),
+  FriendDecl(DeclContext *DC, SourceLocation L, FriendUnion Friend,
+             SourceLocation FriendL)
+    : Decl(Decl::Friend, DC, L),
+      Friend(Friend),
       FriendLoc(FriendL)
   {}
 
 public:
-  static FriendClassDecl *Create(ASTContext &C, DeclContext *DC,
-                                 SourceLocation L, QualType T,
-                                 SourceLocation FriendL);
+  static FriendDecl *Create(ASTContext &C, DeclContext *DC,
+                            SourceLocation L, FriendUnion Friend_,
+                            SourceLocation FriendL);
+
+  /// If this friend declaration names an (untemplated but
+  /// possibly dependent) type, return the type;  otherwise
+  /// return null.  This is used only for C++0x's unelaborated
+  /// friend type declarations.
+  Type *getFriendType() const {
+    return Friend.dyn_cast<Type*>();
+  }
 
-  QualType getFriendType() const {
-    return FriendType;
+  /// If this friend declaration doesn't name an unelaborated
+  /// type, return the inner declaration.
+  NamedDecl *getFriendDecl() const {
+    return Friend.dyn_cast<NamedDecl*>();
   }
 
+  /// Retrieves the location of the 'friend' keyword.
   SourceLocation getFriendLoc() const {
     return FriendLoc;
   }
 
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { 
-    return D->getKind() == FriendClass;
+    return D->getKind() == Decl::Friend;
   }
-  static bool classof(const FriendClassDecl *D) { return true; }
+  static bool classof(const FriendDecl *D) { return true; }
 };
   
 /// LinkageSpecDecl - This represents a linkage specification.  For example:
index 996d042d14f131fee661b6e56285fef746ce6f82..61df9d052fb1cd9464b4ac7ffa8da7251ef0581a 100644 (file)
@@ -93,7 +93,6 @@ ABSTRACT_DECL(Named,  Decl)
     DECL(EnumConstant, ValueDecl)
     ABSTRACT_DECL(Declarator, NamedDecl)
       DECL(Function, ValueDecl)
-        DECL(FriendFunction, FunctionDecl)
         DECL(CXXMethod, FunctionDecl)
           DECL(CXXConstructor, CXXMethodDecl)
           DECL(CXXDestructor, CXXMethodDecl)
@@ -127,7 +126,7 @@ DECL(ObjCPropertyImpl, Decl)
 DECL(ObjCForwardProtocol, Decl)
 DECL(ObjCClass, Decl)
 DECL(FileScopeAsm, Decl)
-DECL(FriendClass, Decl)
+DECL(Friend, Decl)
 DECL(StaticAssert, Decl)
 LAST_DECL(Block, Decl)
 
index 28d543785fa259da13be99eafffb6b9d3f23b2d8..3ced0eff4c52c8c5626518a6018112d443b344cb 100644 (file)
@@ -200,10 +200,6 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
     case ObjCCompatibleAlias:
       return IDNS_Ordinary;
 
-    case FriendClass:
-    case FriendFunction:
-      return IDNS_Friend;
-      
     case ObjCProtocol:
       return IDNS_ObjCProtocol;
       
@@ -233,6 +229,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
       return IDNS_Tag | IDNS_Ordinary;
     
     // Never have names.
+    case Friend:
     case LinkageSpec:
     case FileScopeAsm:
     case StaticAssert:
index 70e48976daa90cd2ca756d3e9410e50c5f769b24..46acf67d2952c1d936c2ef78cd8930a2cd5f8688 100644 (file)
@@ -777,20 +777,20 @@ 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,
-                                               DeclaratorInfo *DInfo,
-                                               bool isInline,
-                                               SourceLocation FriendL) {
-  return new (C) FriendFunctionDecl(DC, L, N, T, DInfo, isInline, FriendL);
-}
+FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC,
+                               SourceLocation L,
+                               FriendUnion Friend,
+                               SourceLocation FriendL) {
+  if (Friend.is<NamedDecl*>()) {
+    NamedDecl *D = Friend.get<NamedDecl*>();
+    assert(isa<FunctionDecl>(D) ||
+           isa<CXXRecordDecl>(D) ||
+           isa<FunctionTemplateDecl>(D) ||
+           isa<ClassTemplateDecl>(D));
+    assert(D->getFriendObjectKind());
+  }
 
-FriendClassDecl *FriendClassDecl::Create(ASTContext &C, DeclContext *DC,
-                                         SourceLocation L, QualType T,
-                                         SourceLocation FriendL) {
-  return new (C) FriendClassDecl(DC, L, T, FriendL);
+  return new (C) FriendDecl(DC, L, Friend, FriendL);
 }                                               
 
 LinkageSpecDecl *LinkageSpecDecl::Create(ASTContext &C,
index 905b2e99fd7632dbc326da15899efe94c76c2b91..c59224298abcd201e72a70f61fa108068466b8c4 100644 (file)
@@ -2143,6 +2143,9 @@ public:
   virtual DeclPtrTy ActOnFriendDecl(Scope *S,
                           llvm::PointerUnion<const DeclSpec*,Declarator*> D,
                                     bool IsDefinition);
+  DeclPtrTy ActOnFriendTypeDecl(Scope *S, const DeclSpec& DS,
+                                bool IsDefinition);
+  DeclPtrTy ActOnFriendFunctionDecl(Scope *S, Declarator& D, bool IsDefinition);
 
   QualType CheckConstructorDeclarator(Declarator &D, QualType R,
                                       FunctionDecl::StorageClass& SC);
@@ -2955,6 +2958,7 @@ public:
                                      bool Recursive = false);
 
   NamedDecl *FindInstantiatedDecl(NamedDecl *D);
+  DeclContext *FindInstantiatedContext(DeclContext *DC);
     
   // Objective-C declarations.
   virtual DeclPtrTy ActOnStartClassInterface(SourceLocation AtInterfaceLoc,
index 045c12aab5485a3f237bcdf7d61b80eb83bd8680..15e8bc3ab8cee0dd016d663941c98407cbbe2fea 100644 (file)
@@ -735,7 +735,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) {
 
     const CXXMethodDecl* OldMethod = dyn_cast<CXXMethodDecl>(Old);
     const CXXMethodDecl* NewMethod = dyn_cast<CXXMethodDecl>(New);
-    if (OldMethod && NewMethod && 
+    if (OldMethod && NewMethod && !NewMethod->getFriendObjectKind() &&
         NewMethod->getLexicalDeclContext()->isRecord()) {
       //    -- Member function declarations with the same name and the 
       //       same parameter types cannot be overloaded if any of them 
@@ -2411,6 +2411,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
 
   bool isVirtualOkay = false;
   FunctionDecl *NewFD;
+
   if (isFriend) {
     // DC is the namespace in which the function is being declared.
     assert((DC->isFileContext() || PrevDecl) && "previously-undeclared "
@@ -2420,13 +2421,9 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
     //   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, DInfo,
-                                       isInline,
-                                       D.getDeclSpec().getFriendSpecLoc());
-    
-  } else if (D.getKind() == Declarator::DK_Constructor) {
+  if (D.getKind() == Declarator::DK_Constructor) {
     // This is a C++ constructor declaration.
     assert(DC->isRecord() &&
            "Constructors can only be declared in a member context");
@@ -2514,10 +2511,13 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
     NewFD->setInvalidDecl();
   
   // Set the lexical context. If the declarator has a C++
-  // scope specifier, the lexical context will be different
-  // from the semantic context.
+  // scope specifier, or is the object of a friend declaration, the
+  // lexical context will be different from the semantic context.
   NewFD->setLexicalDeclContext(CurContext);
 
+  if (isFriend)
+    NewFD->setObjectOfFriendDecl(/* PreviouslyDeclared= */ PrevDecl != NULL);
+
   // Match up the template parameter lists with the scope specifier, then
   // determine whether we have a template or a template specialization.
   FunctionTemplateDecl *FunctionTemplate = 0;
@@ -4310,6 +4310,10 @@ CreateNewDecl:
   // lexical context will be different from the semantic context.
   New->setLexicalDeclContext(CurContext);
 
+  // Mark this as a friend decl if applicable.
+  if (TUK == TUK_Friend)
+    New->setObjectOfFriendDecl(/* PreviouslyDeclared = */ PrevDecl != NULL);
+
   // Set the access specifier.
   if (!Invalid && TUK != TUK_Friend)
     SetMemberAccessSpecifier(New, PrevDecl, AS);
index 602caf2825735909f2e19f6a7df49a79573edff1..4874c92dece7b8a8e1c54bb3ecb07297c3fc7f35 100644 (file)
@@ -3567,116 +3567,124 @@ Sema::DeclPtrTy Sema::ActOnStaticAssertDeclaration(SourceLocation AssertLoc,
 Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S,
                        llvm::PointerUnion<const DeclSpec*,Declarator*> DU,
                                       bool IsDefinition) {
-  Declarator *D = DU.dyn_cast<Declarator*>();
-  const DeclSpec &DS = (D ? D->getDeclSpec() : *DU.get<const DeclSpec*>());
+  if (DU.is<Declarator*>())
+    return ActOnFriendFunctionDecl(S, *DU.get<Declarator*>(), IsDefinition);
+  else
+    return ActOnFriendTypeDecl(S, *DU.get<const DeclSpec*>(), IsDefinition);
+}
+
+Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S,
+                                          const DeclSpec &DS,
+                                          bool IsDefinition) {
+  SourceLocation Loc = DS.getSourceRange().getBegin();
 
   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 syntactically invalid).
-  if (!D) {
-    SourceLocation Loc = DS.getSourceRange().getBegin();
-
-    QualType T;
-    DeclContext *DC;
-
-    // In C++0x, we just accept any old type.
-    if (getLangOptions().CPlusPlus0x) {
-      bool invalid = false;
-      QualType T = ConvertDeclSpecToType(DS, Loc, invalid);
-      if (invalid)
-        return DeclPtrTy();
-
-      // The semantic context in which to create the decl.  If it's not
-      // a record decl (or we don't yet know if it is), create it in the
-      // current context.
-      DC = CurContext;
-      if (const RecordType *RT = T->getAs<RecordType>())
-        DC = RT->getDecl()->getDeclContext();
-
-    // The C++98 rules are somewhat more complex.
-    } else {
-      // 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;
+  // Check to see if the decl spec was syntactically like "struct foo".
+  RecordDecl *RD = NULL;
+
+  switch (DS.getTypeSpecType()) {
+  case DeclSpec::TST_class:
+  case DeclSpec::TST_struct:
+  case DeclSpec::TST_union:
+    RD = dyn_cast_or_null<CXXRecordDecl>((Decl*) DS.getTypeRep());
+    if (!RD) return DeclPtrTy();
+
+    // The parser doesn't quite handle
+    //   friend class A {}
+    // as we'd like, because it might have been the (valid) prefix of
+    //   friend class A {} foo();
+    // So even in C++0x mode we don't want to 
+    IsDefinition |= RD->isDefinition();
+    break;
+
+  default: break;
+  }
+
+  FriendDecl::FriendUnion FU = RD;
+
+  // 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.
+  // So if we didn't get a record decl above, we're invalid in C++98 mode.
+  if (!RD) {
+    bool invalid = false;
+    QualType T = ConvertDeclSpecToType(DS, Loc, invalid);
+    if (invalid) return DeclPtrTy();
     
-      switch (DS.getTypeSpecType()) {
-      case DeclSpec::TST_class:
-      case DeclSpec::TST_struct:
-      case DeclSpec::TST_union:
-        RD = dyn_cast_or_null<CXXRecordDecl>((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();
+    if (const RecordType *RT = T->getAs<RecordType>()) {
+      FU = RD = cast<CXXRecordDecl>(RT->getDecl());
+      
+      // Untagged typenames are invalid prior to C++0x, but we can
+      // suggest an easy fix which should work.
+      if (!getLangOptions().CPlusPlus0x) {
+        Diag(DS.getFriendSpecLoc(), diag::err_unelaborated_friend_type)
+          << (RD->isUnion())
+          << CodeModificationHint::CreateInsertion(DS.getTypeSpecTypeLoc(),
+                                        RD->isUnion() ? " union" : " class");
         return DeclPtrTy();
       }
-
-      // The record declaration we get from friend declarations is not
-      // canonicalized; see ActOnTag.
-
-      // 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++98 [class.friend]p1: A friend of a class is a function
-      //   or class that is not a member of the class . . .
-      // But that's a silly restriction which nobody implements for
-      // inner classes, and C++0x removes it anyway, so we only report
-      // this (as a warning) if we're being pedantic.
-      // 
-      // Also, 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 &&
-               !getLangOptions().CPlusPlus0x)
-        Diag(DS.getFriendSpecLoc(), diag::ext_friend_inner_class);
-      
-      T = QualType(RD->getTypeForDecl(), 0);
-      DC = RD->getDeclContext();
+    }else if (!getLangOptions().CPlusPlus0x) {
+      Diag(DS.getFriendSpecLoc(), diag::err_unexpected_friend)
+          << DS.getSourceRange();
+      return DeclPtrTy();
+    }else {
+      FU = T.getTypePtr();
     }
+  }
 
-    FriendClassDecl *FCD = FriendClassDecl::Create(Context, DC, Loc, T,
-                                                   DS.getFriendSpecLoc());
-    FCD->setLexicalDeclContext(CurContext);
+  assert(FU && "should have a friend decl/type by here!");
 
-    if (CurContext->isDependentContext())
-      CurContext->addHiddenDecl(FCD);
-    else
-      CurContext->addDecl(FCD);
+  // C++ [class.friend]p2: A class shall not be defined inside
+  //   a friend declaration.
+  if (IsDefinition) {
+    Diag(DS.getFriendSpecLoc(), diag::err_friend_decl_defines_class)
+      << DS.getSourceRange();
+    return DeclPtrTy();
+  }
 
-    return DeclPtrTy::make(FCD);
+  // C++98 [class.friend]p1: A friend of a class is a function
+  //   or class that is not a member of the class . . .
+  // But that's a silly restriction which nobody implements for
+  // inner classes, and C++0x removes it anyway, so we only report
+  // this (as a warning) if we're being pedantic.
+  if (!getLangOptions().CPlusPlus0x) {
+    assert(RD && "must have a record decl in C++98 mode");
+    if (RD->getDeclContext() == CurContext)
+      Diag(DS.getFriendSpecLoc(), diag::ext_friend_inner_class);
   }
 
-  // We have a declarator.
-  assert(D);
+  FriendDecl *FD = FriendDecl::Create(Context, CurContext, Loc, FU,
+                                      DS.getFriendSpecLoc());
+  CurContext->addDecl(FD);
 
-  SourceLocation Loc = D->getIdentifierLoc();
+  return DeclPtrTy::make(FD);
+}
+
+Sema::DeclPtrTy Sema::ActOnFriendFunctionDecl(Scope *S,
+                                              Declarator &D,
+                                              bool IsDefinition) {
+  const DeclSpec &DS = D.getDeclSpec();
+
+  assert(DS.isFriendSpecified());
+  assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified);
+
+  SourceLocation Loc = D.getIdentifierLoc();
   DeclaratorInfo *DInfo = 0;
-  QualType T = GetTypeForDeclarator(*D, S, &DInfo);
+  QualType T = GetTypeForDeclarator(D, S, &DInfo);
 
   // C++ [class.friend]p1
   //   A friend of a class is a function or class....
   // Note that this sees through typedefs, which is intended.
+  // It *doesn't* see through dependent types, which is correct
+  // according to [temp.arg.type]p3:
+  //   If a declaration acquires a function type through a
+  //   type dependent on a template-parameter and this causes
+  //   a declaration that does not use the syntactic form of a
+  //   function declarator to have a function type, the program
+  //   is ill-formed.
   if (!T->isFunctionType()) {
     Diag(Loc, diag::err_unexpected_friend);
 
@@ -3700,8 +3708,8 @@ Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S,
   //    declared as a friend, scopes outside the innermost enclosing
   //    namespace scope are not considered.
 
-  CXXScopeSpec &ScopeQual = D->getCXXScopeSpec();
-  DeclarationName Name = GetNameForDeclarator(*D);
+  CXXScopeSpec &ScopeQual = D.getCXXScopeSpec();
+  DeclarationName Name = GetNameForDeclarator(D);
   assert(Name);
 
   // The existing declaration we found.
@@ -3727,7 +3735,7 @@ Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S,
     // TODO: better diagnostics for this case.  Suggesting the right
     // qualified scope would be nice...
     if (!Dec || Dec->getDeclContext() != DC) {
-      D->setInvalidType();
+      D.setInvalidType();
       Diag(Loc, diag::err_qualified_friend_not_found) << Name << T;
       return DeclPtrTy();
     }
@@ -3789,36 +3797,36 @@ Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S,
     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) {
+    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);
+        (D.getKind() == Declarator::DK_Constructor ? 0 :
+         D.getKind() == Declarator::DK_Destructor ? 1 : 2);
       return DeclPtrTy();
     }
   }
 
-  NamedDecl *ND = ActOnFunctionDeclarator(S, *D, DC, T, DInfo,
+  NamedDecl *ND = ActOnFunctionDeclarator(S, D, DC, T, DInfo,
                                           /* PrevDecl = */ FD,
                                           MultiTemplateParamsArg(*this),
                                           IsDefinition,
                                           Redeclaration);
-  FD = cast_or_null<FriendFunctionDecl>(ND);
+  if (!ND) return DeclPtrTy();
+  FD = cast<FunctionDecl>(ND);
 
   assert(FD->getDeclContext() == DC);
   assert(FD->getLexicalDeclContext() == CurContext);
 
-  // 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);
+  // We only add the function declaration to the lookup tables, not
+  // the decl list, and only if the context isn't dependent.
+  if (!CurContext->isDependentContext())
+    DC->makeDeclVisibleInContext(FD);
+
+  FriendDecl *FrD = FriendDecl::Create(Context, CurContext,
+                                       D.getIdentifierLoc(), FD,
+                                       DS.getFriendSpecLoc());
+  CurContext->addDecl(FrD);
 
   return DeclPtrTy::make(FD);
 }
index 6627499d12d3a4ab5534ee736d75183ccf9f00b5..4a699de6c8d9597a65b5d0fd3a7b5443beb54bdc 100644 (file)
@@ -643,6 +643,13 @@ Sema::CppLookupName(Scope *S, DeclarationName Name,
          "Can perform only C++ lookup");
   unsigned IDNS 
     = getIdentifierNamespacesFromLookupNameKind(NameKind, /*CPlusPlus*/ true);
+
+  // If we're testing for redeclarations, also look in the friend namespaces.
+  if (RedeclarationOnly) {
+    if (IDNS & Decl::IDNS_Tag) IDNS |= Decl::IDNS_TagFriend;
+    if (IDNS & Decl::IDNS_Ordinary) IDNS |= Decl::IDNS_OrdinaryFriend;
+  }
+
   Scope *Initial = S;
   DeclContext *OutOfLineCtx = 0;
   IdentifierResolver::iterator 
@@ -1769,9 +1776,9 @@ void Sema::ArgumentDependentLookup(DeclarationName Name,
     DeclContext::lookup_iterator I, E;
     for (llvm::tie(I, E) = (*NS)->lookup(Name); I != E; ++I) {
       Decl *D = *I;
-      // Only count friend declarations which were declared in
-      // associated classes.
-      if (D->isInIdentifierNamespace(Decl::IDNS_Friend)) {
+      // If the only declaration here is an ordinary friend, consider
+      // it only if it was declared in an associated classes.
+      if (D->getIdentifierNamespace() == Decl::IDNS_OrdinaryFriend) {
         DeclContext *LexDC = D->getLexicalDeclContext();
         if (!AssociatedClasses.count(cast<CXXRecordDecl>(LexDC)))
           continue;
index a0353e3c539ebeaa52015eb82f919db653da4ae8..0bf832f3ebdf06bf04ccc8a8bbacec2893eb29bf 100644 (file)
@@ -45,7 +45,7 @@ namespace {
     Decl *VisitStaticAssertDecl(StaticAssertDecl *D);
     Decl *VisitEnumDecl(EnumDecl *D);
     Decl *VisitEnumConstantDecl(EnumConstantDecl *D);
-    Decl *VisitFriendClassDecl(FriendClassDecl *D);
+    Decl *VisitFriendDecl(FriendDecl *D);
     Decl *VisitFunctionDecl(FunctionDecl *D);
     Decl *VisitCXXRecordDecl(CXXRecordDecl *D);
     Decl *VisitCXXMethodDecl(CXXMethodDecl *D,
@@ -250,25 +250,35 @@ Decl *TemplateDeclInstantiator::VisitFieldDecl(FieldDecl *D) {
   return Field;
 }
 
-Decl *TemplateDeclInstantiator::VisitFriendClassDecl(FriendClassDecl *D) {
-  QualType T = D->getFriendType();
-  if (T->isDependentType())  {
-    T = SemaRef.SubstType(T, TemplateArgs, D->getLocation(),
-                          DeclarationName());
-    assert(T.isNull() || getLangOptions().CPlusPlus0x || T->isRecordType());
-  }
+Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
+  FriendDecl::FriendUnion FU;
+
+  // Handle friend type expressions by simply substituting template
+  // parameters into the pattern type.
+  if (Type *Ty = D->getFriendType()) {
+    QualType T = SemaRef.SubstType(QualType(Ty,0), TemplateArgs,
+                                   D->getLocation(), DeclarationName());
+    if (T.isNull()) return 0;
 
-  // FIXME: the target context might be dependent.
-  DeclContext *DC = D->getDeclContext();
-  assert(DC->isFileContext());
+    assert(getLangOptions().CPlusPlus0x || T->isRecordType());
+    FU = T.getTypePtr();
 
-  FriendClassDecl *NewD =
-    FriendClassDecl::Create(SemaRef.Context, DC, D->getLocation(), T,
-                            D->getFriendLoc());
-  NewD->setLexicalDeclContext(Owner);
+  // Handle everything else by appropriate substitution.
+  } else {
+    NamedDecl *ND = D->getFriendDecl();
+    assert(ND && "friend decl must be a decl or a type!");
+
+    Decl *NewND = Visit(ND);
+    if (!NewND) return 0;
 
-  Owner->addDecl(NewD);
-  return NewD;
+    FU = cast<NamedDecl>(NewND);
+  }
+  
+  FriendDecl *FD =
+    FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(), FU,
+                       D->getFriendLoc());
+  Owner->addDecl(FD);
+  return FD;
 }
 
 Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) {
@@ -424,10 +434,20 @@ Decl *TemplateDeclInstantiator::VisitCXXRecordDecl(CXXRecordDecl *D) {
   if (!D->isInjectedClassName())
     Record->setInstantiationOfMemberClass(D);
 
+  // If the original function was part of a friend declaration,
+  // inherit its namespace state.
+  if (Decl::FriendObjectKind FOK = D->getFriendObjectKind())
+    Record->setObjectOfFriendDecl(FOK == Decl::FOK_Declared);
+
   Owner->addDecl(Record);
   return Record;
 }
 
+/// Normal class members are of more specific types and therefore
+/// don't make it here.  This function serves two purposes:
+///   1) instantiating function templates
+///   2) substituting friend declarations
+/// FIXME: preserve function definitions in case #2
 Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D) {
   // Check whether there is already a function template specialization for
   // this declaration.
@@ -457,33 +477,27 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D) {
     return 0;
 
   // Build the instantiated method declaration.
-  FunctionDecl *Function;
-  if (FriendFunctionDecl* FFD = dyn_cast<FriendFunctionDecl>(D)) {
-    // The new decl's semantic context.  FIXME:  this might need
-    // to be instantiated.
-    DeclContext *DC = D->getDeclContext();
-
-    // This assert is bogus and exists only to catch cases we don't
-    // handle yet.
-    assert(!DC->isDependentContext());
-
-    Function =
-      FriendFunctionDecl::Create(SemaRef.Context, DC, D->getLocation(),
-                                 D->getDeclName(), T, D->getDeclaratorInfo(),
-                                 D->isInline(), FFD->getFriendLoc());
-    Function->setLexicalDeclContext(Owner);
-  } else {
-    Function =
-      FunctionDecl::Create(SemaRef.Context, Owner, D->getLocation(), 
+  DeclContext *DC = SemaRef.FindInstantiatedContext(D->getDeclContext());
+  FunctionDecl *Function =
+      FunctionDecl::Create(SemaRef.Context, DC, D->getLocation(), 
                            D->getDeclName(), T, D->getDeclaratorInfo(),
                            D->getStorageClass(),
                            D->isInline(), D->hasWrittenPrototype());
-  }
-  
+  Function->setLexicalDeclContext(Owner);
+
   // Attach the parameters
   for (unsigned P = 0; P < Params.size(); ++P)
     Params[P]->setOwningFunction(Function);
   Function->setParams(SemaRef.Context, Params.data(), Params.size());
+
+  // If the original function was part of a friend declaration,
+  // inherit its namespace state and add it to the owner.
+  if (Decl::FriendObjectKind FOK = D->getFriendObjectKind()) {
+    bool WasDeclared = (FOK == Decl::FOK_Declared);
+    Function->setObjectOfFriendDecl(WasDeclared);
+    if (!Owner->isDependentContext())
+      DC->makeDeclVisibleInContext(Function);
+  }
   
   if (InitFunctionInstantiation(Function, D))
     Function->setInvalidDecl();
@@ -502,17 +516,6 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D) {
                                                 InsertPos);
   }
 
-  // If this was a friend function decl, it's a member which
-  // needs to be added.
-  if (isa<FriendFunctionDecl>(Function)) {
-    // If the new context is still dependent, this declaration
-    // needs to remain hidden.
-    if (Owner->isDependentContext())
-      Owner->addHiddenDecl(Function);
-    else
-      Owner->addDecl(Function);
-  }
-
   return Function;
 }
 
@@ -1127,6 +1130,17 @@ static NamedDecl *findInstantiationOf(ASTContext &Ctx,
   return 0;
 }
 
+/// \brief Finds the instantiation of the given declaration context
+/// within the current instantiation.
+///
+/// \returns NULL if there was an error
+DeclContext *Sema::FindInstantiatedContext(DeclContext* DC) {
+  if (NamedDecl *D = dyn_cast<NamedDecl>(DC)) {
+    Decl* ID = FindInstantiatedDecl(D);
+    return cast_or_null<DeclContext>(ID);
+  } else return DC;
+}
+
 /// \brief Find the instantiation of the given declaration within the
 /// current instantiation.
 ///
@@ -1161,13 +1175,8 @@ NamedDecl * Sema::FindInstantiatedDecl(NamedDecl *D) {
     return cast<NamedDecl>(CurrentInstantiationScope->getInstantiationOf(D));
   }
 
-  if (NamedDecl *ParentDecl = dyn_cast<NamedDecl>(ParentDC)) {
-    ParentDecl = FindInstantiatedDecl(ParentDecl);
-    if (!ParentDecl)
-      return 0;
-
-    ParentDC = cast<DeclContext>(ParentDecl);
-  }
+  ParentDC = FindInstantiatedContext(ParentDC);
+  if (!ParentDC) return 0;
 
   if (ParentDC != D->getDeclContext()) {
     // We performed some kind of instantiation in the parent context,
index 1b2c4dfcd4d1f7c4cb8d510056069c23c03fdc53..5a9114006f04db09716365f16b851eafe27f17af 100644 (file)
@@ -37,6 +37,9 @@ class A {
   friend void global_function();
   friend void global_c_function();
 
+  friend class UndeclaredSoFar;
+  UndeclaredSoFar x; // expected-error {{ unknown type name 'UndeclaredSoFar' }}
+
   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)
@@ -60,6 +63,10 @@ class A {
   friend ftypedef typedeffed_function; // okay (because it's not declared as a member)
 };
 
+class UndeclaredSoFar { };
+
+A::UndeclaredSoFar y; // expected-error {{ unknown type name 'UndeclaredSoFar' }}
+
 class PreDeclared;
 
 int myoperation(float f) {
index 90174585cc83b8dc1120ddf38baa7e074fad27a2..b834257889eff8a7d07bbd670d8d60a7586ee235 100644 (file)
@@ -1,20 +1,44 @@
 // RUN: clang-cc -fsyntax-only -verify %s
 
-template <typename T> class Num {
+template <typename T> struct Num {
   T value_;
 
 public:
   Num(T value) : value_(value) {}
   T get() const { return value_; }
-  
+
+  template <typename U> struct Rep {
+    U count_;
+    Rep(U count) : count_(count) {}
+
+    friend Num operator*(const Num &a, const Rep &n) {
+      Num x = 0;
+      for (U count = n.count_; count; --count)
+        x += a;
+      return x;
+    } 
+  };
+
   friend Num operator+(const Num &a, const Num &b) {
     return a.value_ + b.value_;
   }
+
+  Num& operator+=(const Num& b) {
+    value_ += b.value_;
+    return *this;
+  }
 };
 
-int main() {
+int calc1() {
   Num<int> left = -1;
   Num<int> right = 1;
   Num<int> result = left + right;
   return result.get();
 }
+
+int calc2() {
+  Num<int> x = 3;
+  Num<int>::Rep<char> n = 10;
+  Num<int> result = x * n;
+  return result.get();
+}