diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 007869c43749..113a8fc3e540 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3658,6 +3658,8 @@ def err_member_def_undefined_record : Error< "out-of-line definition of %0 from class %1 without definition">; def err_member_def_does_not_match : Error< "out-of-line definition of %0 does not match any declaration in %1">; +def err_friend_decl_does_not_match : Error< + "friend declaration of %0 does not match any declaration in %1">; def err_member_def_does_not_match_suggest : Error< "out-of-line definition of %0 does not match any declaration in %1; " "did you mean %2?">; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index f1581e0c3497..d4b09753d309 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -969,20 +969,15 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, PrevDecl = (*Previous.begin())->getUnderlyingDecl(); } } - - if (CurContext->isDependentContext() && PrevClassTemplate) { - // If this is a dependent context, we don't want to link the friend - // class template to the template in scope, because that would perform - // checking of the template parameter lists that can't be performed - // until the outer context is instantiated. - PrevDecl = PrevClassTemplate = 0; - } } else if (PrevDecl && !isDeclInScope(PrevDecl, SemanticContext, S)) PrevDecl = PrevClassTemplate = 0; if (PrevClassTemplate) { - // Ensure that the template parameter lists are compatible. - if (!TemplateParameterListsAreEqual(TemplateParams, + // Ensure that the template parameter lists are compatible. Skip this check + // for a friend in a dependent context: the template parameter list itself + // could be dependent. + if (!(TUK == TUK_Friend && CurContext->isDependentContext()) && + !TemplateParameterListsAreEqual(TemplateParams, PrevClassTemplate->getTemplateParameters(), /*Complain=*/true, TPL_TemplateMatch)) @@ -1031,8 +1026,10 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, // Check the template parameter list of this declaration, possibly // merging in the template parameter list from the previous class - // template declaration. - if (CheckTemplateParameterList(TemplateParams, + // template declaration. Skip this check for a friend in a dependent + // context, because the template parameter list might be dependent. + if (!(TUK == TUK_Friend && CurContext->isDependentContext()) && + CheckTemplateParameterList(TemplateParams, PrevClassTemplate? PrevClassTemplate->getTemplateParameters() : 0, (SS.isSet() && SemanticContext && SemanticContext->isRecord() && @@ -1044,9 +1041,9 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, if (SS.isSet()) { // If the name of the template was qualified, we must be defining the // template out-of-line. - if (!SS.isInvalid() && !Invalid && !PrevClassTemplate && - !(TUK == TUK_Friend && CurContext->isDependentContext())) { - Diag(NameLoc, diag::err_member_def_does_not_match) + if (!SS.isInvalid() && !Invalid && !PrevClassTemplate) { + Diag(NameLoc, TUK == TUK_Friend ? diag::err_friend_decl_does_not_match + : diag::err_member_def_does_not_match) << Name << SemanticContext << SS.getRange(); Invalid = true; } diff --git a/clang/test/PCH/cxx-friends.cpp b/clang/test/PCH/cxx-friends.cpp index a8d75586e431..bdba42bbcb51 100644 --- a/clang/test/PCH/cxx-friends.cpp +++ b/clang/test/PCH/cxx-friends.cpp @@ -11,3 +11,11 @@ class F { a->x = 0; } }; + +template class PR12585::future_base::setter { +public: + int f() { + return promise().k; + } +}; +int k = PR12585::future_base::setter().f(); diff --git a/clang/test/PCH/cxx-friends.h b/clang/test/PCH/cxx-friends.h index 2a33f15a5329..05dcc9606636 100644 --- a/clang/test/PCH/cxx-friends.h +++ b/clang/test/PCH/cxx-friends.h @@ -4,3 +4,15 @@ class A { int x; friend class F; }; + +namespace PR12585 { + struct future_base { + template class setter; + }; + template class promise { + // We used to inject this into future_base with no access specifier, + // then crash during AST writing. + template friend class future_base::setter; + int k; + }; +} diff --git a/clang/test/SemaTemplate/friend-template.cpp b/clang/test/SemaTemplate/friend-template.cpp index 2b05527cf171..9acbfdcea29a 100644 --- a/clang/test/SemaTemplate/friend-template.cpp +++ b/clang/test/SemaTemplate/friend-template.cpp @@ -267,3 +267,38 @@ namespace PR12557 { Bar b; } + +namespace PR12585 { + struct A { }; + template struct B { + template friend class A::does_not_exist; // \ + // expected-error {{friend declaration of 'does_not_exist' does not match any declaration in 'PR12585::A'}} + }; + + struct C { + template struct D; + }; + template class E { + int n; + template friend struct C::D; + }; + template struct C::D { + int f() { + return E().n; + } + }; + int n = C::D().f(); + + struct F { + template struct G; + }; + template 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 friend struct F::G; // \ + // expected-error {{different type 'char' in template redeclaration}} \ + // expected-note {{previous}} + }; + H h1; // ok + H h2; // expected-note {{instantiation}} +}