Files
clang-p2996/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
Matheus Izvekov e29c085812 [clang] disallow narrowing when matching template template parameters (#124313)
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).
2025-01-28 15:51:17 -03:00

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