erik.pilkington created this revision. erik.pilkington added a reviewer: rsmith. erik.pilkington added a subscriber: cfe-commits.
Clang erroneously rejects the following code because it does not find the default argument for `makeType` : ``` namespace n { template <class> struct Type { template <class T> friend Type<T> makeType(); }; template <class T = void> Type<T> makeType(); void f() { Type<void> t; n::makeType<>(); // Error, lookup finds wrong version of makeType } } // end namespace n ``` The problem is that during instantiation of `Type<void>` on the previous line the default template argument version of `makeType` is incorrectly swapped out of `DeclContext::LookupPtr` for the friend version. This patch fixes that by disallowing the swap in `NamedDecl::declarationReplaces()`. This patch looks like it's a fix for PR10856, PR18038, PR20877, and PR26962. http://reviews.llvm.org/D20192 Files: lib/AST/Decl.cpp test/SemaTemplate/friend-template.cpp Index: test/SemaTemplate/friend-template.cpp =================================================================== --- test/SemaTemplate/friend-template.cpp +++ test/SemaTemplate/friend-template.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s // PR5057 namespace test0 { namespace std { @@ -69,16 +69,13 @@ 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 +294,10 @@ int n = C::D<void*>().f(); struct F { - template<int> struct G; + template<int> struct G; // expected-note {{previous non-type template parameter with type 'int' is here}} }; 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}} + template<T> friend struct F::G; // expected-error {{different type 'char' in template redeclaration}} }; H<int> h1; // ok H<char> h2; // expected-note {{instantiation}} @@ -329,3 +322,16 @@ foo(b); // expected-note {{in instantiation}} } } + +namespace PR26962 { +template <class> struct Type { + template <class T> friend Type<T> makeType(); +}; + +template <class T = void> Type<T> makeType(); + +void f() { + Type<void> t; + PR26962::makeType<>(); +} +} // end PR26962 Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -1520,6 +1520,10 @@ if (OldD->isFromASTFile() && isFromASTFile()) return false; + // Do not replace a declaration with a friend. + if (getFriendObjectKind() != FOK_None) + return false; + // A kind mismatch implies that the declaration is not replaced. if (OldD->getKind() != getKind()) return false;
Index: test/SemaTemplate/friend-template.cpp =================================================================== --- test/SemaTemplate/friend-template.cpp +++ test/SemaTemplate/friend-template.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s // PR5057 namespace test0 { namespace std { @@ -69,16 +69,13 @@ 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 +294,10 @@ int n = C::D<void*>().f(); struct F { - template<int> struct G; + template<int> struct G; // expected-note {{previous non-type template parameter with type 'int' is here}} }; 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}} + template<T> friend struct F::G; // expected-error {{different type 'char' in template redeclaration}} }; H<int> h1; // ok H<char> h2; // expected-note {{instantiation}} @@ -329,3 +322,16 @@ foo(b); // expected-note {{in instantiation}} } } + +namespace PR26962 { +template <class> struct Type { + template <class T> friend Type<T> makeType(); +}; + +template <class T = void> Type<T> makeType(); + +void f() { + Type<void> t; + PR26962::makeType<>(); +} +} // end PR26962 Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -1520,6 +1520,10 @@ if (OldD->isFromASTFile() && isFromASTFile()) return false; + // Do not replace a declaration with a friend. + if (getFriendObjectKind() != FOK_None) + return false; + // A kind mismatch implies that the declaration is not replaced. if (OldD->getKind() != getKind()) return false;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits