yaron.keren created this revision. Address http://bugs.llvm.org/pr30994 so that a non-friend can properly replace a friend, and a visible friend can properly replace an invisible friend but not vice verse. This also fixes two FIXME in SemaTemplate/friend-template.cpp.
Repository: rL LLVM https://reviews.llvm.org/D31540 Files: tools/clang/include/clang/AST/DeclBase.h tools/clang/lib/AST/Decl.cpp tools/clang/lib/AST/DeclBase.cpp tools/clang/test/SemaTemplate/friend-template.cpp Index: tools/clang/test/SemaTemplate/friend-template.cpp =================================================================== --- tools/clang/test/SemaTemplate/friend-template.cpp +++ tools/clang/test/SemaTemplate/friend-template.cpp @@ -68,17 +68,12 @@ Foo<int> foo; template<typename T, T Value> struct X2a; - - template<typename T, int Size> struct X2b; + template<typename T, int Size> struct X2b; // expected-note {{previous non-type template parameter with type 'int' is here}} template<typename T> class X3 { template<typename U, U Value> friend struct X2a; - - // FIXME: the redeclaration note ends up here because redeclaration - // lookup ends up finding the friend target from X3<int>. - template<typename U, T Value> friend struct X2b; // expected-error {{template non-type parameter has a different type 'long' in template redeclaration}} \ - // expected-note {{previous non-type template parameter with type 'int' is here}} + template<typename U, T Value> friend struct X2b; // expected-error {{template non-type parameter has a different type 'long' in template redeclaration}} }; X3<int> x3i; // okay @@ -297,14 +292,11 @@ int n = C::D<void*>().f(); struct F { - template<int> struct G; + template<int> struct G; // expected-note {{previous}} }; template<typename T> struct H { - // FIXME: As with cases above, the note here is on an unhelpful declaration, - // and should point to the declaration of G within F. template<T> friend struct F::G; // \ - // expected-error {{different type 'char' in template redeclaration}} \ - // expected-note {{previous}} + // expected-error {{different type 'char' in template redeclaration}} }; H<int> h1; // ok H<char> h2; // expected-note {{instantiation}} Index: tools/clang/lib/AST/DeclBase.cpp =================================================================== --- tools/clang/lib/AST/DeclBase.cpp +++ tools/clang/lib/AST/DeclBase.cpp @@ -861,6 +861,21 @@ return Ty->getAs<FunctionType>(); } +bool Decl::isThisDeclarationADefinition() const { + if (auto *TD = dyn_cast<TagDecl>(this)) + return TD->isThisDeclarationADefinition(); + if (auto *FD = dyn_cast<FunctionDecl>(this)) + return FD->isThisDeclarationADefinition(); + if (auto *VD = dyn_cast<VarDecl>(this)) + return VD->isThisDeclarationADefinition(); + if (auto *CTD = dyn_cast<ClassTemplateDecl>(this)) + return CTD->isThisDeclarationADefinition(); + if (auto *FTD = dyn_cast<FunctionTemplateDecl>(this)) + return FTD->isThisDeclarationADefinition(); + if (auto *VTD = dyn_cast<VarTemplateDecl>(this)) + return VTD->isThisDeclarationADefinition(); + return false; +} /// Starting at a given context (a Decl or DeclContext), look for a /// code context that is not a closure (a lambda, block, etc.). Index: tools/clang/lib/AST/Decl.cpp =================================================================== --- tools/clang/lib/AST/Decl.cpp +++ tools/clang/lib/AST/Decl.cpp @@ -1536,6 +1536,10 @@ if (isa<ObjCMethodDecl>(this)) return false; + if (getFriendObjectKind() > OldD->getFriendObjectKind() && + !isThisDeclarationADefinition()) + return false; + // For parameters, pick the newer one. This is either an error or (in // Objective-C) permitted as an extension. if (isa<ParmVarDecl>(this)) Index: tools/clang/include/clang/AST/DeclBase.h =================================================================== --- tools/clang/include/clang/AST/DeclBase.h +++ tools/clang/include/clang/AST/DeclBase.h @@ -417,6 +417,8 @@ return const_cast<Decl*>(this)->getTranslationUnitDecl(); } + bool isThisDeclarationADefinition() const; + bool isInAnonymousNamespace() const; bool isInStdNamespace() const;
Index: tools/clang/test/SemaTemplate/friend-template.cpp =================================================================== --- tools/clang/test/SemaTemplate/friend-template.cpp +++ tools/clang/test/SemaTemplate/friend-template.cpp @@ -68,17 +68,12 @@ Foo<int> foo; template<typename T, T Value> struct X2a; - - template<typename T, int Size> struct X2b; + template<typename T, int Size> struct X2b; // expected-note {{previous non-type template parameter with type 'int' is here}} template<typename T> class X3 { template<typename U, U Value> friend struct X2a; - - // FIXME: the redeclaration note ends up here because redeclaration - // lookup ends up finding the friend target from X3<int>. - template<typename U, T Value> friend struct X2b; // expected-error {{template non-type parameter has a different type 'long' in template redeclaration}} \ - // expected-note {{previous non-type template parameter with type 'int' is here}} + template<typename U, T Value> friend struct X2b; // expected-error {{template non-type parameter has a different type 'long' in template redeclaration}} }; X3<int> x3i; // okay @@ -297,14 +292,11 @@ int n = C::D<void*>().f(); struct F { - template<int> struct G; + template<int> struct G; // expected-note {{previous}} }; template<typename T> struct H { - // FIXME: As with cases above, the note here is on an unhelpful declaration, - // and should point to the declaration of G within F. template<T> friend struct F::G; // \ - // expected-error {{different type 'char' in template redeclaration}} \ - // expected-note {{previous}} + // expected-error {{different type 'char' in template redeclaration}} }; H<int> h1; // ok H<char> h2; // expected-note {{instantiation}} Index: tools/clang/lib/AST/DeclBase.cpp =================================================================== --- tools/clang/lib/AST/DeclBase.cpp +++ tools/clang/lib/AST/DeclBase.cpp @@ -861,6 +861,21 @@ return Ty->getAs<FunctionType>(); } +bool Decl::isThisDeclarationADefinition() const { + if (auto *TD = dyn_cast<TagDecl>(this)) + return TD->isThisDeclarationADefinition(); + if (auto *FD = dyn_cast<FunctionDecl>(this)) + return FD->isThisDeclarationADefinition(); + if (auto *VD = dyn_cast<VarDecl>(this)) + return VD->isThisDeclarationADefinition(); + if (auto *CTD = dyn_cast<ClassTemplateDecl>(this)) + return CTD->isThisDeclarationADefinition(); + if (auto *FTD = dyn_cast<FunctionTemplateDecl>(this)) + return FTD->isThisDeclarationADefinition(); + if (auto *VTD = dyn_cast<VarTemplateDecl>(this)) + return VTD->isThisDeclarationADefinition(); + return false; +} /// Starting at a given context (a Decl or DeclContext), look for a /// code context that is not a closure (a lambda, block, etc.). Index: tools/clang/lib/AST/Decl.cpp =================================================================== --- tools/clang/lib/AST/Decl.cpp +++ tools/clang/lib/AST/Decl.cpp @@ -1536,6 +1536,10 @@ if (isa<ObjCMethodDecl>(this)) return false; + if (getFriendObjectKind() > OldD->getFriendObjectKind() && + !isThisDeclarationADefinition()) + return false; + // For parameters, pick the newer one. This is either an error or (in // Objective-C) permitted as an extension. if (isa<ParmVarDecl>(this)) Index: tools/clang/include/clang/AST/DeclBase.h =================================================================== --- tools/clang/include/clang/AST/DeclBase.h +++ tools/clang/include/clang/AST/DeclBase.h @@ -417,6 +417,8 @@ return const_cast<Decl*>(this)->getTranslationUnitDecl(); } + bool isThisDeclarationADefinition() const; + bool isInAnonymousNamespace() const; bool isInStdNamespace() const;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits