PR12585: When processing a friend template inside a class template, don't
pretend there was no previous declaration -- that can lead us to injecting a class template (with no access specifier) into a class scope. Instead, just avoid the problematic checks. llvm-svn: 155303
This commit is contained in:
parent
4304101fb2
commit
e85e176600
|
@ -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?">;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -11,3 +11,11 @@ class F {
|
|||
a->x = 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class PR12585::future_base::setter {
|
||||
public:
|
||||
int f() {
|
||||
return promise<T*>().k;
|
||||
}
|
||||
};
|
||||
int k = PR12585::future_base::setter<int>().f();
|
||||
|
|
|
@ -4,3 +4,15 @@ class A {
|
|||
int x;
|
||||
friend class F;
|
||||
};
|
||||
|
||||
namespace PR12585 {
|
||||
struct future_base {
|
||||
template<typename> class setter;
|
||||
};
|
||||
template<typename> class promise {
|
||||
// We used to inject this into future_base with no access specifier,
|
||||
// then crash during AST writing.
|
||||
template<typename> friend class future_base::setter;
|
||||
int k;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -267,3 +267,38 @@ namespace PR12557 {
|
|||
|
||||
Bar<int> b;
|
||||
}
|
||||
|
||||
namespace PR12585 {
|
||||
struct A { };
|
||||
template<typename> struct B {
|
||||
template<typename> 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<typename> struct D;
|
||||
};
|
||||
template<typename> class E {
|
||||
int n;
|
||||
template<typename> friend struct C::D;
|
||||
};
|
||||
template<typename T> struct C::D {
|
||||
int f() {
|
||||
return E<int>().n;
|
||||
}
|
||||
};
|
||||
int n = C::D<void*>().f();
|
||||
|
||||
struct F {
|
||||
template<int> struct G;
|
||||
};
|
||||
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}}
|
||||
};
|
||||
H<int> h1; // ok
|
||||
H<char> h2; // expected-note {{instantiation}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue