forked from OSchip/llvm-project
PR32185: Revert r291512 and add a testcase for PR32185.
This reverts an attempt to check that types match when matching a dependently-typed non-type template parameter. (This comes up when matching the parameters of a template template parameter against the parameters of a template template argument.) The matching rules here are murky at best. Our behavior after this revert is definitely wrong for certain C++17 features (for 'auto' template parameter types within the parameter list of a template template argument in particular), but our behavior before this revert is wrong for some pre-existing testcases, so reverting to our prior behavior seems like our best option. llvm-svn: 300262
This commit is contained in:
parent
d0e9481c55
commit
1389418e18
|
@ -2109,7 +2109,6 @@ struct DependencyChecker : RecursiveASTVisitor<DependencyChecker> {
|
|||
typedef RecursiveASTVisitor<DependencyChecker> super;
|
||||
|
||||
unsigned Depth;
|
||||
bool FindLessThanDepth;
|
||||
|
||||
// Whether we're looking for a use of a template parameter that makes the
|
||||
// overall construct type-dependent / a dependent type. This is strictly
|
||||
|
@ -2120,16 +2119,25 @@ struct DependencyChecker : RecursiveASTVisitor<DependencyChecker> {
|
|||
bool Match;
|
||||
SourceLocation MatchLoc;
|
||||
|
||||
DependencyChecker(unsigned Depth, bool IgnoreNonTypeDependent,
|
||||
bool FindLessThanDepth = false)
|
||||
: Depth(Depth), FindLessThanDepth(FindLessThanDepth),
|
||||
IgnoreNonTypeDependent(IgnoreNonTypeDependent), Match(false) {}
|
||||
DependencyChecker(unsigned Depth, bool IgnoreNonTypeDependent)
|
||||
: Depth(Depth), IgnoreNonTypeDependent(IgnoreNonTypeDependent),
|
||||
Match(false) {}
|
||||
|
||||
DependencyChecker(TemplateParameterList *Params, bool IgnoreNonTypeDependent)
|
||||
: DependencyChecker(Params->getDepth(), IgnoreNonTypeDependent) {}
|
||||
: IgnoreNonTypeDependent(IgnoreNonTypeDependent), Match(false) {
|
||||
NamedDecl *ND = Params->getParam(0);
|
||||
if (TemplateTypeParmDecl *PD = dyn_cast<TemplateTypeParmDecl>(ND)) {
|
||||
Depth = PD->getDepth();
|
||||
} else if (NonTypeTemplateParmDecl *PD =
|
||||
dyn_cast<NonTypeTemplateParmDecl>(ND)) {
|
||||
Depth = PD->getDepth();
|
||||
} else {
|
||||
Depth = cast<TemplateTemplateParmDecl>(ND)->getDepth();
|
||||
}
|
||||
}
|
||||
|
||||
bool Matches(unsigned ParmDepth, SourceLocation Loc = SourceLocation()) {
|
||||
if (FindLessThanDepth ^ (ParmDepth >= Depth)) {
|
||||
if (ParmDepth >= Depth) {
|
||||
Match = true;
|
||||
MatchLoc = Loc;
|
||||
return true;
|
||||
|
@ -6432,15 +6440,6 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg,
|
|||
return E;
|
||||
}
|
||||
|
||||
static bool isDependentOnOuter(NonTypeTemplateParmDecl *NTTP) {
|
||||
if (NTTP->getDepth() == 0 || !NTTP->getType()->isDependentType())
|
||||
return false;
|
||||
DependencyChecker Checker(NTTP->getDepth(), /*IgnoreNonTypeDependent*/ false,
|
||||
/*FindLessThanDepth*/ true);
|
||||
Checker.TraverseType(NTTP->getType());
|
||||
return Checker.Match;
|
||||
}
|
||||
|
||||
/// \brief Match two template parameters within template parameter lists.
|
||||
static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
|
||||
bool Complain,
|
||||
|
@ -6497,10 +6496,11 @@ static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
|
|||
|
||||
// If we are matching a template template argument to a template
|
||||
// template parameter and one of the non-type template parameter types
|
||||
// is dependent on an outer template's parameter, then we must wait until
|
||||
// template instantiation time to actually compare the arguments.
|
||||
// is dependent, then we must wait until template instantiation time
|
||||
// to actually compare the arguments.
|
||||
if (Kind == Sema::TPL_TemplateTemplateArgumentMatch &&
|
||||
(isDependentOnOuter(OldNTTP) || isDependentOnOuter(NewNTTP)))
|
||||
(OldNTTP->getType()->isDependentType() ||
|
||||
NewNTTP->getType()->isDependentType()))
|
||||
return true;
|
||||
|
||||
if (!S.Context.hasSameType(OldNTTP->getType(), NewNTTP->getType())) {
|
||||
|
|
|
@ -49,8 +49,14 @@ void g() {
|
|||
// expected-note@Inputs/cxx-templates-a.h:11 {{candidate}}
|
||||
// expected-note@Inputs/cxx-templates-b.h:11 {{candidate}}
|
||||
|
||||
template_param_kinds_3<Tmpl_T_T_A>();
|
||||
template_param_kinds_3<Tmpl_T_T_B>();
|
||||
// FIXME: This should be valid, but we incorrectly match the template template
|
||||
// argument against both template template parameters.
|
||||
template_param_kinds_3<Tmpl_T_T_A>(); // expected-error {{ambiguous}}
|
||||
// expected-note@Inputs/cxx-templates-a.h:12 {{candidate}}
|
||||
// expected-note@Inputs/cxx-templates-b.h:12 {{candidate}}
|
||||
template_param_kinds_3<Tmpl_T_T_B>(); // expected-error {{ambiguous}}
|
||||
// expected-note@Inputs/cxx-templates-a.h:12 {{candidate}}
|
||||
// expected-note@Inputs/cxx-templates-b.h:12 {{candidate}}
|
||||
|
||||
// Trigger the instantiation of a template in 'a' that uses a type defined in
|
||||
// 'common'. That type is not visible here.
|
||||
|
|
|
@ -179,9 +179,9 @@ namespace default_args_from_ctor {
|
|||
|
||||
namespace transform_params {
|
||||
template<typename T, T N, template<T (*v)[N]> typename U, T (*X)[N]>
|
||||
struct A { // expected-note 2{{candidate}}
|
||||
struct A {
|
||||
template<typename V, V M, V (*Y)[M], template<V (*v)[M]> typename W>
|
||||
A(U<X>, W<Y>); // expected-note {{[with V = int, M = 12, Y = &transform_params::n]}}
|
||||
A(U<X>, W<Y>);
|
||||
|
||||
static constexpr T v = N;
|
||||
};
|
||||
|
@ -189,9 +189,7 @@ namespace transform_params {
|
|||
int n[12];
|
||||
template<int (*)[12]> struct Q {};
|
||||
Q<&n> qn;
|
||||
// FIXME: The class template argument deduction result here is correct, but
|
||||
// we incorrectly fail to deduce arguments for the constructor!
|
||||
A a(qn, qn); // expected-error {{no matching constructor for initialization of 'transform_params::A<int, 12, Q, &transform_params::n>'}}
|
||||
A a(qn, qn);
|
||||
static_assert(a.v == 12);
|
||||
|
||||
template<typename ...T> struct B {
|
||||
|
@ -203,16 +201,12 @@ namespace transform_params {
|
|||
};
|
||||
B b({1, 2, 3}, "foo", {'x', 'y', 'z', 'w'}); // ok
|
||||
|
||||
// This should be accepted once -std=c++1z implies
|
||||
// -frelaxed-template-template-args. Without that, a template template
|
||||
// parameter 'template<int, int, int> typename' cannot bind to a template
|
||||
// template argument 'template<int...> typename'.
|
||||
template<typename ...T> struct C { // expected-note {{candidate}}
|
||||
template<typename ...T> struct C {
|
||||
template<T ...V, template<T...> typename X>
|
||||
C(X<V...>); // expected-note {{substitution failure [with T = <int, int, int>, V = <0, 1, 2>]}}
|
||||
C(X<V...>);
|
||||
};
|
||||
template<int...> struct Y {};
|
||||
C c(Y<0, 1, 2>{}); // expected-error {{no viable constructor or deduction guide}}
|
||||
C c(Y<0, 1, 2>{});
|
||||
|
||||
template<typename ...T> struct D {
|
||||
template<T ...V> D(Y<V...>);
|
||||
|
|
|
@ -483,16 +483,15 @@ namespace check_extended_pack {
|
|||
}
|
||||
|
||||
namespace dependent_template_template_param_non_type_param_type {
|
||||
template<int N> struct A { // expected-note 2{{candidate}}
|
||||
template<int N> struct A {
|
||||
template<typename V = int, V M = 12, V (*Y)[M], template<V (*v)[M]> class W>
|
||||
A(W<Y>); // expected-note {{[with V = int, M = 12, Y = &dependent_template_template_param_non_type_param_type::n]}}
|
||||
A(W<Y>);
|
||||
};
|
||||
|
||||
int n[12];
|
||||
template<int (*)[12]> struct Q {};
|
||||
Q<&n> qn;
|
||||
// FIXME: This should be accepted, but we somehow fail to deduce W.
|
||||
A<0> a(qn); // expected-error {{no matching constructor for initialization}}
|
||||
A<0> a(qn);
|
||||
}
|
||||
|
||||
namespace dependent_list_deduction {
|
||||
|
|
|
@ -102,7 +102,42 @@ void foo() {
|
|||
}
|
||||
|
||||
namespace CheckDependentNonTypeParamTypes {
|
||||
template<template<typename T, typename U, T v> class> struct A {}; // expected-note {{previous}}
|
||||
template<typename T, typename U, U v> struct B {}; // expected-note {{different type}}
|
||||
A<B> ab; // expected-error {{different template parameters}}
|
||||
template<template<typename T, typename U, T v> class X> struct A {
|
||||
void f() {
|
||||
X<int, void*, 3> x; // expected-error {{does not refer to any declaration}}
|
||||
}
|
||||
void g() {
|
||||
X<int, long, 3> x;
|
||||
}
|
||||
void h() {
|
||||
// FIXME: If we accept A<B> at all, it's not obvious what should happen
|
||||
// here. While parsing the template, we form
|
||||
// X<unsigned char, int, (unsigned char)1234>
|
||||
// but in the final instantiation do we get
|
||||
// B<unsigned char, int, (int)1234>
|
||||
// or
|
||||
// B<unsigned char, int, (int)(unsigned char)1234>
|
||||
// ?
|
||||
X<unsigned char, int, 1234> x;
|
||||
int check[x.value == 1234 ? 1 : -1];
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U, U v> struct B { // expected-note {{parameter}}
|
||||
static const U value = v;
|
||||
};
|
||||
|
||||
// FIXME: This should probably be rejected, but the rules are at best unclear.
|
||||
A<B> ab;
|
||||
|
||||
void use() {
|
||||
ab.f(); // expected-note {{instantiation of}}
|
||||
ab.g();
|
||||
ab.h();
|
||||
}
|
||||
}
|
||||
|
||||
namespace PR32185 {
|
||||
template<template<typename T, T> class U> struct A {};
|
||||
template<template<typename T, T> class U> struct B : A<U> {};
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ namespace Auto {
|
|||
template<int*> struct IntPtr;
|
||||
|
||||
TInt<Auto> ia;
|
||||
TInt<AutoPtr> iap; // expected-error {{different template parameters}}
|
||||
TInt<AutoPtr> iap; // FIXME: ill-formed (?)
|
||||
TInt<DecltypeAuto> ida;
|
||||
TInt<Int> ii;
|
||||
TInt<IntPtr> iip; // expected-error {{different template parameters}}
|
||||
|
@ -90,18 +90,18 @@ namespace Auto {
|
|||
TIntPtr<IntPtr> ipip;
|
||||
|
||||
TAuto<Auto> aa;
|
||||
TAuto<AutoPtr> aap; // expected-error {{different template parameters}}
|
||||
TAuto<Int> ai; // expected-error {{different template parameters}}
|
||||
TAuto<IntPtr> aip; // expected-error {{different template parameters}}
|
||||
TAuto<AutoPtr> aap; // FIXME: ill-formed (?)
|
||||
TAuto<Int> ai; // FIXME: ill-formed (?)
|
||||
TAuto<IntPtr> aip; // FIXME: ill-formed (?)
|
||||
|
||||
TAutoPtr<Auto> apa;
|
||||
TAutoPtr<AutoPtr> apap;
|
||||
TAutoPtr<Int> api; // expected-error {{different template parameters}}
|
||||
TAutoPtr<IntPtr> apip; // expected-error {{different template parameters}}
|
||||
TAutoPtr<Int> api; // FIXME: ill-formed (?)
|
||||
TAutoPtr<IntPtr> apip; // FIXME: ill-formed (?)
|
||||
|
||||
TDecltypeAuto<DecltypeAuto> dada;
|
||||
TDecltypeAuto<Int> dai; // expected-error {{different template parameters}}
|
||||
TDecltypeAuto<IntPtr> daip; // expected-error {{different template parameters}}
|
||||
TDecltypeAuto<Int> dai; // FIXME: ill-formed (?)
|
||||
TDecltypeAuto<IntPtr> daip; // FIXME: ill-formed (?)
|
||||
|
||||
// FIXME: It's completely unclear what should happen here, but these results
|
||||
// seem at least plausible:
|
||||
|
@ -111,7 +111,7 @@ namespace Auto {
|
|||
// parameters (such as 'user-defined-type &') that are not valid 'auto'
|
||||
// parameters.
|
||||
TDecltypeAuto<Auto> daa;
|
||||
TDecltypeAuto<AutoPtr> daa; // expected-error {{different template parameters}}
|
||||
TDecltypeAuto<AutoPtr> daap; // FIXME: should probably be ill-formed
|
||||
|
||||
int n;
|
||||
template<auto A, decltype(A) B = &n> struct SubstFailure;
|
||||
|
|
Loading…
Reference in New Issue