Here our named return value optimization was breaking the required
destructor when the goto takes 'a' out of scope. A simple fix for the
release branches is to disable the optimization in the presence of backward
goto.
We could do better by disabling the optimization only if there is a backward
goto across the variable declaration, but we don't track that, and in GCC 14
we instead make the goto work with NRV.
PR c++/92407
gcc/cp/ChangeLog:
* cp-tree.h (struct language_function): Add backward_goto.
* decl.cc (check_goto): Set it.
* typeck.cc (check_return_expr): Prevent NRV if set.
gcc/testsuite/ChangeLog:
* g++.dg/opt/nrv22.C: New test.
(cherry picked from commit
a645347c19b07cc7abd7bf276c6769fc41afc932)
BOOL_BITFIELD invalid_constexpr : 1;
BOOL_BITFIELD throwing_cleanup : 1;
+ BOOL_BITFIELD backward_goto : 1;
hash_table<named_label_hash> *x_named_labels;
return;
}
+ cp_function_chain->backward_goto = true;
+
bool saw_catch = false, complained = false;
int identified = 0;
tree bad;
if (fn_returns_value_p && flag_elide_constructors)
{
if (named_return_value_okay_p
+ /* The current NRV implementation breaks if a backward goto needs to
+ destroy the object (PR92407). */
+ && !cp_function_chain->backward_goto
&& (current_function_return_value == NULL_TREE
|| current_function_return_value == bare_retval))
current_function_return_value = bare_retval;
--- /dev/null
+// PR c++/92407
+// { dg-do run }
+
+struct A
+{
+ A () { a++; }
+ A (const A &) { a++; }
+ ~A () { a--; }
+ static int a;
+};
+int A::a = 0;
+
+A
+foo ()
+{
+ int cnt = 10;
+lab:
+ A a;
+ if (cnt--)
+ goto lab;
+ return a;
+}
+
+int
+main ()
+{
+ foo ();
+ if (A::a)
+ __builtin_abort ();
+}