[gcc r16-9476] c++: Don't cache calls which rethrow etc. [PR126508]

Jakub Jelinek via Gcc-cvs <[email protected]> Fri, 31 Jul 2026 07:44:25 +0000 (GMT)
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:22a1038ed2080e604fbc6499d4e3b8b30c258fa4

commit r16-9476-g22a1038ed2080e604fbc6499d4e3b8b30c258fa4
Author: Jakub Jelinek <[email protected]>
Date:   Fri Jul 31 08:58:27 2026 +0200

    c++: Don't cache calls which rethrow etc. [PR126508]
    
    The first 3 testcase below are miscompiled, we happily cache
    calls during constant evaluation which don't depend just on their
    arguments, but also on the current exceptions (uncaught or caught).
    If we decide to cache such functions and then try to evaluate them
    with different uncaught/caught exceptions (or none), we can get wrong
    results.
    We already don't cache calls which allocate and don't free all heap
    allocations, or free some heap allocations they haven't allocated,
    or which call (right now any) metafunctions, or have exited through
    exception, or aren't constant.
    This patch just adds the rethrow/__builtin_uncaught_exceptions/
    __builtin_current_exception calls to the set of non-cacheable operations
    (to be precise, e.g. rethrow would be safe to cache if we can prove
    that the current exception was always thrown from within that function,
    ditto __builtin_current_exception, but it is hard to figure out).
    The last testcase attempts to check if we don't need something similar
    also for __builtin_eh_ptr_adjust_ref, but the call to foo for some reason
    isn't cached and so I don't have a proof we need to handle it too.
    
    2026-07-31  Jakub Jelinek  <[email protected]>
    
            PR c++/126508
            * constexpr.cc (cxx_eval_cxa_builtin_fn): Avoid caching
            calls which rethrow or call __builtin_uncaught_exceptions
            or __builtin_current_exception.
    
            * g++.dg/cpp26/constexpr-eh20.C: New test.
            * g++.dg/cpp26/constexpr-eh21.C: New test.
            * g++.dg/cpp26/constexpr-eh22.C: New test.
            * g++.dg/cpp26/constexpr-eh23.C: New test.
    
    (cherry picked from commit a5e5c22ad4862846ea705d43c640ca18cc9dd78a)

Diff:
---
 gcc/cp/constexpr.cc                         | 11 ++++++++
 gcc/testsuite/g++.dg/cpp26/constexpr-eh20.C | 31 +++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp26/constexpr-eh21.C | 43 +++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp26/constexpr-eh22.C | 41 +++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp26/constexpr-eh23.C | 22 +++++++++++++++
 5 files changed, 148 insertions(+)

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 9e783607a698..7a09f2947c67 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -2118,6 +2118,9 @@ cxx_eval_cxa_builtin_fn (const constexpr_ctx *ctx, tree call,
       DECL_EXCEPTION_REFCOUNT (arg)
 	= size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), size_one_node);
       ++ctx->global->uncaught_exceptions;
+      /* Don't cache calls which rethrow, they depend on the current
+	 exception which might be caught in the caller.  */
+      ctx->global->metafns_called = true;
       *jump_target = arg;
       return void_node;
     case CXA_BAD_CAST:
@@ -2196,6 +2199,10 @@ cxx_eval_cxa_builtin_fn (const constexpr_ctx *ctx, tree call,
 	  *non_constant_p = true;
 	  return call;
 	}
+      /* Don't cache calls which call __builtin_uncaught_exceptions (),
+	 they depend on the current uncaught exceptions which might
+	 be the state from their caller.  */
+      ctx->global->metafns_called = true;
       return build_int_cst (integer_type_node,
 			    ctx->global->uncaught_exceptions);
     case BUILTIN_CURRENT_EXCEPTION:
@@ -2247,6 +2254,10 @@ cxx_eval_cxa_builtin_fn (const constexpr_ctx *ctx, tree call,
 			      size_one_node);
 	      arg = fold_convert (ptr_type_node, build_address (arg));
 	    }
+	  /* Don't cache calls which call __builtin_current_exception (),
+	     they depend on the current exception which might be caught
+	     in the caller.  */
+	  ctx->global->metafns_called = true;
 	  return build_constructor_single (TREE_TYPE (decl), fld, arg);
 	}
     case STD_RETHROW_EXCEPTION:
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh20.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh20.C
new file mode 100644
index 000000000000..438469825d49
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh20.C
@@ -0,0 +1,31 @@
+// PR c++/126508
+// { dg-do compile { target c++26 } }
+
+constexpr int
+foo ()
+{
+  try
+    {
+      throw;
+    }
+  catch (const int x)
+    {
+      return x;
+    }
+}
+
+constexpr int
+bar (int x)
+{
+  try
+    {
+      throw x;
+    }
+  catch (...)
+    {
+      return foo ();
+    }
+}
+
+static_assert (bar (42) == 42);
+static_assert (bar (43) == 43);
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh21.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh21.C
new file mode 100644
index 000000000000..30fd6dc84b9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh21.C
@@ -0,0 +1,43 @@
+// PR c++/126508
+// { dg-do compile { target c++26 } }
+
+constexpr int
+foo ()
+{
+  return __builtin_uncaught_exceptions ();
+}
+
+constexpr int
+bar ()
+{
+  return __builtin_uncaught_exceptions ();
+}
+
+struct A { constexpr A () : a (0) {} constexpr ~A () { if (foo () != a) asm (""); } int a; };
+struct B { constexpr B () : b (0) {} constexpr ~B () { if (bar () != b) asm (""); } int b; };
+
+constexpr bool
+baz ()
+{
+  {
+    A a;
+  }
+  try
+    {
+      A a;
+      B b;
+      a.a = 1;
+      b.b = 1;
+      throw 42;
+    }
+  catch (...)
+    {
+    }
+  {
+    A a;
+    B b;
+  }
+  return true;
+}
+
+static_assert (baz ());
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh22.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh22.C
new file mode 100644
index 000000000000..291ec3386cc8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh22.C
@@ -0,0 +1,41 @@
+// PR c++/126508
+// { dg-do compile { target c++26 } }
+
+#include <exception>
+
+constexpr bool
+foo ()
+{
+  return __builtin_current_exception () != nullptr;
+}
+
+constexpr int
+bar ()
+{
+  return __builtin_current_exception () != nullptr;
+}
+
+constexpr bool
+baz ()
+{
+  if (foo ())
+    return false;
+  try
+    {
+      throw 42;
+    }
+  catch (...)
+    {
+      if (!foo ())
+	return false;
+      if (!bar ())
+	return false;
+    }
+  if (foo ())
+    return false;
+  if (bar ())
+    return false;
+  return true;
+}
+
+static_assert (baz ());
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh23.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh23.C
new file mode 100644
index 000000000000..1b1a77d69d6e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh23.C
@@ -0,0 +1,22 @@
+// PR c++/126508
+// { dg-do compile { target c++26 } }
+
+#include <exception>
+
+constexpr std::exception_ptr
+foo (const std::exception_ptr &x)
+{
+  return x;
+}
+
+constexpr bool
+baz ()
+{
+  std::exception_ptr a = std::make_exception_ptr (42);
+  auto b = foo (a);
+  auto c = foo (a);
+  auto d = foo (a);
+  return true;
+}
+
+static_assert (baz ());