On Sat, Jan 11, 2020 at 6:13 AM Jason Merrill <ja...@redhat.com> wrote:
>
> This is a pretty rare situation since the C++11 change to make all
> destructors default to noexcept, but it is still possible to define throwing
> destructors, and if a destructor for a local variable throws during the
> return, we've already constructed the return value, so now we need to
> destroy it.  I handled this somewhat like the new-expression cleanup; as in
> that case, this cleanup can't properly nest with the cleanups for local
> variables, so I introduce a cleanup region around the whole function and a
> flag variable to indicate whether the return value actually needs to be
> destroyed.
>
> Setting the flag requires giving a COMPOUND_EXPR as the operand of a
> RETURN_EXPR.  Simply allowing that in gimplify_return_expr makes the most
> sense to me, is it OK for trunk?

It works for me.

Richard.

> This doesn't currently work with deduced return type because we don't know
> the type when we're deciding whether to introduce the cleanup region.
>
> Tested x86_64-pc-linux-gnu.
>
> gcc/
>         * gimplify.c (gimplify_return_expr): Handle COMPOUND_EXPR.
> gcc/cp/
>         * cp-tree.h (current_retval_sentinel): New macro.
>         * decl.c (start_preparsed_function): Set up cleanup for retval.
>         * typeck.c (check_return_expr): Set current_retval_sentinel.
> ---
>  gcc/cp/cp-tree.h                  |  7 ++++++
>  gcc/cp/decl.c                     | 14 +++++++++++
>  gcc/cp/typeck.c                   |  9 +++++++
>  gcc/gimplify.c                    |  8 +++++++
>  gcc/testsuite/g++.dg/eh/return1.C | 40 +++++++++++++++++++++++++++++++
>  5 files changed, 78 insertions(+)
>  create mode 100644 gcc/testsuite/g++.dg/eh/return1.C
>
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 98572bdbad1..c0f780df685 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -1952,6 +1952,13 @@ struct GTY(()) language_function {
>
>  #define current_vtt_parm cp_function_chain->x_vtt_parm
>
> +/* A boolean flag to control whether we need to clean up the return value if 
> a
> +   local destructor throws.  Only used in functions that return by value a
> +   class with a destructor.  Which 'tors don't, so we can use the same
> +   field as current_vtt_parm.  */
> +
> +#define current_retval_sentinel current_vtt_parm
> +
>  /* Set to 0 at beginning of a function definition, set to 1 if
>     a return statement that specifies a return value is seen.  */
>
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index 094e32edf58..52da0deef40 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -16418,6 +16418,20 @@ start_preparsed_function (tree decl1, tree attrs, 
> int flags)
>    if (!DECL_OMP_DECLARE_REDUCTION_P (decl1))
>      start_lambda_scope (decl1);
>
> +  /* If cleaning up locals on return throws an exception, we need to destroy
> +     the return value that we just constructed.  */
> +  if (!processing_template_decl
> +      && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (TREE_TYPE (decl1))))
> +    {
> +      tree retval = DECL_RESULT (decl1);
> +      tree dtor = build_cleanup (retval);
> +      current_retval_sentinel = get_temp_regvar (boolean_type_node,
> +                                                boolean_false_node);
> +      dtor = build3 (COND_EXPR, void_type_node, current_retval_sentinel,
> +                    dtor, void_node);
> +      push_cleanup (retval, dtor, /*eh-only*/true);
> +    }
> +
>    return true;
>  }
>
> diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
> index 7b653cebca0..8288b662ec0 100644
> --- a/gcc/cp/typeck.c
> +++ b/gcc/cp/typeck.c
> @@ -10088,6 +10088,15 @@ check_return_expr (tree retval, bool *no_warning)
>    if (retval && retval != result)
>      retval = build2 (INIT_EXPR, TREE_TYPE (result), result, retval);
>
> +  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (valtype)
> +      /* FIXME doesn't work with deduced return type.  */
> +      && current_retval_sentinel)
> +    {
> +      tree set = build2 (MODIFY_EXPR, boolean_type_node,
> +                        current_retval_sentinel, boolean_true_node);
> +      retval = build2 (COMPOUND_EXPR, void_type_node, retval, set);
> +    }
> +
>    return retval;
>  }
>
> diff --git a/gcc/gimplify.c b/gcc/gimplify.c
> index fe7236de4c3..05d7922116b 100644
> --- a/gcc/gimplify.c
> +++ b/gcc/gimplify.c
> @@ -1599,6 +1599,14 @@ gimplify_return_expr (tree stmt, gimple_seq *pre_p)
>
>    if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))))
>      result_decl = NULL_TREE;
> +  else if (TREE_CODE (ret_expr) == COMPOUND_EXPR)
> +    {
> +      /* Used in C++ for handling EH cleanup of the return value if a local
> +        cleanup throws.  Assume the front-end knows what it's doing.  */
> +      result_decl = DECL_RESULT (current_function_decl);
> +      /* But crash if we end up trying to modify ret_expr below.  */
> +      ret_expr = NULL_TREE;
> +    }
>    else
>      {
>        result_decl = TREE_OPERAND (ret_expr, 0);
> diff --git a/gcc/testsuite/g++.dg/eh/return1.C 
> b/gcc/testsuite/g++.dg/eh/return1.C
> new file mode 100644
> index 00000000000..7469d3128dc
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/eh/return1.C
> @@ -0,0 +1,40 @@
> +// PR c++/33799
> +// { dg-do run }
> +
> +extern "C" void abort();
> +
> +int c, d;
> +
> +#if __cplusplus >= 201103L
> +#define THROWS noexcept(false)
> +#else
> +#define THROWS
> +#endif
> +
> +struct X
> +{
> +  X(bool throws) : throws_(throws) { ++c; }
> +  X(const X& x) : throws_(x.throws_) { ++c; }
> +  ~X() THROWS
> +  {
> +    ++d;
> +    if (throws_) { throw 1; }
> +  }
> +private:
> +  bool throws_;
> +};
> +
> +X f()
> +{
> +  X x(true);
> +  return X(false);
> +}
> +
> +int main()
> +{
> +  try { f(); }
> +  catch (...) {}
> +
> +  if (c != d)
> +    throw;
> +}
>
> base-commit: d3cf980217ce13b1eda01d4c42a7e3afd2bf3217
> --
> 2.18.1
>

Reply via email to