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 <ja...@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) + 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" } +} Jakub