Message ID | 20200528232308.1206783-1-polacek@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: Make braced-init-list as template arg work with aggr init [PR95369] | expand |
On 5/28/20 7:23 PM, Marek Polacek wrote: > Barry pointed out to me that our braced-init-list as a template-argument > extension doesn't work as expected when we aggregate-initialize. Thus > fixed by calling digest_init in convert_nontype_argument so that we can > actually convert the CONSTRUCTOR. > > I don't think we can call digest_init any earlier, and it needs to > happen before the call to build_converted_constant_expr. Or we could fix build_converted_constant_expr to allow it; aggregate list-initialization is a user-defined conversion sequence, so I tihnk it should be allowed as part of a converted constant expression. Jason > Barry also noticed that we allow designated initializers for > non-aggregate types in the template-argument argument context, i.e. this > > struct S { > unsigned a; > unsigned b; > constexpr S(unsigned _a, unsigned _b) noexcept: a{_a}, b{_b} { } > }; > > template<S s> struct X { }; > > void f() > { > X<{.a = 1, .b = 2}> x; > } > > probably should not compile. But I'm not too sure about it, and don't > know how I would fix it anyway, so I'm not dealing with it in this > patch. > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > gcc/cp/ChangeLog: > > PR c++/95369 > * pt.c (convert_nontype_argument): In C++20, reshape and digest > a braced-init-list if the type is an aggregate. > > gcc/testsuite/ChangeLog: > > PR c++/95369 > * g++.dg/cpp2a/nontype-class38.C: New test. > --- > gcc/cp/pt.c | 13 +++++++++ > gcc/testsuite/g++.dg/cpp2a/nontype-class38.C | 30 ++++++++++++++++++++ > 2 files changed, 43 insertions(+) > create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class38.C > > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > index 90dafff3aa7..adb7593f77d 100644 > --- a/gcc/cp/pt.c > +++ b/gcc/cp/pt.c > @@ -7133,6 +7133,19 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) > return error_mark_node; > } > > + /* For a { } template argument, like in X<{ 1, 2 }>, we need to digest > + here so that build_converted_constant_expr below is able to convert > + it to TYPE. */ > + if (cxx_dialect >= cxx20 > + && BRACE_ENCLOSED_INITIALIZER_P (expr) > + && CP_AGGREGATE_TYPE_P (type)) > + { > + expr = reshape_init (type, expr, complain); > + expr = digest_init (type, expr, complain); > + if (expr == error_mark_node) > + return error_mark_node; > + } > + > /* If we are in a template, EXPR may be non-dependent, but still > have a syntactic, rather than semantic, form. For example, EXPR > might be a SCOPE_REF, rather than the VAR_DECL to which the > diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class38.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class38.C > new file mode 100644 > index 00000000000..5b440fd1c9e > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class38.C > @@ -0,0 +1,30 @@ > +// PR c++/95369 > +// { dg-do compile { target c++20 } } > + > +struct S { > + int a; > + int b; > +}; > + > +struct W { > + int i; > + S s; > +}; > + > +template <S p> > +void fnc() > +{ > +} > + > +template<S s> struct X { }; > +template<W w> struct Y { }; > + > +void f() > +{ > + fnc<{ .a = 10, .b = 20 }>(); > + fnc<{ 10, 20 }>(); > + X<{ .a = 1, .b = 2 }> x; > + X<{ 1, 2 }> x2; > + // Brace elision is likely to be allowed. > + Y<{ 1, 2, 3 }> x3; > +} > > base-commit: 3d8d5ddb539a5254c7ef83414377f4c74c7701d4 >
On Fri, May 29, 2020 at 03:17:51PM -0400, Jason Merrill via Gcc-patches wrote: > On 5/28/20 7:23 PM, Marek Polacek wrote: > > Barry pointed out to me that our braced-init-list as a template-argument > > extension doesn't work as expected when we aggregate-initialize. Thus > > fixed by calling digest_init in convert_nontype_argument so that we can > > actually convert the CONSTRUCTOR. > > > > I don't think we can call digest_init any earlier, and it needs to > > happen before the call to build_converted_constant_expr. > > Or we could fix build_converted_constant_expr to allow it; aggregate > list-initialization is a user-defined conversion sequence, so I tihnk it > should be allowed as part of a converted constant expression. Ah, nice. It works just fine. Here's the complete patch: Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- Barry pointed out to me that our braced-init-list as a template-argument extension doesn't work as expected when we aggregate-initialize. Since aggregate list-initialization is a user-defined conversion sequence, we allow it as part of a converted constant expression. Co-authored-by: Jason Merrill <jason@redhat.com> gcc/cp/ChangeLog: PR c++/95369 * call.c (build_converted_constant_expr_internal): Allow list-initialization. gcc/testsuite/ChangeLog: PR c++/95369 * g++.dg/cpp2a/nontype-class38.C: New test. --- gcc/cp/call.c | 4 ++- gcc/testsuite/g++.dg/cpp2a/nontype-class38.C | 30 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class38.C diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 2b393f96e5b..3c97b9846e2 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -4348,7 +4348,7 @@ build_converted_constant_expr_internal (tree type, tree expr, and where the reference binding (if any) binds directly. */ for (conversion *c = conv; - conv && c->kind != ck_identity; + c && c->kind != ck_identity; c = next_conversion (c)) { switch (c->kind) @@ -4356,6 +4356,8 @@ build_converted_constant_expr_internal (tree type, tree expr, /* A conversion function is OK. If it isn't constexpr, we'll complain later that the argument isn't constant. */ case ck_user: + /* List-initialization is OK. */ + case ck_aggr: /* The lvalue-to-rvalue conversion is OK. */ case ck_rvalue: /* Array-to-pointer and function-to-pointer. */ diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class38.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class38.C new file mode 100644 index 00000000000..5b440fd1c9e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class38.C @@ -0,0 +1,30 @@ +// PR c++/95369 +// { dg-do compile { target c++20 } } + +struct S { + int a; + int b; +}; + +struct W { + int i; + S s; +}; + +template <S p> +void fnc() +{ +} + +template<S s> struct X { }; +template<W w> struct Y { }; + +void f() +{ + fnc<{ .a = 10, .b = 20 }>(); + fnc<{ 10, 20 }>(); + X<{ .a = 1, .b = 2 }> x; + X<{ 1, 2 }> x2; + // Brace elision is likely to be allowed. + Y<{ 1, 2, 3 }> x3; +} base-commit: 640e05e02b567fa5ccf4c207e6fc6c3e9a93b17c
On 6/5/20 3:13 PM, Marek Polacek wrote: > On Fri, May 29, 2020 at 03:17:51PM -0400, Jason Merrill via Gcc-patches wrote: >> On 5/28/20 7:23 PM, Marek Polacek wrote: >>> Barry pointed out to me that our braced-init-list as a template-argument >>> extension doesn't work as expected when we aggregate-initialize. Thus >>> fixed by calling digest_init in convert_nontype_argument so that we can >>> actually convert the CONSTRUCTOR. >>> >>> I don't think we can call digest_init any earlier, and it needs to >>> happen before the call to build_converted_constant_expr. >> >> Or we could fix build_converted_constant_expr to allow it; aggregate >> list-initialization is a user-defined conversion sequence, so I tihnk it >> should be allowed as part of a converted constant expression. > > Ah, nice. It works just fine. Here's the complete patch: > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? OK. > -- >8 -- > Barry pointed out to me that our braced-init-list as a template-argument > extension doesn't work as expected when we aggregate-initialize. Since > aggregate list-initialization is a user-defined conversion sequence, we > allow it as part of a converted constant expression. > > Co-authored-by: Jason Merrill <jason@redhat.com> > > gcc/cp/ChangeLog: > > PR c++/95369 > * call.c (build_converted_constant_expr_internal): Allow > list-initialization. > > gcc/testsuite/ChangeLog: > > PR c++/95369 > * g++.dg/cpp2a/nontype-class38.C: New test. > --- > gcc/cp/call.c | 4 ++- > gcc/testsuite/g++.dg/cpp2a/nontype-class38.C | 30 ++++++++++++++++++++ > 2 files changed, 33 insertions(+), 1 deletion(-) > create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class38.C > > diff --git a/gcc/cp/call.c b/gcc/cp/call.c > index 2b393f96e5b..3c97b9846e2 100644 > --- a/gcc/cp/call.c > +++ b/gcc/cp/call.c > @@ -4348,7 +4348,7 @@ build_converted_constant_expr_internal (tree type, tree expr, > and where the reference binding (if any) binds directly. */ > > for (conversion *c = conv; > - conv && c->kind != ck_identity; > + c && c->kind != ck_identity; > c = next_conversion (c)) > { > switch (c->kind) > @@ -4356,6 +4356,8 @@ build_converted_constant_expr_internal (tree type, tree expr, > /* A conversion function is OK. If it isn't constexpr, we'll > complain later that the argument isn't constant. */ > case ck_user: > + /* List-initialization is OK. */ > + case ck_aggr: > /* The lvalue-to-rvalue conversion is OK. */ > case ck_rvalue: > /* Array-to-pointer and function-to-pointer. */ > diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class38.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class38.C > new file mode 100644 > index 00000000000..5b440fd1c9e > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class38.C > @@ -0,0 +1,30 @@ > +// PR c++/95369 > +// { dg-do compile { target c++20 } } > + > +struct S { > + int a; > + int b; > +}; > + > +struct W { > + int i; > + S s; > +}; > + > +template <S p> > +void fnc() > +{ > +} > + > +template<S s> struct X { }; > +template<W w> struct Y { }; > + > +void f() > +{ > + fnc<{ .a = 10, .b = 20 }>(); > + fnc<{ 10, 20 }>(); > + X<{ .a = 1, .b = 2 }> x; > + X<{ 1, 2 }> x2; > + // Brace elision is likely to be allowed. > + Y<{ 1, 2, 3 }> x3; > +} > > base-commit: 640e05e02b567fa5ccf4c207e6fc6c3e9a93b17c >
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 90dafff3aa7..adb7593f77d 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -7133,6 +7133,19 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain) return error_mark_node; } + /* For a { } template argument, like in X<{ 1, 2 }>, we need to digest + here so that build_converted_constant_expr below is able to convert + it to TYPE. */ + if (cxx_dialect >= cxx20 + && BRACE_ENCLOSED_INITIALIZER_P (expr) + && CP_AGGREGATE_TYPE_P (type)) + { + expr = reshape_init (type, expr, complain); + expr = digest_init (type, expr, complain); + if (expr == error_mark_node) + return error_mark_node; + } + /* If we are in a template, EXPR may be non-dependent, but still have a syntactic, rather than semantic, form. For example, EXPR might be a SCOPE_REF, rather than the VAR_DECL to which the diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class38.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class38.C new file mode 100644 index 00000000000..5b440fd1c9e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class38.C @@ -0,0 +1,30 @@ +// PR c++/95369 +// { dg-do compile { target c++20 } } + +struct S { + int a; + int b; +}; + +struct W { + int i; + S s; +}; + +template <S p> +void fnc() +{ +} + +template<S s> struct X { }; +template<W w> struct Y { }; + +void f() +{ + fnc<{ .a = 10, .b = 20 }>(); + fnc<{ 10, 20 }>(); + X<{ .a = 1, .b = 2 }> x; + X<{ 1, 2 }> x2; + // Brace elision is likely to be allowed. + Y<{ 1, 2, 3 }> x3; +}