[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).
This commit is contained in:
Matheus Izvekov 2025-01-28 15:51:17 -03:00 committed by GitHub
parent eb10e94180
commit e29c085812
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 230 additions and 98 deletions

View File

@ -337,6 +337,9 @@ C++17 Feature Support
^^^^^^^^^^^^^^^^^^^^^
- The implementation of the relaxed template template argument matching rules is
more complete and reliable, and should provide more accurate diagnostics.
This implements:
- `P3310R5: Solving issues introduced by relaxed template template parameter matching <https://wg21.link/p3310r5>`_.
- `P3579R0: Fix matching of non-type template parameters when matching template template parameters <https://wg21.link/p3579r0>`_.
Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -82,11 +82,11 @@ def err_typecheck_converted_constant_expression_indirect : Error<
"conversion from %0 to %1 in converted constant expression would "
"bind reference to a temporary">;
def err_expr_not_cce : Error<
"%select{case value|enumerator value|non-type template argument|"
"%select{case value|enumerator value|non-type template argument|non-type parameter of template template parameter|"
"array size|explicit specifier argument|noexcept specifier argument|"
"call to 'size()'|call to 'data()'}0 is not a constant expression">;
def ext_cce_narrowing : ExtWarn<
"%select{case value|enumerator value|non-type template argument|"
"%select{case value|enumerator value|non-type template argument|non-type parameter of template template parameter|"
"array size|explicit specifier argument|noexcept specifier argument|"
"call to 'size()'|call to 'data()'}0 %select{cannot be narrowed from "
"type %2 to %3|evaluates to %2, which cannot be narrowed to type %3}1">,

View File

@ -10000,6 +10000,7 @@ public:
CCEK_CaseValue, ///< Expression in a case label.
CCEK_Enumerator, ///< Enumerator value with fixed underlying type.
CCEK_TemplateArg, ///< Value of a non-type template parameter.
CCEK_InjectedTTP, ///< Injected parameter of a template template parameter.
CCEK_ArrayBound, ///< Array bound in array declarator or new-expression.
CCEK_ExplicitBool, ///< Condition in an explicit(bool) specifier.
CCEK_Noexcept, ///< Condition in a noexcept(bool) specifier.
@ -11683,6 +11684,7 @@ public:
SmallVectorImpl<TemplateArgument> &SugaredConverted,
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
CheckTemplateArgumentKind CTAK, bool PartialOrdering,
bool PartialOrderingTTP,
bool *MatchedPackOnParmToNonPackOnArg);
/// Check that the given template arguments can be provided to
@ -11756,6 +11758,7 @@ public:
QualType InstantiatedParamType, Expr *Arg,
TemplateArgument &SugaredConverted,
TemplateArgument &CanonicalConverted,
bool PartialOrderingTTP,
CheckTemplateArgumentKind CTAK);
/// Check a template argument against its corresponding

View File

@ -3726,7 +3726,7 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
if (CheckTemplateArgument(
Params->getParam(0), Arg, FD, R.getNameLoc(), R.getNameLoc(),
0, SugaredChecked, CanonicalChecked, CTAK_Specified,
/*PartialOrdering=*/false,
/*PartialOrdering=*/false, /*PartialOrderingTTP=*/false,
/*MatchedPackOnParmToNonPackOnArg=*/nullptr) ||
Trap.hasErrorOccurred())
IsTemplate = false;

View File

