[clang] [Clang] Implement P2308R1 - Template Parameter Initialization. (PR #73103)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 29 09:07:53 PST 2023
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/73103
>From 4709819fb2e7f45f9429f1a7fc79923abf0f9691 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 22 Nov 2023 11:43:07 +0100
Subject: [PATCH 1/3] [Clang] Implement P2308R1 - Template Parameter
Initialization.
https://wiki.edg.com/pub/Wg21kona2023/StrawPolls/p2308r1.html
This implements P2308R1 as a DR and resolves CWG2459, CWG2450
and CWG2049.
---
clang/docs/ReleaseNotes.rst | 3 +
clang/include/clang/Sema/Sema.h | 4 +
clang/lib/Parse/ParseTemplate.cpp | 11 ++-
clang/lib/Sema/SemaOverload.cpp | 9 ++
clang/lib/Sema/SemaTemplate.cpp | 96 ++++++++++++-------
clang/test/CXX/drs/dr20xx.cpp | 8 ++
clang/test/CXX/drs/dr24xx.cpp | 32 +++++++
.../SemaTemplate/temp_arg_nontype_cxx20.cpp | 6 +-
.../SemaTemplate/temp_arg_nontype_cxx2c.cpp | 74 ++++++++++++++
clang/www/cxx_dr_status.html | 6 +-
clang/www/cxx_status.html | 2 +-
11 files changed, 208 insertions(+), 43 deletions(-)
create mode 100644 clang/test/CXX/drs/dr24xx.cpp
create mode 100644 clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e4beeee0a2cb5e0..70b6a24e6054814 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -182,6 +182,9 @@ C++2c Feature Support
This is applied to both C++ standard attributes, and other attributes supported by Clang.
This completes the implementation of `P2361R6 Unevaluated Strings <https://wg21.link/P2361R6>`_
+- Implemented `P2361R6 Template parameter initialization <https://wg21.link/P2308R1>`_.
+ This change is applied as a DR in all language modes.
+
Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index e75a8bdb1fc72ff..937fcb3ce6079d7 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3932,6 +3932,10 @@ class Sema final {
APValue &Value, CCEKind CCE,
NamedDecl *Dest = nullptr);
+ ExprResult EvaluateConvertedConstantExpression(Expr *E, QualType T,
+ APValue &Value, CCEKind CCE,
+ bool RequireInt);
+
/// Abstract base class used to perform a contextual implicit
/// conversion from an expression to any type passing a filter.
class ContextualImplicitConverter {
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index f556d0e6d4f8b6e..64fe4d50bba27bf 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -1062,8 +1062,7 @@ Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) {
++CurTemplateDepthTracker;
EnterExpressionEvaluationContext ConstantEvaluated(
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
- DefaultArg =
- Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression());
+ DefaultArg = Actions.CorrectDelayedTyposInExpr(ParseInitializer());
if (DefaultArg.isInvalid())
SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch);
}
@@ -1582,6 +1581,8 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
/// constant-expression
/// type-id
/// id-expression
+/// braced-init-list [C++26, DR]
+///
ParsedTemplateArgument Parser::ParseTemplateArgument() {
// C++ [temp.arg]p2:
// In a template-argument, an ambiguity between a type-id and an
@@ -1619,8 +1620,12 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() {
}
// Parse a non-type template argument.
+ ExprResult ExprArg;
SourceLocation Loc = Tok.getLocation();
- ExprResult ExprArg = ParseConstantExpressionInExprEvalContext(MaybeTypeCast);
+ if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace))
+ ExprArg = ParseBraceInitializer();
+ else
+ ExprArg = ParseConstantExpressionInExprEvalContext(MaybeTypeCast);
if (ExprArg.isInvalid() || !ExprArg.get()) {
return ParsedTemplateArgument();
}
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 3a3e9234469d393..e2ca2a0cfb89433 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6226,6 +6226,15 @@ ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T,
return R;
}
+ExprResult Sema::EvaluateConvertedConstantExpression(Expr *E, QualType T,
+ APValue &Value,
+ Sema::CCEKind CCE,
+ bool RequireInt) {
+
+ APValue PreNarrowingValue;
+ return ::EvaluateConvertedConstantExpression(*this, E, T, Value, CCE,
+ RequireInt, PreNarrowingValue);
+}
/// dropPointerConversions - If the given standard conversion sequence
/// involves any pointer conversions, remove them. This may change
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 34d7b8c731e9076..1b13f76ae02c0d1 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -7313,49 +7313,73 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
return E;
}
+ QualType CanonParamType = Context.getCanonicalType(ParamType);
+ // Avoid making a copy when initializing a template parameter of class type
+ // from a template parameter object of the same type. This is going beyond
+ // the standard, but is required for soundness: in
+ // template<A a> struct X { X *p; X<a> *q; };
+ // ... we need p and q to have the same type.
+ //
+ // Similarly, don't inject a call to a copy constructor when initializing
+ // from a template parameter of the same type.
+ Expr *InnerArg = Arg->IgnoreParenImpCasts();
+ if (ParamType->isRecordType() && isa<DeclRefExpr>(InnerArg) &&
+ Context.hasSameUnqualifiedType(ParamType, InnerArg->getType())) {
+ NamedDecl *ND = cast<DeclRefExpr>(InnerArg)->getDecl();
+ if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(ND)) {
+
+ SugaredConverted = TemplateArgument(TPO, ParamType);
+ CanonicalConverted =
+ TemplateArgument(TPO->getCanonicalDecl(), CanonParamType);
+ return Arg;
+ }
+ if (isa<NonTypeTemplateParmDecl>(ND)) {
+ SugaredConverted = TemplateArgument(Arg);
+ CanonicalConverted =
+ Context.getCanonicalTemplateArgument(SugaredConverted);
+ return Arg;
+ }
+ }
+
// The initialization of the parameter from the argument is
// a constant-evaluated context.
EnterExpressionEvaluationContext ConstantEvaluated(
*this, Sema::ExpressionEvaluationContext::ConstantEvaluated);
- if (getLangOpts().CPlusPlus17) {
- QualType CanonParamType = Context.getCanonicalType(ParamType);
-
- // Avoid making a copy when initializing a template parameter of class type
- // from a template parameter object of the same type. This is going beyond
- // the standard, but is required for soundness: in
- // template<A a> struct X { X *p; X<a> *q; };
- // ... we need p and q to have the same type.
- //
- // Similarly, don't inject a call to a copy constructor when initializing
- // from a template parameter of the same type.
- Expr *InnerArg = Arg->IgnoreParenImpCasts();
- if (ParamType->isRecordType() && isa<DeclRefExpr>(InnerArg) &&
- Context.hasSameUnqualifiedType(ParamType, InnerArg->getType())) {
- NamedDecl *ND = cast<DeclRefExpr>(InnerArg)->getDecl();
- if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(ND)) {
-
- SugaredConverted = TemplateArgument(TPO, ParamType);
- CanonicalConverted =
- TemplateArgument(TPO->getCanonicalDecl(), CanonParamType);
- return Arg;
- }
- if (isa<NonTypeTemplateParmDecl>(ND)) {
- SugaredConverted = TemplateArgument(Arg);
- CanonicalConverted =
- Context.getCanonicalTemplateArgument(SugaredConverted);
- return Arg;
- }
- }
+ bool IsConvertedConstantExpression = true;
+ if (isa<InitListExpr>(Arg) || ParamType->isRecordType()) {
+ InitializationKind Kind = InitializationKind::CreateForInit(
+ Arg->getBeginLoc(), /*DirectInit*/ false, Arg);
+ Expr *Inits[1] = {Arg};
+ InitializedEntity Entity =
+ InitializedEntity::InitializeTemplateParameter(ParamType, Param);
+ InitializationSequence InitSeq(*this, Entity, Kind, Inits);
+ ExprResult Result = InitSeq.Perform(*this, Entity, Kind, Inits);
+ if (Result.isInvalid() || !Result.get())
+ return ExprError();
+ Result = ActOnConstantExpression(Result.get());
+ if (Result.isInvalid() || !Result.get())
+ return ExprError();
+ Arg = ActOnFinishFullExpr(Result.get(), Arg->getBeginLoc(), false,
+ /*IsConstexpr=*/true, /*IsTemplateArgument=*/true)
+ .get();
+ IsConvertedConstantExpression = false;
+ }
+ if (getLangOpts().CPlusPlus17) {
// 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 = CheckConvertedConstantExpression(
- Arg, ParamType, Value, CCEK_TemplateArg, Param);
- if (ArgResult.isInvalid())
- return ExprError();
+ ExprResult ArgResult;
+ if (IsConvertedConstantExpression) {
+ ArgResult = BuildConvertedConstantExpression(Arg, ParamType,
+ CCEK_TemplateArg, Param);
+ if (ArgResult.isInvalid())
+ return ExprError();
+ } else {
+ ArgResult = Arg;
+ }
// For a value-dependent argument, CheckConvertedConstantExpression is
// permitted (and expected) to be unable to determine a value.
@@ -7366,6 +7390,12 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
return ArgResult;
}
+ ArgResult = EvaluateConvertedConstantExpression(
+ ArgResult.get(), ParamType, Value, CCEK_TemplateArg, /*RequireInt=*/
+ false);
+ if (ArgResult.isInvalid())
+ return ExprError();
+
// Convert the APValue to a TemplateArgument.
switch (Value.getKind()) {
case APValue::None:
diff --git a/clang/test/CXX/drs/dr20xx.cpp b/clang/test/CXX/drs/dr20xx.cpp
index dd60af14bb6b71d..7b9a5fe0b8076b0 100644
--- a/clang/test/CXX/drs/dr20xx.cpp
+++ b/clang/test/CXX/drs/dr20xx.cpp
@@ -61,6 +61,14 @@ namespace dr2026 { // dr2026: 11
}
}
+namespace dr2049 { // dr2049: 18 drafting
+#if __cplusplus > 202002L
+template <int* x = {}> struct X {};
+X<> a;
+X<nullptr> b;
+#endif
+}
+
namespace dr2061 { // dr2061: yes
#if __cplusplus >= 201103L
namespace A {
diff --git a/clang/test/CXX/drs/dr24xx.cpp b/clang/test/CXX/drs/dr24xx.cpp
new file mode 100644
index 000000000000000..0e6a90e582c19ec
--- /dev/null
+++ b/clang/test/CXX/drs/dr24xx.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors \
+// RUN: -Wno-variadic-macros -Wno-c11-extensions
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// expected-no-diagnostics
+
+namespace dr2450 { // dr2450: 18 drafting
+#if __cplusplus > 202002L
+struct S {int a;};
+template <S s>
+void f(){}
+
+void test() {
+f<{0}>();
+f<{.a= 0}>();
+}
+
+#endif
+}
+
+namespace dr2459 { // dr2459: 18 drafting
+#if __cplusplus > 202002L
+struct A {
+ constexpr A(float) {}
+};
+template<A> struct X {};
+X<1> x;
+#endif
+}
diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
index 9f9aad604d5f119..5e8cba979751d7d 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
@@ -62,7 +62,7 @@ namespace ClassNTTP {
template<A a> constexpr int f() { return a.y; }
static_assert(f<A{1,2}>() == 2);
- template<A a> int id;
+ template<A a> int id; // expected-note {{passing argument to parameter 'a' here}}
constexpr A a = {1, 2};
static_assert(&id<A{1,2}> == &id<a>);
static_assert(&id<A{1,3}> != &id<a>);
@@ -90,8 +90,8 @@ namespace ConvertedConstant {
constexpr A(float) {}
};
template <A> struct X {};
- void f(X<1.0f>) {} // OK, user-defined conversion
- void f(X<2>) {} // expected-error {{conversion from 'int' to 'A' is not allowed in a converted constant expression}}
+ void f(X<1.0f>) {}
+ void g(X<2>) {}
}
namespace CopyCounting {
diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp
new file mode 100644
index 000000000000000..3ca948f6ca3a666
--- /dev/null
+++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -Wconversion -verify %s
+
+struct Test {
+ int a = 0;
+ int b = 42;
+};
+
+template <Test t>
+struct A {
+ static constexpr auto a = t.a;
+ static constexpr auto b = t.b;
+};
+
+template <auto N>
+struct Auto {};
+
+template <typename T, T elem>
+struct Explicit{};
+
+struct L {};
+struct M {};
+
+struct Constructor {
+ Constructor(L) {}; // expected-note {{here}}
+ constexpr Constructor(M){};
+};
+
+template < Test = {} >
+struct DefaultParam1{};
+
+template < Test = {1, 2} >
+struct DefaultParam2{};
+
+template < Test = {. b = 5} >
+struct DefaultParam3{};
+
+void test() {
+ static_assert(A<{}>::a == 0);
+ static_assert(A<{}>::b == 42);
+ static_assert(A<{.a = 3}>::a == 3);
+ static_assert(A<{.b = 4}>::b == 4);
+
+ Auto<{0}> a; // expected-error {{cannot deduce type of initializer list}}
+
+ int notconst = 0; // expected-note {{declared here}}
+ A<{notconst}> _; // expected-error {{non-type template argument is not a constant expression}} \
+ // expected-note {{read of non-const variable 'notconst' is not allowed in a constant expression}}
+
+
+ Explicit<Constructor, {L{}}> err; // expected-error {{non-type template argument is not a constant expression}} \
+ // expected-note {{non-constexpr constructor 'Constructor' cannot be used in a constant expression}}
+ Explicit<Constructor, {M{}}> ok;
+
+
+ DefaultParam1<> d1;
+ DefaultParam2<> d2;
+ DefaultParam3<> d3;
+}
+
+template<auto n> struct B { /* ... */ };
+template<int i> struct C { /* ... */ };
+C<{ 42 }> c1; // expected-warning {{braces around scalar initializer}}
+
+struct J1 {
+ J1 *self=this;
+};
+B<J1{}> j1; // expected-error {{pointer to temporary object is not allowed in a template argument}}
+
+struct J2 {
+ J2 *self=this;
+ constexpr J2() {}
+ constexpr J2(const J2&) {}
+};
+B<J2{}> j2; // expected-error {{pointer to temporary object is not allowed in a template argument}}
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 266891a2e373090..ed1976ce8c3c0ad 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -12101,7 +12101,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/2049.html">2049</a></td>
<td>drafting</td>
<td>List initializer in non-type template default argument</td>
- <td align="center">Not resolved</td>
+ <td class="unreleased" align="center">Clang 18</td>
</tr>
<tr id="2050">
<td><a href="https://cplusplus.github.io/CWG/issues/2050.html">2050</a></td>
@@ -14507,7 +14507,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/2450.html">2450</a></td>
<td>drafting</td>
<td><I>braced-init-list</I> as a <I>template-argument</I></td>
- <td align="center">Not resolved</td>
+ <td class="unreleased" align="center">Clang 18</td>
</tr>
<tr id="2451">
<td><a href="https://cplusplus.github.io/CWG/issues/2451.html">2451</a></td>
@@ -14561,7 +14561,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/2459.html">2459</a></td>
<td>drafting</td>
<td>Template parameter initialization</td>
- <td align="center">Not resolved</td>
+ <td class="unreleased" align="center">Clang 18</td>
</tr>
<tr id="2460">
<td><a href="https://cplusplus.github.io/CWG/issues/2460.html">2460</a></td>
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 621439d0bae9666..78ac341e4507c70 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -151,7 +151,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
<tr>
<td>Template parameter initialization</td>
<td><a href="https://wg21.link/P2308R1">P2308R1</a> (<a href="#dr">DR</a>)</td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 18</td>
</tr>
<tr>
<td>Pack Indexing</td>
>From a2c6a3c38e9c0d38877c99c1637f8a38f7ecaab1 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 29 Nov 2023 17:57:23 +0100
Subject: [PATCH 2/3] Address review comments
---
clang/test/CXX/drs/dr24xx.cpp | 9 ++-------
clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp | 3 ++-
2 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/clang/test/CXX/drs/dr24xx.cpp b/clang/test/CXX/drs/dr24xx.cpp
index 0e6a90e582c19ec..3fd8539be53d810 100644
--- a/clang/test/CXX/drs/dr24xx.cpp
+++ b/clang/test/CXX/drs/dr24xx.cpp
@@ -1,10 +1,5 @@
-// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors \
-// RUN: -Wno-variadic-macros -Wno-c11-extensions
-// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++20 %s -verify
+// RUN: %clang_cc1 -std=c++23 %s -verify
// expected-no-diagnostics
namespace dr2450 { // dr2450: 18 drafting
diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
index 5e8cba979751d7d..792dc78464b2a87 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
@@ -62,12 +62,13 @@ namespace ClassNTTP {
template<A a> constexpr int f() { return a.y; }
static_assert(f<A{1,2}>() == 2);
- template<A a> int id; // expected-note {{passing argument to parameter 'a' here}}
+ template<A a> int id; // #ClassNTTP1
constexpr A a = {1, 2};
static_assert(&id<A{1,2}> == &id<a>);
static_assert(&id<A{1,3}> != &id<a>);
int k = id<1>; // expected-error {{no viable conversion from 'int' to 'A'}}
+ // expected-note@#ClassNTTP1 {{passing argument to parameter 'a' here}}
struct B {
constexpr B() {}
>From 89d2ea0a2c4d7e604938b19c730c063333b2a1ef Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 29 Nov 2023 18:07:36 +0100
Subject: [PATCH 3/3] Add tests for #58434 and #73666
---
.../SemaTemplate/temp_arg_nontype_cxx2c.cpp | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp
index 3ca948f6ca3a666..9fb6b440b6b2af9 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx2c.cpp
@@ -72,3 +72,33 @@ struct J2 {
constexpr J2(const J2&) {}
};
B<J2{}> j2; // expected-error {{pointer to temporary object is not allowed in a template argument}}
+
+
+namespace GH58434 {
+
+template<int>
+void f();
+
+void test() {
+ f<{42}>();
+}
+
+}
+
+namespace GH73666 {
+
+template<class T, int I>
+struct A {
+ T x[I];
+};
+
+template< class T, class... U >
+A( T, U... ) -> A<T, 1 + sizeof...(U)>;
+
+template<A a> void foo() { }
+
+void bar() {
+ foo<{1}>();
+}
+
+}
More information about the cfe-commits
mailing list