Message ID | 20211028112634.GO304296@tucnak |
---|---|
State | New |
Headers | show |
Series | c++, v2: Implement DR2351 - void{} [PR102820] | expand |
On 10/28/21 07:26, Jakub Jelinek wrote: > On Wed, Oct 27, 2021 at 04:58:53PM -0400, Jason Merrill wrote: >> On 10/21/21 04:42, Jakub Jelinek wrote: >>> Hi! >>> >>> Here is an attempt to implement DR2351 - void{} - where void{} after >>> pack expansion is considered valid and the same thing as void(). >>> For templates, dunno if we have some better way to check if a CONSTRUCTOR >>> might be empty after pack expansion. Would that only if the constructor >>> only contains EXPR_PACK_EXPANSION elements and nothing else, or something >>> else too? >> >> I think that's the only case. For template args there's the >> pack_expansion_args_count function, but I don't think there's anything >> similar for constructor elts; please feel free to add it. > > Ok. But counting how many packs its CONSTRUCTOR_ELTS have and then comparing > that number against CONSTRUCTOR_NELTS seems to be unnecessarily expensive if > there are many elements, for the purpose the DR2351 code needs we can stop > as soon as we see first non-pack element. > > So what about this if it passes bootstrap/regtest? > > 2021-10-28 Jakub Jelinek <jakub@redhat.com> > > PR c++/102820 > * semantics.c (maybe_zero_constructor_nelts): New function. > (finish_compound_literal): Implement DR2351 - void{}. > If type is cv void and compound_literal has no elements, return > void_node. If type is cv void and compound_literal might have no > elements after expansion, handle it like other dependent compound > literals. > > * g++.dg/cpp0x/dr2351.C: New test. > > --- gcc/cp/semantics.c.jj 2021-10-27 09:16:41.161600606 +0200 > +++ gcc/cp/semantics.c 2021-10-28 13:06:59.325791588 +0200 > @@ -3079,6 +3079,24 @@ finish_unary_op_expr (location_t op_loc, > return result; > } > > +/* Return true if CONSTRUCTOR EXPR after pack expansion could have no > + elements. */ > + > +static bool > +maybe_zero_constructor_nelts (tree expr) > +{ > + if (CONSTRUCTOR_NELTS (expr) == 0) > + return true; > + if (!processing_template_decl) > + return false; > + unsigned int i; > + tree val; > + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (expr), i, val) Let's use for (constructor_elt &elt : CONSTRUCTOR_ELTS (t)) > + if (!PACK_EXPANSION_P (val)) > + return false; > + return true; > +} > + > /* Finish a compound-literal expression or C++11 functional cast with aggregate > initializer. TYPE is the type to which the CONSTRUCTOR in COMPOUND_LITERAL > is being cast. */ > @@ -3104,9 +3122,20 @@ finish_compound_literal (tree type, tree > > if (!TYPE_OBJ_P (type)) > { > - if (complain & tf_error) > - error ("compound literal of non-object type %qT", type); > - return error_mark_node; > + /* DR2351 */ > + if (VOID_TYPE_P (type) && CONSTRUCTOR_NELTS (compound_literal) == 0) > + return void_node; This test now seems redundant with the one below (if you remove the && processing_template_decl). OK with those tweaks. > + else if (VOID_TYPE_P (type) > + && processing_template_decl > + && maybe_zero_constructor_nelts (compound_literal)) > + /* If there are only packs in compound_literal, it could > + be void{} after pack expansion. */; > + else > + { > + if (complain & tf_error) > + error ("compound literal of non-object type %qT", type); > + return error_mark_node; > + } > } > > if (template_placeholder_p (type)) > --- gcc/testsuite/g++.dg/cpp0x/dr2351.C.jj 2021-10-28 12:59:27.987120315 +0200 > +++ gcc/testsuite/g++.dg/cpp0x/dr2351.C 2021-10-28 13:15:20.532760871 +0200 > @@ -0,0 +1,51 @@ > +// DR2351 > +// { dg-do compile { target c++11 } } > + > +void > +foo () > +{ > + void{}; > + void(); > +} > + > +template <class ...T> > +void > +bar (T... t) > +{ > + void{t...}; > + void(t...); > +} > + > +void > +baz () > +{ > + bar (); > +} > + > +template <class ...T> > +void > +qux (T... t) > +{ > + void{t...}; // { dg-error "compound literal of non-object type" } > +} > + > +void > +corge () > +{ > + qux (1, 2); > +} > + > +template <class ...T> > +void > +garply (T... t) > +{ > + void{t..., t..., t...}; > + void(t..., t..., t...); > +} > + > +template <class ...T> > +void > +grault (T... t) > +{ > + void{t..., 1}; // { dg-error "compound literal of non-object type" } > +} > > > Jakub >
On Thu, Oct 28, 2021 at 08:01:27AM -0400, Jason Merrill wrote: > > --- gcc/cp/semantics.c.jj 2021-10-27 09:16:41.161600606 +0200 > > +++ gcc/cp/semantics.c 2021-10-28 13:06:59.325791588 +0200 > > @@ -3079,6 +3079,24 @@ finish_unary_op_expr (location_t op_loc, > > return result; > > } > > +/* Return true if CONSTRUCTOR EXPR after pack expansion could have no > > + elements. */ > > + > > +static bool > > +maybe_zero_constructor_nelts (tree expr) > > +{ > > + if (CONSTRUCTOR_NELTS (expr) == 0) > > + return true; > > + if (!processing_template_decl) > > + return false; > > + unsigned int i; > > + tree val; > > + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (expr), i, val) > > Let's use > > for (constructor_elt &elt : CONSTRUCTOR_ELTS (t)) Ok, will do. > > @@ -3104,9 +3122,20 @@ finish_compound_literal (tree type, tree > > if (!TYPE_OBJ_P (type)) > > { > > - if (complain & tf_error) > > - error ("compound literal of non-object type %qT", type); > > - return error_mark_node; > > + /* DR2351 */ > > + if (VOID_TYPE_P (type) && CONSTRUCTOR_NELTS (compound_literal) == 0) > > + return void_node; > > This test now seems redundant with the one below (if you remove the && > processing_template_decl). It is not redundant, for the maybe case it doesn't return void_node, but falls through into if (processing_template_decl), which, because compound_literal is necessarily instantiation_dependent_expression_p (it contains packs) will just create CONSTRUCTOR_IS_DEPENDENT CONSTRUCTOR and we'll get here back during instantiation. For the CONSTRUCTOR_NELTS == 0 case even in templates we know compound_literal isn't dependent (it doesn't contain anything) and type isn't either, so we can return void_node right away (and when !processing_template_decl we have to do that). Jakub
On 10/28/21 08:19, Jakub Jelinek wrote: > On Thu, Oct 28, 2021 at 08:01:27AM -0400, Jason Merrill wrote: >>> --- gcc/cp/semantics.c.jj 2021-10-27 09:16:41.161600606 +0200 >>> +++ gcc/cp/semantics.c 2021-10-28 13:06:59.325791588 +0200 >>> @@ -3079,6 +3079,24 @@ finish_unary_op_expr (location_t op_loc, >>> return result; >>> } >>> +/* Return true if CONSTRUCTOR EXPR after pack expansion could have no >>> + elements. */ >>> + >>> +static bool >>> +maybe_zero_constructor_nelts (tree expr) >>> +{ >>> + if (CONSTRUCTOR_NELTS (expr) == 0) >>> + return true; >>> + if (!processing_template_decl) >>> + return false; >>> + unsigned int i; >>> + tree val; >>> + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (expr), i, val) >> >> Let's use >> >> for (constructor_elt &elt : CONSTRUCTOR_ELTS (t)) > > Ok, will do. > >>> @@ -3104,9 +3122,20 @@ finish_compound_literal (tree type, tree >>> if (!TYPE_OBJ_P (type)) >>> { >>> - if (complain & tf_error) >>> - error ("compound literal of non-object type %qT", type); >>> - return error_mark_node; >>> + /* DR2351 */ >>> + if (VOID_TYPE_P (type) && CONSTRUCTOR_NELTS (compound_literal) == 0) >>> + return void_node; >> >> This test now seems redundant with the one below (if you remove the && >> processing_template_decl). > > It is not redundant, for the maybe case it doesn't return void_node, but > falls through into if (processing_template_decl), which, because > compound_literal is necessarily instantiation_dependent_expression_p > (it contains packs) will just create CONSTRUCTOR_IS_DEPENDENT CONSTRUCTOR > and we'll get here back during instantiation. > For the CONSTRUCTOR_NELTS == 0 case even in templates we know > compound_literal isn't dependent (it doesn't contain anything) and type > isn't either, so we can return void_node right away (and when > !processing_template_decl we have to do that). Ah, right. Never mind that comment, then. Jason
--- gcc/cp/semantics.c.jj 2021-10-27 09:16:41.161600606 +0200 +++ gcc/cp/semantics.c 2021-10-28 13:06:59.325791588 +0200 @@ -3079,6 +3079,24 @@ finish_unary_op_expr (location_t op_loc, return result; } +/* Return true if CONSTRUCTOR EXPR after pack expansion could have no + elements. */ + +static bool +maybe_zero_constructor_nelts (tree expr) +{ + if (CONSTRUCTOR_NELTS (expr) == 0) + return true; + if (!processing_template_decl) + return false; + unsigned int i; + tree val; + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (expr), i, val) + if (!PACK_EXPANSION_P (val)) + return false; + return true; +} + /* Finish a compound-literal expression or C++11 functional cast with aggregate initializer. TYPE is the type to which the CONSTRUCTOR in COMPOUND_LITERAL is being cast. */ @@ -3104,9 +3122,20 @@ finish_compound_literal (tree type, tree if (!TYPE_OBJ_P (type)) { - if (complain & tf_error) - error ("compound literal of non-object type %qT", type); - return error_mark_node; + /* DR2351 */ + if (VOID_TYPE_P (type) && CONSTRUCTOR_NELTS (compound_literal) == 0) + return void_node; + else if (VOID_TYPE_P (type) + && processing_template_decl + && maybe_zero_constructor_nelts (compound_literal)) + /* If there are only packs in compound_literal, it could + be void{} after pack expansion. */; + else + { + if (complain & tf_error) + error ("compound literal of non-object type %qT", type); + return error_mark_node; + } } if (template_placeholder_p (type)) --- gcc/testsuite/g++.dg/cpp0x/dr2351.C.jj 2021-10-28 12:59:27.987120315 +0200 +++ gcc/testsuite/g++.dg/cpp0x/dr2351.C 2021-10-28 13:15:20.532760871 +0200 @@ -0,0 +1,51 @@ +// DR2351 +// { dg-do compile { target c++11 } } + +void +foo () +{ + void{}; + void(); +} + +template <class ...T> +void +bar (T... t) +{ + void{t...}; + void(t...); +} + +void +baz () +{ + bar (); +} + +template <class ...T> +void +qux (T... t) +{ + void{t...}; // { dg-error "compound literal of non-object type" } +} + +void +corge () +{ + qux (1, 2); +} + +template <class ...T> +void +garply (T... t) +{ + void{t..., t..., t...}; + void(t..., t..., t...); +} + +template <class ...T> +void +grault (T... t) +{ + void{t..., 1}; // { dg-error "compound literal of non-object type" } +}