@ -6151,8 +6151,8 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From,
Sema::CCEKind CCE,
NamedDecl *Dest,
APValue &PreNarrowingValue) {
assert(S.getLangOpts().CPlusPlus11 &&
"converted constant expression outside C++11");
assert((S.getLangOpts().CPlusPlus11 || CCE == Sema::CCEK_InjectedTTP) &&
"converted constant expression outside C++11 or TTP matching");
if (checkPlaceholderForOverload(S, From))
return ExprError();
@ -6221,8 +6221,10 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From,
// earlier, but that's not guaranteed to work when initializing an object of
// class type.
ExprResult Result;
bool IsTemplateArgument =
CCE == Sema::CCEK_TemplateArg || CCE == Sema::CCEK_InjectedTTP;
if (T->isRecordType()) {
assert(CCE == Sema::CCEK_TemplateArg &&
assert(IsTemplateArgument &&
"unexpected class type converted constant expr");
Result = S.PerformCopyInitialization(
InitializedEntity::InitializeTemplateParameter(
@ -6239,7 +6241,7 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From,
// A full-expression is [...] a constant-expression [...]
Result = S.ActOnFinishFullExpr(Result.get(), From->getExprLoc(),
/*DiscardedValue=*/false, /*IsConstexpr=*/true,
CCE == Sema::CCEKind::CCEK_TemplateArg);
IsTemplateArgument);
if (Result.isInvalid())
return Result;
@ -6248,9 +6250,6 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From,
QualType PreNarrowingType;
switch (SCS->getNarrowingKind(S.Context, Result.get(), PreNarrowingValue,
PreNarrowingType)) {
case NK_Dependent_Narrowing:
// Implicit conversion to a narrower type, but the expression is
// value-dependent so we can't tell whether it's actually narrowing.
case NK_Variable_Narrowing:
// Implicit conversion to a narrower type, and the value is not a constant
// expression. We'll diagnose this in a moment.
@ -6271,6 +6270,14 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From,
<< PreNarrowingValue.getAsString(S.Context, PreNarrowingType) << T;
break;
case NK_Dependent_Narrowing:
// Implicit conversion to a narrower type, but the expression is
// value-dependent so we can't tell whether it's actually narrowing.
// For matching the parameters of a TTP, the conversion is ill-formed
// if it may narrow.
if (CCE != Sema::CCEK_InjectedTTP)
break;
[[fallthrough]];
case NK_Type_Narrowing:
// FIXME: It would be better to diagnose that the expression is not a
// constant expression.
@ -6343,6 +6350,8 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, QualType T, APValue &Value,
Expr::EvalResult Eval;
Eval.Diag = &Notes;
assert(CCE != Sema::CCEK_InjectedTTP && "unnexpected CCE Kind");
ConstantExprKind Kind;
if (CCE == Sema::CCEK_TemplateArg && T->isRecordType())
Kind = ConstantExprKind::ClassTemplateArgument;

View File

@ -5205,7 +5205,7 @@ bool Sema::CheckTemplateArgument(
SmallVectorImpl<TemplateArgument> &SugaredConverted,
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
CheckTemplateArgumentKind CTAK, bool PartialOrdering,
bool *MatchedPackOnParmToNonPackOnArg) {
bool PartialOrderingTTP, bool *MatchedPackOnParmToNonPackOnArg) {
// Check template type parameters.
if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(Param))
return CheckTemplateTypeArgument(TTP, Arg, SugaredConverted,
@ -5260,8 +5260,9 @@ bool Sema::CheckTemplateArgument(
Expr *E = Arg.getArgument().getAsExpr();
TemplateArgument SugaredResult, CanonicalResult;
unsigned CurSFINAEErrors = NumSFINAEErrors;
ExprResult Res = CheckTemplateArgument(NTTP, NTTPType, E, SugaredResult,
CanonicalResult, CTAK);
ExprResult Res =
CheckTemplateArgument(NTTP, NTTPType, E, SugaredResult,
CanonicalResult, PartialOrderingTTP, CTAK);
if (Res.isInvalid())
return true;
// If the current template argument causes an error, give up now.
@ -5326,7 +5327,8 @@ bool Sema::CheckTemplateArgument(
TemplateArgument SugaredResult, CanonicalResult;
E = CheckTemplateArgument(NTTP, NTTPType, E.get(), SugaredResult,
CanonicalResult, CTAK_Specified);
CanonicalResult, /*PartialOrderingTTP=*/false,
CTAK_Specified);
if (E.isInvalid())
return true;
@ -5585,11 +5587,11 @@ bool Sema::CheckTemplateArgumentList(
getExpandedPackSize(*Param))
Arg = Arg.getPackExpansionPattern();
TemplateArgumentLoc NewArgLoc(Arg, ArgLoc.getLocInfo());
if (CheckTemplateArgument(*Param, NewArgLoc, Template, TemplateLoc,
RAngleLoc, SugaredArgumentPack.size(),
SugaredConverted, CanonicalConverted,
CTAK_Specified, /*PartialOrdering=*/false,
MatchedPackOnParmToNonPackOnArg))
if (CheckTemplateArgument(
*Param, NewArgLoc, Template, TemplateLoc, RAngleLoc,
SugaredArgumentPack.size(), SugaredConverted,
CanonicalConverted, CTAK_Specified, /*PartialOrdering=*/false,
/*PartialOrderingTTP=*/true, MatchedPackOnParmToNonPackOnArg))
return true;
Arg = NewArgLoc.getArgument();
CanonicalConverted.back().setIsDefaulted(
@ -5601,11 +5603,11 @@ bool Sema::CheckTemplateArgumentList(
TemplateArgumentLoc(TemplateArgument::CreatePackCopy(Context, Args),
ArgLoc.getLocInfo());
} else {
if (CheckTemplateArgument(*Param, ArgLoc, Template, TemplateLoc,
RAngleLoc, SugaredArgumentPack.size(),
SugaredConverted, CanonicalConverted,
CTAK_Specified, /*PartialOrdering=*/false,
MatchedPackOnParmToNonPackOnArg))
if (CheckTemplateArgument(
*Param, ArgLoc, Template, TemplateLoc, RAngleLoc,
SugaredArgumentPack.size(), SugaredConverted,
CanonicalConverted, CTAK_Specified, /*PartialOrdering=*/false,
PartialOrderingTTP, MatchedPackOnParmToNonPackOnArg))
return true;
CanonicalConverted.back().setIsDefaulted(
clang::isSubstitutedDefaultArgument(Context, ArgLoc.getArgument(),
@ -5753,6 +5755,7 @@ bool Sema::CheckTemplateArgumentList(
if (CheckTemplateArgument(*Param, Arg, Template, TemplateLoc, RAngleLoc, 0,
SugaredConverted, CanonicalConverted,
CTAK_Specified, /*PartialOrdering=*/false,
/*PartialOrderingTTP=*/false,
/*MatchedPackOnParmToNonPackOnArg=*/nullptr))
return true;
@ -6740,6 +6743,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
QualType ParamType, Expr *Arg,
TemplateArgument &SugaredConverted,
TemplateArgument &CanonicalConverted,
bool PartialOrderingTTP,
CheckTemplateArgumentKind CTAK) {
SourceLocation StartLoc = Arg->getBeginLoc();
@ -6930,17 +6934,21 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
IsConvertedConstantExpression = false;
}
if (getLangOpts().CPlusPlus17) {
if (getLangOpts().CPlusPlus17 || PartialOrderingTTP) {
// C++17 [temp.arg.nontype]p1:
// A template-argument for a non-type template parameter shall be
// a converted constant expression of the type of the template-parameter.
APValue Value;
ExprResult ArgResult;
if (IsConvertedConstantExpression) {
ArgResult = BuildConvertedConstantExpression(Arg, ParamType,
CCEK_TemplateArg, Param);
if (ArgResult.isInvalid())
ArgResult = BuildConvertedConstantExpression(
Arg, ParamType,
PartialOrderingTTP ? CCEK_InjectedTTP : CCEK_TemplateArg, Param);
assert(!ArgResult.isUnset());
if (ArgResult.isInvalid()) {
NoteTemplateParameterLocation(*Param);
return ExprError();
}
} else {
ArgResult = Arg;
}

View File

@ -2979,7 +2979,8 @@ static bool ConvertDeducedTemplateArgument(
? (Arg.wasDeducedFromArrayBound() ? Sema::CTAK_DeducedFromArrayBound
: Sema::CTAK_Deduced)
: Sema::CTAK_Specified,
PartialOrdering, &MatchedPackOnParmToNonPackOnArg);
PartialOrdering, /*PartialOrderingTTP=*/false,
&MatchedPackOnParmToNonPackOnArg);
if (MatchedPackOnParmToNonPackOnArg)
Info.setMatchedPackOnParmToNonPackOnArg();
return Res;
@ -3179,6 +3180,7 @@ static TemplateDeductionResult ConvertDeducedTemplateArguments(
Param, DefArg, TD, TD->getLocation(), TD->getSourceRange().getEnd(),
/*ArgumentPackIndex=*/0, SugaredBuilder, CanonicalBuilder,
Sema::CTAK_Specified, /*PartialOrdering=*/false,
/*PartialOrderingTTP=*/false,
/*MatchedPackOnParmToNonPackOnArg=*/nullptr)) {
Info.Param = makeTemplateParameter(
const_cast<NamedDecl *>(TemplateParams->getParam(I)));

View File

@ -2373,9 +2373,10 @@ TemplateInstantiator::TransformSubstNonTypeTemplateParmExpr(
// The call to CheckTemplateArgument here produces the ImpCast.
TemplateArgument SugaredConverted, CanonicalConverted;
if (SemaRef
.CheckTemplateArgument(E->getParameter(), SubstType,
SubstReplacement.get(), SugaredConverted,
CanonicalConverted, Sema::CTAK_Specified)
.CheckTemplateArgument(
E->getParameter(), SubstType, SubstReplacement.get(),
SugaredConverted, CanonicalConverted,
/*PartialOrderingTTP=*/false, Sema::CTAK_Specified)
.isInvalid())
return true;
return transformNonTypeTemplateParmRef(E->getAssociatedDecl(),

View File

@ -521,12 +521,12 @@ namespace example1 {
namespace A {
int i;
}
namespace A1 {
using A::i;
using A::i;
}
void f()
{
using A::i;
@ -1371,7 +1371,7 @@ namespace cwg92 { // cwg92: 4 c++17
// considered in this context. In C++17, we *do* perform an implicit
// conversion (which performs initialization), and the exception specification
// is part of the type of the parameter, so this is invalid.
template<void() throw()> struct X {};
template<void() throw()> struct X {}; // since-cxx17-note {{template parameter is declared here}}
X<&f> xp;
// since-cxx17-error@-1 {{value of type 'void (*)() throw(int, float)' is not implicitly convertible to 'void (*)() throw()'}}

View File

@ -155,6 +155,8 @@ namespace cwg1295 { // cwg1295: 4
// cxx98-14-error@-1 {{non-type template argument does not refer to any declaration}}
// cxx98-14-note@#cwg1295-Y {{template parameter is declared here}}
// since-cxx17-error@#cwg1295-y {{reference cannot bind to bit-field in converted constant expression}}
// since-cxx17-note@#cwg1295-Y {{template parameter is declared here}}
#if __cplusplus >= 201103L
const unsigned other = 0;

View File

@ -444,7 +444,7 @@ namespace cwg329 { // cwg329: 3.5
// expected-note@#cwg329-b {{in instantiation of template class 'cwg329::A<char>' requested here}}
// expected-note@#cwg329-i {{previous definition is here}}
};
A<int> a;
A<int> a;
A<char> b; // #cwg329-b
void test() {
@ -688,9 +688,9 @@ namespace cwg341 { // cwg341: sup 1708
namespace B {
extern "C" int &cwg341_a = cwg341_a;
// expected-error@-1 {{redefinition of 'cwg341_a'}}
// expected-note@#cwg341_a {{previous definition is here}}
// expected-note@#cwg341_a {{previous definition is here}}
}
extern "C" void cwg341_b(); // #cwg341_b
extern "C" void cwg341_b(); // #cwg341_b
}
int cwg341_a;
// expected-error@-1 {{declaration of 'cwg341_a' in global scope conflicts with declaration with C language linkage}}
@ -708,7 +708,7 @@ namespace cwg341 {
// expected-error@-1 {{declaration of 'cwg341_d' with C language linkage conflicts with declaration in global scope}}
// expected-note@#cwg341_d {{declared in global scope here}}
namespace A { extern "C" int cwg341_e; } // #cwg341_e
namespace A { extern "C" int cwg341_e; } // #cwg341_e
namespace B { extern "C" void cwg341_e(); }
// expected-error@-1 {{redefinition of 'cwg341_e' as different kind of symbol}}
// expected-note@#cwg341_e {{previous definition is here}}
@ -960,6 +960,7 @@ namespace cwg354 { // cwg354: 3.1 c++11
// cxx11-14-error@#cwg354-p0 {{null non-type template argument must be cast to template parameter type 'int *'}}
// cxx11-14-note@#cwg354-ptr {{template parameter is declared here}}
// since-cxx17-error@#cwg354-p0 {{conversion from 'int' to 'int *' is not allowed in a converted constant expression}}
// since-cxx17-note@#cwg354-ptr {{template parameter is declared here}}
ptr<(int*)0> p1;
// cxx98-error@-1 {{non-type template argument does not refer to any declaration}}
// cxx98-note@#cwg354-ptr {{template parameter is declared here}}
@ -969,12 +970,14 @@ namespace cwg354 { // cwg354: 3.1 c++11
// cxx11-14-error@#cwg354-p2 {{null non-type template argument of type 'float *' does not match template parameter of type 'int *'}}
// cxx11-14-note@#cwg354-ptr {{template parameter is declared here}}
// since-cxx17-error@#cwg354-p2 {{value of type 'float *' is not implicitly convertible to 'int *'}}
// since-cxx17-note@#cwg354-ptr {{template parameter is declared here}}
ptr<(int S::*)0> p3; // #cwg354-p3
// cxx98-error@#cwg354-p3 {{non-type template argument does not refer to any declaration}}
// cxx98-note@#cwg354-ptr {{template parameter is declared here}}
// cxx11-14-error@#cwg354-p3 {{null non-type template argument of type 'int S::*' does not match template parameter of type 'int *'}}
// cxx11-14-note@#cwg354-ptr {{template parameter is declared here}}
// since-cxx17-error@#cwg354-p3 {{value of type 'int S::*' is not implicitly convertible to 'int *'}}
// since-cxx17-note@#cwg354-ptr {{template parameter is declared here}}
template<int*> int both(); // #cwg354-both-int-ptr
template<int> int both(); // #cwg354-both-int
@ -991,6 +994,7 @@ namespace cwg354 { // cwg354: 3.1 c++11
// cxx11-14-error@#cwg354-m0 {{null non-type template argument must be cast to template parameter type 'int S::*'}}
// cxx11-14-note@#cwg354-ptr_mem {{template parameter is declared here}}
// since-cxx17-error@#cwg354-m0 {{conversion from 'int' to 'int S::*' is not allowed in a converted constant expression}}
// since-cxx17-note@#cwg354-ptr_mem {{template parameter is declared here}}
ptr_mem<(int S::*)0> m1;
// cxx98-error@-1 {{non-type template argument is not a pointer to member constant}}
ptr_mem<(float S::*)0> m2; // #cwg354-m2
@ -999,12 +1003,14 @@ namespace cwg354 { // cwg354: 3.1 c++11
// cxx11-14-error@#cwg354-m2 {{null non-type template argument of type 'float S::*' does not match template parameter of type 'int S::*'}}
// cxx11-14-note@#cwg354-ptr_mem {{template parameter is declared here}}
// since-cxx17-error@#cwg354-m2 {{value of type 'float S::*' is not implicitly convertible to 'int S::*'}}
// since-cxx17-note@#cwg354-ptr_mem {{template parameter is declared here}}
ptr_mem<(int *)0> m3; // #cwg354-m3
// cxx98-error@#cwg354-m3 {{non-type template argument of type 'int *' cannot be converted to a value of type 'int S::*'}}
// cxx98-note@#cwg354-ptr_mem {{template parameter is declared here}}
// cxx11-14-error@#cwg354-m3 {{null non-type template argument of type 'int *' does not match template parameter of type 'int S::*'}}
// cxx11-14-note@#cwg354-ptr_mem {{template parameter is declared here}}
// since-cxx17-error@#cwg354-m3 {{value of type 'int *' is not implicitly convertible to 'int S::*'}}
// since-cxx17-note@#cwg354-ptr_mem {{template parameter is declared here}}
} // namespace cwg354
struct cwg355_S; // cwg355: 2.7
@ -1116,7 +1122,7 @@ namespace cwg364 { // cwg364: 2.7
} // namespace cwg364
namespace cwg366 { // cwg366: 2.7
#if "foo" // expected-error {{invalid token at start of a preprocessor expression}}
#if "foo" // expected-error {{invalid token at start of a preprocessor expression}}
#endif
} // namespace cwg366

View File

@ -1,5 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
// RUN: %clang_cc1 -fsyntax-only -std=c++1z -verify %s
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify=expected,cxx11 %s
// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify=expected,cxx17 %s
// A converted constant expression of type T is a core constant expression,
int nonconst = 8; // expected-note 3 {{here}}
@ -66,7 +66,8 @@ enum class EEE : unsigned short {
e = 123456, // expected-error {{enumerator value evaluates to 123456, which cannot be narrowed to type 'unsigned short'}}
f = -3 // expected-error {{enumerator value evaluates to -3, which cannot be narrowed to type 'unsigned short'}}
};
template<unsigned char> using A = int;
template<unsigned char> using A = int; // cxx17-note 2{{template parameter is declared here}}
using Int = A<E6>;
using Int = A<EE::EE32>; // expected-error {{not implicitly convertible}}
using Int = A<(int)EE::EE32>;
@ -78,6 +79,7 @@ using Int = A<-3>; // expected-error {{template argument evaluates to -3, which
// integral conversions as well as boolean conversions.
// FIXME: Per core issue 1407, this is not correct.
template<typename T, T v> struct Val { static constexpr T value = v; };
// cxx17-note@-1 2{{template parameter is declared here}}
static_assert(Val<bool, E1>::value == 1, ""); // ok
static_assert(Val<bool, '\0'>::value == 0, ""); // ok
static_assert(Val<bool, U'\1'>::value == 1, ""); // ok

View File

@ -8,7 +8,7 @@
// be one of:
// -- an integral constant expression; or
// -- the name of a non-type template-parameter ; or
#ifndef CPP11ONLY
#ifndef CPP11ONLY
namespace non_type_tmpl_param {
template <int N> struct X0 { X0(); };
@ -31,15 +31,18 @@ namespace non_type_tmpl_param {
// omitted if the name refers to a function or array and shall be omitted
// if the corresopnding template-parameter is a reference; or
namespace addr_of_obj_or_func {
template <int* p> struct X0 { }; // precxx17-note 5{{here}}
template <int* p> struct X0 { }; // expected-note 5{{here}}
#if __cplusplus >= 201103L
// precxx17-note@-2 2{{template parameter is declared here}}
#endif
template <int (*fp)(int)> struct X1 { };
template <int &p> struct X2 { }; // precxx17-note 4{{here}}
template <const int &p> struct X2k { }; // precxx17-note {{here}}
template <int (&fp)(int)> struct X3 { }; // precxx17-note 4{{here}}
template <int (*fp)(int)> struct X1 { }; // cxx17-note {{here}}
#if __cplusplus <= 199711L
// precxx17-note@-2 {{here}}
#endif
template <int &p> struct X2 { }; // expected-note 4{{here}}
template <const int &p> struct X2k { }; // expected-note {{here}}
template <int (&fp)(int)> struct X3 { }; // expected-note 4{{here}}
int i = 42;
#if __cplusplus >= 201103L

View File

@ -18,8 +18,9 @@ eval<D<int, 17>> eD; // expected-error{{implicit instantiation of undefined temp
eval<E<int, float>> eE; // expected-error{{implicit instantiation of undefined template 'eval<E<int, float>>}}
template<
template <int ...N> // expected-error{{deduced non-type template argument does not have the same type as the corresponding template parameter ('int' vs 'void *')}}
class TT // expected-note {{previous template template parameter is here}}
template <int ...N> // expected-error {{cannot be narrowed from type 'int' to 'short'}}
// expected-error@-1 {{conversion from 'int' to 'void *' is not allowed in a converted constant expression}}
class TT // expected-note 2{{previous template template parameter is here}}
> struct X0 { };
template<int I, int J, int ...Rest> struct X0a;
@ -31,12 +32,13 @@ template<int I, void *J> struct X0e; // expected-note{{template parameter is dec
X0<X0a> inst_x0a;
X0<X0b> inst_x0b;
X0<X0c> inst_x0c;
X0<X0d> inst_x0d;
X0<X0d> inst_x0d; // expected-note {{has different template parameters}}
X0<X0e> inst_x0e; // expected-note{{template template argument has different template parameters than its corresponding template template parameter}}
template<typename T,
template <T ...N> // expected-error{{deduced non-type template argument does not have the same type as the corresponding template parameter ('short' vs 'void *')}}
class TT // expected-note {{previous template template parameter is here}}
template <T ...N> // expected-error {{conversion from 'short' to 'void *' is not allowed in a converted constant expression}}
// expected-error@-1 {{cannot be narrowed from type 'int' to 'short'}}
class TT // expected-note 2{{previous template template parameter is here}}
> struct X1 { };
template<int I, int J, int ...Rest> struct X1a;
@ -49,8 +51,8 @@ X1<int, X1a> inst_x1a;
X1<long, X1b> inst_x1b;
X1<short, X1c> inst_x1c;
X1<short, X1d> inst_sx1d;
X1<int, X1d> inst_ix1d;
X1<short, X1e> inst_x1e; // expected-note{{template template argument has different template parameters than its corresponding template template parameter}}
X1<int, X1d> inst_ix1d; // expected-note {{has different template parameters}}
X1<short, X1e> inst_x1e; // expected-note {{has different template parameters}}
template <int> class X2; // expected-note{{template is declared here}} \
// expected-note{{template is declared here}}

View File

@ -40,7 +40,10 @@ void g() {
template_param_kinds_1<0>(); // ok, from cxx-templates-a.h
template_param_kinds_1<int>(); // ok, from cxx-templates-b.h
template_param_kinds_2<Tmpl_T_C>(); // ok, from cxx-templates-b.h
template_param_kinds_2<Tmpl_T_C>(); // expected-error {{no matching function for call}}
// expected-note@Inputs/cxx-templates-a.h:11 {{candidate}}
// expected-note@Inputs/cxx-templates-b.h:11 {{candidate}}
template_param_kinds_2<Tmpl_T_I_I>(); // expected-error {{ambiguous}}
// expected-note@Inputs/cxx-templates-a.h:11 {{candidate}}

View File

@ -77,12 +77,11 @@ void func0(int *);
void (*fnptr0)(int *);
void (*fnptr1)(__attribute__((noescape)) int *);
template<void (*fn)(int*)> struct S4 {};
template<void (*fn)(int* __attribute__((noescape)))> struct S5 {};
#if __cplusplus < 201406
// expected-note@-4 {{template parameter is declared here}}
// expected-note@-4 {{template parameter is declared here}}
// expected-note@-2 {{template parameter is declared here}}
#endif
template<void (*fn)(int* __attribute__((noescape)))> struct S5 {};
// expected-note@-1 {{template parameter is declared here}}
void test0() {
fnptr0 = &func0;

View File

@ -362,15 +362,17 @@ namespace classes {
namespace packs {
namespace t1 {
// FIXME: This should be rejected
template<template<int, int...> class> struct A {};
// old-note@-1 {{previous non-type template parameter with type 'int' is here}}
// new-error@-1 {{non-type parameter of template template parameter cannot be narrowed from type 'int' to 'char'}}
// new-note@-2 {{previous template template parameter is here}}
// old-note@-3 {{previous non-type template parameter with type 'int' is here}}
template<char> struct B;
// old-note@-1 {{template non-type parameter has a different type 'char' in template argument}}
template struct A<B>;
// old-error@-1 {{has different template parameters}}
// new-note@-1 {{has different template parameters}}
// old-error@-2 {{has different template parameters}}
} // namespace t1
namespace t2 {
template<template<char, int...> class> struct A {};
@ -383,15 +385,17 @@ namespace packs {
// old-error@-1 {{has different template parameters}}
} // namespace t2
namespace t3 {
// FIXME: This should be rejected
template<template<int...> class> struct A {};
// old-note@-1 {{previous non-type template parameter with type 'int' is here}}
// new-error@-1 {{non-type parameter of template template parameter cannot be narrowed from type 'int' to 'char'}}
// new-note@-2 {{previous template template parameter is here}}
// old-note@-3 {{previous non-type template parameter with type 'int' is here}}
template<char> struct B;
// old-note@-1 {{template non-type parameter has a different type 'char' in template argument}}
template struct A<B>;
// old-error@-1 {{has different template parameters}}
// new-note@-1 {{has different template parameters}}
// old-error@-2 {{has different template parameters}}
} // namespace t3
namespace t4 {
template<template<char...> class> struct A {};
@ -427,6 +431,14 @@ namespace fun_tmpl_call {
template <class...> struct A {};
void test() { f(A<int>()); }
} // namespace order_func_pack
namespace match_enum {
enum A {};
template<template<A> class TT1> void f(TT1<{}>) {}
// old-note@-1 {{invalid explicitly-specified argument}}
template<int> struct B {};
template void f<B>(B<{}>);
// old-error@-1 {{does not refer to a function template}}
} // namespace match_enum
namespace match_method {
struct A {
template <template <class> class TT> void f(TT<int>) {};
@ -435,7 +447,7 @@ namespace fun_tmpl_call {
template <class...> struct B {};
void test() { A().f(B<int>()); }
// old-error@-1 {{no matching member function for call to 'f'}}
} // namespace t2
} // namespace match_method
namespace order_method_nonpack {
struct A {
template <template <class> class TT> void f(TT<int>) {}
@ -648,3 +660,72 @@ namespace nttp_auto {
// new-note@-1 {{different template parameters}}
} // namespace t3
} // namespace nttp_auto
namespace nttp_partial_order {
namespace t1 {
// FIXME: This should pick the second overload.
template<template<short> class TT1> void f(TT1<0>);
// new-note@-1 {{here}}
template<template<int> class TT2> void f(TT2<0>) {}
// new-note@-1 {{here}}
template<int> struct B {};
template void f<B>(B<0>);
// new-error@-1 {{ambiguous}}
} // namespace t1
namespace t2 {
// FIXME: This should pick the second overload.
struct A {} a;
template<template<A&> class TT1> void f(TT1<a>);
// new-note@-1 {{here}}
template<template<const A&> class TT2> void f(TT2<a>) {}
// new-note@-1 {{here}}
template<const A&> struct B {};
template void f<B>(B<a>);
// new-error@-1 {{ambiguous}}
} // namespace t2
namespace t3 {
// FIXME: This should pick the second overload.
enum A {};
template<template<A> class TT1> void f(TT1<{}>);
// new-note@-1 {{here}}
template<template<int> class TT2> void f(TT2<{}>) {}
// new-note@-1 {{here}}
template<int> struct B {};
template void f<B>(B<{}>);
// new-error@-1 {{ambiguous}}
} // namespace t3
namespace t4 {
// FIXME: This should pick the second overload.
struct A {} a;
template<template<A*> class TT1> void f(TT1<&a>);
// new-note@-1 {{here}}
template<template<const A*> class TT2> void f(TT2<&a>) {}
// new-note@-1 {{here}}
template<const A*> struct B {};
template void f<B>(B<&a>);
// new-error@-1 {{ambiguous}}
} // namespace t4
namespace t5 {
// FIXME: This should pick the second overload.
struct A { int m; };
template<template<int A::*> class TT1> void f(TT1<&A::m>);
// new-note@-1 {{here}}
template<template<const int A::*> class TT2> void f(TT2<&A::m>) {}
// new-note@-1 {{here}}
template<const int A::*> struct B {};
template void f<B>(B<&A::m>);
// new-error@-1 {{ambiguous}}
} // namespace t5
namespace t6 {
// FIXME: This should pick the second overload.
struct A {};
using nullptr_t = decltype(nullptr);
template<template<nullptr_t> class TT2> void f(TT2<nullptr>);
// new-note@-1 {{here}}
template<template<A*> class TT1> void f(TT1<nullptr>) {}
// new-note@-1 {{here}}
template<A*> struct B {};
template void f<B>(B<nullptr>);
// new-error@-1 {{ambiguous}}
} // namespace t6
} // namespace nttp_partial_order

View File

@ -52,20 +52,20 @@ struct X2 {
template<typename U = typename X1<T>::type> // expected-error{{no type named 'type' in 'X1<int>'}} \
// expected-error{{no type named 'type' in 'X1<char>'}}
struct Inner1 { }; // expected-note{{template is declared here}}
template<T Value = X1<T>::value> // expected-error{{no member named 'value' in 'X1<int>'}} \
// expected-error{{no member named 'value' in 'X1<char>'}}
struct NonType1 { }; // expected-note{{template is declared here}}
template<T Value>
struct Inner2 { };
template<typename U>
struct Inner3 {
template<typename X = T, typename V = U>
struct VeryInner { };
template<T Value1 = sizeof(T), T Value2 = sizeof(U),
template<T Value1 = sizeof(T), T Value2 = sizeof(U),
T Value3 = Value1 + Value2>
struct NonType2 { };
};
@ -89,11 +89,11 @@ struct is_same { static const bool value = false; };
template<typename T>
struct is_same<T, T> { static const bool value = true; };
int array1[is_same<__typeof__(vi),
int array1[is_same<__typeof__(vi),
X2<int>::Inner3<float>::VeryInner<int, float> >::value? 1 : -1];
int array2[is_same<__typeof(x2_deep_nontype),
X2<char>::Inner3<int>::NonType2<sizeof(char), sizeof(int),
X2<char>::Inner3<int>::NonType2<sizeof(char), sizeof(int),
sizeof(char)+sizeof(int)> >::value? 1 : -1];
// Template template parameter defaults
@ -109,18 +109,20 @@ struct add_pointer {
template<typename T, template<typename> class X = T::template apply>
struct X4;
int array4[is_same<X4<add_pointer>,
int array4[is_same<X4<add_pointer>,
X4<add_pointer, add_pointer::apply> >::value? 1 : -1];
template<int> struct X5 {};
template<long> struct X5b {};
template<typename T,
template<T> class B = X5>
template<long long> struct X5b {};
template<typename T,
template<T> class B = X5> // expected-error {{cannot be narrowed from type 'long long' to 'int'}}
// expected-note@-1 {{has different template parameters}}
// expected-note@-2 {{previous template template parameter is here}}
struct X6 {};
X6<int> x6a;
X6<long> x6b;
X6<long, X5b> x6c;
X6<long long> x6b; // expected-note {{while checking a default template argument used here}}
X6<long long, X5b> x6c;
template<template<class> class X = B<int> > struct X7; // expected-error{{must be a class template}}

View File

@ -24,11 +24,13 @@ template<int> struct B;
template<typename T,
template<T Value> class X> // expected-error{{cannot have type 'float'}}
// expected-error@-1 {{cannot be narrowed from type 'long long' to 'int'}}
// expected-note@-2 {{previous template template parameter is here}}
struct X0 { };
X0<int, B> x0b1;
X0<float, B> x0b2; // expected-note{{while substituting}}
X0<long, B> x0b3;
X0<long long, B> x0b3; // expected-note {{has different template parameters}}
template<template<int V> class TT>
struct X1 { };

View File

@ -2,8 +2,10 @@
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17,precxx20 -std=c++17 %s
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx20 %std_cxx20- %s
template<typename T, T Value> struct Constant; // precxx17-note{{template parameter is declared here}} \
// FIXME: bad location precxx20-error{{a non-type template parameter cannot have type 'float'}}
template<typename T, T Value> struct Constant;
// FIXME: bad location precxx20-error@-1 {{a non-type template parameter cannot have type 'float'}}
// expected-note@-2 {{template parameter is declared here}}
// cxx20-note@-3 {{template parameter is declared here}}
Constant<int, 5> *c1;

View File

@ -112,7 +112,9 @@ template struct X1<int>::B<bool>;
// Template template parameters
template<typename T>
struct X2 {
template<template<class U, T Value> class> // expected-error{{cannot have type 'float'}}
template<template<class U, T Value> class> // expected-error {{cannot have type 'float'}}
// expected-error@-1 {{cannot be narrowed from type 'long long' to 'int'}}
// expected-note@-2 {{previous template template parameter is here}}
struct Inner { };
};
@ -121,7 +123,7 @@ template<typename T, int Value>
X2<int>::Inner<X2_arg> x2i1;
X2<float> x2a; // expected-note{{instantiation}}
X2<long>::Inner<X2_arg> x2i3;
X2<long long>::Inner<X2_arg> x2i3; // expected-note {{has different template parameters}}
namespace PR10896 {
template<typename TN>

View File

@ -1,6 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s
template<typename T, T val> struct A {};
template<typename T, T val> struct A {}; // expected-note 3{{template parameter is declared here}}
template<typename T, typename U> constexpr bool is_same = false;
template<typename T> constexpr bool is_same<T, T> = true;
@ -122,13 +122,13 @@ namespace DeduceDifferentType {
int a_imp = a(A<3>()); // expected-error {{no matching function}}
int a_exp = a<3>(A<3>());
template<decltype(nullptr)> struct B {};
template<decltype(nullptr)> struct B {}; // expected-note {{template parameter is declared here}}
template<int *P> int b(B<P>); // expected-error {{value of type 'int *' is not implicitly convertible to 'decltype(nullptr)'}}
int b_imp = b(B<nullptr>()); // expected-error {{no matching function}}
int b_exp = b<nullptr>(B<nullptr>()); // expected-error {{no matching function}}
struct X { constexpr operator int() { return 0; } } x;
template<X &> struct C {};
template<X &> struct C {}; // expected-note {{template parameter is declared here}}
template<int N> int c(C<N>); // expected-error {{value of type 'int' is not implicitly convertible to 'X &'}}
int c_imp = c(C<x>()); // expected-error {{no matching function}}
int c_exp = c<x>(C<x>()); // expected-error {{no matching function}}
@ -448,7 +448,7 @@ namespace PR42108 {
struct R {};
struct S { constexpr S() {} constexpr S(R) {} };
struct T { constexpr operator S() { return {}; } };
template <const S &> struct A {};
template <const S &> struct A {}; // expected-note {{template parameter is declared here}}
void f() {
A<R{}>(); // expected-error {{would bind reference to a temporary}}
A<S{}>(); // expected-error {{reference to temporary object}}

View File

@ -101,7 +101,7 @@ namespace ConvertedConstant {
struct {
int i : 2;
} b;
template <const int&> struct Y {};
template <const int&> struct Y {}; // expected-note {{template parameter is declared here}}
void f(Y<b.i>) {} // expected-error {{reference cannot bind to bit-field in converted constant expression}}
}
@ -238,7 +238,7 @@ namespace UnnamedBitfield {
}
namespace Temporary {
template<const int &> struct A {};
template<const int &> struct A {}; // expected-note {{template parameter is declared here}}
A<0> a0; // expected-error {{conversion from 'int' to 'const int &' in converted constant expression would bind reference to a temporary}}
A<(const int&)1> a1; // expected-error {{reference to temporary object is not allowed in a template argument}}

View File

@ -7,14 +7,13 @@ template<template<typename T> class X> struct A; // #A
template<template<typename T, int I> class X> struct B; // expected-note{{previous template template parameter is here}}
template<template<int I> class X> struct C;
// precxx17-error@-1 {{deduced non-type template argument does not have the same type as the corresponding template parameter ('int' vs 'const int &')}}
// cxx17-error@-2 {{conversion from 'int' to 'const int &' in converted constant expression would bind reference to a temporary}}
// expected-note@-3 {{previous template template parameter is here}}
// expected-error@-1 {{conversion from 'int' to 'const int &' in converted constant expression would bind reference to a temporary}}
// expected-note@-2 {{previous template template parameter is here}}
template<class> struct X; // expected-note {{template is declared here}}
template<int N> struct Y; // expected-note {{template parameter is declared here}}
template<long N> struct Ylong;
template<const int &N> struct Yref; // precxx17-note {{template parameter is declared here}}
template<const int &N> struct Yref; // expected-note {{template parameter is declared here}}
namespace N {
template<class> struct Z;

View File

@ -66,8 +66,9 @@ 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>; // FIXME: should this be OK?
using err2a = tT0<long long, i>; // FIXME: should this be OK (if long long is larger than int)?
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}}