From: John McCall Date: Fri, 28 Aug 2009 07:59:38 +0000 (+0000) Subject: Omnibus friend decl refactoring. Instead of cloning AST classes for friend X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=02cace78cf48cc26686bd5b07c78606abca13bcd;p=clang Omnibus friend decl refactoring. Instead of cloning AST classes for friend 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 --- diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index e43f15d79a..9aea17bd1b 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -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; } diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 2d16557d95..45e85c0a49 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -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 class A { +/// friend int foo(T); +/// friend class B; +/// friend T; // only in C++0x +/// template friend class C; +/// template 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 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(); + } - 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(); } + /// 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: diff --git a/include/clang/AST/DeclNodes.def b/include/clang/AST/DeclNodes.def index 996d042d14..61df9d052f 100644 --- a/include/clang/AST/DeclNodes.def +++ b/include/clang/AST/DeclNodes.def @@ -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) diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 28d543785f..3ced0eff4c 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -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: diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 70e48976da..46acf67d29 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -777,20 +777,20 @@ 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, - 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 *D = Friend.get(); + assert(isa(D) || + isa(D) || + isa(D) || + isa(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, diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 905b2e99fd..c59224298a 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2143,6 +2143,9 @@ public: virtual DeclPtrTy ActOnFriendDecl(Scope *S, llvm::PointerUnion 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, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 045c12aab5..15e8bc3ab8 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -735,7 +735,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) { const CXXMethodDecl* OldMethod = dyn_cast(Old); const CXXMethodDecl* NewMethod = dyn_cast(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); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 602caf2825..4874c92dec 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -3567,116 +3567,124 @@ Sema::DeclPtrTy Sema::ActOnStaticAssertDeclaration(SourceLocation AssertLoc, Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S, llvm::PointerUnion DU, bool IsDefinition) { - Declarator *D = DU.dyn_cast(); - const DeclSpec &DS = (D ? D->getDeclSpec() : *DU.get()); + if (DU.is()) + return ActOnFriendFunctionDecl(S, *DU.get(), IsDefinition); + else + return ActOnFriendTypeDecl(S, *DU.get(), 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()) - 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((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((Decl*) DS.getTypeRep()); - if (!RD) return DeclPtrTy(); - break; - - case DeclSpec::TST_typename: - if (const RecordType *RT = - ((const Type*) DS.getTypeRep())->getAs()) - RD = dyn_cast(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()) { + FU = RD = cast(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(ND); + if (!ND) return DeclPtrTy(); + FD = cast(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); } diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index 6627499d12..4a699de6c8 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -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(LexDC))) continue; diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index a0353e3c53..0bf832f3eb 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -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(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(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(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(DC)) { + Decl* ID = FindInstantiatedDecl(D); + return cast_or_null(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(CurrentInstantiationScope->getInstantiationOf(D)); } - if (NamedDecl *ParentDecl = dyn_cast(ParentDC)) { - ParentDecl = FindInstantiatedDecl(ParentDecl); - if (!ParentDecl) - return 0; - - ParentDC = cast(ParentDecl); - } + ParentDC = FindInstantiatedContext(ParentDC); + if (!ParentDC) return 0; if (ParentDC != D->getDeclContext()) { // We performed some kind of instantiation in the parent context, diff --git a/test/CXX/class/class.friend/p1.cpp b/test/CXX/class/class.friend/p1.cpp index 1b2c4dfcd4..5a9114006f 100644 --- a/test/CXX/class/class.friend/p1.cpp +++ b/test/CXX/class/class.friend/p1.cpp @@ -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) { diff --git a/test/CXX/temp/temp.decls/temp.friend/p1.cpp b/test/CXX/temp/temp.decls/temp.friend/p1.cpp index 90174585cc..b834257889 100644 --- a/test/CXX/temp/temp.decls/temp.friend/p1.cpp +++ b/test/CXX/temp/temp.decls/temp.friend/p1.cpp @@ -1,20 +1,44 @@ // RUN: clang-cc -fsyntax-only -verify %s -template class Num { +template struct Num { T value_; public: Num(T value) : value_(value) {} T get() const { return value_; } - + + template 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 left = -1; Num right = 1; Num result = left + right; return result.get(); } + +int calc2() { + Num x = 3; + Num::Rep n = 10; + Num result = x * n; + return result.get(); +}