c++, v2: Implement DR2351 - void{} [PR102820]
Commit Message
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.
Jakub
Comments
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
@@ -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))
@@ -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" }
+}