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 <corentinja...@gmail.com> Date: Wed, 22 Nov 2023 11:43:07 +0100 Subject: [PATCH 1/2] [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 <corentinja...@gmail.com> Date: Wed, 29 Nov 2023 17:57:23 +0100 Subject: [PATCH 2/2] 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() {} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits