This fixes the core issue described in P3579, following the design intent of P0522 to not introduce any new cases where a template template parameter match is allowed for a template which is not valid for all possible uses. With this patch, narrowing conversions are disallowed for TTP matching. This reuses the existing machinery for diagnosing narrowing in a converted constant expression. Since P0522 is a DR and we apply it all the way back to C++98, this brings that machinery to use in older standards, in this very narrow scope of TTP matching. This still doesn't solve the ambiguity when partial ordering NTTPs of different integral types, this is blocked by a different bug which will be fixed in a subsequent patch (but the test cases are added).
170 lines
7.4 KiB
C++
170 lines
7.4 KiB
C++
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
|
|
|
|
// expected-note@temp_arg_template_p0522.cpp:* 1+{{template is declared here}}
|
|
// expected-note@temp_arg_template_p0522.cpp:* 1+{{template parameter is declared here}}
|
|
// expected-note@temp_arg_template_p0522.cpp:* 1+{{previous template template parameter is here}}
|
|
|
|
template<template<int> typename> struct Ti; // #Ti
|
|
template<template<int...> typename> struct TPi; // #TPi
|
|
template<template<int, int...> typename> struct TiPi;
|
|
template<template<int..., int...> typename> struct TPiPi; // FIXME: Why is this not ill-formed?
|
|
|
|
template<typename T, template<T> typename> struct tT0; // #tT0
|
|
template<template<typename T, T> typename> struct Tt0; // #Tt0
|
|
|
|
template<template<typename> typename> struct Tt;
|
|
template<template<typename, typename...> typename> struct TtPt;
|
|
|
|
template<int> struct i;
|
|
template<int, int = 0> struct iDi;
|
|
template<int, int> struct ii;
|
|
template<int...> struct Pi;
|
|
template<int, int, int...> struct iiPi;
|
|
|
|
template<int, typename = int> struct iDt; // #iDt
|
|
template<int, typename> struct it; // #it
|
|
|
|
template<typename T, T v> struct t0;
|
|
|
|
template<typename...> struct Pt;
|
|
|
|
namespace IntParam {
|
|
using ok = Pt<Ti<i>,
|
|
Ti<iDi>,
|
|
Ti<Pi>,
|
|
Ti<iDt>>;
|
|
using err1 = Ti<ii>; // expected-error {{too few template arguments for class template 'ii'}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
using err2 = Ti<iiPi>; // expected-error {{too few template arguments for class template 'iiPi'}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
using err3 = Ti<t0>; // expected-error@#Ti {{template argument for template type parameter must be a type}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
using err4 = Ti<it>; // expected-error {{too few template arguments for class template 'it'}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
}
|
|
|
|
// These are accepted by the backwards-compatibility "parameter pack in
|
|
// parameter matches any number of parameters in arguments" rule.
|
|
namespace IntPackParam {
|
|
using ok = TPi<Pi>;
|
|
using ok_compat = Pt<TPi<i>, TPi<iDi>, TPi<ii>, TPi<iiPi>>;
|
|
using err1 = TPi<t0>; // expected-error@#TPi {{template argument for template type parameter must be a type}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
using err2 = TPi<iDt>; // expected-error@#TPi {{template argument for template type parameter must be a type}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
using err3 = TPi<it>; // expected-error@#TPi {{template argument for template type parameter must be a type}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
}
|
|
|
|
namespace IntAndPackParam {
|
|
using ok = TiPi<Pi>;
|
|
using ok_compat = Pt<TiPi<ii>, TiPi<iDi>, TiPi<iiPi>>;
|
|
using err = TiPi<iDi>;
|
|
}
|
|
|
|
namespace DependentType {
|
|
using ok = Pt<tT0<int, i>, tT0<int, iDi>>;
|
|
using err1 = tT0<int, ii>; // expected-error {{too few template arguments for class template 'ii'}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
using err2 = tT0<short, i>;
|
|
using err2a = tT0<long long, i>; // expected-error@#tT0 {{cannot be narrowed from type 'long long' to 'int'}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
using err2b = tT0<void*, i>; // expected-error@#tT0 {{value of type 'void *' is not implicitly convertible to 'int'}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
using err3 = tT0<short, t0>; // expected-error@#tT0 {{template argument for template type parameter must be a type}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
|
|
using ok2 = Tt0<t0>;
|
|
using err4 = Tt0<it>; // expected-error@#Tt0 {{template argument for non-type template parameter must be an expression}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
}
|
|
|
|
namespace Auto {
|
|
template<template<int> typename T> struct TInt {}; // #TInt
|
|
template<template<int*> typename T> struct TIntPtr {}; // #TIntPtr
|
|
template<template<auto> typename T> struct TAuto {};
|
|
template<template<auto*> typename T> struct TAutoPtr {};
|
|
template<template<decltype(auto)> typename T> struct TDecltypeAuto {};
|
|
template<auto> struct Auto;
|
|
template<auto*> struct AutoPtr; // #AutoPtr
|
|
template<decltype(auto)> struct DecltypeAuto;
|
|
template<int> struct Int;
|
|
template<int*> struct IntPtr;
|
|
|
|
TInt<Auto> ia;
|
|
TInt<AutoPtr> iap; // expected-error@#TInt {{non-type template parameter '' with type 'auto *' has incompatible initializer of type 'int'}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
TInt<DecltypeAuto> ida;
|
|
TInt<Int> ii;
|
|
TInt<IntPtr> iip; // expected-error@#TInt {{conversion from 'int' to 'int *' is not allowed in a converted constant expression}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
|
|
TIntPtr<Auto> ipa;
|
|
TIntPtr<AutoPtr> ipap;
|
|
TIntPtr<DecltypeAuto> ipda;
|
|
TIntPtr<Int> ipi; // expected-error@#TIntPtr {{value of type 'int *' is not implicitly convertible to 'int'}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
TIntPtr<IntPtr> ipip;
|
|
|
|
TAuto<Auto> aa;
|
|
TAuto<AutoPtr> aap; // expected-error@#AutoPtr {{could not match 'auto *' against 'auto'}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
TAuto<Int> ai; // FIXME: ill-formed (?)
|
|
TAuto<IntPtr> aip; // FIXME: ill-formed (?)
|
|
|
|
TAutoPtr<Auto> apa;
|
|
TAutoPtr<AutoPtr> apap;
|
|
TAutoPtr<Int> api; // FIXME: ill-formed (?)
|
|
TAutoPtr<IntPtr> apip; // FIXME: ill-formed (?)
|
|
|
|
TDecltypeAuto<DecltypeAuto> dada;
|
|
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:
|
|
TAuto<DecltypeAuto> ada;
|
|
TAutoPtr<DecltypeAuto> apda;
|
|
// Perhaps this case should be invalid, as there are valid 'decltype(auto)'
|
|
// parameters (such as 'user-defined-type &') that are not valid 'auto'
|
|
// parameters.
|
|
TDecltypeAuto<Auto> daa;
|
|
TDecltypeAuto<AutoPtr> daap; // expected-error@#AutoPtr {{could not match 'auto *' against 'decltype(auto)'}}
|
|
// expected-note@-1 {{different template parameters}}
|
|
|
|
int n;
|
|
template<auto A, decltype(A) B = &n> struct SubstFailure;
|
|
TInt<SubstFailure> isf; // FIXME: this should be ill-formed
|
|
TIntPtr<SubstFailure> ipsf;
|
|
}
|
|
|
|
namespace GH62529 {
|
|
// Note: the constraint here is just for bypassing a fast-path.
|
|
template<class T1> requires(true) using A = int;
|
|
template<template<class ...T2s> class TT1, class T3> struct B {};
|
|
template<class T4> B<A, T4> f();
|
|
auto t = f<int>();
|
|
} // namespace GH62529
|
|
|
|
namespace GH101394 {
|
|
struct X {}; // #X
|
|
struct Y {
|
|
constexpr Y(const X &) {}
|
|
};
|
|
|
|
namespace t1 {
|
|
template<template<X> class> struct A {};
|
|
template<Y> struct B;
|
|
template struct A<B>;
|
|
} // namespace t1
|
|
namespace t2 {
|
|
template<template<Y> class> struct A {}; // #A
|
|
template<X> struct B; // #B
|
|
template struct A<B>;
|
|
// expected-error@#A {{no viable conversion from 'const Y' to 'X'}}
|
|
// expected-note@-2 {{different template parameters}}
|
|
// expected-note@#X 2{{not viable}}
|
|
// expected-note@#B {{passing argument to parameter here}}
|
|
} // namespace t2
|
|
} // namespace GH101394
|