Hi Jason,
Thank you so much for taking a look!

> This is OK, but I wonder about making do_warn_unused_result ignore empty
> types in general; if there's no data in the type, what does it matter if
> we ignore the empty box?

I'm not strongly opinionated and I think there are good arguments both
ways. Looking at other implementations, I see clang and msvc both warn
on warn_unused_result/nodiscard for empty types. I think the primary
time this would really matter is generic programming, if I'm working
with `template<T> [[nodiscard]] T foo();` I would probably want a
warning if I discarded the result, even if it's being instantiated
with T = empty type.

Cheers,
Jeremy

On Tue, Jul 8, 2025 at 11:23 AM Jason Merrill <ja...@redhat.com> wrote:
>
> On 6/9/25 9:00 PM, Jeremy Rifkin wrote:
> > Hi,
> > This fixes PR c/82134 which concerns gcc emitting an incorrect unused
> > result diagnostic for empty types. This diagnostic is emitted from
> > tree-cfg.cc because of a couple code paths which attempt to avoid
> > copying empty types, resulting in GIMPLE that isn't using the returned
> > value of a call. To fix this I've added suppress_warning in three locations
> > and a corresponding check in do_warn_unused_result.
>
> This is OK, but I wonder about making do_warn_unused_result ignore empty
> types in general; if there's no data in the type, what does it matter if
> we ignore the empty box?
>
> > Cheers,
> > Jeremy
> >
> >
> >      PR c/82134
> >
> > gcc/cp/ChangeLog:
> >
> >      * call.cc (build_call_a): Add suppress_warning
> >      * cp-gimplify.cc (cp_gimplify_expr): Add suppress_warning
> >
> > gcc/ChangeLog:
> >
> >      * gimplify.cc (gimplify_modify_expr): Add suppress_warning
> >      * tree-cfg.cc (do_warn_unused_result): Check warning_suppressed_p
> >
> > gcc/testsuite/ChangeLog:
> >
> >      * c-c++-common/attr-warn-unused-result-2.c: New test.
> >
> > Signed-off-by: Jeremy Rifkin <jer...@rifkin.dev>
> > ---
> >   gcc/cp/call.cc                                    |  1 +
> >   gcc/cp/cp-gimplify.cc                             |  1 +
> >   gcc/gimplify.cc                                   |  1 +
> >   .../c-c++-common/attr-warn-unused-result-2.c      | 15 +++++++++++++++
> >   gcc/tree-cfg.cc                                   |  2 ++
> >   5 files changed, 20 insertions(+)
> >   create mode 100644 gcc/testsuite/c-c++-common/attr-warn-unused-result-2.c
> >
> > diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> > index 2c3ef3dfc35..a70fc13c6a4 100644
> > --- a/gcc/cp/call.cc
> > +++ b/gcc/cp/call.cc
> > @@ -412,6 +412,7 @@ build_call_a (tree function, int n, tree *argarray)
> >             /* We're disconnecting the initializer from its target,
> >            don't create a temporary.  */
> >             arg = TARGET_EXPR_INITIAL (arg);
> > +        suppress_warning (arg, OPT_Wunused_result);
> >           tree t = build0 (EMPTY_CLASS_EXPR, TREE_TYPE (arg));
> >           arg = build2 (COMPOUND_EXPR, TREE_TYPE (t), arg, t);
> >           CALL_EXPR_ARG (function, i) = arg;
> > diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
> > index 0fcfa16d2c5..2a21e960994 100644
> > --- a/gcc/cp/cp-gimplify.cc
> > +++ b/gcc/cp/cp-gimplify.cc
> > @@ -690,6 +690,7 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p,
> > gimple_seq *post_p)
> >               && (REFERENCE_CLASS_P (op1) || DECL_P (op1)))
> >             op1 = build_fold_addr_expr (op1);
> >
> > +        suppress_warning (op1, OPT_Wunused_result);
> >           gimplify_and_add (op1, pre_p);
> >             }
> >           gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
> > diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
> > index 9f9ff92d064..fa9890e7cea 100644
> > --- a/gcc/gimplify.cc
> > +++ b/gcc/gimplify.cc
> > @@ -7305,6 +7305,7 @@ gimplify_modify_expr (tree *expr_p, gimple_seq
> > *pre_p, gimple_seq *post_p,
> >         && !(TREE_ADDRESSABLE (TREE_TYPE (*from_p))
> >          && TREE_CODE (*from_p) == CALL_EXPR))
> >       {
> > +      suppress_warning (*from_p, OPT_Wunused_result);
> >         gimplify_stmt (from_p, pre_p);
> >         gimplify_stmt (to_p, pre_p);
> >         *expr_p = NULL_TREE;
> > diff --git a/gcc/testsuite/c-c++-common/attr-warn-unused-result-2.c 
> > b/gcc/testsuite/c-c++-common/attr-warn-unused-result-2.c
> > new file mode 100644
> > index 00000000000..09be1a933c9
> > --- /dev/null
> > +++ b/gcc/testsuite/c-c++-common/attr-warn-unused-result-2.c
> > @@ -0,0 +1,15 @@
> > +// PR c/82134
> > +/* { dg-do compile } */
> > +
> > +struct S {};
> > +
> > +__attribute__((warn_unused_result)) struct S foo();
> > +
> > +void use_s(struct S);
> > +
> > +void
> > +test (void)
> > +{
> > +  struct S s = foo(); /* { dg-bogus "ignoring return value of" } */
> > +  use_s(foo()); /* { dg-bogus "ignoring return value of" } */
> > +}
> > diff --git a/gcc/tree-cfg.cc b/gcc/tree-cfg.cc
> > index fad308e7f7b..5cd72ed1b3e 100644
> > --- a/gcc/tree-cfg.cc
> > +++ b/gcc/tree-cfg.cc
> > @@ -9961,6 +9961,8 @@ do_warn_unused_result (gimple_seq seq)
> >           break;
> >         if (gimple_call_internal_p (g))
> >           break;
> > +      if (warning_suppressed_p (g, OPT_Wunused_result))
> > +        break;
> >
> >         /* This is a naked call, as opposed to a GIMPLE_CALL with an
> >            LHS.  All calls whose value is ignored should be
>

Reply via email to