[PATCH] c++, libstdc++: Implement C++26 P3068R5 - constexpr exceptions [PR117785]

Jakub Jelinek jakub@redhat.com
Mon Jun 2 08:54:19 GMT 2025


Hi!

The following patch implements the C++26 P3068R5 - constexpr exceptions
paper.

As the IL cxx_eval_constant* functions see already contains the low
level calls like __cxa_{allocate,free}_exception, __cxa_{,re}throw etc.,
the patch just makes 10 extern "C" __cxa_* functions magic builtins which
during constant evaluation pretend to be constexpr even when not declared
so and handle them directly, plus does the same for 3 std namespace
functions - std::uncaught_exceptions, std::current_exception and
std::rethrow_exception and adds one new FE builtin -
__builtin_eh_ptr_adjust_ref which the library can use instead of the
_M_addref and _M_release out of line methods (this one instead of
recognizing _M_* as magic too because those are clearly specific to
libstdc++ and e.g. libc++ could use something else).

The patch uses magic VAR_DECLs with heap_{uninit_,,deleted_}identifier
DECL_NAME like for operator new/delete for objects allocated with
__cxa_allocate_exception, just sets their DECL_LANG_SPECIFIC so that
we can track their reference count as well (with std::exception_ptr
the same exception object can be referenced multiple times and we want
to destruct and free only when it reaches zero refcount).

For uncaught exceptions being propagated, the patch uses new kind of
*jump_target, which is that magic VAR_DECL described above.
The largest change in the patch is making jump_target argument non-optional
in cxa_eval_constant_exception and all functions it calls that need it.
This is because exceptions can be thrown from pretty much everywhere, e.g.
binary expression can throw in either operand.  And the patch also adds
if (*jump_target) return NULL_TREE; or similar in many spots, so that we
don't crash because cxx_eval_constant_expression returned NULL_TREE
somewhere before actually trying to use it and so that we don't uselessly
dive into other operands etc.
Note, with statement expressions actually this was something we just didn't
handle correctly before, one can validly have:
  a = ({ if (x) return 42; 12; }) + b;
or in the other operand, or break/continue instead of return if it is
somewhere in a loop/switch; and it isn't ok to branch from one operand to
another one through some kind of goto.

On the potential_constant_expression_1 side, important change was to
set *jump_target conservatively on calls that could throw for C++26 (the
patch uses magic void_node for potential_constant_expression* instead of
VAR_DECL, so that we don't have to create new VAR_DECLs there uselessly).
Without that change, several methods in libstdc++ wouldn't work correctly.
I'm not sure what exactly potential_constant_expression_1 maps to in the
C++26 standard wording now and whether doing that is ok, because basically
after the first call to non-noexcept function it stops checking stuff.

And, in some spots where I know potential_constant_expression_1 didn't
check some subexpressions (e.g. the EH only cleanups or TRY_BLOCK handlers)
I've added *potential_constant_expression* calls during cxx_eval_constant*,
not sure if I need to do that because potential_constant_expression_1 is
very conservative and just doesn't recurse on subexpressions in many cases.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2025-06-02  Jakub Jelinek  <jakub@redhat.com>

	PR c++/117785
gcc/c-family/
	* c-cppbuiltin.cc (c_cpp_builtins): Predefine
	__cpp_constexpr_exceptions=202411L for C++26.
gcc/cp/
	* constexpr.cc: Implement C++26 P3068R5 - constexpr exceptions.
	(class constexpr_global_ctx): Add caught_exceptions and
	uncaught_exceptions members.
	(constexpr_global_ctx::constexpr_global_ctx): Initialize
	uncaught_exceptions.
	(returns, breaks, continues, switches): Move earlier.
	(throws): New function.
	(exception_what_str, diagnose_std_terminate,
	diagnose_uncaught_exception): New functions.
	(enum cxa_builtin): New type.
	(cxx_cxa_builtin_fn_p, cxx_eval_cxa_builtin_fn): New functions.
	(cxx_eval_builtin_function_call): Add jump_target argument.  Call
	cxx_eval_cxa_builtin_fn for __builtin_eh_ptr_adjust_ref.  Adjust
	cxx_eval_constant_expression calls, if it results in jmp_target,
	set *jump_target to it and return.
	(cxx_bind_parameters_in_call): Add jump_target argument.  Pass
	it through to cxx_eval_constant_expression.  If it sets *jump_target,
	break.
	(fold_operand): Adjust cxx_eval_constant_expression caller.
	(cxx_eval_assert): Likewise.  If it set jmp_target, return true.
	(cxx_eval_internal_function): Add jump_target argument.  Pass it
	through to cxx_eval_constant_expression.  Return early if
	*jump_target after recursing on args.
	(cxx_eval_dynamic_cast_fn): Likewise.  Don't set reference_p for
	C++26 with -fexceptions.
	(cxx_eval_thunk_call): Add jump_target argument.  Pass it through
	to cxx_eval_constant_expression.
	(cxx_set_object_constness): Likewise.  Don't set TREE_READONLY if
	throws (jump_target).
	(cxx_eval_call_expression): Add jump_target argument.  Pass it
	through to cxx_eval_internal_function, cxx_eval_builtin_function_call,
	cxx_eval_thunk_call, cxx_eval_dynamic_cast_fn and
	cxx_set_object_constness.  Pass it through also
	cxx_eval_constant_expression on arguments, cxx_bind_parameters_in_call
	and cxx_fold_indirect_ref and for those cases return early if
	*jump_target.  Call cxx_eval_cxa_builtin_fn for cxx_cxa_builtin_fn_p
	functions.  For cxx_eval_constant_expression on body, pass address of
	cleared jmp_target automatic variable, if it throws propagate to
	*jump_target and make it non-cacheable.
	(cxx_eval_unary_expression): Add jump_target argument.  Pass it
	through to cxx_eval_constant_expression and return early if
	*jump_target after the call.
	(cxx_fold_pointer_plus_expression): Likewise.
	(cxx_eval_binary_expression): Likewise and similarly for
	cxx_fold_pointer_plus_expression call.
	(cxx_eval_conditional_expression): Pass jump_target to
	cxx_eval_constant_expression on first operand and return early if
	*jump_target after the call.
	(cxx_eval_vector_conditional_expression): Add jump_target argument.
	Pass it through to cxx_eval_constant_expression for all 3 arguments
	and return early if *jump_target after any of those calls.
	(get_array_or_vector_nelts): Add jump_target argument.  Pass it
	through to cxx_eval_constant_expression.
	(eval_and_check_array_index): Add jump_target argument.  Pass it
	through to cxx_eval_constant_expression calls and return early after
	each of them if *jump_target.
	(cxx_eval_array_reference): Likewise.
	(cxx_eval_component_reference): Likewise.
	(cxx_eval_bit_field_ref): Likewise.
	(cxx_eval_bit_cast): Likewise.  Assert CHECKING_P call doesn't
	throw or return.
	(cxx_eval_logical_expression): Add jump_target argument.  Pass it
	through to cxx_eval_constant_expression calls and return early after
	each of them if *jump_target.
	(cxx_eval_bare_aggregate): Likewise.
	(cxx_eval_vec_init_1): Add jump_target argument.  Pass it through
	to cxx_eval_bare_aggregate and recursive call.  Pass it through
	to get_array_or_vector_nelts and cxx_eval_constant_expression
	and return early after it if *jump_target.
	(cxx_eval_vec_init): Add jump_target argument.  Pass it through
	to cxx_eval_constant_expression and cxx_eval_vec_init_1.
	(cxx_union_active_member): Add jump_target argument.  Pass it
	through to cxx_eval_constant_expression and return early after it
	if *jump_target.
	(cxx_fold_indirect_ref_1): Add jump_target argument.  Pass it
	through to cxx_union_active_member and recursive calls.
	(cxx_eval_indirect_ref): Add jump_target argument.  Pass it through
	to cxx_fold_indirect_ref_1 calls and to recursive call, in which
	case return early after it if *jump_target.
	(cxx_fold_indirect_ref): Add jump_target argument.  Pass it through
	to cxx_fold_indirect_ref and cxx_eval_constant_expression calls and
	return early after those if *jump_target.
	(cxx_eval_trinary_expression): Add jump_target argument.  Pass it
	through to cxx_eval_constant_expression calls and return early after
	those if *jump_target.
	(cxx_eval_store_expression): Add jump_target argument.  Pass it
	through to cxx_eval_constant_expression and eval_and_check_array_index
	calls and return early after those if *jump_target.
	(cxx_eval_increment_expression): Add jump_target argument.  Pass it
	through to cxx_eval_constant_expression calls and return early after
	those if *jump_target.
	(label_matches): Handle VAR_DECL case.
	(cxx_eval_statement_list): Remove local_target variable and
	!jump_target handling.  Handle throws (jump_target) like returns or
	breaks.
	(cxx_eval_loop_expr): Remove local_target variable and !jump_target
	handling.  Pass it through to cxx_eval_constant_expression.  Handle
	throws (jump_target) like returns.
	(cxx_eval_switch_expr): Pass jump_target through to
	cxx_eval_constant_expression on cond, return early after it if
	*jump_target.
	(build_new_constexpr_heap_type): Add jump_target argument.  Pass it
	through to cxx_eval_constant_expression calls, return early after
	those if *jump_target.
	(cxx_eval_constant_expression): Make jump_target argument no longer
	defaulted, don't test jump_target for NULL.  Pass jump_target
	through to recursive calls, cxx_eval_call_expression,
	cxx_eval_store_expression, cxx_eval_indirect_ref,
	cxx_eval_unary_expression, cxx_eval_binary_expression,
	cxx_eval_logical_expression, cxx_eval_array_reference,
	cxx_eval_component_reference, cxx_eval_bit_field_ref,
	cxx_eval_vector_conditional_expression, cxx_eval_bare_aggregate,
	cxx_eval_vec_init, cxx_eval_trinary_expression, cxx_fold_indirect_ref,
	build_new_constexpr_heap_type, cxx_eval_increment_expression,
	cxx_eval_bit_cast and return earlyu after some of those if
	*jump_target as needed.
	(cxx_eval_constant_expression) <case TARGET_EXPR>: For C++26 push
	also CLEANUP_EH_ONLY cleanups, with NULL_TREE marker after them.
	(cxx_eval_constant_expression) <case RETURN_EXPR>: Don't override
	*jump_target if throws (jump_target).
	(cxx_eval_constant_expression) <case TRY_CATCH_EXPR, case TRY_BLOCK,
	case MUST_NOT_THROW_EXPR, case TRY_FINALLY_EXPR, case CLEANUP_STMT>:
	Handle C++26 constant expressions.
	(cxx_eval_constant_expression) <case CLEANUP_POINT_EXPR>: Double the
	default size of cleanups vector.  For C++26 with throws (jump_target)
	evaluate the CLEANUP_EH_ONLY cleanups as well, and if not
	throws (jump_target) skip those.  Set *jump_target if some of the
	cleanups threw.
	(cxx_eval_constant_expression) <case THROW_EXPR>: Recurse on operand
	for C++26.
	(cxx_eval_outermost_constant_expr): Diagnose uncaught exceptions both
	from main expression and cleanups, diagnose also
	break/continue/returns from the main expression.  Handle
	CLEANUP_EH_ONLY cleanup markers.  Don't diagnose mutable poison stuff
	if non_constant_p.  Use different diagnostics for non-deleted heap
	allocations if they were allocated by __cxa_allocate_exception.
	(struct check_for_return_continue_data): Add could_throw field.
	(check_for_return_continue): Handle AGGR_INIT_EXPR and CALL_EXPR and
	set d->could_throw if they could throw.
	(potential_constant_expression_1): For CALL_EXPR allow
	cxx_dynamic_cast_fn_p calls.  For C++26 set *jump_target to void_node
	for calls that could throw.  Adjust check_for_return_continue_data
	initializers and set *jump_target to void_node if data.could_throw_p.
	For C++26 recurse on THROW_EXPR argument.  Add comment explaining
	TRY_BLOCK handling with C++26 exceptions.  Handle throws like returns
	in some cases.
	* cp-tree.h (MUST_NOT_THROW_NOEXCEPT_P, MUST_NOT_THROW_THROW_P,
	MUST_NOT_THROW_CATCH_P, DECL_EXCEPTION_REFCOUNT): Define.
	(DECL_LOCAL_DECL_P): Fix comment typo, VARIABLE_DECL -> VAR_DECL.
	(enum cp_built_in_function): Add CP_BUILT_IN_EH_PTR_ADJUST_REF,
	(handler_match_for_exception_type): Declare.
	* call.cc (handler_match_for_exception_type): New function.
	* except.cc (initialize_handler_parm): Set MUST_NOT_THROW_CATCH_P
	on newly created MUST_NOT_THROW_EXPR.
	(begin_eh_spec_block): Set MUST_NOT_THROW_NOEXCEPT_P.
	(wrap_cleanups_r): Set MUST_NOT_THROW_THROW_P.
	(build_throw): For C++26 add another TARGET_EXPR whose scope spans
	until after the __cxa_throw call and copy pointer value from ptr
	to it and use it in __cxa_throw argument.
	* tree.cc (builtin_valid_in_constant_expr_p): Handle
	CP_BUILT_IN_EH_PTR_ADJUST_REF.
	* decl.cc (cxx_init_decl_processing): Initialize
	__builtin_eh_ptr_adjust_ref FE builtin.
	* pt.cc (tsubst_stmt) <case MUST_NOT_THROW_EXPR>: Copy the
	MUST_NOT_THROW_NOEXCEPT_P, MUST_NOT_THROW_THROW_P and
	MUST_NOT_THROW_CATCH_P flags.
	* cp-gimplify.cc (cp_gimplify_expr) <case CALL_EXPR>: Error on
	non-folded CP_BUILT_IN_EH_PTR_ADJUST_REF calls.
gcc/testsuite/
	* g++.dg/cpp0x/constexpr-neg2.C: Don't expect error for C++26.
	* g++.dg/cpp0x/constexpr-throw.C: Expect different diagnostics for
	C++26.
	* g++.dg/cpp1y/constexpr-84192.C: Expect different diagnostics.
	* g++.dg/cpp1y/constexpr-throw.C: Expect different diagnostics for
	C++26.
	* g++.dg/cpp1z/constexpr-asm-5.C: Likewise.
	* g++.dg/cpp26/constexpr-eh1.C: New test.
	* g++.dg/cpp26/constexpr-eh2.C: New test.
	* g++.dg/cpp26/constexpr-eh3.C: New test.
	* g++.dg/cpp26/constexpr-eh4.C: New test.
	* g++.dg/cpp26/constexpr-eh5.C: New test.
	* g++.dg/cpp26/constexpr-eh6.C: New test.
	* g++.dg/cpp26/constexpr-eh7.C: New test.
	* g++.dg/cpp26/constexpr-eh8.C: New test.
	* g++.dg/cpp26/constexpr-eh9.C: New test.
	* g++.dg/cpp26/constexpr-eh10.C: New test.
	* g++.dg/cpp26/constexpr-eh11.C: New test.
	* g++.dg/cpp26/constexpr-eh12.C: New test.
	* g++.dg/cpp26/constexpr-eh13.C: New test.
	* g++.dg/cpp26/constexpr-eh14.C: New test.
	* g++.dg/cpp26/feat-cxx26.C: Change formatting in __cpp_pack_indexing
	and __cpp_pp_embed test.  Add __cpp_constexpr_exceptions test.
	* g++.dg/cpp26/static_assert1.C: Expect different diagnostics for
	C++26.
	* g++.dg/cpp2a/consteval34.C: Likewise.
	* g++.dg/cpp2a/consteval-memfn1.C: Likewise.
	* g++.dg/cpp2a/constexpr-dynamic4.C: For C++26 add std::exception and
	std::bad_cast definitions and expect different diagnostics.
	* g++.dg/cpp2a/constexpr-dynamic6.C: Likewise.
	* g++.dg/cpp2a/constexpr-dynamic7.C: Likewise.
	* g++.dg/cpp2a/constexpr-dynamic8.C: Likewise.
	* g++.dg/cpp2a/constexpr-dynamic9.C: Likewise.
	* g++.dg/cpp2a/constexpr-dynamic11.C: Likewise.
	* g++.dg/cpp2a/constexpr-dynamic14.C: Likewise.
	* g++.dg/cpp2a/constexpr-dynamic18.C: Likewise.
	* g++.dg/cpp2a/constexpr-new27.C: New test.
	* g++.dg/cpp2a/constexpr-typeid5.C: New test.
libstdc++-v3/
	* include/bits/version.def (constexpr_exceptions): New.
	* include/bits/version.h: Regenerate.
	* libsupc++/exception (std::bad_exception::bad_exception): Add
	_GLIBCXX26_CONSTEXPR.
	(std::bad_exception::~bad_exception, std::bad_exception::what): For
	C++26 add constexpr and define inline.
	* libsupc++/exception.h (std::exception::exception,
	std::exception::operator=): Add _GLIBCXX26_CONSTEXPR.
	(std::exception::~exception, std::exception::what): For C++26 add
	constexpr and define inline.
	* libsupc++/exception_ptr.h (std::make_exception_ptr): Add
	_GLIBCXX26_CONSTEXPR.  For if consteval use just throw with
	current_exception() in catch.
	(std::exception_ptr::exception_ptr(void*)): For C++26 add constexpr
	and define inline.
	(std::exception_ptr::exception_ptr()): Add _GLIBCXX26_CONSTEXPR.
	(std::exception_ptr::exception_ptr(const exception_ptr&)): Likewise.
	Use __builtin_eh_ptr_adjust_ref if consteval and compiler has it
	instead of _M_addref.
	(std::exception_ptr::exception_ptr(nullptr_t)): Add
	_GLIBCXX26_CONSTEXPR.
	(std::exception_ptr::exception_ptr(exception_ptr&&)): Likewise.
	(std::exception_ptr::operator=): Likewise.
	(std::exception_ptr::~exception_ptr): Likewise.  Use
	__builtin_eh_ptr_adjust_ref if consteval and compiler has it
	instead of _M_release.
	(std::exception_ptr::swap): Add _GLIBCXX26_CONSTEXPR.
	(std::exception_ptr::operator bool): Likewise.
	(std::exception_ptr::operator==): Likewise.
	* libsupc++/nested_exception.h
	(std::nested_exception::nested_exception): Add _GLIBCXX26_CONSTEXPR.
	(std::nested_exception::operator=): Likewise.
	(std::nested_exception::~nested_exception): For C++26 add constexpr
	and define inline.
	(std::nested_exception::rethrow_if_nested): Add _GLIBCXX26_CONSTEXPR.
	(std::nested_exception::nested_ptr): Likewise.
	(std::_Nested_exception::_Nested_exception): Likewise.
	(std::throw_with_nested, std::rethrow_if_nested): Likewise.
	* libsupc++/new (std::bad_alloc::bad_alloc): Likewise.
	(std::bad_alloc::operator=): Likewise.
	(std::bad_alloc::~bad_alloc): For C++26 add constexpr and define
	inline.
	(std::bad_alloc::what): Likewise.
	(std::bad_array_new_length::bad_array_new_length): Add
	_GLIBCXX26_CONSTEXPR.
	(std::bad_array_new_length::~bad_array_new_length): For C++26 add
	constexpr and define inline.
	(std::bad_array_new_length::what): Likewise.
	* libsupc++/typeinfo (std::bad_cast::bad_cast): Add
	_GLIBCXX26_CONSTEXPR.
	(std::bad_cast::~bad_cast): For C++26 add constexpr and define inline.
	(std::bad_cast::what): Likewise.
	(std::bad_typeid::bad_typeid): Add _GLIBCXX26_CONSTEXPR.
	(std::bad_typeid::~bad_typeid): For C++26 add constexpr and define
	inline.
	(std::bad_typeid::what): Likewise.

--- gcc/c-family/c-cppbuiltin.cc.jj	2025-05-31 00:43:13.824238680 +0200
+++ gcc/c-family/c-cppbuiltin.cc	2025-05-31 00:43:32.179996716 +0200
@@ -1087,6 +1087,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	{
 	  /* Set feature test macros for C++26.  */
 	  cpp_define (pfile, "__cpp_constexpr=202406L");
+	  cpp_define (pfile, "__cpp_constexpr_exceptions=202411L");
 	  cpp_define (pfile, "__cpp_static_assert=202306L");
 	  cpp_define (pfile, "__cpp_placeholder_variables=202306L");
 	  cpp_define (pfile, "__cpp_structured_bindings=202403L");
--- gcc/cp/constexpr.cc.jj	2025-05-31 00:43:13.830238601 +0200
+++ gcc/cp/constexpr.cc	2025-05-31 11:04:53.267297141 +0200
@@ -1182,6 +1182,10 @@ public:
   /* Heap VAR_DECLs created during the evaluation of the outermost constant
      expression.  */
   auto_vec<tree, 16> heap_vars;
+  /* Vector of caught exceptions, including exceptions still not active at
+     the start of a handler (those are immediately followed up by HANDLER_TYPE
+     until __cxa_begin_catch finishes).  */
+  auto_vec<tree, 2> caught_exceptions;
   /* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR.  */
   vec<tree> *cleanups;
   /* If non-null, only allow modification of existing values of the variables
@@ -1189,10 +1193,13 @@ public:
   hash_set<tree> *modifiable;
   /* Number of heap VAR_DECL deallocations.  */
   unsigned heap_dealloc_count;
+  /* Number of uncaught exceptions.  */
+  unsigned uncaught_exceptions;
+
   /* Constructor.  */
   constexpr_global_ctx ()
     : constexpr_ops_count (0), cleanups (NULL), modifiable (nullptr),
-      heap_dealloc_count (0) {}
+      heap_dealloc_count (0), uncaught_exceptions (0) {}
 
   bool is_outside_lifetime (tree t)
   {
@@ -1306,6 +1313,48 @@ struct constexpr_ctx {
   mce_value manifestly_const_eval;
 };
 
+/* Predicates for the meaning of *jump_target.  */
+
+static bool
+returns (tree *jump_target)
+{
+  return *jump_target && TREE_CODE (*jump_target) == RETURN_EXPR;
+}
+
+static bool
+breaks (tree *jump_target)
+{
+  return (*jump_target
+	  && ((TREE_CODE (*jump_target) == LABEL_DECL
+	       && LABEL_DECL_BREAK (*jump_target))
+	      || TREE_CODE (*jump_target) == BREAK_STMT
+	      || TREE_CODE (*jump_target) == EXIT_EXPR));
+}
+
+static bool
+continues (tree *jump_target)
+{
+  return (*jump_target
+	  && ((TREE_CODE (*jump_target) == LABEL_DECL
+	       && LABEL_DECL_CONTINUE (*jump_target))
+	      || TREE_CODE (*jump_target) == CONTINUE_STMT));
+}
+
+static bool
+switches (tree *jump_target)
+{
+  return *jump_target && TREE_CODE (*jump_target) == INTEGER_CST;
+}
+
+static bool
+throws (tree *jump_target)
+{
+  /* void_node is for use in potential_constant_expression_1, otherwise
+     it should an artificial VAR_DECL created by constant evaluation
+     of __cxa_allocate_exception ().  */
+  return (*jump_target && (VAR_P (*jump_target) || *jump_target == void_node));
+}
+
 /* True if the constexpr relaxations afforded by P2280R4 for unknown
    references and objects are in effect.  */
 
@@ -1541,13 +1590,656 @@ enum value_cat {
 };
 
 static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
-					  value_cat, bool *, bool *, tree * = NULL);
+					  value_cat, bool *, bool *, tree *);
 static tree cxx_eval_bare_aggregate (const constexpr_ctx *, tree,
-				     value_cat, bool *, bool *);
+				     value_cat, bool *, bool *, tree *);
 static tree cxx_fold_indirect_ref (const constexpr_ctx *, location_t, tree, tree,
-				   bool * = NULL);
+				   bool *, tree *);
 static tree find_heap_var_refs (tree *, int *, void *);
 
+/* For exception object EXC if it has class type and usable what () method
+   which returns cv char * return the xmalloced string literal which it returns
+   if possible, otherwise return NULL.  */
+
+static char *
+exception_what_str (const constexpr_ctx *ctx, tree exc)
+{
+  tree type = strip_array_types (TREE_TYPE (exc));
+  if (!CLASS_TYPE_P (type))
+    return NULL;
+  if (type != TREE_TYPE (exc))
+    exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL);
+  tree call
+    = finish_class_member_access_expr (exc, get_identifier ("what"), false,
+				       tf_none);
+  if (call == error_mark_node)
+    return NULL;
+  releasing_vec what_args;
+  call = finish_call_expr (call, &what_args, false, false, tf_none);
+  if (call == error_mark_node)
+    return NULL;
+  if (TREE_CODE (TREE_TYPE (call)) != POINTER_TYPE
+      || !INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (call)))
+      || !COMPLETE_TYPE_P (TREE_TYPE (TREE_TYPE (call)))
+      || !tree_int_cst_equal (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (call))),
+			      TYPE_SIZE_UNIT (char_type_node))
+      || TYPE_PRECISION (TREE_TYPE (TREE_TYPE (call))) != BITS_PER_UNIT)
+    return NULL;
+  if (!potential_constant_expression (call))
+    return NULL;
+  bool non_constant_p = false, overflow_p = false;
+  tree jmp_target = NULL;
+  tree ptr = cxx_eval_constant_expression (ctx, call, vc_prvalue,
+					   &non_constant_p, &overflow_p,
+					   &jmp_target);
+  if (!throws (&jmp_target)
+      && !non_constant_p
+      && reduced_constant_expression_p (ptr))
+    if (const char *msg = c_getstr (ptr))
+      return xstrdup (msg);
+  auto_vec <char, 32> v;
+  for (unsigned i = 0; i < INT_MAX; ++i)
+    {
+      tree t = call;
+      if (i)
+	t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (call), call, size_int (i));
+      t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t);
+      non_constant_p = false;
+      overflow_p = false;
+      jmp_target = NULL;
+      tree t2 = cxx_eval_constant_expression (ctx, t, vc_prvalue,
+					      &non_constant_p, &overflow_p,
+					      &jmp_target);
+      if (throws (&jmp_target)
+	  || non_constant_p
+	  || !tree_fits_shwi_p (t2))
+	return NULL;
+      char c = tree_to_shwi (t2);
+      v.safe_push (c);
+      if (c == '\0')
+	break;
+    }
+  return xstrdup (v.address ());
+}
+
+/* Diagnose constant expression evaluation encountering call to
+   std::terminate due to exception EXC.  */
+
+static void
+diagnose_std_terminate (location_t loc, const constexpr_ctx *ctx, tree exc)
+{
+  tree type = strip_array_types (TREE_TYPE (exc));
+  if (char *str = exception_what_str (ctx, exc))
+    {
+      error_at (loc, "%qs called after throwing an exception of type %qT; "
+		     "%<what()%>: %qs", "std::terminate", type, str);
+      free (str);
+    }
+  else
+    {
+      if (type != TREE_TYPE (exc))
+	exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL);
+      bool non_constant_p = false, overflow_p = false;
+      tree jmp_target = NULL;
+      tree val = cxx_eval_constant_expression (ctx, exc, vc_prvalue,
+					       &non_constant_p, &overflow_p,
+					       &jmp_target);
+      if (!throws (&jmp_target)
+	  && !non_constant_p
+	  && reduced_constant_expression_p (val))
+	error_at (loc, "%qs called after throwing an exception %qE",
+		  "std::terminate", val);
+      else
+	error_at (loc, "%qs called after throwing an exception of type %qT",
+		  "std::terminate", type);
+    }
+}
+
+/* Diagnose constant expression evaluation encountering call to
+   uncaught exception EXC.  */
+
+static void
+diagnose_uncaught_exception (location_t loc, const constexpr_ctx *ctx, tree exc)
+{
+  tree type = strip_array_types (TREE_TYPE (exc));
+  if (char *str = exception_what_str (ctx, exc))
+    {
+      error_at (loc, "uncaught exception of type %qT; %<what()%>: %qs", type, str);
+      free (str);
+    }
+  else
+    {
+      if (type != TREE_TYPE (exc))
+	exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL);
+      bool non_constant_p = false, overflow_p = false;
+      tree jmp_target = NULL;
+      tree val = cxx_eval_constant_expression (ctx, exc, vc_prvalue,
+					       &non_constant_p, &overflow_p,
+					       &jmp_target);
+      if (!throws (&jmp_target)
+	  && !non_constant_p
+	  && reduced_constant_expression_p (val))
+	error_at (loc, "uncaught exception %qE", val);
+      else
+	error_at (loc, "uncaught exception of type %qT", type);
+    }
+}
+
+/* Kinds of __cxa_* functions (and a few other EH related ones) we handle as
+   magic constexpr functions for C++26.  */
+
+enum cxa_builtin {
+  CXA_NONE = 0,
+  CXA_ALLOCATE_EXCEPTION = 1,
+  CXA_FREE_EXCEPTION = 2,
+  CXA_THROW = 3,
+  CXA_BEGIN_CATCH = 4,
+  CXA_END_CATCH = 5,
+  CXA_RETHROW = 6,
+  CXA_GET_EXCEPTION_PTR = 7,
+  CXA_BAD_CAST = 8,
+  CXA_BAD_TYPEID = 9,
+  CXA_THROW_BAD_ARRAY_NEW_LENGTH = 10,
+  STD_UNCAUGHT_EXCEPTIONS = 11,
+  STD_CURRENT_EXCEPTION = 12,
+  STD_RETHROW_EXCEPTION = 13,
+  BUILTIN_EH_PTR_ADJUST_REF = 14
+};
+
+/* Return cxa_builtin if FNDECL is a __cxa_* function handled as
+   magic constexpr function for C++26.  Return CXA_NONE otherwise.  */
+
+static enum cxa_builtin
+cxx_cxa_builtin_fn_p (tree fndecl)
+{
+  if (cxx_dialect < cxx26)
+    return CXA_NONE;
+  if (DECL_LANGUAGE (fndecl) != lang_c)
+    {
+      if (!decl_in_std_namespace_p (fndecl))
+	return CXA_NONE;
+      if (id_equal (DECL_NAME (fndecl), "uncaught_exceptions"))
+	return STD_UNCAUGHT_EXCEPTIONS;
+      if (id_equal (DECL_NAME (fndecl), "current_exception"))
+	return STD_CURRENT_EXCEPTION;
+      if (id_equal (DECL_NAME (fndecl), "rethrow_exception"))
+	return STD_RETHROW_EXCEPTION;
+      return CXA_NONE;
+    }
+  if (!startswith (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "__cxa_"))
+    return CXA_NONE;
+  if (id_equal (DECL_NAME (fndecl), "__cxa_allocate_exception"))
+    return CXA_ALLOCATE_EXCEPTION;
+  if (id_equal (DECL_NAME (fndecl), "__cxa_free_exception"))
+    return CXA_FREE_EXCEPTION;
+  if (id_equal (DECL_NAME (fndecl), "__cxa_throw"))
+    return CXA_THROW;
+  if (id_equal (DECL_NAME (fndecl), "__cxa_begin_catch"))
+    return CXA_BEGIN_CATCH;
+  if (id_equal (DECL_NAME (fndecl), "__cxa_end_catch"))
+    return CXA_END_CATCH;
+  if (id_equal (DECL_NAME (fndecl), "__cxa_rethrow"))
+    return CXA_RETHROW;
+  if (id_equal (DECL_NAME (fndecl), "__cxa_get_exception_ptr"))
+    return CXA_GET_EXCEPTION_PTR;
+  if (id_equal (DECL_NAME (fndecl), "__cxa_bad_cast"))
+    return CXA_BAD_CAST;
+  if (id_equal (DECL_NAME (fndecl), "__cxa_bad_typeid"))
+    return CXA_BAD_TYPEID;
+  if (id_equal (DECL_NAME (fndecl), "__cxa_throw_bad_array_new_length"))
+    return CXA_THROW_BAD_ARRAY_NEW_LENGTH;
+  return CXA_NONE;
+}
+
+/* Evaluate various __cxa_* calls as magic constexpr builtins for
+   C++26 constexpr exception support (P3068R5).  */
+
+static tree
+cxx_eval_cxa_builtin_fn (const constexpr_ctx *ctx, tree call,
+			 enum cxa_builtin kind, tree fndecl,
+			 bool *non_constant_p, bool *overflow_p,
+			 tree *jump_target)
+{
+  int nargs = call_expr_nargs (call);
+  location_t loc = cp_expr_loc_or_input_loc (call);
+  tree args[4], arg;
+  if (nargs > 4)
+    {
+    invalid_nargs:
+      if (!ctx->quiet)
+	error_at (loc, "call to %qD function with incorrect"
+		  "number of arguments", fndecl);
+      *non_constant_p = true;
+      return call;
+    }
+  if ((kind == CXA_BEGIN_CATCH || kind == CXA_GET_EXCEPTION_PTR)
+      && nargs == 1
+      && (arg = CALL_EXPR_ARG (call, 0))
+      && TREE_CODE (arg) == CALL_EXPR
+      && call_expr_nargs (arg) == 1
+      && integer_zerop (CALL_EXPR_ARG (arg, 0)))
+    if (tree fun = get_function_named_in_call (arg))
+      if (fndecl_built_in_p (fun, BUILT_IN_EH_POINTER))
+	{
+	  if (ctx->global->caught_exceptions.length () < 2)
+	    {
+	    no_caught_exceptions:
+	      if (!ctx->quiet)
+		error_at (loc, "%qD called with no caught exceptions pending",
+			  fndecl);
+	      *non_constant_p = true;
+	      return call;
+	    }
+	  tree handler_type = ctx->global->caught_exceptions.last ();
+	  if (handler_type && VAR_P (handler_type))
+	    goto no_caught_exceptions;
+	  unsigned idx = ctx->global->caught_exceptions.length () - 2;
+	  arg = ctx->global->caught_exceptions[idx];
+	  gcc_assert (VAR_P (arg));
+	  if (kind == CXA_BEGIN_CATCH)
+	    {
+	      ctx->global->caught_exceptions.pop ();
+	      --ctx->global->uncaught_exceptions;
+	    }
+	  if (handler_type == NULL_TREE)
+	    /* __cxa_begin_catch used for catch (...).  Just return
+	       void.  */
+	    return void_node;
+	  else if (POINTER_TYPE_P (handler_type))
+	    {
+	      /* __cxa_begin_catch used for catch of a pointer.  */
+	      if (TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE)
+		arg = build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (arg)), arg,
+			      size_zero_node, NULL_TREE, NULL_TREE);
+	      arg = cp_convert (handler_type, arg,
+				ctx->quiet ? tf_none : tf_warning_or_error);
+	      if (arg == error_mark_node)
+		{
+		  *non_constant_p = true;
+		  return call;
+		}
+	    }
+	  else
+	    {
+	      /* __cxa_begin_catch used for catch of a non-pointer type.  */
+	      tree exc_type = strip_array_types (TREE_TYPE (arg));
+	      tree exc_ptr_type = build_pointer_type (exc_type);
+	      arg = build_fold_addr_expr_with_type (arg, exc_ptr_type);
+	      if (CLASS_TYPE_P (handler_type))
+		{
+		  tree ptr_type = build_pointer_type (handler_type);
+		  arg = cp_build_c_cast (loc, ptr_type, arg,
+					 ctx->quiet ? tf_none
+					 : tf_warning_or_error);
+		  if (arg == error_mark_node)
+		    {
+		      *non_constant_p = true;
+		      return call;
+		    }
+		}
+	    }
+	  return cxx_eval_constant_expression (ctx, arg, vc_prvalue,
+					       non_constant_p, overflow_p,
+					       jump_target);
+	}
+  for (int i = 0; i < nargs; ++i)
+    {
+      args[i] = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (call, i),
+					      vc_prvalue, non_constant_p,
+					      overflow_p, jump_target);
+      if (*non_constant_p)
+	return call;
+      if (*jump_target)
+	return NULL_TREE;
+    }
+  switch (kind)
+    {
+    case CXA_ALLOCATE_EXCEPTION:
+      if (nargs != 1)
+	goto invalid_nargs;
+      if (!tree_fits_uhwi_p (args[0]))
+	{
+	  if (!ctx->quiet)
+	    error_at (loc, "cannot allocate exception: size not constant");
+	  *non_constant_p = true;
+	  return call;
+	}
+      else
+	{
+	  tree type = build_array_type_nelts (char_type_node,
+					      tree_to_uhwi (args[0]));
+	  tree var = build_decl (loc, VAR_DECL, heap_uninit_identifier,
+				 type);
+	  DECL_ARTIFICIAL (var) = 1;
+	  retrofit_lang_decl (var);
+	  DECL_EXCEPTION_REFCOUNT (var) = size_zero_node;
+	  ctx->global->heap_vars.safe_push (var);
+	  ctx->global->put_value (var, NULL_TREE);
+	  return fold_convert (ptr_type_node, build_address (var));
+	}
+    case CXA_FREE_EXCEPTION:
+      if (nargs != 1)
+	goto invalid_nargs;
+      STRIP_NOPS (args[0]);
+      if (TREE_CODE (args[0]) != ADDR_EXPR)
+	{
+	invalid_ptr:
+	  if (!ctx->quiet)
+	    error_at (loc, "first argument to %qD function not result of "
+		      "%<__cxa_allocate_exception%>", fndecl);
+	  *non_constant_p = true;
+	  return call;
+	}
+      arg = TREE_OPERAND (args[0], 0);
+      if (!VAR_P (arg)
+	  || !DECL_ARTIFICIAL (arg)
+	  || (DECL_NAME (arg) != heap_uninit_identifier
+	      && DECL_NAME (arg) != heap_identifier)
+	  || !DECL_LANG_SPECIFIC (arg))
+	goto invalid_ptr;
+      DECL_NAME (arg) = heap_deleted_identifier;
+      ctx->global->destroy_value (arg);
+      ctx->global->heap_dealloc_count++;
+      return void_node;
+    case CXA_THROW:
+      if (nargs != 3)
+	goto invalid_nargs;
+      STRIP_NOPS (args[0]);
+      if (TREE_CODE (args[0]) != ADDR_EXPR)
+	goto invalid_ptr;
+      arg = TREE_OPERAND (args[0], 0);
+      if (!VAR_P (arg)
+	  || !DECL_ARTIFICIAL (arg)
+	  || DECL_NAME (arg) != heap_identifier
+	  || !DECL_LANG_SPECIFIC (arg))
+	goto invalid_ptr;
+      DECL_EXCEPTION_REFCOUNT (arg)
+	= size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg),
+		      size_one_node);
+      ++ctx->global->uncaught_exceptions;
+      *jump_target = arg;
+      return void_node;
+    case CXA_BEGIN_CATCH:
+    case CXA_GET_EXCEPTION_PTR:
+      goto invalid_nargs;
+    case CXA_END_CATCH:
+      if (nargs != 0)
+	goto invalid_nargs;
+      if (ctx->global->caught_exceptions.is_empty ())
+	{
+	  if (!ctx->quiet)
+	    error_at (loc, "%qD called with no caught exceptions active",
+		      fndecl);
+	  *non_constant_p = true;
+	  return call;
+	}
+      else
+	{
+	  arg = ctx->global->caught_exceptions.pop ();
+	  if (arg == NULL_TREE || !VAR_P (arg))
+	    {
+	      gcc_assert (!ctx->global->caught_exceptions.is_empty ());
+	      arg = ctx->global->caught_exceptions.pop ();
+	    }
+	  gcc_assert (VAR_P (arg));
+	free_except:
+	  DECL_EXCEPTION_REFCOUNT (arg)
+	    = size_binop (MINUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg),
+			  size_one_node);
+	  if (integer_zerop (DECL_EXCEPTION_REFCOUNT (arg)))
+	    {
+	      tree type = strip_array_types (TREE_TYPE (arg));
+	      if (type_build_dtor_call (type))
+		{
+		  tree cleanup
+		    = cxx_maybe_build_cleanup (arg, (ctx->quiet ? tf_none
+						     : tf_warning_or_error));
+		  if (cleanup == error_mark_node)
+		    *non_constant_p = true;
+		  tree jmp_target = NULL_TREE;
+		  cxx_eval_constant_expression (ctx, cleanup, vc_discard,
+						non_constant_p, overflow_p,
+						&jmp_target);
+		  if (throws (&jmp_target))
+		    *jump_target = jmp_target;
+		}
+	      DECL_NAME (arg) = heap_deleted_identifier;
+	      ctx->global->destroy_value (arg);
+	      ctx->global->heap_dealloc_count++;
+	    }
+	}
+      return void_node;
+    case CXA_RETHROW:
+      if (nargs != 0)
+	goto invalid_nargs;
+      unsigned idx;
+      FOR_EACH_VEC_ELT_REVERSE (ctx->global->caught_exceptions, idx, arg)
+	if (arg == NULL_TREE || !VAR_P (arg))
+	  --idx;
+	else
+	  break;
+      if (arg == NULL_TREE)
+	{
+	  if (!ctx->quiet)
+	    error_at (loc, "%qD called with no caught exceptions active",
+		      fndecl);
+	  *non_constant_p = true;
+	  return call;
+	}
+      DECL_EXCEPTION_REFCOUNT (arg)
+	= size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), size_one_node);
+      ++ctx->global->uncaught_exceptions;
+      *jump_target = arg;
+      return void_node;
+    case CXA_BAD_CAST:
+    case CXA_BAD_TYPEID:
+    case CXA_THROW_BAD_ARRAY_NEW_LENGTH:
+      if (nargs != 0)
+	goto invalid_nargs;
+      else
+	{
+	  tree name;
+	  switch (kind)
+	    {
+	    case CXA_BAD_CAST:
+	      name = get_identifier ("bad_cast");
+	      break;
+	    case CXA_BAD_TYPEID:
+	      name = get_identifier ("bad_typeid");
+	      break;
+	    case CXA_THROW_BAD_ARRAY_NEW_LENGTH:
+	      name = get_identifier ("bad_array_new_length");
+	      break;
+	    default:
+	      gcc_unreachable ();
+	    }
+	  tree decl = lookup_qualified_name (std_node, name);
+	  if (TREE_CODE (decl) != TYPE_DECL
+	      || !CLASS_TYPE_P (TREE_TYPE (decl))
+	      || !type_build_ctor_call (TREE_TYPE (decl)))
+	    {
+	      if (!ctx->quiet)
+		error_at (loc, "%qD called without %<std::%D%> being defined",
+			  fndecl, name);
+	      *non_constant_p = true;
+	      return call;
+	    }
+	  tree type = TREE_TYPE (decl);
+	  tree var = build_decl (loc, VAR_DECL, heap_identifier, type);
+	  DECL_ARTIFICIAL (var) = 1;
+	  retrofit_lang_decl (var);
+	  DECL_EXCEPTION_REFCOUNT (var) = size_one_node;
+	  tree ctor
+	    = build_special_member_call (var, complete_ctor_identifier,
+					 NULL, type, LOOKUP_NORMAL,
+					 ctx->quiet ? tf_none
+					 : tf_warning_or_error);
+	  if (ctor == error_mark_node)
+	    {
+	      *non_constant_p = true;
+	      return call;
+	    }
+	  ctx->global->heap_vars.safe_push (var);
+	  if (TREE_CONSTANT (ctor))
+	    ctx->global->put_value (var, ctor);
+	  else
+	    {
+	      ctx->global->put_value (var, NULL_TREE);
+	      cxx_eval_constant_expression (ctx, ctor, vc_discard,
+					    non_constant_p, overflow_p,
+					    jump_target);
+	      if (*non_constant_p)
+		return call;
+	      if (throws (jump_target))
+		return NULL_TREE;
+	    }
+	  ++ctx->global->uncaught_exceptions;
+	  *jump_target = var;
+	}
+      return void_node;
+    case STD_UNCAUGHT_EXCEPTIONS:
+      if (nargs != 0)
+	goto invalid_nargs;
+      /* Similarly to __builtin_is_constant_evaluated (), we don't
+	 want to give a definite answer during mce_unknown evaluation,
+	 because that might prevent evaluation later on when some
+	 exceptions might be uncaught.  But unlikely to that, we don't
+	 want to constant fold it even during cp_fold, because at runtime
+	 std::uncaught_exceptions () might still be non-zero.  */
+      if (ctx->manifestly_const_eval != mce_true)
+	{
+	  *non_constant_p = true;
+	  return call;
+	}
+      return build_int_cst (integer_type_node,
+			    ctx->global->uncaught_exceptions);
+    case STD_CURRENT_EXCEPTION:
+      if (nargs != 0)
+	goto invalid_nargs;
+      else
+	{
+	  tree name = get_identifier ("exception_ptr");
+	  tree decl = lookup_qualified_name (std_node, name);
+	  tree fld;
+	  if (TREE_CODE (decl) != TYPE_DECL
+	      || !CLASS_TYPE_P (TREE_TYPE (decl))
+	      || !COMPLETE_TYPE_P (TREE_TYPE (decl))
+	      || !(fld = next_aggregate_field (TYPE_FIELDS (TREE_TYPE (decl))))
+	      || DECL_ARTIFICIAL (fld)
+	      || TREE_CODE (TREE_TYPE (fld)) != POINTER_TYPE
+	      || next_aggregate_field (DECL_CHAIN (fld))
+	      || !tree_int_cst_equal (TYPE_SIZE (TREE_TYPE (decl)),
+				      TYPE_SIZE (TREE_TYPE (fld))))
+	    {
+	      if (!ctx->quiet)
+		error_at (loc, "%qD called without supportable %qs",
+			  fndecl, "std::exception_ptr");
+	      *non_constant_p = true;
+	      return call;
+	    }
+	  FOR_EACH_VEC_ELT_REVERSE (ctx->global->caught_exceptions, idx, arg)
+	    if (arg == NULL_TREE || !VAR_P (arg))
+	      --idx;
+	    else
+	      break;
+	  /* Similarly to __builtin_is_constant_evaluated (), we don't
+	     want to give a definite answer during mce_unknown evaluation,
+	     because that might prevent evaluation later on when some
+	     exceptions might be current.  But unlikely to that, we don't
+	     want to constant fold it even during cp_fold, because at runtime
+	     std::current_exception () might still be non-null.  */
+	  if (ctx->manifestly_const_eval == mce_unknown
+	      || (ctx->manifestly_const_eval == mce_false && arg == NULL_TREE))
+	    {
+	      *non_constant_p = true;
+	      return call;
+	    }
+	  if (arg == NULL_TREE)
+	    arg = build_zero_cst (TREE_TYPE (fld));
+	  else
+	    {
+	      DECL_EXCEPTION_REFCOUNT (arg)
+		= size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg),
+			      size_one_node);
+	      arg = fold_convert (ptr_type_node, build_address (arg));
+	    }
+	  return build_constructor_single (TREE_TYPE (decl), fld, arg);
+	}
+    case STD_RETHROW_EXCEPTION:
+      if (nargs != 1)
+	goto invalid_nargs;
+      if (TYPE_REF_P (TREE_TYPE (args[0])))
+	{
+	  arg = args[0];
+	  STRIP_NOPS (arg);
+	  if (TREE_CODE (arg) == ADDR_EXPR)
+	    {
+	      args[0]
+		= cxx_eval_constant_expression (ctx, TREE_OPERAND (arg, 0),
+						vc_prvalue, non_constant_p,
+						overflow_p, jump_target);
+	      if (*non_constant_p)
+		return call;
+	      if (*jump_target)
+		return NULL_TREE;
+	    }
+	}
+      if (TREE_CODE (args[0]) != CONSTRUCTOR
+	  || CONSTRUCTOR_NELTS (args[0]) != 1)
+	{
+	invalid_std_rethrow:
+	  if (!ctx->quiet)
+	    error_at (loc, "%qD called with %qs argument other than "
+		      "null pointer", fndecl, "std::exception_ptr");
+	  *non_constant_p = true;
+	  return void_node;
+	}
+      arg = CONSTRUCTOR_ELT (args[0], 0)->value;
+      STRIP_NOPS (arg);
+      if (TREE_CODE (arg) != ADDR_EXPR)
+	goto invalid_std_rethrow;
+      arg = TREE_OPERAND (arg, 0);
+      if (!VAR_P (arg)
+	  || !DECL_ARTIFICIAL (arg)
+	  || DECL_NAME (arg) != heap_identifier
+	  || !DECL_LANG_SPECIFIC (arg))
+	goto invalid_std_rethrow;
+      DECL_EXCEPTION_REFCOUNT (arg)
+	= size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), size_one_node);
+      ++ctx->global->uncaught_exceptions;
+      *jump_target = arg;
+      return void_node;
+    case BUILTIN_EH_PTR_ADJUST_REF:
+      if (nargs != 2)
+	goto invalid_nargs;
+      STRIP_NOPS (args[0]);
+      if (TREE_CODE (args[0]) != ADDR_EXPR)
+	goto invalid_ptr;
+      arg = TREE_OPERAND (args[0], 0);
+      if (!VAR_P (arg)
+	  || !DECL_ARTIFICIAL (arg)
+	  || DECL_NAME (arg) != heap_identifier
+	  || !DECL_LANG_SPECIFIC (arg))
+	goto invalid_ptr;
+      if (integer_onep (args[1]))
+	DECL_EXCEPTION_REFCOUNT (arg)
+	  = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg),
+			size_one_node);
+      else if (integer_minus_onep (args[1]))
+	goto free_except;
+      else
+	{
+	  if (!ctx->quiet)
+	    error_at (loc, "%qD called with second argument "
+		      "other than 1 or -1", fndecl);
+	  *non_constant_p = true;
+	}
+      return void_node;
+    default:
+      gcc_unreachable ();
+    }
+}
+
 /* Attempt to evaluate T which represents a call to a builtin function.
    We assume here that all builtin functions evaluate to scalar types
    represented by _CST nodes.  */
@@ -1555,7 +2247,8 @@ static tree find_heap_var_refs (tree *,
 static tree
 cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
 				value_cat lval,
-				bool *non_constant_p, bool *overflow_p)
+				bool *non_constant_p, bool *overflow_p,
+				tree *jump_target)
 {
   const int nargs = call_expr_nargs (t);
   tree *args = (tree *) alloca (nargs * sizeof (tree));
@@ -1601,6 +2294,12 @@ cxx_eval_builtin_function_call (const co
       return fold_builtin_source_location (t);
     }
 
+  if (fndecl_built_in_p (fun, CP_BUILT_IN_EH_PTR_ADJUST_REF,
+			 BUILT_IN_FRONTEND))
+    return cxx_eval_cxa_builtin_fn (ctx, t, BUILTIN_EH_PTR_ADJUST_REF,
+				    fun, non_constant_p, overflow_p,
+				    jump_target);
+
   int strops = 0;
   int strret = 0;
   if (fndecl_built_in_p (fun, BUILT_IN_NORMAL))
@@ -1675,8 +2374,14 @@ cxx_eval_builtin_function_call (const co
 	  || potential_constant_expression (arg))
 	{
 	  bool dummy1 = false, dummy2 = false;
+	  tree jmp_target = NULL_TREE;
 	  arg = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
-					      &dummy1, &dummy2);
+					      &dummy1, &dummy2, &jmp_target);
+	  if (jmp_target)
+	    {
+	      *jump_target = jmp_target;
+	      return NULL_TREE;
+	    }
 	}
 
       if (bi_const_p)
@@ -1765,7 +2470,8 @@ cxx_eval_builtin_function_call (const co
     }
 
   return cxx_eval_constant_expression (&new_ctx, new_call, lval,
-				       non_constant_p, overflow_p);
+				       non_constant_p, overflow_p,
+				       jump_target);
 }
 
 /* TEMP is the constant value of a temporary object of type TYPE.  Adjust
@@ -1880,7 +2586,7 @@ addr_of_non_const_var (tree *tp, int *wa
 static tree
 cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun,
 			     bool *non_constant_p, bool *overflow_p,
-			     bool *non_constant_args)
+			     bool *non_constant_args, tree *jump_target)
 {
   const int nargs = call_expr_nargs (t);
   tree parms = DECL_ARGUMENTS (fun);
@@ -1925,14 +2631,16 @@ cxx_bind_parameters_in_call (const const
 	  /* Undo convert_for_arg_passing work here.  */
 	  x = convert_from_reference (x);
 	  arg = cxx_eval_constant_expression (ctx, x, vc_glvalue,
-					      non_constant_p, overflow_p);
+					      non_constant_p, overflow_p,
+					      jump_target);
 	}
       else
 	/* Normally we would strip a TARGET_EXPR in an initialization context
 	   such as this, but here we do the elision differently: we keep the
 	   TARGET_EXPR, and use its CONSTRUCTOR as the value of the parm.  */
 	arg = cxx_eval_constant_expression (ctx, x, vc_prvalue,
-					    non_constant_p, overflow_p);
+					    non_constant_p, overflow_p,
+					    jump_target);
       /* Check we aren't dereferencing a null pointer when calling a non-static
 	 member function, which is undefined behaviour.  */
       if (i == 0 && DECL_OBJECT_MEMBER_FUNCTION_P (fun)
@@ -1950,6 +2658,8 @@ cxx_bind_parameters_in_call (const const
       /* Don't VERIFY_CONSTANT here.  */
       if (*non_constant_p && ctx->quiet)
 	break;
+      if (*jump_target)
+	break;
       /* Just discard ellipsis args after checking their constantitude.  */
       if (!parms)
 	continue;
@@ -2061,9 +2771,10 @@ fold_operand (tree e, const constexpr_ct
   if (ctx)
     {
       bool new_non_constant_p = false, new_overflow_p = false;
+      tree jmp_target = NULL_TREE;
       e = cxx_eval_constant_expression (ctx, e, vc_prvalue,
 					&new_non_constant_p,
-					&new_overflow_p);
+					&new_overflow_p, &jmp_target);
     }
   else
     e = fold_non_dependent_expr (e, tf_none, /*manifestly_const_eval=*/true);
@@ -2150,7 +2861,7 @@ cxx_eval_assert (const constexpr_ctx *ct
   if (*non_constant_p)
     return true;
 
-  tree eval;
+  tree eval, jmp_target = NULL_TREE;
   if (!evaluated)
     {
       if (!potential_rvalue_constant_expression (arg))
@@ -2163,12 +2874,15 @@ cxx_eval_assert (const constexpr_ctx *ct
       modifiable_tracker ms (new_ctx.global);
       eval = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
 					   &new_non_constant_p,
-					   &new_overflow_p);
+					   &new_overflow_p, &jmp_target);
     }
   else
     eval = cxx_eval_constant_expression (ctx, arg, vc_prvalue,
 					 non_constant_p,
-					 overflow_p);
+					 overflow_p, &jmp_target);
+  if (jmp_target)
+    return true;
+
   if (!*non_constant_p && integer_zerop (eval))
     {
       if (!ctx->quiet)
@@ -2200,7 +2914,8 @@ cxx_eval_assert (const constexpr_ctx *ct
 static tree
 cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
 			    value_cat lval,
-			    bool *non_constant_p, bool *overflow_p)
+			    bool *non_constant_p, bool *overflow_p,
+			    tree *jump_target)
 {
   enum tree_code opcode = ERROR_MARK;
 
@@ -2233,13 +2948,15 @@ cxx_eval_internal_function (const conste
     case IFN_LAUNDER:
       return cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
 					   vc_prvalue, non_constant_p,
-					   overflow_p);
+					   overflow_p, jump_target);
 
     case IFN_VEC_CONVERT:
       {
 	tree arg = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
 						 vc_prvalue, non_constant_p,
-						 overflow_p);
+						 overflow_p, jump_target);
+	if (*jump_target)
+	  return NULL_TREE;
 	if (TREE_CODE (arg) == VECTOR_CST)
 	  if (tree r = fold_const_call (CFN_VEC_CONVERT, TREE_TYPE (t), arg))
 	    return r;
@@ -2257,10 +2974,13 @@ cxx_eval_internal_function (const conste
   /* Evaluate constant arguments using OPCODE and return a complex
      number containing the result and the overflow bit.  */
   tree arg0 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), lval,
-					    non_constant_p, overflow_p);
+					    non_constant_p, overflow_p,
+					    jump_target);
   tree arg1 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 1), lval,
-					    non_constant_p, overflow_p);
-
+					    non_constant_p, overflow_p,
+					    jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
     {
       location_t loc = cp_expr_loc_or_input_loc (t);
@@ -2536,7 +3256,8 @@ get_component_with_type (tree path, tree
 
 static tree
 cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
-			  bool *non_constant_p, bool *overflow_p)
+			  bool *non_constant_p, bool *overflow_p,
+			  tree *jump_target)
 {
   /* T will be something like
       __dynamic_cast ((B*) b, &_ZTI1B, &_ZTI1D, 8)
@@ -2559,15 +3280,18 @@ cxx_eval_dynamic_cast_fn (const constexp
   bool reference_p = false;
   while (CONVERT_EXPR_P (obj) || TREE_CODE (obj) == SAVE_EXPR)
     {
-      reference_p |= TYPE_REF_P (TREE_TYPE (obj));
+      if (cxx_dialect < cxx26 || !flag_exceptions)
+	reference_p |= TYPE_REF_P (TREE_TYPE (obj));
       obj = TREE_OPERAND (obj, 0);
     }
 
   /* Evaluate the object so that we know its dynamic type.  */
   obj = cxx_eval_constant_expression (ctx, obj, vc_prvalue, non_constant_p,
-				      overflow_p);
+				      overflow_p, jump_target);
   if (*non_constant_p)
     return call;
+  if (*jump_target)
+    return NULL_TREE;
 
   /* We expect OBJ to be in form of &d.D.2102 when HINT == 0,
      but when HINT is > 0, it can also be something like
@@ -2601,9 +3325,12 @@ cxx_eval_dynamic_cast_fn (const constexp
      constructor or destructor's class.  */
   tree vtable = build_vfield_ref (obj, objtype);
   vtable = cxx_eval_constant_expression (ctx, vtable, vc_prvalue,
-					 non_constant_p, overflow_p);
+					 non_constant_p, overflow_p,
+					 jump_target);
   if (*non_constant_p)
     return call;
+  if (*jump_target)
+    return NULL_TREE;
   /* With -fsanitize=vptr, we initialize all vtable pointers to null,
      so it's possible that we got a null pointer now.  */
   if (integer_zerop (vtable))
@@ -2786,7 +3513,7 @@ replace_decl (tree *tp, tree decl, tree
 static tree
 cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl,
 		     value_cat lval,
-		     bool *non_constant_p, bool *overflow_p)
+		     bool *non_constant_p, bool *overflow_p, tree *jump_target)
 {
   tree function = THUNK_TARGET (thunk_fndecl);
 
@@ -2829,7 +3556,8 @@ cxx_eval_thunk_call (const constexpr_ctx
 		       new_call, offset);
 
   return cxx_eval_constant_expression (ctx, new_call, lval,
-				       non_constant_p, overflow_p);
+				       non_constant_p, overflow_p,
+				       jump_target);
 }
 
 /* If OBJECT is of const class type, evaluate it to a CONSTRUCTOR and set
@@ -2839,7 +3567,7 @@ cxx_eval_thunk_call (const constexpr_ctx
 static void
 cxx_set_object_constness (const constexpr_ctx *ctx, tree object,
 			  bool readonly_p, bool *non_constant_p,
-			  bool *overflow_p)
+			  bool *overflow_p, tree *jump_target)
 {
   if (CLASS_TYPE_P (TREE_TYPE (object))
       && CP_TYPE_CONST_P (TREE_TYPE (object)))
@@ -2847,8 +3575,11 @@ cxx_set_object_constness (const constexp
       /* Subobjects might not be stored in ctx->global->values but we
 	 can get its CONSTRUCTOR by evaluating *this.  */
       tree e = cxx_eval_constant_expression (ctx, object, vc_prvalue,
-					     non_constant_p, overflow_p);
-      if (TREE_CODE (e) == CONSTRUCTOR && !*non_constant_p)
+					     non_constant_p, overflow_p,
+					     jump_target);
+      if (!*non_constant_p
+	  && !throws (jump_target)
+	  && TREE_CODE (e) == CONSTRUCTOR)
 	TREE_READONLY (e) = readonly_p;
     }
 }
@@ -2860,20 +3591,25 @@ cxx_set_object_constness (const constexp
 static tree
 cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 			  value_cat lval,
-			  bool *non_constant_p, bool *overflow_p)
+			  bool *non_constant_p, bool *overflow_p,
+			  tree *jump_target)
 {
   location_t loc = cp_expr_loc_or_input_loc (t);
   tree fun = get_function_named_in_call (t);
 
   if (fun == NULL_TREE)
     return cxx_eval_internal_function (ctx, t, lval,
-				       non_constant_p, overflow_p);
+				       non_constant_p, overflow_p,
+				       jump_target);
 
   if (TREE_CODE (fun) != FUNCTION_DECL)
     {
       /* Might be a constexpr function pointer.  */
       fun = cxx_eval_constant_expression (ctx, fun, vc_prvalue,
-					  non_constant_p, overflow_p);
+					  non_constant_p, overflow_p,
+					  jump_target);
+      if (*jump_target)
+	return NULL_TREE;
       STRIP_NOPS (fun);
       if (TREE_CODE (fun) == ADDR_EXPR)
 	fun = TREE_OPERAND (fun, 0);
@@ -2924,9 +3660,11 @@ cxx_eval_call_expression (const constexp
 
   if (fndecl_built_in_p (fun))
     return cxx_eval_builtin_function_call (ctx, t, fun,
-					   lval, non_constant_p, overflow_p);
+					   lval, non_constant_p, overflow_p,
+					   jump_target);
   if (DECL_THUNK_P (fun))
-    return cxx_eval_thunk_call (ctx, t, fun, lval, non_constant_p, overflow_p);
+    return cxx_eval_thunk_call (ctx, t, fun, lval, non_constant_p, overflow_p,
+				jump_target);
   if (!maybe_constexpr_fn (fun))
     {
       if (TREE_CODE (t) == CALL_EXPR
@@ -2941,7 +3679,10 @@ cxx_eval_call_expression (const constexp
 	    {
 	      tree arg = CALL_EXPR_ARG (t, i);
 	      arg = cxx_eval_constant_expression (ctx, arg, vc_prvalue,
-						  non_constant_p, overflow_p);
+						  non_constant_p, overflow_p,
+						  jump_target);
+	      if (*jump_target)
+		return NULL_TREE;
 	      /* Deleting a non-constant pointer has a better error message
 		 below.  */
 	      if (new_op_p || i != 0)
@@ -3056,7 +3797,10 @@ cxx_eval_call_expression (const constexp
 	    {
 	      tree arg = CALL_EXPR_ARG (t, i);
 	      arg = cxx_eval_constant_expression (ctx, arg, vc_prvalue,
-						  non_constant_p, overflow_p);
+						  non_constant_p, overflow_p,
+						  jump_target);
+	      if (*jump_target)
+		return NULL_TREE;
 	      if (i == 1)
 		arg1 = arg;
 	      else
@@ -3066,7 +3810,12 @@ cxx_eval_call_expression (const constexp
 	  return arg1;
 	}
       else if (cxx_dynamic_cast_fn_p (fun))
-	return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p);
+	return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p,
+					 jump_target);
+      else if (enum cxa_builtin kind = cxx_cxa_builtin_fn_p (fun))
+	return cxx_eval_cxa_builtin_fn (ctx, t, kind, fun,
+					non_constant_p, overflow_p,
+					jump_target);
 
       if (!ctx->quiet)
 	{
@@ -3111,7 +3860,8 @@ cxx_eval_call_expression (const constexp
   constexpr_call new_call;
   new_call.bindings
     = cxx_bind_parameters_in_call (ctx, t, fun, non_constant_p,
-				   overflow_p, &non_constant_args);
+				   overflow_p, &non_constant_args,
+				   jump_target);
 
   /* We build up the bindings list before we know whether we already have this
      call cached.  If we don't end up saving these bindings, ggc_free them when
@@ -3125,6 +3875,8 @@ cxx_eval_call_expression (const constexp
     void preserve () { bindings = NULL; }
   } fb (new_call.bindings);
 
+  if (*jump_target)
+    return NULL_TREE;
   if (*non_constant_p)
     return t;
 
@@ -3199,7 +3951,9 @@ cxx_eval_call_expression (const constexp
       new_obj = TREE_VEC_ELT (new_call.bindings, 0);
       bool empty_base = false;
       new_obj = cxx_fold_indirect_ref (ctx, loc, DECL_CONTEXT (fun), new_obj,
-				       &empty_base);
+				       &empty_base, jump_target);
+      if (*jump_target)
+	return NULL_TREE;
       /* If we're initializing an empty class, don't set constness, because
 	 cxx_fold_indirect_ref will return the wrong object to set constness
 	 of.  */
@@ -3348,7 +4102,7 @@ cxx_eval_call_expression (const constexp
 	     semantics are no longer in effect; see [class.dtor]p5.  */
 	  if (new_obj && DECL_DESTRUCTOR_P (fun))
 	    cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/false,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p, jump_target);
 
 	  /* If this is a constructor, we are beginning the lifetime of the
 	     object we are initializing.  */
@@ -3362,16 +4116,25 @@ cxx_eval_call_expression (const constexp
 				      build_constructor (TREE_TYPE (new_obj),
 							 NULL));
 	      cxx_eval_constant_expression (ctx, activate,
-					    lval, non_constant_p, overflow_p);
+					    lval, non_constant_p, overflow_p,
+					    jump_target);
 	      ggc_free (activate);
+	      if (*jump_target)
+		return NULL_TREE;
 	    }
 
-	  tree jump_target = NULL_TREE;
+	  tree jmp_target = NULL_TREE;
 	  cxx_eval_constant_expression (&call_ctx, body,
 					vc_discard, non_constant_p, overflow_p,
-					&jump_target);
+					&jmp_target);
 
-	  if (DECL_CONSTRUCTOR_P (fun))
+	  if (!*non_constant_p && throws (&jmp_target))
+	    {
+	      result = NULL_TREE;
+	      cacheable = false;
+	      *jump_target = jmp_target;
+	    }
+	  else if (DECL_CONSTRUCTOR_P (fun))
 	    /* This can be null for a subobject constructor call, in
 	       which case what we care about is the initialization
 	       side-effects rather than the value.  We could get at the
@@ -3399,7 +4162,7 @@ cxx_eval_call_expression (const constexp
 	     marking the CONSTRUCTOR TREE_READONLY.  */
 	  if (new_obj && DECL_CONSTRUCTOR_P (fun))
 	    cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/true,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p, jump_target);
 
 	  /* Remove the parms/result from the values map.  */
 	  destroy_value_checked (ctx, res, non_constant_p);
@@ -3459,7 +4222,13 @@ cxx_eval_call_expression (const constexp
 		    cacheable = false;
 		    result = cxx_eval_constant_expression (ctx, result, lval,
 							   non_constant_p,
-							   overflow_p);
+							   overflow_p,
+							   jump_target);
+		    if (*jump_target)
+		      {
+			cacheable = false;
+			result = NULL_TREE;
+		      }
 		  }
 	      }
 
@@ -3802,12 +4571,16 @@ cxx_eval_check_shift_p (location_t loc,
 static tree
 cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t,
 			   bool /*lval*/,
-			   bool *non_constant_p, bool *overflow_p)
+			   bool *non_constant_p, bool *overflow_p,
+			   tree *jump_target)
 {
   tree r;
   tree orig_arg = TREE_OPERAND (t, 0);
   tree arg = cxx_eval_constant_expression (ctx, orig_arg, vc_prvalue,
-					   non_constant_p, overflow_p);
+					   non_constant_p, overflow_p,
+					   jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   VERIFY_CONSTANT (arg);
   location_t loc = EXPR_LOCATION (t);
   enum tree_code code = TREE_CODE (t);
@@ -3831,7 +4604,7 @@ cxx_eval_unary_expression (const constex
 static tree
 cxx_fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t,
 				  tree lhs, tree rhs, bool *non_constant_p,
-				  bool *overflow_p)
+				  bool *overflow_p, tree *jump_target)
 {
   STRIP_NOPS (lhs);
   if (TREE_CODE (lhs) != ADDR_EXPR)
@@ -3853,9 +4626,12 @@ cxx_fold_pointer_plus_expression (const
       t = fold_convert_loc (loc, ssizetype, TREE_OPERAND (lhs, 1));
       tree nelts = array_type_nelts_top (TREE_TYPE (TREE_OPERAND (lhs, 0)));
       nelts = cxx_eval_constant_expression (ctx, nelts, vc_prvalue,
-					    non_constant_p, overflow_p);
+					    non_constant_p, overflow_p,
+					    jump_target);
       if (*non_constant_p)
 	return NULL_TREE;
+      if (*jump_target)
+	return NULL_TREE;
       /* Don't fold an out-of-bound access.  */
       if (!tree_int_cst_le (t, nelts))
 	return NULL_TREE;
@@ -3875,7 +4651,8 @@ cxx_fold_pointer_plus_expression (const
       t = cp_build_addr_expr (t, tf_warning_or_error);
       t = cp_fold_convert (orig_type, t);
       return cxx_eval_constant_expression (ctx, t, vc_prvalue,
-					   non_constant_p, overflow_p);
+					   non_constant_p, overflow_p,
+					   jump_target);
     }
 
   return NULL_TREE;
@@ -3919,22 +4696,29 @@ cxx_maybe_fold_addr_pointer_plus (tree t
 static tree
 cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
 			    value_cat lval,
-			    bool *non_constant_p, bool *overflow_p)
+			    bool *non_constant_p, bool *overflow_p,
+			    tree *jump_target)
 {
   tree r = NULL_TREE;
   tree orig_lhs = TREE_OPERAND (t, 0);
   tree orig_rhs = TREE_OPERAND (t, 1);
   tree lhs, rhs;
   lhs = cxx_eval_constant_expression (ctx, orig_lhs, vc_prvalue,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p,
+				      jump_target);
   /* Don't VERIFY_CONSTANT here, it's unnecessary and will break pointer
      subtraction.  */
   if (*non_constant_p)
     return t;
+  if (*jump_target)
+    return NULL_TREE;
   rhs = cxx_eval_constant_expression (ctx, orig_rhs, vc_prvalue,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p,
+				      jump_target);
   if (*non_constant_p)
     return t;
+  if (*jump_target)
+    return NULL_TREE;
 
   location_t loc = EXPR_LOCATION (t);
   enum tree_code code = TREE_CODE (t);
@@ -3990,13 +4774,17 @@ cxx_eval_binary_expression (const conste
       return t;
     }
   else if (code == POINTER_PLUS_EXPR)
-    r = cxx_fold_pointer_plus_expression (ctx, t, lhs, rhs, non_constant_p,
-					  overflow_p);
+    {
+      r = cxx_fold_pointer_plus_expression (ctx, t, lhs, rhs, non_constant_p,
+					    overflow_p, jump_target);
+      if (*jump_target)
+	return NULL_TREE;
+    }
   else if (code == SPACESHIP_EXPR)
     {
       r = genericize_spaceship (loc, type, lhs, rhs);
       return cxx_eval_constant_expression (ctx, r, lval, non_constant_p,
-					   overflow_p);
+					   overflow_p, jump_target);
     }
 
   if (r == NULL_TREE)
@@ -4055,7 +4843,10 @@ cxx_eval_conditional_expression (const c
 {
   tree val = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					   vc_prvalue,
-					   non_constant_p, overflow_p);
+					   non_constant_p, overflow_p,
+					   jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   VERIFY_CONSTANT (val);
   if (TREE_CODE (t) == IF_STMT && IF_STMT_CONSTEVAL_P (t))
     {
@@ -4116,19 +4907,29 @@ cxx_eval_conditional_expression (const c
 
 static tree
 cxx_eval_vector_conditional_expression (const constexpr_ctx *ctx, tree t,
-					bool *non_constant_p, bool *overflow_p)
+					bool *non_constant_p, bool *overflow_p,
+					tree *jump_target)
 {
   tree arg1 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					    vc_prvalue,
-					    non_constant_p, overflow_p);
+					    non_constant_p, overflow_p,
+					    jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   VERIFY_CONSTANT (arg1);
   tree arg2 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 					    vc_prvalue,
-					    non_constant_p, overflow_p);
+					    non_constant_p, overflow_p,
+					    jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   VERIFY_CONSTANT (arg2);
   tree arg3 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
 					    vc_prvalue,
-					    non_constant_p, overflow_p);
+					    non_constant_p, overflow_p,
+					    jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   VERIFY_CONSTANT (arg3);
   location_t loc = EXPR_LOCATION (t);
   tree type = TREE_TYPE (t);
@@ -4516,7 +5317,8 @@ diag_array_subscript (location_t loc, co
 
 static tree
 get_array_or_vector_nelts (const constexpr_ctx *ctx, tree type,
-			   bool *non_constant_p, bool *overflow_p)
+			   bool *non_constant_p, bool *overflow_p,
+			   tree *jump_target)
 {
   tree nelts;
   if (TREE_CODE (type) == ARRAY_TYPE)
@@ -4533,7 +5335,8 @@ get_array_or_vector_nelts (const constex
 
   /* For VLAs, the number of elements won't be an integer constant.  */
   nelts = cxx_eval_constant_expression (ctx, nelts, vc_prvalue,
-					non_constant_p, overflow_p);
+					non_constant_p, overflow_p,
+					jump_target);
   return nelts;
 }
 
@@ -4564,13 +5367,17 @@ extract_string_elt (tree string, unsigne
 static tree
 eval_and_check_array_index (const constexpr_ctx *ctx,
 			    tree t, bool allow_one_past,
-			    bool *non_constant_p, bool *overflow_p)
+			    bool *non_constant_p, bool *overflow_p,
+			    tree *jump_target)
 {
   location_t loc = cp_expr_loc_or_input_loc (t);
   tree ary = TREE_OPERAND (t, 0);
   t = TREE_OPERAND (t, 1);
   tree index = cxx_eval_constant_expression (ctx, t, vc_prvalue,
-					     non_constant_p, overflow_p);
+					     non_constant_p, overflow_p,
+					     jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   VERIFY_CONSTANT (index);
 
   if (!tree_fits_shwi_p (index)
@@ -4582,7 +5389,9 @@ eval_and_check_array_index (const conste
     }
 
   tree nelts = get_array_or_vector_nelts (ctx, TREE_TYPE (ary), non_constant_p,
-					  overflow_p);
+					  overflow_p, jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   VERIFY_CONSTANT (nelts);
   if (allow_one_past
       ? !tree_int_cst_le (index, nelts)
@@ -4602,14 +5411,18 @@ eval_and_check_array_index (const conste
 static tree
 cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 			  value_cat lval,
-			  bool *non_constant_p, bool *overflow_p)
+			  bool *non_constant_p, bool *overflow_p,
+			  tree *jump_target)
 {
   tree oldary = TREE_OPERAND (t, 0);
   tree ary = cxx_eval_constant_expression (ctx, oldary,
 					   lval,
-					   non_constant_p, overflow_p);
+					   non_constant_p, overflow_p,
+					   jump_target);
   if (*non_constant_p)
     return t;
+  if (*jump_target)
+    return NULL_TREE;
   if (!lval
       && TREE_CODE (ary) == VIEW_CONVERT_EXPR
       && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (ary, 0)))
@@ -4619,9 +5432,12 @@ cxx_eval_array_reference (const constexp
 
   tree oldidx = TREE_OPERAND (t, 1);
   tree index = eval_and_check_array_index (ctx, t, lval,
-					   non_constant_p, overflow_p);
+					   non_constant_p, overflow_p,
+					   jump_target);
   if (*non_constant_p)
     return t;
+  if (*jump_target)
+    return NULL_TREE;
 
   if (lval && ary == oldary && index == oldidx)
     return t;
@@ -4739,7 +5555,7 @@ cxx_eval_array_reference (const constexp
       ctx = &new_ctx;
     }
   t = cxx_eval_constant_expression (ctx, val, lval, non_constant_p,
-				    overflow_p);
+				    overflow_p, jump_target);
   if (new_ctor && t != ctx->ctor)
     free_constructor (ctx->ctor);
   return t;
@@ -4751,7 +5567,8 @@ cxx_eval_array_reference (const constexp
 static tree
 cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
 			      value_cat lval,
-			      bool *non_constant_p, bool *overflow_p)
+			      bool *non_constant_p, bool *overflow_p,
+			      tree *jump_target)
 {
   unsigned HOST_WIDE_INT i;
   tree field;
@@ -4760,9 +5577,12 @@ cxx_eval_component_reference (const cons
   tree orig_whole = TREE_OPERAND (t, 0);
   tree whole = cxx_eval_constant_expression (ctx, orig_whole,
 					     lval,
-					     non_constant_p, overflow_p);
+					     non_constant_p, overflow_p,
+					     jump_target);
   if (*non_constant_p)
     return t;
+  if (*jump_target)
+    return NULL_TREE;
   if (INDIRECT_REF_P (whole)
       && integer_zerop (TREE_OPERAND (whole, 0)))
     {
@@ -4871,7 +5691,8 @@ cxx_eval_component_reference (const cons
   value = build_value_init (TREE_TYPE (t), tf_warning_or_error);
   return cxx_eval_constant_expression (ctx, value,
 				       lval,
-				       non_constant_p, overflow_p);
+				       non_constant_p, overflow_p,
+				       jump_target);
 }
 
 /* Subroutine of cxx_eval_constant_expression.
@@ -4881,7 +5702,8 @@ cxx_eval_component_reference (const cons
 static tree
 cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t,
 			value_cat lval,
-			bool *non_constant_p, bool *overflow_p)
+			bool *non_constant_p, bool *overflow_p,
+			tree *jump_target)
 {
   tree orig_whole = TREE_OPERAND (t, 0);
   tree retval, fldval, utype, mask;
@@ -4889,10 +5711,13 @@ cxx_eval_bit_field_ref (const constexpr_
   HOST_WIDE_INT istart, isize;
   tree whole = cxx_eval_constant_expression (ctx, orig_whole,
 					     lval,
-					     non_constant_p, overflow_p);
+					     non_constant_p, overflow_p,
+					     jump_target);
   tree start, field, value;
   unsigned HOST_WIDE_INT i;
 
+  if (*jump_target)
+    return NULL_TREE;
   if (whole == orig_whole)
     return t;
   /* Don't VERIFY_CONSTANT here; we only want to check that we got a
@@ -5173,7 +5998,7 @@ clear_uchar_or_std_byte_in_mask (locatio
 
 static tree
 cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
-		   bool *overflow_p)
+		   bool *overflow_p, tree *jump_target)
 {
   if (check_bit_cast_type (ctx, EXPR_LOCATION (t), TREE_TYPE (t),
 			   TREE_TYPE (t))
@@ -5187,9 +6012,12 @@ cxx_eval_bit_cast (const constexpr_ctx *
     }
 
   tree op = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue,
-					  non_constant_p, overflow_p);
+					  non_constant_p, overflow_p,
+					  jump_target);
   if (*non_constant_p)
     return t;
+  if (*jump_target)
+    return NULL_TREE;
 
   location_t loc = EXPR_LOCATION (t);
   if (BITS_PER_UNIT != 8 || CHAR_BIT != 8)
@@ -5267,8 +6095,11 @@ cxx_eval_bit_cast (const constexpr_ctx *
 	  if (CHECKING_P)
 	    {
 	      tree e = cxx_eval_bare_aggregate (ctx, r, vc_prvalue,
-						non_constant_p, overflow_p);
-	      gcc_checking_assert (e == r);
+						non_constant_p, overflow_p,
+						jump_target);
+	      gcc_checking_assert (e == r
+				   && !throws (jump_target)
+				   && !returns (jump_target));
 	      r = e;
 	    }
 	}
@@ -5309,19 +6140,24 @@ cxx_eval_bit_cast (const constexpr_ctx *
 static tree
 cxx_eval_logical_expression (const constexpr_ctx *ctx, tree t,
                              tree bailout_value, tree continue_value,
-			     bool *non_constant_p, bool *overflow_p)
+			     bool *non_constant_p, bool *overflow_p,
+			     tree *jump_target)
 {
   tree r;
   tree lhs = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					   vc_prvalue, non_constant_p,
-					   overflow_p);
+					   overflow_p, jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   VERIFY_CONSTANT (lhs);
   if (tree_int_cst_equal (lhs, bailout_value))
     return lhs;
   gcc_assert (tree_int_cst_equal (lhs, continue_value));
   r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 				    vc_prvalue, non_constant_p,
-				    overflow_p);
+				    overflow_p, jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   VERIFY_CONSTANT (r);
   return r;
 }
@@ -5478,7 +6314,8 @@ verify_ctor_sanity (const constexpr_ctx
 static tree
 cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
 			 value_cat lval,
-			 bool *non_constant_p, bool *overflow_p)
+			 bool *non_constant_p, bool *overflow_p,
+			 tree *jump_target)
 {
   vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (t);
   bool changed = false;
@@ -5531,7 +6368,10 @@ cxx_eval_bare_aggregate (const constexpr
 	get_or_insert_ctor_field (ctx->ctor, index);
       tree elt = cxx_eval_constant_expression (&new_ctx, value,
 					       lval,
-					       non_constant_p, overflow_p);
+					       non_constant_p, overflow_p,
+					       jump_target);
+      if (*jump_target)
+	return NULL_TREE;
       /* Don't VERIFY_CONSTANT here.  */
       if (ctx->quiet && *non_constant_p)
 	break;
@@ -5621,7 +6461,8 @@ cxx_eval_bare_aggregate (const constexpr
 static tree
 cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
 		     bool value_init, value_cat lval,
-		     bool *non_constant_p, bool *overflow_p)
+		     bool *non_constant_p, bool *overflow_p,
+		     tree *jump_target)
 {
   tree elttype = TREE_TYPE (atype);
   verify_ctor_sanity (ctx, atype);
@@ -5632,7 +6473,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx
 
   if (init && TREE_CODE (init) == CONSTRUCTOR)
     return cxx_eval_bare_aggregate (ctx, init, lval,
-				    non_constant_p, overflow_p);
+				    non_constant_p, overflow_p, jump_target);
 
   /* For the default constructor, build up a call to the default
      constructor of the element type.  We only need to handle class types
@@ -5669,7 +6510,9 @@ cxx_eval_vec_init_1 (const constexpr_ctx
     }
 
   tree nelts = get_array_or_vector_nelts (ctx, atype, non_constant_p,
-					  overflow_p);
+					  overflow_p, jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   unsigned HOST_WIDE_INT max = tree_to_uhwi (nelts);
   for (i = 0; i < max; ++i)
     {
@@ -5695,9 +6538,9 @@ cxx_eval_vec_init_1 (const constexpr_ctx
 	    }
 	  else
 	    eltinit = cp_build_array_ref (input_location, init, idx, complain);
-	  eltinit = cxx_eval_vec_init_1 (&new_ctx, elttype, eltinit, value_init,
-					 lval,
-					 non_constant_p, overflow_p);
+	  eltinit = cxx_eval_vec_init_1 (&new_ctx, elttype, eltinit,
+					 value_init, lval, non_constant_p,
+					 overflow_p, jump_target);
 	}
       else if (pre_init)
 	{
@@ -5711,7 +6554,8 @@ cxx_eval_vec_init_1 (const constexpr_ctx
 	    /* Clarify what object is being initialized (118285).  */
 	    eltinit = build2 (INIT_EXPR, elttype, new_ctx.object, eltinit);
 	  eltinit = cxx_eval_constant_expression (&new_ctx, eltinit, lval,
-						  non_constant_p, overflow_p);
+						  non_constant_p, overflow_p,
+						  jump_target);
 	  reuse = i == 0;
 	}
       else
@@ -5727,8 +6571,11 @@ cxx_eval_vec_init_1 (const constexpr_ctx
 	    /* Clarify what object is being initialized (118285).  */
 	    eltinit = build2 (INIT_EXPR, elttype, new_ctx.object, eltinit);
 	  eltinit = cxx_eval_constant_expression (&new_ctx, eltinit, lval,
-						  non_constant_p, overflow_p);
+						  non_constant_p, overflow_p,
+						  jump_target);
 	}
+      if (*jump_target)
+	return NULL_TREE;
       if (*non_constant_p)
 	break;
       if (no_slot)
@@ -5778,7 +6625,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx
 static tree
 cxx_eval_vec_init (const constexpr_ctx *ctx, tree t,
 		   value_cat lval,
-		   bool *non_constant_p, bool *overflow_p)
+		   bool *non_constant_p, bool *overflow_p, tree *jump_target)
 {
   tree atype = TREE_TYPE (t);
   tree init = VEC_INIT_EXPR_INIT (t);
@@ -5810,10 +6657,10 @@ cxx_eval_vec_init (const constexpr_ctx *
 	}
       init = expand_vec_init_expr (ctx->object, t, complain);
       return cxx_eval_constant_expression (ctx, init, lval, non_constant_p,
-					   overflow_p);
+					   overflow_p, jump_target);
     }
   tree r = cxx_eval_vec_init_1 (ctx, atype, init, value_init,
-				lval, non_constant_p, overflow_p);
+				lval, non_constant_p, overflow_p, jump_target);
   if (*non_constant_p)
     return t;
   else
@@ -5842,14 +6689,16 @@ same_type_ignoring_tlq_and_bounds_p (tre
    otherwise return NULL_TREE.  */
 
 static tree
-cxx_union_active_member (const constexpr_ctx *ctx, tree t)
+cxx_union_active_member (const constexpr_ctx *ctx, tree t, tree *jump_target)
 {
   constexpr_ctx new_ctx = *ctx;
   new_ctx.quiet = true;
   bool non_constant_p = false, overflow_p = false;
   tree ctor = cxx_eval_constant_expression (&new_ctx, t, vc_prvalue,
 					    &non_constant_p,
-					    &overflow_p);
+					    &overflow_p, jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   if (TREE_CODE (ctor) == CONSTRUCTOR
       && CONSTRUCTOR_NELTS (ctor) == 1
       && CONSTRUCTOR_ELT (ctor, 0)->index
@@ -5862,7 +6711,8 @@ cxx_union_active_member (const constexpr
 
 static tree
 cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type,
-			 tree op, unsigned HOST_WIDE_INT off, bool *empty_base)
+			 tree op, unsigned HOST_WIDE_INT off, bool *empty_base,
+			 tree *jump_target)
 {
   tree optype = TREE_TYPE (op);
   unsigned HOST_WIDE_INT const_nunits;
@@ -5911,7 +6761,7 @@ cxx_fold_indirect_ref_1 (const constexpr
 	  op = build4_loc (loc, ARRAY_REF, TREE_TYPE (optype), op, index,
 			   NULL_TREE, NULL_TREE);
 	  return cxx_fold_indirect_ref_1 (ctx, loc, type, op, rem,
-					  empty_base);
+					  empty_base, jump_target);
 	}
     }
   /* ((foo *)&struct_with_foo_field)[x] => COMPONENT_REF */
@@ -5920,7 +6770,7 @@ cxx_fold_indirect_ref_1 (const constexpr
     {
       if (TREE_CODE (optype) == UNION_TYPE)
 	/* For unions prefer the currently active member.  */
-	if (tree field = cxx_union_active_member (ctx, op))
+	if (tree field = cxx_union_active_member (ctx, op, jump_target))
 	  {
 	    unsigned HOST_WIDE_INT el_sz
 	      = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
@@ -5929,7 +6779,8 @@ cxx_fold_indirect_ref_1 (const constexpr
 		tree cop = build3 (COMPONENT_REF, TREE_TYPE (field),
 				   op, field, NULL_TREE);
 		if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc, type, cop,
-							off, empty_base))
+							off, empty_base,
+							jump_target))
 		  return ret;
 	      }
 	  }
@@ -5969,7 +6820,8 @@ cxx_fold_indirect_ref_1 (const constexpr
 				   op, field, NULL_TREE);
 		if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc, type, cop,
 							off - upos,
-							empty_base))
+							empty_base,
+							jump_target))
 		  return ret;
 	      }
 	  }
@@ -5989,7 +6841,7 @@ cxx_fold_indirect_ref_1 (const constexpr
 
 static tree
 cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type,
-		       tree op0, bool *empty_base /* = NULL*/)
+		       tree op0, bool *empty_base, tree *jump_target)
 {
   tree sub = op0;
   tree subtype;
@@ -6052,7 +6904,8 @@ cxx_fold_indirect_ref (const constexpr_c
 	  tree off = integer_zero_node;
 	  canonicalize_obj_off (op, off);
 	  gcc_assert (integer_zerop (off));
-	  return cxx_fold_indirect_ref_1 (ctx, loc, type, op, 0, empty_base);
+	  return cxx_fold_indirect_ref_1 (ctx, loc, type, op, 0, empty_base,
+					  jump_target);
 	}
     }
   else if (TREE_CODE (sub) == POINTER_PLUS_EXPR
@@ -6067,7 +6920,8 @@ cxx_fold_indirect_ref (const constexpr_c
 	  tree obj = TREE_OPERAND (op00, 0);
 	  canonicalize_obj_off (obj, off);
 	  return cxx_fold_indirect_ref_1 (ctx, loc, type, obj,
-					  tree_to_uhwi (off), empty_base);
+					  tree_to_uhwi (off), empty_base,
+					  jump_target);
 	}
     }
   /* *(foo *)fooarrptr => (*fooarrptr)[0] */
@@ -6077,7 +6931,10 @@ cxx_fold_indirect_ref (const constexpr_c
       tree type_domain;
       tree min_val = size_zero_node;
       tree newsub
-	= cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (subtype), sub, NULL);
+	= cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (subtype), sub, NULL,
+				 jump_target);
+      if (*jump_target)
+	return NULL_TREE;
       if (newsub)
 	sub = newsub;
       else
@@ -6095,7 +6952,8 @@ cxx_fold_indirect_ref (const constexpr_c
 static tree
 cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
 		       value_cat lval,
-		       bool *non_constant_p, bool *overflow_p)
+		       bool *non_constant_p, bool *overflow_p,
+		       tree *jump_target)
 {
   tree orig_op0 = TREE_OPERAND (t, 0);
   bool empty_base = false;
@@ -6113,13 +6971,17 @@ cxx_eval_indirect_ref (const constexpr_c
 
   /* First try to simplify it directly.  */
   tree r = cxx_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t),
-				  orig_op0, &empty_base);
+				  orig_op0, &empty_base, jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   if (!r)
     {
       /* If that didn't work, evaluate the operand first.  */
       tree op0 = cxx_eval_constant_expression (ctx, orig_op0,
 					       vc_prvalue, non_constant_p,
-					       overflow_p);
+					       overflow_p, jump_target);
+      if (*jump_target)
+	return NULL_TREE;
       /* Don't VERIFY_CONSTANT here.  */
       if (*non_constant_p)
 	return t;
@@ -6133,7 +6995,9 @@ cxx_eval_indirect_ref (const constexpr_c
 	}
 
       r = cxx_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t), op0,
-				 &empty_base);
+				 &empty_base, jump_target);
+      if (*jump_target)
+	return NULL_TREE;
       if (r == NULL_TREE)
 	{
 	  /* We couldn't fold to a constant value.  Make sure it's not
@@ -6163,7 +7027,10 @@ cxx_eval_indirect_ref (const constexpr_c
     }
 
   r = cxx_eval_constant_expression (ctx, r,
-				    lval, non_constant_p, overflow_p);
+				    lval, non_constant_p, overflow_p,
+				    jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   if (*non_constant_p)
     return t;
 
@@ -6273,7 +7140,8 @@ non_const_var_error (location_t loc, tre
 static tree
 cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t,
 			     value_cat lval,
-			     bool *non_constant_p, bool *overflow_p)
+			     bool *non_constant_p, bool *overflow_p,
+			     tree *jump_target)
 {
   int i;
   tree args[3];
@@ -6283,7 +7151,10 @@ cxx_eval_trinary_expression (const const
     {
       args[i] = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, i),
 					      lval,
-					      non_constant_p, overflow_p);
+					      non_constant_p, overflow_p,
+					      jump_target);
+      if (*jump_target)
+	return NULL_TREE;
       VERIFY_CONSTANT (args[i]);
     }
 
@@ -6405,7 +7276,8 @@ modifying_const_object_p (tree_code code
 static tree
 cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 			   value_cat lval,
-			   bool *non_constant_p, bool *overflow_p)
+			   bool *non_constant_p, bool *overflow_p,
+			   tree *jump_target)
 {
   constexpr_ctx new_ctx = *ctx;
 
@@ -6430,7 +7302,10 @@ cxx_eval_store_expression (const constex
       if (!SCALAR_TYPE_P (type))
 	new_ctx.ctor = new_ctx.object = NULL_TREE;
       init = cxx_eval_constant_expression (&new_ctx, init, vc_prvalue,
-					   non_constant_p, overflow_p);
+					   non_constant_p, overflow_p,
+					   jump_target);
+      if (*jump_target)
+	return NULL_TREE;
       if (*non_constant_p)
 	return t;
     }
@@ -6442,8 +7317,11 @@ cxx_eval_store_expression (const constex
 	 as a whole; otherwise, only evaluate the innermost piece to avoid
 	 building up unnecessary *_REFs.  */
       target = cxx_eval_constant_expression (ctx, target, lval,
-					     non_constant_p, overflow_p);
+					     non_constant_p, overflow_p,
+					     jump_target);
       evaluated = true;
+      if (*jump_target)
+	return NULL_TREE;
       if (*non_constant_p)
 	return t;
     }
@@ -6469,7 +7347,10 @@ cxx_eval_store_expression (const constex
 	    if (TREE_CODE (probe) == ARRAY_REF)
 	      {
 		elt = eval_and_check_array_index (ctx, probe, false,
-						  non_constant_p, overflow_p);
+						  non_constant_p, overflow_p,
+						  jump_target);
+		if (*jump_target)
+		  return NULL_TREE;
 		if (*non_constant_p)
 		  return t;
 	      }
@@ -6526,8 +7407,11 @@ cxx_eval_store_expression (const constex
 		  break;
 		}
 	      probe = cxx_eval_constant_expression (ctx, probe, vc_glvalue,
-						    non_constant_p, overflow_p);
+						    non_constant_p, overflow_p,
+						    jump_target);
 	      evaluated = true;
+	      if (*jump_target)
+		return NULL_TREE;
 	      if (*non_constant_p)
 		return t;
 	    }
@@ -6871,7 +7755,10 @@ cxx_eval_store_expression (const constex
 	if (tree tinit = TARGET_EXPR_INITIAL (init))
 	  init = tinit;
       init = cxx_eval_constant_expression (&new_ctx, init, vc_prvalue,
-					   non_constant_p, overflow_p);
+					   non_constant_p, overflow_p,
+					   jump_target);
+      if (*jump_target)
+	return NULL_TREE;
       /* The hash table might have moved since the get earlier, and the
 	 initializer might have mutated the underlying CONSTRUCTORs, so we must
 	 recompute VALP. */
@@ -6997,7 +7884,8 @@ cxx_eval_store_expression (const constex
 static tree
 cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
 			      value_cat lval,
-			      bool *non_constant_p, bool *overflow_p)
+			      bool *non_constant_p, bool *overflow_p,
+			      tree *jump_target)
 {
   enum tree_code code = TREE_CODE (t);
   tree type = TREE_TYPE (t);
@@ -7011,12 +7899,18 @@ cxx_eval_increment_expression (const con
 
   /* The operand as an lvalue.  */
   op = cxx_eval_constant_expression (ctx, op, vc_glvalue,
-				     non_constant_p, overflow_p);
+				     non_constant_p, overflow_p,
+				     jump_target);
+  if (*jump_target)
+    return NULL_TREE;
 
   /* The operand as an rvalue.  */
   tree val
     = cxx_eval_constant_expression (ctx, op, vc_prvalue,
-				    non_constant_p, overflow_p);
+				    non_constant_p, overflow_p,
+				    jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   /* Don't VERIFY_CONSTANT if this might be dealing with a pointer to
      a local array in a constexpr function.  */
   bool ptr = INDIRECT_TYPE_P (TREE_TYPE (val));
@@ -7055,8 +7949,11 @@ cxx_eval_increment_expression (const con
   tree store = build2_loc (cp_expr_loc_or_loc (t, input_location),
 			   MODIFY_EXPR, type, op, mod);
   mod = cxx_eval_constant_expression (ctx, store, lval,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p,
+				      jump_target);
   ggc_free (store);
+  if (*jump_target)
+    return NULL_TREE;
   if (*non_constant_p)
     return t;
 
@@ -7070,42 +7967,6 @@ cxx_eval_increment_expression (const con
     return val;
 }
 
-/* Predicates for the meaning of *jump_target.  */
-
-static bool
-returns (tree *jump_target)
-{
-  return *jump_target
-    && TREE_CODE (*jump_target) == RETURN_EXPR;
-}
-
-static bool
-breaks (tree *jump_target)
-{
-  return *jump_target
-    && ((TREE_CODE (*jump_target) == LABEL_DECL
-	 && LABEL_DECL_BREAK (*jump_target))
-	|| TREE_CODE (*jump_target) == BREAK_STMT
-	|| TREE_CODE (*jump_target) == EXIT_EXPR);
-}
-
-static bool
-continues (tree *jump_target)
-{
-  return *jump_target
-    && ((TREE_CODE (*jump_target) == LABEL_DECL
-	 && LABEL_DECL_CONTINUE (*jump_target))
-	|| TREE_CODE (*jump_target) == CONTINUE_STMT);
-
-}
-
-static bool
-switches (tree *jump_target)
-{
-  return *jump_target
-    && TREE_CODE (*jump_target) == INTEGER_CST;
-}
-
 /* Subroutine of cxx_eval_statement_list.  Determine whether the statement
    STMT matches *jump_target.  If we're looking for a case label and we see
    the default label, note it in ctx->css_state.  */
@@ -7153,6 +8014,9 @@ label_matches (const constexpr_ctx *ctx,
 	 breaks (jump_target) or continues (jump_target).  */
       break;
 
+    case VAR_DECL:
+      break;
+
     default:
       gcc_unreachable ();
     }
@@ -7167,15 +8031,9 @@ cxx_eval_statement_list (const constexpr
 			 bool *non_constant_p, bool *overflow_p,
 			 tree *jump_target)
 {
-  tree local_target;
   /* In a statement-expression we want to return the last value.
      For empty statement expression return void_node.  */
   tree r = void_node;
-  if (!jump_target)
-    {
-      local_target = NULL_TREE;
-      jump_target = &local_target;
-    }
   for (tree_stmt_iterator i = tsi_start (t); !tsi_end_p (i); ++i)
     {
       tree stmt = *i;
@@ -7203,18 +8061,11 @@ cxx_eval_statement_list (const constexpr
 					jump_target);
       if (*non_constant_p)
 	break;
-      if (returns (jump_target) || breaks (jump_target))
+      if (returns (jump_target)
+	  || breaks (jump_target)
+	  || throws (jump_target))
 	break;
     }
-  if (*jump_target && jump_target == &local_target)
-    {
-      /* We aren't communicating the jump to our caller, so give up.  We don't
-	 need to support evaluation of jumps out of statement-exprs.  */
-      if (!ctx->quiet)
-	error_at (cp_expr_loc_or_input_loc (r),
-		  "statement is not a constant expression");
-      *non_constant_p = true;
-    }
   return r;
 }
 
@@ -7226,13 +8077,6 @@ cxx_eval_loop_expr (const constexpr_ctx
 		    bool *non_constant_p, bool *overflow_p,
 		    tree *jump_target)
 {
-  tree local_target;
-  if (!jump_target)
-    {
-      local_target = NULL_TREE;
-      jump_target = &local_target;
-    }
-
   tree body, cond = NULL_TREE, expr = NULL_TREE;
   tree cond_prep = NULL_TREE, cond_cleanup = NULL_TREE;
   unsigned cond_cleanup_depth = 0;
@@ -7288,7 +8132,7 @@ cxx_eval_loop_expr (const constexpr_ctx
 	tree c;
 	FOR_EACH_VEC_ELT_REVERSE (cleanups, i, c)
 	  cxx_eval_constant_expression (ctx, c, vc_discard, non_constant_p,
-					overflow_p);
+					overflow_p, jump_target);
       }
     if (cond_prep)
       for (tree decl = BIND_EXPR_VARS (cond_prep);
@@ -7383,7 +8227,8 @@ cxx_eval_loop_expr (const constexpr_ctx
 		      if (*non_constant_p
 			  || returns (jump_target)
 			  || breaks (jump_target)
-			  || continues (jump_target))
+			  || continues (jump_target)
+			  || throws (jump_target))
 			{
 			  depth = 1;
 			  break;
@@ -7430,6 +8275,7 @@ cxx_eval_loop_expr (const constexpr_ctx
 	 && !breaks (jump_target)
 	 && !continues (jump_target)
 	 && (!switches (jump_target) || count == 0)
+	 && !throws (jump_target)
 	 && !*non_constant_p);
 
   cleanup_cond ();
@@ -7448,7 +8294,10 @@ cxx_eval_switch_expr (const constexpr_ct
   tree cond
     = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_COND (t) : SWITCH_COND (t);
   cond = cxx_eval_constant_expression (ctx, cond, vc_prvalue,
-				       non_constant_p, overflow_p);
+				       non_constant_p, overflow_p,
+				       jump_target);
+  if (*jump_target)
+    return NULL_TREE;
   VERIFY_CONSTANT (cond);
   if (TREE_CODE (cond) != INTEGER_CST)
     {
@@ -7581,7 +8430,8 @@ maybe_warn_about_constant_value (locatio
 static tree
 build_new_constexpr_heap_type (const constexpr_ctx *ctx, tree elt_type,
 			       tree cookie_size, tree full_size, tree arg_size,
-			       bool *non_constant_p, bool *overflow_p)
+			       bool *non_constant_p, bool *overflow_p,
+			       tree *jump_target)
 {
   gcc_assert (cookie_size == NULL_TREE || tree_fits_uhwi_p (cookie_size));
   gcc_assert (tree_fits_uhwi_p (full_size));
@@ -7617,13 +8467,17 @@ build_new_constexpr_heap_type (const con
 	  if (integer_zerop (op0))
 	    arg_size
 	      = cxx_eval_constant_expression (ctx, op1, vc_prvalue,
-					      non_constant_p, overflow_p);
+					      non_constant_p, overflow_p,
+					      jump_target);
 	  else if (integer_zerop (op1))
 	    arg_size
 	      = cxx_eval_constant_expression (ctx, op0, vc_prvalue,
-					      non_constant_p, overflow_p);
+					      non_constant_p, overflow_p,
+					      jump_target);
 	  else
 	    arg_size = NULL_TREE;
+	  if (*jump_target)
+	    return NULL_TREE;
 	}
       else
 	arg_size = NULL_TREE;
@@ -7653,9 +8507,9 @@ static tree
 cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 			      value_cat lval,
 			      bool *non_constant_p, bool *overflow_p,
-			      tree *jump_target /* = NULL */)
+			      tree *jump_target)
 {
-  if (jump_target && *jump_target)
+  if (*jump_target)
     {
       /* If we are jumping, ignore all statements/expressions except those
 	 that could have LABEL_EXPR or CASE_LABEL_EXPR in their bodies.  */
@@ -7779,7 +8633,7 @@ cxx_eval_constant_expression (const cons
 		r = convert_from_reference (r);
 	    }
 	  return cxx_eval_constant_expression (ctx, r, lval, non_constant_p,
-					       overflow_p);
+					       overflow_p, jump_target);
 	}
       /* fall through */
     case CONST_DECL:
@@ -7857,7 +8711,10 @@ cxx_eval_constant_expression (const cons
 	  r = v;
 	  if (TREE_ADDRESSABLE (TREE_TYPE (t)))
 	    r = cxx_eval_constant_expression (ctx, r, vc_prvalue,
-					      non_constant_p, overflow_p);
+					      non_constant_p, overflow_p,
+					      jump_target);
+	  if (*jump_target)
+	    return NULL_TREE;
 	}
       else if (lval)
 	/* Defer in case this is only used for its type.  */;
@@ -7890,7 +8747,7 @@ cxx_eval_constant_expression (const cons
     case CALL_EXPR:
     case AGGR_INIT_EXPR:
       r = cxx_eval_call_expression (ctx, t, lval,
-				    non_constant_p, overflow_p);
+				    non_constant_p, overflow_p, jump_target);
       break;
 
     case DECL_EXPR:
@@ -7954,7 +8811,10 @@ cxx_eval_constant_expression (const cons
 	if (tree init = DECL_INITIAL (r))
 	  {
 	    init = cxx_eval_constant_expression (ctx, init, vc_prvalue,
-						 non_constant_p, overflow_p);
+						 non_constant_p, overflow_p,
+						 jump_target);
+	    if (*jump_target)
+	      return NULL_TREE;
 	    /* Don't share a CONSTRUCTOR that might be changed.  */
 	    init = unshare_constructor (init);
 	    /* Remember that a constant object's constructor has already
@@ -8016,9 +8876,21 @@ cxx_eval_constant_expression (const cons
 	/* Pass vc_prvalue because this indicates
 	   initialization of a temporary.  */
 	r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_prvalue,
-					  non_constant_p, overflow_p);
+					  non_constant_p, overflow_p,
+					  jump_target);
 	if (*non_constant_p)
 	  break;
+	if (TARGET_EXPR_CLEANUP (t)
+	    && (!CLEANUP_EH_ONLY (t) || cxx_dialect >= cxx26))
+	  {
+	    ctx->global->cleanups->safe_push (TARGET_EXPR_CLEANUP (t));
+	    /* Mark CLEANUP_EH_ONLY cleanups by pushing NULL_TREE after
+	       them.  */
+	    if (CLEANUP_EH_ONLY (t))
+	      ctx->global->cleanups->safe_push (NULL_TREE);
+	  }
+	if (*jump_target)
+	  return NULL_TREE;
 	/* If the initializer is complex, evaluate it to initialize slot.  */
 	bool is_complex = target_expr_needs_replace (t);
 	if (!is_complex)
@@ -8028,8 +8900,6 @@ cxx_eval_constant_expression (const cons
 	    r = adjust_temp_type (type, r);
 	    ctx->global->put_value (slot, r);
 	  }
-	if (TARGET_EXPR_CLEANUP (t) && !CLEANUP_EH_ONLY (t))
-	  ctx->global->cleanups->safe_push (TARGET_EXPR_CLEANUP (t));
 	if (ctx->save_exprs)
 	  ctx->save_exprs->safe_push (slot);
 	if (lval)
@@ -8043,33 +8913,28 @@ cxx_eval_constant_expression (const cons
     case MODIFY_EXPR:
       gcc_assert (jump_target == NULL || *jump_target == NULL_TREE);
       r = cxx_eval_store_expression (ctx, t, lval,
-				     non_constant_p, overflow_p);
+				     non_constant_p, overflow_p, jump_target);
       break;
 
     case SCOPE_REF:
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 					lval,
-					non_constant_p, overflow_p);
+					non_constant_p, overflow_p,
+					jump_target);
       break;
 
     case RETURN_EXPR:
       if (TREE_OPERAND (t, 0) != NULL_TREE)
 	r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					  lval,
-					  non_constant_p, overflow_p);
-      /* FALLTHRU */
+					  non_constant_p, overflow_p,
+					  jump_target);
+      if (!throws (jump_target))
+	*jump_target = t;
+      break;
     case BREAK_STMT:
     case CONTINUE_STMT:
-      if (jump_target)
-	*jump_target = t;
-      else
-	{
-	  /* Can happen with ({ return true; }) && false; passed to
-	     maybe_constant_value.  There is nothing to jump over in this
-	     case, and the bug will be diagnosed later.  */
-	  gcc_assert (ctx->quiet);
-	  *non_constant_p = true;
-	}
+      *jump_target = t;
       break;
 
     case SAVE_EXPR:
@@ -8078,9 +8943,10 @@ cxx_eval_constant_expression (const cons
 	r = v;
       else
 	{
-	  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue,
-					    non_constant_p, overflow_p);
-	  if (*non_constant_p)
+	  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
+					    vc_prvalue, non_constant_p,
+					    overflow_p, jump_target);
+	  if (*non_constant_p || *jump_target)
 	    break;
 	  ctx->global->put_value (t, r);
 	  if (ctx->save_exprs)
@@ -8088,16 +8954,7 @@ cxx_eval_constant_expression (const cons
 	}
       break;
 
-    case TRY_CATCH_EXPR:
-      if (TREE_OPERAND (t, 0) == NULL_TREE)
-	{
-	  r = void_node;
-	  break;
-	}
-      /* FALLTHRU */
     case NON_LVALUE_EXPR:
-    case TRY_BLOCK:
-    case MUST_NOT_THROW_EXPR:
     case EXPR_STMT:
     case EH_SPEC_BLOCK:
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
@@ -8106,9 +8963,52 @@ cxx_eval_constant_expression (const cons
 					jump_target);
       break;
 
+    case TRY_BLOCK:
+      r = cxx_eval_constant_expression (ctx, TRY_STMTS (t), lval,
+					non_constant_p, overflow_p,
+					jump_target);
+      if (!*non_constant_p && throws (jump_target))
+	if (tree h = TRY_HANDLERS (t))
+	  {
+	    tree type = strip_array_types (TREE_TYPE (*jump_target));
+	    if (TREE_CODE (h) == STATEMENT_LIST)
+	      {
+		for (tree stmt : tsi_range (h))
+		  if (TREE_CODE (stmt) == HANDLER
+		      && handler_match_for_exception_type (stmt, type))
+		    {
+		      h = stmt;
+		      break;
+		    }
+		if (TREE_CODE (h) == STATEMENT_LIST)
+		  h = NULL_TREE;
+	      }
+	    else if (TREE_CODE (h) != HANDLER
+		     || !handler_match_for_exception_type (h, type))
+	      h = NULL_TREE;
+	    if (h)
+	      {
+		gcc_assert (VAR_P (*jump_target));
+		ctx->global->caught_exceptions.safe_push (*jump_target);
+		ctx->global->caught_exceptions.safe_push (HANDLER_TYPE (h));
+		*jump_target = NULL_TREE;
+		/* potential_constant_expression_1 doesn't recurse into
+		   TRY_BLOCK handlers, so call it now.  */
+		if (!ctx->quiet
+		    ? !require_potential_constant_expression (HANDLER_BODY (h))
+		    : !potential_constant_expression (HANDLER_BODY (h)))
+		  *non_constant_p = true;
+		else
+		  r = cxx_eval_constant_expression (ctx, HANDLER_BODY (h),
+						    lval, non_constant_p,
+						    overflow_p, jump_target);
+	      }
+	  }
+      break;
+
     case CLEANUP_POINT_EXPR:
       {
-	auto_vec<tree, 2> cleanups;
+	auto_vec<tree, 4> cleanups;
 	vec<tree> *prev_cleanups = ctx->global->cleanups;
 	ctx->global->cleanups = &cleanups;
 
@@ -8123,47 +9023,194 @@ cxx_eval_constant_expression (const cons
 
 	ctx->global->cleanups = prev_cleanups;
 	unsigned int i;
-	tree cleanup;
+	tree cleanup, jmp_target = NULL_TREE;
+	bool eh = throws (jump_target);
 	/* Evaluate the cleanups.  */
 	FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup)
-	  cxx_eval_constant_expression (&new_ctx, cleanup, vc_discard,
-					non_constant_p, overflow_p);
+	  if (cleanup == NULL_TREE)
+	    {
+	      /* NULL_TREE cleanup is a marker that before it is
+		 CLEANUP_EH_ONLY cleanup.  Skip the cleanup before it
+		 if the body didn't throw.  */
+	      if (!eh)
+		--i;
+	    }
+	  else
+	    cxx_eval_constant_expression (&new_ctx, cleanup, vc_discard,
+					  non_constant_p, overflow_p,
+					  &jmp_target);
 
 	/* Forget SAVE_EXPRs and TARGET_EXPRs created by this
 	   full-expression.  */
 	for (tree save_expr : save_exprs)
 	  destroy_value_checked (ctx, save_expr, non_constant_p);
+	if (throws (&jmp_target))
+	  *jump_target = jmp_target;
       }
       break;
 
+    case MUST_NOT_THROW_EXPR:
+      r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
+					lval,
+					non_constant_p, overflow_p,
+					jump_target);
+      if (throws (jump_target))
+	{
+	  /* [except.handle]/7 - If the search for a handler exits the
+	     function body of a function with a non-throwing exception
+	     specification, the function std::terminate is invoked.  */
+	  if (!ctx->quiet)
+	    {
+	      auto_diagnostic_group d;
+	      diagnose_std_terminate (loc, ctx, *jump_target);
+	      if (MUST_NOT_THROW_NOEXCEPT_P (t)
+		  && ctx->call
+		  && ctx->call->fundef)
+		inform (loc, "uncaught exception exited from %<noexcept%> "
+			     "function %qD",
+			ctx->call->fundef->decl);
+	      else if (MUST_NOT_THROW_THROW_P (t))
+		inform (loc, "destructor exited with an exception");
+	      else if (MUST_NOT_THROW_CATCH_P (t))
+		inform (loc, "constructor exited with another exception while "
+			     "entering handler");
+	    }
+	  *non_constant_p = true;
+	  *jump_target = NULL_TREE;
+	  r = NULL_TREE;
+	}
+      break;
+
+    case TRY_CATCH_EXPR:
+      if (TREE_OPERAND (t, 0) == NULL_TREE)
+	{
+	  r = void_node;
+	  break;
+	}
+      r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
+					non_constant_p, overflow_p,
+					jump_target);
+      if (!*non_constant_p && throws (jump_target))
+	{
+	  tree jmp_target = NULL_TREE;
+	  cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard,
+					non_constant_p, overflow_p,
+					&jmp_target);
+	  if (throws (&jmp_target))
+	    {
+	      if (throws (jump_target))
+		{
+		  /* [except.throw]/9 - If the exception handling mechanism
+		     handling an uncaught exception directly invokes a function
+		     that exits via an exception, the function std::terminate is
+		     invoked.  */
+		  if (!ctx->quiet)
+		    {
+		      auto_diagnostic_group d;
+		      diagnose_std_terminate (loc, ctx, *jump_target);
+		      inform (loc, "destructor exited with an exception");
+		    }
+		  *non_constant_p = true;
+		  *jump_target = NULL_TREE;
+		  r = NULL_TREE;
+		}
+	      else
+		*jump_target = jmp_target;
+	    }
+	}
+      break;
+
     case TRY_FINALLY_EXPR:
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
 					non_constant_p, overflow_p,
 					jump_target);
       if (!*non_constant_p)
-	/* Also evaluate the cleanup.  */
-	cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard,
-				      non_constant_p, overflow_p);
+	{
+	  tree jmp_target = NULL_TREE;
+	  /* Also evaluate the cleanup.  */
+	  if (TREE_CODE (TREE_OPERAND (t, 1)) == EH_ELSE_EXPR
+	      && throws (jump_target))
+	    cxx_eval_constant_expression (ctx,
+					  TREE_OPERAND (TREE_OPERAND (t, 1),
+							1), vc_discard,
+					  non_constant_p, overflow_p,
+					  &jmp_target);
+	  else
+	    cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard,
+					  non_constant_p, overflow_p,
+					  &jmp_target);
+	  if (throws (&jmp_target))
+	    {
+	      if (throws (jump_target))
+		{
+		  /* [except.throw]/9 - If the exception handling mechanism
+		     handling an uncaught exception directly invokes a function
+		     that exits via an exception, the function std::terminate is
+		     invoked.  */
+		  if (!ctx->quiet)
+		    {
+		      auto_diagnostic_group d;
+		      diagnose_std_terminate (loc, ctx, *jump_target);
+		      inform (loc, "destructor exited with an exception");
+		    }
+		  *non_constant_p = true;
+		  *jump_target = NULL_TREE;
+		  r = NULL_TREE;
+		}
+	      else
+		*jump_target = jmp_target;
+	    }
+	}
       break;
 
     case EH_ELSE_EXPR:
       /* Evaluate any cleanup that applies to non-EH exits.  */
       cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_discard,
-				    non_constant_p, overflow_p);
+				    non_constant_p, overflow_p,
+				    jump_target);
 
-      /* We do not have constexpr exceptions yet, so skip the EH path.  */
+      /* The EH path is handled in TRY_FINALLY_EXPR handling above.  */
       break;
 
     case CLEANUP_STMT:
       r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
 					non_constant_p, overflow_p,
 					jump_target);
-      if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
+      if ((!CLEANUP_EH_ONLY (t) || throws (jump_target)) && !*non_constant_p)
 	{
 	  iloc_sentinel ils (loc);
-	  /* Also evaluate the cleanup.  */
-	  cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), vc_discard,
-					non_constant_p, overflow_p);
+	  tree jmp_target = NULL_TREE;
+	  if (CLEANUP_EH_ONLY (t)
+	      && (!ctx->quiet
+		  ? !require_potential_constant_expression (CLEANUP_EXPR (t))
+		  : !potential_constant_expression (CLEANUP_EXPR (t))))
+	    *non_constant_p = true;
+	  else
+	    /* Also evaluate the cleanup.  */
+	    cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), vc_discard,
+					  non_constant_p, overflow_p,
+					  &jmp_target);
+	  if (throws (&jmp_target))
+	    {
+	      if (throws (jump_target))
+		{
+		  /* [except.throw]/9 - If the exception handling mechanism
+		     handling an uncaught exception directly invokes a function
+		     that exits via an exception, the function std::terminate is
+		     invoked.  */
+		  if (!ctx->quiet)
+		    {
+		      auto_diagnostic_group d;
+		      diagnose_std_terminate (loc, ctx, *jump_target);
+		      inform (loc, "destructor exited with an exception");
+		    }
+		  *non_constant_p = true;
+		  *jump_target = NULL_TREE;
+		  r = NULL_TREE;
+		}
+	      else
+		*jump_target = jmp_target;
+	    }
 	}
       break;
 
@@ -8173,14 +9220,18 @@ cxx_eval_constant_expression (const cons
     case MEM_REF:
     case INDIRECT_REF:
       r = cxx_eval_indirect_ref (ctx, t, lval,
-				 non_constant_p, overflow_p);
+				 non_constant_p, overflow_p,
+				 jump_target);
       break;
 
     case ADDR_EXPR:
       {
 	tree oldop = TREE_OPERAND (t, 0);
 	tree op = cxx_eval_constant_expression (ctx, oldop, vc_glvalue,
-						non_constant_p, overflow_p);
+						non_constant_p, overflow_p,
+						jump_target);
+	if (*jump_target)
+	  return NULL_TREE;
 	/* Don't VERIFY_CONSTANT here.  */
 	if (*non_constant_p)
 	  return t;
@@ -8200,7 +9251,10 @@ cxx_eval_constant_expression (const cons
       if (lval)
 	{
 	  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
-					    non_constant_p, overflow_p);
+					    non_constant_p, overflow_p,
+					    jump_target);
+	  if (*jump_target)
+	    return NULL_TREE;
 	  if (r == error_mark_node)
 	    ;
 	  else if (r == TREE_OPERAND (t, 0) || lval == vc_discard)
@@ -8221,7 +9275,8 @@ cxx_eval_constant_expression (const cons
     case FIXED_CONVERT_EXPR:
     case VEC_DUPLICATE_EXPR:
       r = cxx_eval_unary_expression (ctx, t, lval,
-				     non_constant_p, overflow_p);
+				     non_constant_p, overflow_p,
+				     jump_target);
       break;
 
     case SIZEOF_EXPR:
@@ -8259,6 +9314,8 @@ cxx_eval_constant_expression (const cons
 	    cxx_eval_constant_expression (ctx, op0, vc_discard,
 					  non_constant_p, overflow_p,
 					  jump_target);
+	    if (*jump_target)
+	      return NULL_TREE;
 	    if (*non_constant_p)
 	      return t;
 	    op1 = TREE_OPERAND (t, 1);
@@ -8311,7 +9368,8 @@ cxx_eval_constant_expression (const cons
     case RANGE_EXPR:
     case COMPLEX_EXPR:
       r = cxx_eval_binary_expression (ctx, t, lval,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p,
+				      jump_target);
       break;
 
       /* fold can introduce non-IF versions of these; still treat them as
@@ -8320,19 +9378,22 @@ cxx_eval_constant_expression (const cons
     case TRUTH_ANDIF_EXPR:
       r = cxx_eval_logical_expression (ctx, t, boolean_false_node,
 				       boolean_true_node,
-				       non_constant_p, overflow_p);
+				       non_constant_p, overflow_p,
+				       jump_target);
       break;
 
     case TRUTH_OR_EXPR:
     case TRUTH_ORIF_EXPR:
       r = cxx_eval_logical_expression (ctx, t, boolean_true_node,
 				       boolean_false_node,
-				       non_constant_p, overflow_p);
+				       non_constant_p, overflow_p,
+				       jump_target);
       break;
 
     case ARRAY_REF:
       r = cxx_eval_array_reference (ctx, t, lval,
-				    non_constant_p, overflow_p);
+				    non_constant_p, overflow_p,
+				    jump_target);
       break;
 
     case COMPONENT_REF:
@@ -8347,17 +9408,19 @@ cxx_eval_constant_expression (const cons
 	  return t;
 	}
       r = cxx_eval_component_reference (ctx, t, lval,
-					non_constant_p, overflow_p);
+					non_constant_p, overflow_p,
+					jump_target);
       break;
 
     case BIT_FIELD_REF:
       r = cxx_eval_bit_field_ref (ctx, t, lval,
-				  non_constant_p, overflow_p);
+				  non_constant_p, overflow_p,
+				  jump_target);
       break;
 
     case COND_EXPR:
     case IF_STMT:
-      if (jump_target && *jump_target)
+      if (*jump_target)
 	{
 	  tree orig_jump = *jump_target;
 	  tree arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 1))
@@ -8395,7 +9458,7 @@ cxx_eval_constant_expression (const cons
       break;
     case VEC_COND_EXPR:
       r = cxx_eval_vector_conditional_expression (ctx, t, non_constant_p,
-						  overflow_p);
+						  overflow_p, jump_target);
       break;
 
     case CONSTRUCTOR:
@@ -8407,7 +9470,7 @@ cxx_eval_constant_expression (const cons
 	    return t;
 	}
       r = cxx_eval_bare_aggregate (ctx, t, lval,
-				   non_constant_p, overflow_p);
+				   non_constant_p, overflow_p, jump_target);
       break;
 
     case VEC_INIT_EXPR:
@@ -8417,12 +9480,13 @@ cxx_eval_constant_expression (const cons
 	 or xvalue of the same type, meaning direct-initialization from the
 	 corresponding member.  */
       r = cxx_eval_vec_init (ctx, t, lval,
-			     non_constant_p, overflow_p);
+			     non_constant_p, overflow_p, jump_target);
       break;
 
     case VEC_PERM_EXPR:
       r = cxx_eval_trinary_expression (ctx, t, lval,
-				       non_constant_p, overflow_p);
+				       non_constant_p, overflow_p,
+				       jump_target);
       break;
 
     case PAREN_EXPR:
@@ -8430,7 +9494,8 @@ cxx_eval_constant_expression (const cons
       /* A PAREN_EXPR resulting from __builtin_assoc_barrier has no effect in
          constant expressions since it's unaffected by -fassociative-math.  */
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
-					non_constant_p, overflow_p);
+					non_constant_p, overflow_p,
+					jump_target);
       break;
 
     case NOP_EXPR:
@@ -8454,7 +9519,10 @@ cxx_eval_constant_expression (const cons
 						? vc_discard
 						: tcode == VIEW_CONVERT_EXPR
 						? lval : vc_prvalue,
-						non_constant_p, overflow_p);
+						non_constant_p, overflow_p,
+						jump_target);
+	if (*jump_target)
+	  return NULL_TREE;
 	if (*non_constant_p)
 	  return t;
 	tree type = TREE_TYPE (t);
@@ -8511,7 +9579,10 @@ cxx_eval_constant_expression (const cons
 	      {
 		if (integer_zerop (sop))
 		  return build_int_cst (type, 0);
-		r = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (type), sop);
+		r = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (type), sop,
+					   NULL, jump_target);
+		if (*jump_target)
+		  return NULL_TREE;
 		if (r)
 		  {
 		    r = build1 (ADDR_EXPR, type, r);
@@ -8638,10 +9709,14 @@ cxx_eval_constant_expression (const cons
 		if (cxx_replaceable_global_alloc_fn (fun)
 		    && IDENTIFIER_NEW_OP_P (DECL_NAME (fun)))
 		  arg_size = CALL_EXPR_ARG (oldop, 0);
-	    TREE_TYPE (var)
+	    tree new_type
 	      = build_new_constexpr_heap_type (ctx, elt_type, cookie_size,
 					       var_size, arg_size,
-					       non_constant_p, overflow_p);
+					       non_constant_p, overflow_p,
+					       jump_target);
+	    if (*jump_target)
+	      return NULL_TREE;
+	    TREE_TYPE (var) = new_type;
 	    TREE_TYPE (TREE_OPERAND (op, 0))
 	      = build_pointer_type (TREE_TYPE (var));
 	  }
@@ -8680,7 +9755,10 @@ cxx_eval_constant_expression (const cons
 
 	tree op = cxx_eval_constant_expression (ctx, oldop,
 						lval,
-						non_constant_p, overflow_p);
+						non_constant_p, overflow_p,
+						jump_target);
+	if (*jump_target)
+	  return NULL_TREE;
 	if (*non_constant_p)
 	  return t;
 	r = fold_convert (TREE_TYPE (t), op);
@@ -8717,14 +9795,20 @@ cxx_eval_constant_expression (const cons
     case PREDECREMENT_EXPR:
     case POSTDECREMENT_EXPR:
       return cxx_eval_increment_expression (ctx, t,
-					    lval, non_constant_p, overflow_p);
+					    lval, non_constant_p, overflow_p,
+					    jump_target);
 
+    case THROW_EXPR:
+      if (cxx_dialect >= cxx26)
+	return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
+					     non_constant_p, overflow_p,
+					     jump_target);
+      /* FALLTHROUGH */
     case LAMBDA_EXPR:
     case NEW_EXPR:
     case VEC_NEW_EXPR:
     case DELETE_EXPR:
     case VEC_DELETE_EXPR:
-    case THROW_EXPR:
     case MODOP_EXPR:
       /* GCC internal stuff.  */
     case VA_ARG_EXPR:
@@ -8738,7 +9822,8 @@ cxx_eval_constant_expression (const cons
     case OBJ_TYPE_REF:
       /* Virtual function lookup.  We don't need to do anything fancy.  */
       return cxx_eval_constant_expression (ctx, OBJ_TYPE_REF_EXPR (t),
-					   lval, non_constant_p, overflow_p);
+					   lval, non_constant_p, overflow_p,
+					   jump_target);
 
     case PLACEHOLDER_EXPR:
       /* Use of the value or address of the current object.  */
@@ -8748,7 +9833,8 @@ cxx_eval_constant_expression (const cons
 	    return ctor;
 	  else
 	    return cxx_eval_constant_expression (ctx, ctor, lval,
-						 non_constant_p, overflow_p);
+						 non_constant_p, overflow_p,
+						 jump_target);
 	}
       /* A placeholder without a referent.  We can get here when
 	 checking whether NSDMIs are noexcept, or in massage_init_elt;
@@ -8761,7 +9847,10 @@ cxx_eval_constant_expression (const cons
       {
 	tree cond = TREE_OPERAND (t, 0);
 	cond = cxx_eval_constant_expression (ctx, cond, vc_prvalue,
-					     non_constant_p, overflow_p);
+					     non_constant_p, overflow_p,
+					     jump_target);
+	if (*jump_target)
+	  return NULL_TREE;
 	VERIFY_CONSTANT (cond);
 	if (integer_nonzerop (cond))
 	  *jump_target = t;
@@ -8873,7 +9962,7 @@ cxx_eval_constant_expression (const cons
 	  *non_constant_p = true;
 	  return t;
 	}
-      r = cxx_eval_bit_cast (ctx, t, non_constant_p, overflow_p);
+      r = cxx_eval_bit_cast (ctx, t, non_constant_p, overflow_p, jump_target);
       break;
 
     case OMP_PARALLEL:
@@ -9192,8 +10281,34 @@ cxx_eval_outermost_constant_expr (tree t
 
   if (manifestly_const_eval == mce_true)
     instantiate_constexpr_fns (r);
+  tree jmp_target = NULL_TREE;
   r = cxx_eval_constant_expression (&ctx, r, vc_prvalue,
-				    &non_constant_p, &overflow_p);
+				    &non_constant_p, &overflow_p,
+				    &jmp_target);
+  if (throws (&jmp_target) && !non_constant_p)
+    {
+      if (!ctx.quiet)
+	diagnose_uncaught_exception (input_location, &ctx, jmp_target);
+      non_constant_p = true;
+      jmp_target = NULL_TREE;
+      r = t;
+    }
+  else if (!non_constant_p && jmp_target)
+    {
+      non_constant_p = true;
+      if (!ctx.quiet)
+	{
+	  if (breaks (&jmp_target))
+	    error ("%<break%> outside of a loop or %<switch%>");
+	  else if (continues (&jmp_target))
+	    error ("%<continue%> outside of a loop");
+	  else if (returns (&jmp_target))
+	    error ("%<return%> in a statement expression");
+	  else
+	    gcc_unreachable ();
+	}
+      r = t;
+    }
 
   /* If we got a non-simple TARGET_EXPR, the initializer was a sequence
      of statements, and the result ought to be stored in ctx.ctor.  */
@@ -9202,15 +10317,31 @@ cxx_eval_outermost_constant_expr (tree t
 
   unsigned int i;
   tree cleanup;
+  jmp_target = NULL_TREE;
   /* Evaluate the cleanups.  */
   FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup)
-    cxx_eval_constant_expression (&ctx, cleanup, vc_discard,
-				  &non_constant_p, &overflow_p);
+    if (cleanup == NULL_TREE)
+      /* NULL_TREE cleanup is a marker that before it is
+	 CLEANUP_EH_ONLY cleanup.  Skip the cleanup before it.  */
+      --i;
+    else
+      cxx_eval_constant_expression (&ctx, cleanup, vc_discard,
+				    &non_constant_p, &overflow_p,
+				    &jmp_target);
+  if (throws (&jmp_target) && !non_constant_p)
+    {
+      if (!ctx.quiet)
+	diagnose_uncaught_exception (input_location, &ctx, jmp_target);
+      non_constant_p = true;
+      r = t;
+    }
 
   /* Mutable logic is a bit tricky: we want to allow initialization of
      constexpr variables with mutable members, but we can't copy those
      members to another constexpr variable.  */
-  if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_MUTABLE_POISON (r))
+  if (!non_constant_p
+      && TREE_CODE (r) == CONSTRUCTOR
+      && CONSTRUCTOR_MUTABLE_POISON (r))
     {
       if (!allow_non_constant)
 	error ("%qE is not a constant expression because it refers to "
@@ -9228,8 +10359,13 @@ cxx_eval_outermost_constant_expr (tree t
 	{
 	  if (!allow_non_constant && !non_constant_p)
 	    {
-	      error ("%qE is not a constant expression because it refers to "
-		     "a result of %<operator new%>", t);
+	      if (DECL_LANG_SPECIFIC (heap_var))
+		error ("%qE is not a constant expression because it refers to "
+		       "exception object allocated with "
+		       "%<__cxa_allocate_exception%>", t);
+	      else
+		error ("%qE is not a constant expression because it refers to "
+		       "a result of %<operator new%>", t);
 	      inform (DECL_SOURCE_LOCATION (heap_var), "allocated here");
 	    }
 	  r = t;
@@ -9812,11 +10948,13 @@ struct check_for_return_continue_data {
   hash_set<tree> *pset;
   tree continue_stmt;
   tree break_stmt;
+  bool could_throw;
 };
 
 /* Helper function for potential_constant_expression_1 SWITCH_STMT handling,
    called through cp_walk_tree.  Return the first RETURN_EXPR found, or note
-   the first CONTINUE_STMT and/or BREAK_STMT if RETURN_EXPR is not found.  */
+   the first CONTINUE_STMT and/or BREAK_STMT if RETURN_EXPR is not found.
+   For C++26 also note presence of possibly throwing calls.  */
 static tree
 check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
 {
@@ -9901,6 +11039,20 @@ check_for_return_continue (tree *tp, int
     case CONSTRUCTOR:
       break;
 
+    case AGGR_INIT_EXPR:
+    case CALL_EXPR:
+      /* In C++26 a function could throw.  */
+      if (cxx_dialect >= cxx26 && flag_exceptions && cp_get_callee (t))
+	{
+	  tree callee = cp_get_callee (t);
+	  if (!flag_enforce_eh_specs
+	      || type_dependent_expression_p (callee)
+	      || !POINTER_TYPE_P (TREE_TYPE (callee))
+	      || !type_noexcept_p (TREE_TYPE (TREE_TYPE (callee))))
+	    d->could_throw = true;
+	}
+      break;
+
     default:
       if (!EXPR_P (t))
 	*walk_subtrees = 0;
@@ -10106,7 +11258,8 @@ potential_constant_expression_1 (tree t,
 			|| TREE_CODE (t) != CALL_EXPR
 			|| current_function_decl == NULL_TREE
 			|| !is_std_construct_at (current_function_decl))
-		    && !cxx_dynamic_cast_fn_p (fun))
+		    && !cxx_dynamic_cast_fn_p (fun)
+		    && !cxx_cxa_builtin_fn_p (fun))
 		  {
 		    if ((flags & tf_error)
 			&& constexpr_error (loc, fundef_p,
@@ -10153,6 +11306,19 @@ potential_constant_expression_1 (tree t,
 						  jump_target))
 	      return false;
           }
+	/* In C++26 a function could throw.  */
+	if (cxx_dialect >= cxx26
+	    && flag_exceptions
+	    && *jump_target == NULL_TREE
+	    && cp_get_callee (t))
+	  {
+	    tree callee = cp_get_callee (t);
+	    if (!flag_enforce_eh_specs
+		|| type_dependent_expression_p (callee)
+		|| !POINTER_TYPE_P (TREE_TYPE (callee))
+		|| !type_noexcept_p (TREE_TYPE (TREE_TYPE (callee))))
+	      *jump_target = void_node;
+	  }
         return true;
       }
 
@@ -10375,11 +11541,13 @@ potential_constant_expression_1 (tree t,
 		 a return.  */
 	      hash_set<tree> pset;
 	      check_for_return_continue_data data = { &pset, NULL_TREE,
-						      NULL_TREE };
+						      NULL_TREE, false };
 	      if (tree ret_expr
 		  = cp_walk_tree (&FOR_BODY (t), check_for_return_continue,
 				  &data, &pset))
 		*jump_target = ret_expr;
+	      if (data.could_throw)
+		*jump_target = void_node;
 	      return true;
 	    }
 	}
@@ -10419,11 +11587,13 @@ potential_constant_expression_1 (tree t,
 	     a return.  */
 	  hash_set<tree> pset;
 	  check_for_return_continue_data data = { &pset, NULL_TREE,
-						  NULL_TREE  };
+						  NULL_TREE, false };
 	  if (tree ret_expr
 	      = cp_walk_tree (&WHILE_BODY (t), check_for_return_continue,
 			      &data, &pset))
 	    *jump_target = ret_expr;
+	  if (data.could_throw)
+	    *jump_target = void_node;
 	  return true;
 	}
       if (!RECUR (WHILE_BODY (t), any))
@@ -10447,7 +11617,7 @@ potential_constant_expression_1 (tree t,
 	{
 	  hash_set<tree> pset;
 	  check_for_return_continue_data data = { &pset, NULL_TREE,
-						  NULL_TREE };
+						  NULL_TREE, false };
 	  if (tree ret_expr
 	      = cp_walk_tree (&SWITCH_STMT_BODY (t), check_for_return_continue,
 			      &data, &pset))
@@ -10456,6 +11626,8 @@ potential_constant_expression_1 (tree t,
 	  else if (data.continue_stmt)
 	    /* The switch can't return, but might continue.  */
 	    *jump_target = data.continue_stmt;
+	  if (data.could_throw)
+	    *jump_target = void_node;
 	}
       return true;
 
@@ -10485,7 +11657,6 @@ potential_constant_expression_1 (tree t,
 
     case DYNAMIC_CAST_EXPR:
     case PSEUDO_DTOR_EXPR:
-    case THROW_EXPR:
     case OMP_PARALLEL:
     case OMP_TASK:
     case OMP_FOR:
@@ -10541,6 +11712,11 @@ potential_constant_expression_1 (tree t,
 	 constant.  */
       return true;
 
+    case THROW_EXPR:
+      if (cxx_dialect < cxx26)
+	goto fail;
+      return RECUR (TREE_OPERAND (t, 0), rval);
+
     case ASM_EXPR:
       if (flags & tf_error)
 	inline_asm_in_constexpr_error (loc, fundef_p);
@@ -10669,6 +11845,22 @@ potential_constant_expression_1 (tree t,
     case CLEANUP_POINT_EXPR:
     case MUST_NOT_THROW_EXPR:
     case TRY_CATCH_EXPR:
+      /* Even for C++26 handle TRY_BLOCK conservatively, if we detect the
+	 body could throw, even with catch (...) among handlers we'd need
+	 to analyze them in detail if they couldn't rethrow it.  More
+	 importantly though, throws (jump_target) is just conservative,
+	 and there could be e.g.
+	   try
+	     {
+	       possibly_throwing_fn (args);
+	       break;
+	     }
+	   catch (...)
+	     {
+	     }
+	 or continue or return instead of break.  So, clearing *jump_target
+	 because we see catch (...) handler might mean we missed break
+	 etc.  */
     case TRY_BLOCK:
     case EH_SPEC_BLOCK:
     case EXPR_STMT:
@@ -10910,9 +12102,9 @@ potential_constant_expression_1 (tree t,
 					       want_rval, strict, now, fundef_p,
 					       tf_none, &this_jump_target))
 	    {
-	      if (returns (&this_jump_target))
+	      if (returns (&this_jump_target) || throws (&this_jump_target))
 		*jump_target = this_jump_target;
-	      else if (!returns (jump_target))
+	      else if (!returns (jump_target) && !throws (jump_target))
 		{
 		  if (breaks (&this_jump_target)
 		      || continues (&this_jump_target))
@@ -10924,7 +12116,8 @@ potential_constant_expression_1 (tree t,
 			 couldn't return, break or continue.  */
 		      hash_set<tree> pset;
 		      check_for_return_continue_data data = { &pset, NULL_TREE,
-							      NULL_TREE };
+							      NULL_TREE,
+							      false };
 		      if (tree ret_expr
 			= cp_walk_tree (&TREE_OPERAND (t, 2),
 					check_for_return_continue, &data,
@@ -10937,6 +12130,8 @@ potential_constant_expression_1 (tree t,
 			  else if (data.break_stmt)
 			    *jump_target = data.break_stmt;
 			}
+		      if (data.could_throw)
+			*jump_target = void_node;
 		    }
 		}
 	      return true;
--- gcc/cp/cp-tree.h.jj	2025-05-31 00:43:13.832238574 +0200
+++ gcc/cp/cp-tree.h	2025-05-31 00:43:32.172996809 +0200
@@ -452,6 +452,7 @@ extern GTY(()) tree cp_global_trees[CPTI
       contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
       RETURN_EXPR_LOCAL_ADDR_P (in RETURN_EXPR)
       PACK_INDEX_PARENTHESIZED_P (in PACK_INDEX_*)
+      MUST_NOT_THROW_NOEXCEPT_P (in MUST_NOT_THROW_EXPR)
    1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE)
       TI_PENDING_TEMPLATE_FLAG.
       TEMPLATE_PARMS_FOR_INLINE.
@@ -472,6 +473,7 @@ extern GTY(()) tree cp_global_trees[CPTI
       BIND_EXPR_VEC_DTOR (in BIND_EXPR)
       ATOMIC_CONSTR_EXPR_FROM_CONCEPT_P (in ATOMIC_CONSTR)
       STATIC_INIT_DECOMP_BASE_P (in the TREE_LIST for {static,tls}_aggregates)
+      MUST_NOT_THROW_THROW_P (in MUST_NOT_THROW_EXPR)
    2: IDENTIFIER_KIND_BIT_2 (in IDENTIFIER_NODE)
       ICS_THIS_FLAG (in _CONV)
       DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL)
@@ -493,6 +495,7 @@ extern GTY(()) tree cp_global_trees[CPTI
       contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
       STATIC_INIT_DECOMP_NONBASE_P (in the TREE_LIST
 				    for {static,tls}_aggregates)
+      MUST_NOT_THROW_CATCH_P (in MUST_NOT_THROW_EXPR)
    3: IMPLICIT_RVALUE_P (in NON_LVALUE_EXPR or STATIC_CAST_EXPR)
       ICS_BAD_FLAG (in _CONV)
       FN_TRY_BLOCK_P (in TRY_BLOCK)
@@ -3007,6 +3010,8 @@ struct GTY(()) lang_decl_min {
      In a lambda-capture proxy VAR_DECL, this is DECL_CAPTURED_VARIABLE.
      In a function-scope TREE_STATIC VAR_DECL or IMPLICIT_TYPEDEF_P TYPE_DECL,
      this is DECL_DISCRIMINATOR.
+     In constexpr exception artificial VAR_DECL, this is
+     DECL_EXCEPTION_REFCOUNT.
      In a DECL_LOCAL_DECL_P decl, this is the namespace decl it aliases.
      Otherwise, in a class-scope DECL, this is DECL_ACCESS.   */
   tree access;
@@ -4461,6 +4466,23 @@ get_vec_init_expr (tree t)
 #define MUST_NOT_THROW_COND(NODE) \
   TREE_OPERAND (MUST_NOT_THROW_EXPR_CHECK (NODE), 1)
 
+/* Reasons why MUST_NOT_THROW_EXPR has been created.  */
+
+/* Indicates MUST_NOT_THROW_EXPR has been created to wrap body of
+   a noexcept function.  */
+#define MUST_NOT_THROW_NOEXCEPT_P(NODE) \
+  TREE_LANG_FLAG_0 (MUST_NOT_THROW_EXPR_CHECK (NODE))
+
+/* Indicates MUST_NOT_THROW_EXPR has been created to wrap construction of
+   exception object during throw.  */
+#define MUST_NOT_THROW_THROW_P(NODE) \
+  TREE_LANG_FLAG_1 (MUST_NOT_THROW_EXPR_CHECK (NODE))
+
+/* Indicates MUST_NOT_THROW_EXPR has been created to wrap construction of
+   handler parameter during catch.  */
+#define MUST_NOT_THROW_CATCH_P(NODE) \
+  TREE_LANG_FLAG_2 (MUST_NOT_THROW_EXPR_CHECK (NODE))
+
 /* The TYPE_MAIN_DECL for a class template type is a TYPE_DECL, not a
    TEMPLATE_DECL.  This macro determines whether or not a given class
    type is really a template type, as opposed to an instantiation or
@@ -4500,7 +4522,7 @@ get_vec_init_expr (tree t)
 #define TYPE_CONTAINS_VPTR_P(NODE)		\
   (TYPE_POLYMORPHIC_P (NODE) || CLASSTYPE_VBASECLASSES (NODE))
 
-/* Nonzero if NODE is a FUNCTION_DECL or VARIABLE_DECL (for a decl
+/* Nonzero if NODE is a FUNCTION_DECL or VAR_DECL (for a decl
    with namespace scope) declared in a local scope.  */
 #define DECL_LOCAL_DECL_P(NODE) \
   DECL_LANG_FLAG_0 (VAR_OR_FUNCTION_DECL_CHECK (NODE))
@@ -5141,6 +5163,10 @@ get_vec_init_expr (tree t)
    protected_access_node will appear in the DECL_ACCESS for the node.  */
 #define DECL_ACCESS(NODE) (LANG_DECL_MIN_CHECK (NODE)->access)
 
+/* In artificial VAR_DECL created by cxx_eval_cxa_builtin_fn with
+   CXA_ALLOCATE_EXCEPTION this is reference count.  */
+#define DECL_EXCEPTION_REFCOUNT(NODE) (LANG_DECL_MIN_CHECK (NODE)->access)
+
 /* Nonzero if the FUNCTION_DECL is a global constructor.  */
 #define DECL_GLOBAL_CTOR_P(NODE) \
   (LANG_DECL_FN_CHECK (NODE)->global_ctor_p)
@@ -6801,6 +6827,7 @@ enum cp_built_in_function {
   CP_BUILT_IN_IS_CORRESPONDING_MEMBER,
   CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
   CP_BUILT_IN_SOURCE_LOCATION,
+  CP_BUILT_IN_EH_PTR_ADJUST_REF,
   CP_BUILT_IN_LAST
 };
 
@@ -6981,6 +7008,7 @@ extern bool type_has_extended_temps		(tr
 extern tree strip_top_quals			(tree);
 extern bool reference_related_p			(tree, tree);
 extern bool reference_compatible_p		(tree, tree);
+extern bool handler_match_for_exception_type	(tree, tree);
 extern int remaining_arguments			(tree);
 extern tree build_implicit_conv_flags		(tree, tree, int);
 extern tree perform_implicit_conversion		(tree, tree, tsubst_flags_t);
--- gcc/cp/call.cc.jj	2025-05-31 00:43:13.828238627 +0200
+++ gcc/cp/call.cc	2025-05-31 00:43:32.163996928 +0200
@@ -1723,6 +1723,56 @@ involves_qualification_conversion_p (tre
   return false;
 }
 
+/* Return true if HANDLER is a match for exception object with EXCEPT_TYPE as
+   per [except.handle]/3.  */
+
+bool
+handler_match_for_exception_type (tree handler, tree except_type)
+{
+  tree handler_type = HANDLER_TYPE (handler);
+  if (handler_type == NULL_TREE)
+    return true; /* ... */
+  if (same_type_ignoring_top_level_qualifiers_p (handler_type, except_type))
+    return true;
+  if (CLASS_TYPE_P (except_type) && CLASS_TYPE_P (handler_type))
+    {
+      base_kind b_kind;
+      tree binfo = lookup_base (except_type, handler_type, ba_check, &b_kind,
+				tf_none);
+      if (binfo && binfo != error_mark_node)
+	return true;
+    }
+  if (TYPE_PTR_P (handler_type) || TYPE_PTRDATAMEM_P (handler_type))
+    {
+      if (TREE_CODE (except_type) == NULLPTR_TYPE)
+	return true;
+      if ((TYPE_PTR_P (handler_type) && TYPE_PTR_P (except_type))
+	  || (TYPE_PTRDATAMEM_P (handler_type)
+	      && TYPE_PTRDATAMEM_P (except_type)))
+	{
+	  conversion *conv
+	    = standard_conversion (handler_type, except_type, NULL_TREE,
+				   /*c_cast_p=*/false, 0, tf_none);
+	  if (conv && !conv->bad_p)
+	    {
+	      for (conversion *t = conv; t; t = next_conversion (t))
+		switch (t->kind)
+		  {
+		  case ck_ptr:
+		  case ck_fnptr:
+		  case ck_qual:
+		  case ck_identity:
+		    break;
+		  default:
+		    return false;
+		  }
+	      return true;
+	    }
+	}
+    }
+  return false;
+}
+
 /* A reference of the indicated TYPE is being bound directly to the
    expression represented by the implicit conversion sequence CONV.
    Return a conversion sequence for this binding.  */
--- gcc/cp/except.cc.jj	2025-05-31 00:43:13.837238508 +0200
+++ gcc/cp/except.cc	2025-05-31 00:43:32.161996954 +0200
@@ -367,6 +367,8 @@ initialize_handler_parm (tree decl, tree
 	 MUST_NOT_THROW_EXPR.  */
       init = fold_build_cleanup_point_expr (TREE_TYPE (init), init);
       init = build_must_not_throw_expr (init, NULL_TREE);
+      if (init && TREE_CODE (init) == MUST_NOT_THROW_EXPR)
+	MUST_NOT_THROW_CATCH_P (init) = 1;
     }
 
   decl = pushdecl (decl);
@@ -523,6 +525,7 @@ begin_eh_spec_block (void)
       r = build_stmt (spec_location, MUST_NOT_THROW_EXPR,
 		      NULL_TREE, NULL_TREE);
       TREE_SIDE_EFFECTS (r) = 1;
+      MUST_NOT_THROW_NOEXCEPT_P (r) = 1;
     }
   else
     r = build_stmt (spec_location, EH_SPEC_BLOCK, NULL_TREE, NULL_TREE);
@@ -614,6 +617,7 @@ wrap_cleanups_r (tree *tp, int *walk_sub
     {
       cleanup = build2 (MUST_NOT_THROW_EXPR, void_type_node, cleanup,
 			NULL_TREE);
+      MUST_NOT_THROW_THROW_P (cleanup) = 1;
       TARGET_EXPR_CLEANUP (exp) = cleanup;
     }
 
@@ -712,6 +716,13 @@ build_throw (location_t loc, tree exp, t
       allocate_expr = do_allocate_exception (temp_type);
       if (allocate_expr == error_mark_node)
 	return error_mark_node;
+      tree ptr_copy = NULL_TREE;
+      /* For C++26, copy ptr inside of the CLEANUP_POINT_EXPR
+	 added below to a TARGET_EXPR slot added outside of it,
+	 otherwise during constant evaluation of throw expression
+	 we'd diagnose accessing ptr outside of its lifetime.  */
+      if (cxx_dialect >= cxx26)
+	ptr_copy = get_internal_target_expr (null_pointer_node);
       allocate_expr = get_internal_target_expr (allocate_expr);
       ptr = TARGET_EXPR_SLOT (allocate_expr);
       TARGET_EXPR_CLEANUP (allocate_expr) = do_free_exception (ptr);
@@ -763,10 +774,21 @@ build_throw (location_t loc, tree exp, t
       /* Prepend the allocation.  */
       exp = build2 (COMPOUND_EXPR, TREE_TYPE (exp), allocate_expr, exp);
 
+      if (ptr_copy)
+	{
+	  exp = build2 (COMPOUND_EXPR, void_type_node, exp,
+			build2 (MODIFY_EXPR, void_type_node,
+				TARGET_EXPR_SLOT (ptr_copy), ptr));
+	  ptr = TARGET_EXPR_SLOT (ptr_copy);
+	}
+
       /* Force all the cleanups to be evaluated here so that we don't have
 	 to do them during unwinding.  */
       exp = build1 (CLEANUP_POINT_EXPR, void_type_node, exp);
 
+      if (ptr_copy)
+	exp = build2 (COMPOUND_EXPR, TREE_TYPE (exp), ptr_copy, exp);
+
       throw_type = build_eh_type_type (prepare_eh_type (TREE_TYPE (object)));
 
       cleanup = NULL_TREE;
--- gcc/cp/tree.cc.jj	2025-05-31 00:43:13.845238403 +0200
+++ gcc/cp/tree.cc	2025-05-31 00:43:32.161996954 +0200
@@ -488,6 +488,7 @@ builtin_valid_in_constant_expr_p (const_
 	  case CP_BUILT_IN_SOURCE_LOCATION:
 	  case CP_BUILT_IN_IS_CORRESPONDING_MEMBER:
 	  case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
+	  case CP_BUILT_IN_EH_PTR_ADJUST_REF:
 	    return true;
 	  default:
 	    break;
--- gcc/cp/decl.cc.jj	2025-05-31 00:43:13.835238535 +0200
+++ gcc/cp/decl.cc	2025-05-31 00:43:32.174996782 +0200
@@ -5070,6 +5070,18 @@ cxx_init_decl_processing (void)
 			    BUILT_IN_FRONTEND, NULL, NULL_TREE);
   set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF);
 
+  if (cxx_dialect >= cxx26)
+    {
+      tree void_ptrintftype
+	= build_function_type_list (void_type_node, ptr_type_node,
+				    integer_type_node, NULL_TREE);
+      decl = add_builtin_function ("__builtin_eh_ptr_adjust_ref",
+				   void_ptrintftype,
+				   CP_BUILT_IN_EH_PTR_ADJUST_REF,
+				   BUILT_IN_FRONTEND, NULL, NULL_TREE);
+      set_call_expr_flags (decl, ECF_NOTHROW | ECF_LEAF);
+    }
+
   integer_two_node = build_int_cst (NULL_TREE, 2);
 
   /* Guess at the initial static decls size.  */
--- gcc/cp/pt.cc.jj	2025-05-31 00:43:13.842238442 +0200
+++ gcc/cp/pt.cc	2025-05-31 00:43:32.171996822 +0200
@@ -20105,7 +20105,14 @@ tsubst_stmt (tree t, tree args, tsubst_f
       {
 	tree op0 = RECUR (TREE_OPERAND (t, 0));
 	tree cond = RECUR (MUST_NOT_THROW_COND (t));
-	RETURN (build_must_not_throw_expr (op0, cond));
+	stmt = build_must_not_throw_expr (op0, cond);
+	if (stmt && TREE_CODE (stmt) == MUST_NOT_THROW_EXPR)
+	  {
+	    MUST_NOT_THROW_NOEXCEPT_P (stmt) = MUST_NOT_THROW_NOEXCEPT_P (t);
+	    MUST_NOT_THROW_THROW_P (stmt) = MUST_NOT_THROW_THROW_P (t);
+	    MUST_NOT_THROW_CATCH_P (stmt) = MUST_NOT_THROW_CATCH_P (t);
+	  }
+	RETURN (stmt);
       }
 
     case EXPR_PACK_EXPANSION:
--- gcc/cp/cp-gimplify.cc.jj	2025-05-31 00:43:13.831238588 +0200
+++ gcc/cp/cp-gimplify.cc	2025-05-31 00:43:32.175996769 +0200
@@ -889,6 +889,12 @@ cp_gimplify_expr (tree *expr_p, gimple_s
 			(EXPR_LOCATION (*expr_p), call_expr_nargs (*expr_p),
 			 &CALL_EXPR_ARG (*expr_p, 0));
 		break;
+	      case CP_BUILT_IN_EH_PTR_ADJUST_REF:
+		error_at (EXPR_LOCATION (*expr_p),
+			  "%qs used outside of constant expressions",
+			  "__builtin_eh_ptr_adjust_ref");
+		*expr_p = void_node;
+		break;
 	      default:
 		break;
 	      }
--- gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C.jj	2022-11-17 22:10:05.807657343 +0100
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C	2025-05-31 00:43:32.178996729 +0200
@@ -21,7 +21,7 @@ constexpr int bogus() { return zero () ?
 constexpr int correct_error() { return nonconst_func(0); } // { dg-error "nonconst_func" "" { target c++20_down } }
 
 // Correctly rejected.
-constexpr int z = bogus();	// { dg-error "" }
+constexpr int z = bogus();	// { dg-error "" "" { target c++23_down } }
 
 // This is also correctly rejected.
 constexpr int correct_failure() { return 0 ? 3 : nonconst_func(0); } // { dg-error "nonconst_func" "" { target c++20_down } }
--- gcc/testsuite/g++.dg/cpp0x/constexpr-throw.C.jj	2023-11-03 12:56:05.385107767 +0100
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-throw.C	2025-05-31 00:43:32.179996716 +0200
@@ -2,8 +2,8 @@
 // Explicit { dg-require-effective-target exceptions_enabled } to avoid verify compiler messages FAILs for '-fno-exceptions'.
 
 constexpr int may_throw(bool decide) {
-	return decide ? 42 : throw -1; // { dg-error "throw" }
+	return decide ? 42 : throw -1; // { dg-error "throw" "" { target c++23_down } }
 }
 
-constexpr int x = may_throw(false); // { dg-message "may_throw" }
-constexpr int y = may_throw(true);
+constexpr int x = may_throw(false); // { dg-message "may_throw" "" { target c++23_down } }
+constexpr int y = may_throw(true); // { dg-error "uncaught exception '-1'" "" { target c++26 } .-1 }
--- gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C.jj	2020-01-14 20:02:46.771610014 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C	2025-05-31 09:33:57.331105470 +0200
@@ -12,7 +12,7 @@ void
 f2 ()
 { 
   for (;;)
-    constexpr bool b = ({ break; false; }) && false;	// { dg-error "is not a constant expression" }
+    constexpr bool b = ({ break; false; }) && false;	// { dg-error "'break' outside of a loop or 'switch'" }
 }
 
 constexpr bool
--- gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C.jj	2023-11-03 12:56:05.385107767 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C	2025-05-31 00:43:32.177996742 +0200
@@ -27,8 +27,8 @@ constexpr int fun(int n) {
   case 0:
     return 1;
   default:
-    throw; // { dg-error "not a constant expression" }
-  }
+    throw; // { dg-error "not a constant expression" "" { target c++23_down } }
+  } // { dg-error "'void __cxa_rethrow\\\(\\\)' called with no caught exceptions active" "" { target c++26 } .-1 }
 }
 
 static_assert(fun(0), "");
--- gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C.jj	2025-04-22 21:26:15.778920109 +0200
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C	2025-05-31 00:43:32.175996769 +0200
@@ -18,7 +18,7 @@ struct H { short size () const { return
 	   constexpr const char *data () const { return ""; } };
 struct I { constexpr signed char size () const { return 0; }
 	   const char *data () const { return ""; } };
-struct J { constexpr int size () const { return j ? throw 1 : 0; }	// { dg-error "expression '<throw-expression>' is not a constant expression" }
+struct J { constexpr int size () const { return j ? throw 1 : 0; }	// { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23_down } }
 	   constexpr const char *data () const { return ""; };
 	   constexpr J (int x) : j (x) {}
 	   int j; };
@@ -114,6 +114,7 @@ foo ()
 
   asm ((J (0)));
   asm ("" :: (J (1)) (1));	// { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" }
+				// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
   asm ((M {}));
 #if __cpp_constexpr_dynamic_alloc >= 201907L
   asm ((N {}));			// { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++20 } }
@@ -188,6 +189,7 @@ bar ()
 
   asm ((J (0)));
   asm ("" :: (J (1)) (1));	// { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" }
+				// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
   asm ((M {}));
 #if __cpp_constexpr_dynamic_alloc >= 201907L
   asm ((N {}));			// { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++20 } }
@@ -272,7 +274,7 @@ namespace NN
 #if __cplusplus >= 201402L
   struct J {
     static constexpr int size () { return 0; }
-    static constexpr const char *data (int x = 0) { if (x) return nullptr; else throw 1; } }; // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++14 } }
+    static constexpr const char *data (int x = 0) { if (x) return nullptr; else throw 1; } }; // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target { c++14 && c++23_down } } }
 #endif
 #if __cpp_if_consteval >= 202106L
   struct K {
@@ -284,12 +286,12 @@ namespace NN
     static constexpr const char *data () { if consteval { return "test"; } else { throw 1; } }
   };
   struct M {
-    static constexpr int size () { if consteval { throw 1; } else { return 4; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23 } }
+    static constexpr int size () { if consteval { throw 1; } else { return 4; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23_only } }
     static constexpr const char *data () { return "test"; }
   };
   struct N {
     static constexpr int size () { return 4; }
-    static constexpr const char *data () { if consteval { throw 1; } else { return "test"; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23 } }
+    static constexpr const char *data () { if consteval { throw 1; } else { return "test"; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23_only } }
   };
 #endif
   struct O { constexpr int operator () () const { return 12; } };
@@ -318,12 +320,15 @@ namespace NN
     asm ((I {}));
 #if __cplusplus >= 201402L
     asm ((J {}));		// { dg-error "constexpr string 'data\\\(\\\)' must be a core constant expression" "" { target c++14 } }
+				// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
 #endif
 #if __cpp_if_consteval >= 202106L
     asm ((K {}));
     asm ((L {}));
     asm ((M {}));		// { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target c++23 } }
+				// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
     asm ((N {}));		// { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++23 } }
+				// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
 #endif
     asm ((Q {}));
     asm ((R {}));
@@ -348,12 +353,15 @@ namespace NN
     asm ((I {}));
 #if __cplusplus >= 201402L
     asm ((J {}));		// { dg-error "constexpr string 'data\\\(\\\)' must be a core constant expression" "" { target c++14 } }
+				// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
 #endif
 #if __cpp_if_consteval >= 202106L
     asm ((K {}));
     asm ((L {}));
     asm ((M {}));		// { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target c++23 } }
+				// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
     asm ((N {}));		// { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++23 } }
+				// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
 #endif
     asm ((Q {}));
     asm ((R {}));
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh1.C.jj	2025-05-31 00:43:32.176996756 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh1.C	2025-05-31 00:43:32.176996756 +0200
@@ -0,0 +1,140 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct S {
+  constexpr S () : s (new int (0)) {}
+  constexpr S (int x) : s (new int (x)) {}
+  constexpr S (const S &x) : s (new int (*x.s)) {}
+  constexpr ~S () { delete s; }
+  int *s;
+};
+struct T : public S {
+  constexpr T () : S () {}
+  constexpr T (int x) : S (x) {}
+  constexpr T (const T &x) : S (*x.s) {}
+  constexpr ~T () {}
+};
+struct U : public T {
+  constexpr U () : T () {}
+  constexpr U (int x) : T (x) {}
+  constexpr U (const U &x) : T (*x.s) {}
+  constexpr ~U () {}
+};
+struct V : public T {
+  constexpr V () : T () {}
+  constexpr V (int x) : T (x) {}
+  constexpr V (const U &x) : T (*x.s) {}
+  constexpr ~V () {}
+};
+
+template <typename X>
+constexpr int
+foo (X x)
+{
+  try { throw x; }
+  catch (int &a) { return 42 + a; }
+  catch (const unsigned b) { return 43 + b; }
+  catch (const long &c) { return 44 + c; }
+  catch (bool d) { return 45 + d; }
+  catch (const U &e) { return 46 + *e.s; }
+  catch (const T &f) { return 47 + *f.s; }
+  catch (S g) { return 48 + *g.s; }
+  catch (int *const &h) { return 49; }
+  catch (long long *) { return 50; }
+  catch (const S *const &) { return 51; }
+  catch (...) { return 52; }
+}
+
+template <typename X>
+constexpr int
+bar (const X &x)
+{
+  throw x;
+}
+
+template <typename X>
+constexpr int
+baz (const X &x)
+{
+  try
+    {
+      try { bar (x); }
+      catch (int &a) { a += 80; throw; }
+      catch (long b) { b += 80; throw; }
+      catch (U &c) { c.s[0] += 82; throw; }
+      catch (V d) { d.s[0] += 83; throw; }
+    }
+  catch (int a) { return 42 + a; }
+  catch (const long &b) { return 43 + b; }
+  catch (S &c) { return 44 + c.s[0]; }
+  catch (long long d) { return 45 + d; }
+  catch (...) { return -1; }
+}
+
+constexpr int
+qux (int x, bool y = true)
+{
+  try
+    {
+      switch (x)
+	{
+	case 0: throw 42; break;
+	case 1: x = y ? throw 43 : 5; break;
+	case 2: x = -(throw 44, 6); break;
+	case 3: x = x + (throw 45, 7); break;
+	case 4: x = (throw 46, 8) + x; break;
+	case 5: x = (throw 47, y) ? 4 : 5; break;
+	case 6: x += (throw 48, y); break;
+	case 7: x = (double) (throw 49, y); break;
+	case 8: x = foo ((throw 50, x)); break;
+	default: break;
+	}
+    }
+  catch (int a) { return a; }
+  return -1;
+}
+
+constexpr int
+corge ()
+{
+  try { throw 0; }
+  catch (int *const &h) { return 49; }
+  catch (long long *) { return 50; }
+  catch (const S *const &) { return 51; }
+  catch (...) { return 52; }
+}
+
+static_assert (foo (12) == 54);
+static_assert (foo (12U) == 55);
+static_assert (foo (12L) == 56);
+static_assert (foo (false) == 45);
+static_assert (foo (true) == 46);
+static_assert (foo (U (12)) == 58);
+static_assert (foo (T (20)) == 67);
+static_assert (foo (S (30)) == 78);
+static_assert (foo (nullptr) == 49);
+static_assert (foo ((int *)nullptr) == 49);
+static_assert (foo ((long long *)nullptr) == 50);
+static_assert (foo ((const S *)nullptr) == 51);
+static_assert (foo ((const T *)nullptr) == 51);
+static_assert (foo ((const U *)nullptr) == 51);
+static_assert (foo (12ULL) == 52);
+static_assert (baz (5) == 127);
+static_assert (baz (6L) == 49);
+static_assert (baz (U (25)) == 151);
+static_assert (baz (V (26)) == 70);
+static_assert (baz (T (27)) == 71);
+static_assert (baz (S (28)) == 72);
+static_assert (baz (7LL) == 52);
+static_assert (baz (8ULL) == -1);
+static_assert (qux (0) == 42);
+static_assert (qux (1) == 43);
+static_assert (qux (2) == 44);
+static_assert (qux (3) == 45);
+static_assert (qux (4) == 46);
+static_assert (qux (5) == 47);
+static_assert (qux (6) == 48);
+static_assert (qux (7) == 49);
+static_assert (qux (8) == 50);
+static_assert (corge () == 52);
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh2.C.jj	2025-05-31 00:43:32.176996756 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh2.C	2025-05-31 00:43:32.176996756 +0200
@@ -0,0 +1,112 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct S {
+  constexpr S () : s (0) {}
+  constexpr S (int x) : s (x) { if (x == 42) throw 42; }
+  constexpr S (const S &x) : s (x.s) {}
+  constexpr ~S () noexcept (false) { if (s == 41) throw 41; }
+  constexpr const char *what () const noexcept { return "S"; }
+  int s;
+};
+struct T : public S {
+  constexpr T () {}
+  constexpr T (int x) : S (x) {}
+  constexpr T (const T &x) : S (x.s) {}
+  constexpr ~T () {}
+  constexpr const char *what () const noexcept { return "T"; }
+};
+struct U {
+  constexpr U () : u (0) {}
+  constexpr U (int x) : u (x) {}
+  constexpr U (const S &x) : u (0) {}
+  constexpr U (const U &x) : u (x.u) { if (u == 42) throw 43; }
+  constexpr ~U () {}
+  constexpr const char *what () const noexcept { return "U"; }
+  int u;
+};
+
+constexpr int
+foo (int x)
+{
+  if (x == 1)
+    throw 43;
+  return x;
+}
+
+constexpr int
+bar (int x) noexcept	// { dg-error "'std::terminate' called" }
+{			// { dg-message "uncaught exception exited from 'noexcept' function 'constexpr int bar\\\(int\\\)'" "" { target *-*-* } .-1 }
+  return foo (x);
+}
+
+constexpr int
+baz (int x)
+{
+  switch (x)
+    {
+    case 0: throw 1; break;
+    case 1: try { x = bar (x); } catch (...) {} break;		// { dg-message "in 'constexpr' expansion of" }
+    case 2: throw S (2); break;
+    case 3: try { throw S (42); } catch (int a) { if (a != 42) throw -1; } break;
+    case 4: try { S s (41); throw 2; } catch (...) {} break;	// { dg-error "'std::terminate' called" }
+    case 5: return 5;						// { dg-message "destructor exited with an exception" "" { target *-*-* } .-1 }
+    case 6:
+      try
+	{
+	  throw S (5);
+	}
+      catch (const T &) {}
+      catch (int) {}
+      catch (const bool &) {}
+      catch (const T **const &) {}
+      break;
+    case 7: try { constexpr int y = foo (2); } catch (...) {} break;
+    case 8:
+      try
+	{
+	  try
+	    {
+	      throw U ();
+	    }
+	  catch (U &u)
+	    {
+	      u.u = 42;
+	      throw;
+	    }
+	}
+      catch (U u)					// { dg-error "'std::terminate' called" }
+	{						// { dg-message "constructor exited with another exception while entering handler" "" { target *-*-* } .-1 }
+	}
+      break;
+    case 9:
+      try
+	{
+	  throw U (S (41));				// { dg-error "'std::terminate' called" }
+	}						// { dg-message "destructor exited with an exception" "" { target *-*-* } .-1 }
+      catch (...)
+	{
+	}
+      break;
+    }
+  return -1;
+}
+
+constexpr int
+qux (int x)
+{
+  try { constexpr int y = foo (1); } catch (...) {}	// { dg-error "uncaught exception" }
+  return 0;
+}
+
+constexpr int a = baz (0);	// { dg-error "uncaught exception" }
+constexpr int b = baz (1);	// { dg-message "in 'constexpr' expansion of" }
+constexpr int c = baz (2);	// { dg-error "uncaught exception" }
+constexpr int d = baz (3);
+constexpr int e = baz (4);	// { dg-message "in 'constexpr' expansion of" }
+constexpr int f = baz (5);
+constexpr int g = baz (6);	// { dg-error "uncaught exception" }
+constexpr int h = baz (7);
+constexpr int i = baz (8);	// { dg-message "in 'constexpr' expansion of" }
+constexpr int j = baz (9);	// { dg-message "in 'constexpr' expansion of" }
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh3.C.jj	2025-05-31 00:43:32.176996756 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh3.C	2025-05-31 00:43:32.176996756 +0200
@@ -0,0 +1,442 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+#include <exception>
+#include <new>
+#include <typeinfo>
+
+constexpr std::exception a;
+constexpr const char *b = a.what ();
+constexpr std::bad_exception c;
+constexpr const char *d = c.what ();
+constexpr std::bad_alloc e;
+constexpr const char *f = e.what ();
+constexpr std::bad_array_new_length g;
+constexpr const char *h = g.what ();
+constexpr std::bad_cast i;
+constexpr const char *j = i.what ();
+constexpr std::bad_typeid k;
+constexpr const char *l = k.what ();
+constexpr std::exception_ptr m = nullptr;
+static_assert (m == nullptr);
+constexpr std::exception_ptr n = std::current_exception ();
+static_assert (n == nullptr);
+constexpr std::exception_ptr o;
+static_assert (o == nullptr);
+constexpr std::nested_exception p;
+static_assert (p.nested_ptr () == nullptr);
+
+struct A { virtual ~A () {} };
+struct B { virtual void b (); };
+struct C { virtual void c (); };
+struct D : private B { virtual void d (); };
+struct E { virtual void e (); };
+struct F : D, E, private C { virtual void f (); };
+struct G { constexpr G () { if (std::uncaught_exceptions () != 0) asm (""); } };
+struct H { constexpr H () : h (0) {} constexpr ~H () { if (std::uncaught_exceptions () != h) asm (""); } int h; };
+struct I : std::nested_exception { };
+struct J { virtual ~J () noexcept = default; };
+struct K final { };
+struct L : J, std::nested_exception { };
+struct M { };
+struct N : I, L { };
+struct O : private std::nested_exception { };
+
+constexpr int
+foo (int x)
+{
+  if (std::uncaught_exceptions () != 0)
+    return -1;
+  switch (x)
+    {
+    case 0:
+      try
+	{
+	  const std::type_info &s = typeid (*(A *) 0);
+	  return -1;
+	}
+      catch (const std::bad_typeid &x)
+	{
+	  if (std::uncaught_exceptions () != 0)
+	    return -1;
+	  const char *p = x.what ();
+	  return 1;
+	}
+      catch (...)
+	{
+	  return -1;
+	}
+      break;
+    case 1:
+      try
+	{
+	  static constexpr F f;
+	  D &d = dynamic_cast<D &>((B &) f);
+	  return -1;
+	}
+      catch (std::bad_cast x)
+	{
+	  const char *p = x.what ();
+	  return 2;
+	}
+      catch (...)
+	{
+	  return -1;
+	}
+      break;
+    case 2:
+      try
+	{
+	  H h;
+	  h.h = 1;
+	  if (std::current_exception () != nullptr)
+	    return -1;
+	  throw G ();
+	}
+      catch (const G &g)
+	{
+	  if (std::uncaught_exceptions () != 0)
+	    return -1;
+	  if (std::current_exception () == nullptr)
+	    return -1;
+	  return 3;
+	}
+      catch (...)
+	{
+	  return -1;
+	}
+      break;
+    case 3:
+      try
+	{
+	  decltype (sizeof 0) x = -64;
+	  char (*a)[2] = new char[x][2];
+	  delete[] a;
+	}
+      catch (std::bad_array_new_length x)
+	{
+	  return 4;
+	}
+      break;
+    case 4:
+      try
+	{
+	  int y = -1;
+	  int *a = new int[y];
+	  delete[] a;
+	}
+      catch (const std::bad_array_new_length &)
+	{
+	  return 5;
+	}
+      break;
+    case 5:
+      try
+	{
+	  int z = 1;
+	  int *a = new int[z]{1, 2, 3};
+	  delete[] a;
+	}
+      catch (std::bad_array_new_length &)
+	{
+	  return 6;
+	}
+      break;
+    case 6:
+      {
+	std::exception_ptr b, d;
+	if (b != nullptr || d != nullptr)
+	  return -1;
+	try
+	  {
+	    throw 1;
+	  }
+	catch (int a)
+	  {
+	    if (a != 1)
+	      return -1;
+	    b = std::current_exception ();
+	    if (b == nullptr)
+	      return -1;
+	    try
+	      {
+		throw 2L;
+	      }
+	    catch (long int c)
+	      {
+		if (c != 2L)
+		  return -1;
+		d = std::current_exception ();
+		if (d == nullptr || b == d)
+		  return -1;
+	      }
+	    if (std::current_exception () != b)
+	      return -1;
+	  }
+	if (std::current_exception () != nullptr)
+	  return -1;
+	try
+	  {
+	    std::rethrow_exception (d);
+	  }
+	catch (long int &e)
+	  {
+	    if (e != 2L)
+	      return -1;
+	    try
+	      {
+		std::rethrow_exception (b);
+	      }
+	    catch (const int &f)
+	      {
+		if (f != 1)
+		  return -1;
+		try
+		  {
+		    std::rethrow_exception (d);
+		  }
+		catch (const long int g)
+		  {
+		    if (g != 2L)
+		      return -1;
+		    try
+		      {
+			std::rethrow_exception (b);
+		      }
+		    catch (int h)
+		      {
+			if (h != 1)
+			  return -1;
+			std::exception_ptr i (b);
+			std::exception_ptr j;
+			if (j != nullptr || i == nullptr || i != b || bool (j))
+			  return -1;
+			j = i;
+			if (j != b || !bool (j))
+			  return -1;
+			j = nullptr;
+			std::swap (i, j);
+			if (j == nullptr || j != b || i != nullptr)
+			  return -1;
+		      }
+		  }
+	      }
+	  }
+	return 7;
+      }
+    case 7:
+      {
+	std::exception_ptr a = std::make_exception_ptr (42);
+	std::exception_ptr b = std::make_exception_ptr (std::exception ());
+	std::exception_ptr c
+	  = std::make_exception_ptr (std::bad_array_new_length ());
+	try
+	  {
+	    std::rethrow_exception (a);
+	  }
+	catch (int d)
+	  {
+	    if (d != 42)
+	      return -1;
+	    try
+	      {
+		std::rethrow_exception (b);
+	      }
+	    catch (const std::exception &e)
+	      {
+		const char *f = e.what ();
+		try
+		  {
+		    std::rethrow_exception (c);
+		  }
+		catch (const std::bad_alloc &g)
+		  {
+		    try
+		      {
+			throw;
+		      }
+		    catch (const std::bad_array_new_length &h)
+		      {
+			const char *i = h.what ();
+			const char *j = g.what ();
+		      }
+		  }
+	      }
+	  }
+	return 8;
+      }
+    case 8:
+      {
+	std::nested_exception a;
+	if (a.nested_ptr () != nullptr)
+	  return -1;
+	try
+	  {
+	    std::nested_exception b;
+	    if (b.nested_ptr () != nullptr)
+	      return -1;
+	    throw 42;
+	  }
+	catch (...)
+	  {
+	    std::nested_exception c;
+	    if (c.nested_ptr () != std::current_exception ())
+	      return -1;
+	    std::nested_exception d = c;
+	    if (d.nested_ptr () != c.nested_ptr ())
+	      return -1;
+	    c = d;
+	    try
+	      {
+		c.rethrow_nested ();
+	      }
+	    catch (const int &e)
+	      {
+		if (e != 42)
+		  return -1;
+	      }
+	  }
+	return 9;
+      }
+    case 9:
+      try
+	{
+	  std::throw_with_nested (I ());
+	}
+      catch (const std::nested_exception &a)
+	{
+	  if (a.nested_ptr () != nullptr)
+	    return -1;
+	  try
+	    {
+	      throw;
+	    }
+	  catch (const I &)
+	    {
+	      return 10;
+	    }
+	}
+      return -1;
+    case 10:
+      try
+	{
+	  std::throw_with_nested (J ());
+	}
+      catch (const std::nested_exception &a)
+	{
+	  if (a.nested_ptr () != nullptr)
+	    return -1;
+	  try
+	    {
+	      throw;
+	    }
+	  catch (const J &)
+	    {
+	      return 11;
+	    }
+	}
+      return -1;
+    case 11:
+      try
+	{
+	  std::throw_with_nested (K ());
+	}
+      catch (const std::nested_exception &)
+	{
+	  return -1;
+	}
+      catch (const K &)
+	{
+	  return 12;
+	}
+      return -1;
+    case 12:
+      try
+	{
+	  throw 42;
+	}
+      catch (...)
+	{
+	  I a;
+	  try
+	    {
+	      std::rethrow_if_nested (a);
+	    }
+	  catch (const int &b)
+	    {
+	      if (b == 42)
+		return 13;
+	    }
+	}
+      return -1;
+    case 13:
+      try
+	{
+	  throw J ();
+	}
+      catch (const J &a)
+	{
+	  std::rethrow_if_nested (a);
+	  return 14;
+	}
+      return -1;
+    case 14:
+      try
+	{
+	  throw 42;
+	}
+      catch (...)
+	{
+	  try
+	    {
+	      throw L ();
+	    }
+	  catch (const J &a)
+	    {
+	      try
+		{
+		  std::rethrow_if_nested (a);
+		}
+	      catch (const int &b)
+		{
+		  if (b == 42)
+		    return 15;
+		}
+	    }
+	}
+      return -1;
+    case 15:
+      {
+	std::rethrow_if_nested (1);
+	M m;
+	std::rethrow_if_nested (m);
+	N n;
+	std::rethrow_if_nested (n);
+	O o;
+	std::rethrow_if_nested (o);
+	return 16;
+      }
+    default:
+      break;
+    }
+  return -1;
+}
+
+static_assert (foo (0) == 1);
+static_assert (foo (1) == 2);
+static_assert (foo (2) == 3);
+static_assert (foo (3) == 4);
+static_assert (foo (4) == 5);
+static_assert (foo (5) == 6);
+static_assert (foo (6) == 7);
+static_assert (foo (7) == 8);
+static_assert (foo (8) == 9);
+static_assert (foo (9) == 10);
+static_assert (foo (10) == 11);
+static_assert (foo (11) == 12);
+static_assert (foo (12) == 13);
+static_assert (foo (13) == 14);
+static_assert (foo (14) == 15);
+static_assert (foo (15) == 16);
+static_assert (std::uncaught_exceptions () == 0);
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh4.C.jj	2025-05-31 00:43:32.175996769 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh4.C	2025-05-31 00:43:32.175996769 +0200
@@ -0,0 +1,72 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct A { virtual ~A () {} };
+struct B { virtual void b (); };
+struct C { virtual void c (); };
+struct D : private B { virtual void d (); };
+struct E { virtual void e (); };
+struct F : D, E, private C { virtual void f (); };
+
+constexpr int
+foo (int x)
+{
+  switch (x)
+    {
+    case 1:
+      try
+	{
+	  static constexpr F f;
+	  D &d = dynamic_cast<D &>((B &) f);	// { dg-error "called without 'std::bad_cast' being defined" }
+	  return -1;
+	}
+      catch (...)
+	{
+	  return -1;
+	}
+      break;
+    case 3:
+      try
+	{
+	  decltype (sizeof 0) x = -64;
+	  char (*a)[2] = new char[x][2];	// { dg-error "called without 'std::bad_array_new_length' being defined" }
+	  delete[] a;
+	}
+      catch (...)
+	{
+	  return -1;
+	}
+      break;
+    case 4:
+      try
+	{
+	  int y = -1;
+	  int *a = new int[y];			// { dg-error "called without 'std::bad_array_new_length' being defined" }
+	  delete[] a;
+	}
+      catch (...)
+	{
+	  return -1;
+	}
+      break;
+    case 5:
+      try
+	{
+	  int z = 1;
+	  int *a = new int[z]{1, 2, 3};		// { dg-error "called without 'std::bad_array_new_length' being defined" }
+	  delete[] a;
+	}
+      catch (...)
+	{
+	  return -1;
+	}
+      break;
+    }
+  return -1;
+}
+
+constexpr int a = foo (1);			// { dg-message "in 'constexpr' expansion of" }
+constexpr int b = foo (3);			// { dg-message "in 'constexpr' expansion of" }
+constexpr int c = foo (4);			// { dg-message "in 'constexpr' expansion of" }
+constexpr int d = foo (5);			// { dg-message "in 'constexpr' expansion of" }
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh5.C.jj	2025-05-31 00:43:32.176996756 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh5.C	2025-05-31 00:43:32.176996756 +0200
@@ -0,0 +1,55 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+constexpr void
+foo ()
+{
+  throw 1;
+}
+
+void
+bar ()
+{
+}
+
+constexpr void
+baz ()
+{
+  foo ();
+  bar ();
+}
+
+constexpr void
+qux ()
+{
+  if consteval {
+    throw 2;
+  }
+  bar ();
+}
+
+constexpr bool
+corge ()
+{
+  try
+    {
+      baz ();
+    }
+  catch (int a)
+    {
+      if (a != 1)
+	return false;
+      try
+	{
+	  qux ();
+	}
+      catch (int b)
+	{
+	  return b == 2;
+	}
+    }
+  return false;
+}
+
+static_assert (corge ());
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh6.C.jj	2025-05-31 00:43:32.176996756 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh6.C	2025-05-31 00:43:32.176996756 +0200
@@ -0,0 +1,134 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct S {
+  constexpr S () : s (0) {}
+  constexpr S (int x) : s (x) {}
+  constexpr S (const S &x) : s (x.s) {}
+  constexpr ~S () {}
+  int s;
+};
+struct T {
+  constexpr T () : t (0) {}
+  constexpr T (int x) : t (x) {}
+  constexpr T (const T &x) : t (x.t) {}
+  constexpr ~T () {}
+  int t;
+};
+struct U : public S, public T {
+  constexpr U () : S (0), T (0) {}
+  constexpr U (int x, int y) : S (x), T (y) {}
+  constexpr U (const U &x) : S (x.s), T (x.t) {}
+  constexpr ~U () {}
+};
+
+constexpr bool
+foo ()
+{
+  try
+    {
+      throw U (1, 2);
+    }
+  catch (const U &x)
+    {
+      if (x.s != 1 || x.t != 2)
+	return false;
+      try
+	{
+	  throw;
+	}
+      catch (const S &y)
+	{
+	  if (y.s != 1)
+	    return false;
+	  try
+	    {
+	      throw;
+	    }
+	  catch (const T &z)
+	    {
+	      if (z.t != 2)
+		return false;
+	      return true;
+	    }
+	}
+    }
+  return false;
+}
+
+constexpr bool
+bar ()
+{
+  try
+    {
+      throw U (1, 2);
+    }
+  catch (U &x)
+    {
+      if (x.s != 1 || x.t != 2)
+	return false;
+      try
+	{
+	  x.s = 3;
+	  x.t = 4;
+	  throw;
+	}
+      catch (S &y)
+	{
+	  if (y.s != 3)
+	    return false;
+	  try
+	    {
+	      throw;
+	    }
+	  catch (T &z)
+	    {
+	      if (z.t != 4)
+		return false;
+	      return true;
+	    }
+	}
+    }
+  return false;
+}
+
+constexpr bool
+baz ()
+{
+  try
+    {
+      throw U (1, 2);
+    }
+  catch (U x)
+    {
+      if (x.s != 1 || x.t != 2)
+	return false;
+      try
+	{
+	  x.s = 3;
+	  x.t = 4;
+	  throw;
+	}
+      catch (S y)
+	{
+	  if (y.s != 1)
+	    return false;
+	  try
+	    {
+	      throw;
+	    }
+	  catch (T z)
+	    {
+	      if (z.t != 2)
+		return false;
+	      return true;
+	    }
+	}
+    }
+  return false;
+}
+
+static_assert (foo ());
+static_assert (bar ());
+static_assert (baz ());
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh7.C.jj	2025-05-31 00:43:32.175996769 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh7.C	2025-05-31 00:43:32.175996769 +0200
@@ -0,0 +1,151 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+constexpr char p[] = "hello";
+constexpr const char *q[] = { &p[0], &p[3] };
+constexpr const char *const *r = &q[0];
+const char *s[] = { &p[0], &p[3] };
+constexpr const char **t = &s[0];
+
+constexpr bool
+foo ()
+{
+  try
+    {
+      throw t;
+    }
+  catch (const char **const &x)
+    {
+      if (x != t)
+	return false;
+      try
+	{
+	  throw;
+	}
+      catch (const char **&y)
+	{
+	  if (y != t)
+	    return false;
+	  try
+	    {
+	      throw;
+	    }
+	  catch (const char **z)
+	    {
+	      if (z != t)
+		return false;
+	      try
+		{
+		  throw;
+		}
+	      catch (const char *const *const &v)
+		{
+		  if (v != (const char *const *) t)
+		    return false;
+		  try
+		    {
+		      throw;
+		    }
+		  catch (const char *const *w)
+		    {
+		      if (w != (const char *const *) t)
+			return false;
+		      return true;
+		    }
+		}
+	    }
+	}
+    }
+  return false;
+}
+
+constexpr bool
+bar ()
+{
+  try
+    {
+      throw nullptr;
+    }
+  catch (const char **const &x)
+    {
+      if (x != nullptr)
+	return false;
+      try
+	{
+	  throw;
+	}
+      catch (const char **&y)
+	{
+	  if (y != nullptr)
+	    return false;
+	  try
+	    {
+	      throw;
+	    }
+	  catch (const char **z)
+	    {
+	      if (z != nullptr)
+		return false;
+	      try
+		{
+		  throw;
+		}
+	      catch (const char *const *const &v)
+		{
+		  if (v != nullptr)
+		    return false;
+		  try
+		    {
+		      throw;
+		    }
+		  catch (const char *const *w)
+		    {
+		      if (w != nullptr)
+			return false;
+		      return true;
+		    }
+		}
+	    }
+	}
+    }
+  return false;
+}
+
+constexpr bool
+baz ()
+{
+  try
+    {
+      throw r;
+    }
+  catch (const char *const *const &x)
+    {
+      if (x != r || **x != 'h')
+	return false;
+      try
+	{
+	  throw;
+	}
+      catch (const char *const *&y)
+	{
+	  if (y != r || **y != 'h')
+	    return false;
+	  try
+	    {
+	      throw;
+	    }
+	  catch (const char *const *z)
+	    {
+	      if (z != r || **z != 'h')
+		return false;
+	      return true;
+	    }
+	}
+    }
+  return false;
+}
+
+static_assert (foo ());
+static_assert (bar ());
+static_assert (baz ());
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh8.C.jj	2025-05-31 00:43:32.175996769 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh8.C	2025-05-31 00:43:32.175996769 +0200
@@ -0,0 +1,36 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+#include <exception>
+
+constexpr std::exception_ptr
+foo ()
+{
+  try
+    {
+      throw 42;
+    }
+  catch (...)
+    {
+      return std::current_exception ();
+    }
+}
+
+constexpr bool
+bar ()
+{
+  try
+    {
+      std::rethrow_exception (foo ());
+    }
+  catch (const int &a)
+    {
+       return a == 42;
+    }
+  return false;
+}
+
+static_assert (bar ());
+constexpr std::exception_ptr a = foo ();				// { dg-error "is not a constant expression because it refers to exception object allocated with '__cxa_allocate_exception'" }
+constexpr std::exception_ptr b = std::make_exception_ptr (42ULL);	// { dg-error "is not a constant expression because it refers to exception object allocated with '__cxa_allocate_exception'" }
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh9.C.jj	2025-05-31 00:43:32.175996769 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh9.C	2025-05-31 00:43:32.175996769 +0200
@@ -0,0 +1,98 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct S {
+  constexpr S () : s (0) {}
+  constexpr S (int x) : s (x) {}
+  constexpr S (const S &x) : s (x.s) {}
+  constexpr virtual ~S () {}
+  constexpr virtual const char *what () noexcept { return "this is S"; }
+  int s;
+};
+struct T {
+  constexpr T () : s (new char[1]), t (0) { s[0] = '\0'; }
+  constexpr T (const char *p, int q) : s (new char[q + 1]), t (q)
+  {
+    for (int i = 0; i <= t; ++i)
+      s[i] = p[i];
+  }
+  constexpr T (const T &x) : s (new char[x.t + 1]), t (x.t)
+  {
+    for (int i = 0; i <= t; ++i)
+      s[i] = x.s[i];
+  }
+  constexpr virtual ~T () { delete[] s; }
+  constexpr virtual const char *what () noexcept { return s; }
+  char *s;
+  int t;
+};
+struct U {
+  constexpr U () : x (0), y (0), z (0) {}
+  constexpr U (int a, long b, unsigned long long c) : x (a), y (b), z (c) {}
+  constexpr U (const U &u) = default;
+  int x;
+  long y;
+  unsigned long long z;
+};
+
+constexpr int
+foo (int x)
+{
+  if (x == 1)
+    throw S (42);
+  else if (x == 2)
+    throw T ("hello, world", sizeof ("hello, world") - 1);
+  else if (x == 3)
+    throw U (1, -2L, 42ULL);
+  else if (x == 4)
+    throw 42;
+  else if (x == 5)
+    throw 1.0;
+  else
+    return 42;
+}
+
+constexpr int
+bar (int x) noexcept
+// { dg-error "'std::terminate' called after throwing an exception of type 'S'; 'what\\\(\\\)': 'this is S'" "" { target *-*-* } .-1 }
+// { dg-message "uncaught exception exited from 'noexcept' function 'constexpr int bar\\\(int\\\)'" "" { target *-*-* } .-2 }
+// { dg-error "'std::terminate' called after throwing an exception of type 'T'; 'what\\\(\\\)': 'hello, world'" "" { target *-*-* } .-3 }
+// { dg-error "'std::terminate' called after throwing an exception 'U\\\{1, -2, 42\\\}'" "" { target *-*-* } .-4 }
+// { dg-error "'std::terminate' called after throwing an exception '42'" "" { target *-*-* } .-5 }
+// { dg-error "'std::terminate' called after throwing an exception '1\\\.0e\\\+0'" "" { target *-*-* } .-6 }
+{
+  return foo (x);
+}
+
+constexpr int
+baz (int x)
+{
+  try
+    {
+      return foo (x);
+    }
+  catch (...)
+    {
+      return -1;
+    }
+}
+
+static_assert (bar (0) == 42);
+constexpr int a = bar (1);		// { dg-message "in 'constexpr' expansion of" }
+constexpr int b = bar (2);		// { dg-message "in 'constexpr' expansion of" }
+constexpr int c = bar (3);		// { dg-message "in 'constexpr' expansion of" }
+constexpr int d = bar (4);		// { dg-message "in 'constexpr' expansion of" }
+constexpr int e = bar (5);		// { dg-message "in 'constexpr' expansion of" }
+static_assert (baz (0) == 42);
+static_assert (baz (1) == -1);
+static_assert (baz (2) == -1);
+static_assert (baz (3) == -1);
+static_assert (baz (4) == -1);
+static_assert (baz (5) == -1);
+static_assert (foo (0) == 42);
+constexpr int f = foo (1);		// { dg-error "uncaught exception of type 'S'; 'what\\\(\\\)': 'this is S'" }
+constexpr int g = foo (2);		// { dg-error "uncaught exception of type 'T'; 'what\\\(\\\)': 'hello, world'" }
+constexpr int h = foo (3);		// { dg-error "uncaught exception 'U\\\{1, -2, 42\\\}'" }
+constexpr int i = foo (4);		// { dg-error "uncaught exception '42'" }
+constexpr int j = foo (5);		// { dg-error "uncaught exception '1\\\.0e\\\+0'" }
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh10.C.jj	2025-05-31 00:43:32.176996756 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh10.C	2025-05-31 00:43:32.176996756 +0200
@@ -0,0 +1,110 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct S {
+};
+struct T {
+  constexpr ~T () noexcept (false) { throw S {}; }
+};
+struct U {
+  int u;
+};
+struct V {
+  int v;
+  constexpr V (int x)
+  try : v { x }
+  {
+    if (v > 42)
+      throw U { 42 };
+  }
+  catch (U &u)
+  {
+    --u.u;
+  }
+};
+struct W {
+  constexpr ~W () { ++w; }
+  int &w;
+};
+struct X : public V {
+  constexpr X (int x)
+  try : V(x)
+  {
+  }
+  catch (U &u)
+  {
+    --u.u;
+  }
+};
+
+constexpr int
+foo (bool x)
+{
+  try
+    {
+      T t;				// { dg-error "'std::terminate' called after throwing an exception '42'" }
+      if (x)				// { dg-message "destructor exited with an exception" "" { target *-*-* } .-1 }
+	throw 42;
+      return 10;
+    }
+  catch (S)
+    {
+      return 11;
+    }
+}
+
+constexpr int
+bar ()
+{
+  V v { 42 };
+  try
+    {
+      V w { 43 };
+    }
+  catch (const U &u)
+    {
+      if (u.u == 41)
+	return 44;
+    }
+  return -1;
+}
+
+constexpr int
+baz ()
+{
+  int i = 42;
+  try
+    {
+      W w { i };
+      throw S ();
+    }
+  catch (...)
+    {
+      if (i == 43)
+	return 42;
+    }
+  return -1;
+}
+
+constexpr int
+qux ()
+{
+  X v { 42 };
+  try
+    {
+      X w { 43 };
+    }
+  catch (const U &u)
+    {
+      if (u.u == 40)
+	return 48;
+    }
+  return -1;
+}
+
+static_assert (foo (false) == 11);
+constexpr int a = foo (true);		// { dg-message "in 'constexpr' expansion of" }
+static_assert (bar () == 44);
+static_assert (baz () == 42);
+static_assert (qux () == 48);
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh11.C.jj	2025-05-31 00:43:32.176996756 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh11.C	2025-05-31 00:43:32.176996756 +0200
@@ -0,0 +1,69 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct A {
+  explicit constexpr A (int x) noexcept : a (x) {}
+  constexpr virtual int foo () const noexcept { return a; }
+  constexpr virtual ~A () {}
+  int a;
+};
+struct B : public A {
+  explicit constexpr B (int x) noexcept : A (x) {}
+  constexpr int foo () const noexcept override { return a | 0x10; }
+};
+struct C : public A {
+  explicit constexpr C (int x) noexcept : A (x) {}
+};
+struct D : public A {
+  explicit constexpr D (int x) noexcept : A (x) {}
+};
+struct E {
+  constexpr E () noexcept : e (0) {}
+  explicit constexpr E (int x) noexcept : e (x) {}
+  int e;
+};
+struct F : public E, public B {
+  explicit constexpr F (int x) noexcept : B (x) {}
+};
+struct G : public E, public C {
+  explicit constexpr G (int x) noexcept : C (x) {}
+};
+struct H : public E, public D {
+  explicit constexpr H (int x) noexcept : D (x) {}
+};
+
+consteval int
+bar (void (*fn) ())
+{
+  try
+    {
+      fn ();
+    }
+  catch (C &a)
+    {
+      return a.foo () | 0x20;
+    }
+  catch (const C &b)		// { dg-warning "exception of type 'C' will be caught by earlier handler" }
+    {
+      return b.foo () | 0x60;
+    }
+  catch (A &c)
+    {
+      return c.foo ();
+    }
+  catch (const A &d)		// { dg-warning "exception of type 'A' will be caught by earlier handler" }
+    {
+      return d.foo () | 0x40;
+    }
+  return -1;
+}
+
+static_assert (bar ([] { throw A { 1 }; }) == 1);
+static_assert (bar ([] { throw B { 2 }; }) == 0x12);
+static_assert (bar ([] { throw C { 3 }; }) == 0x23);
+static_assert (bar ([] { throw D { 4 }; }) == 4);
+constexpr int a = bar ([] { throw E { 5 }; });	// { dg-error "uncaught exception 'E\\\{5\\\}'" }
+static_assert (bar ([] { throw F { 6 }; }) == 0x16);
+static_assert (bar ([] { throw G { 7 }; }) == 0x27);
+static_assert (bar ([] { throw H { 8 }; }) == 8);
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh12.C.jj	2025-05-31 00:43:32.176996756 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh12.C	2025-05-31 00:43:32.176996756 +0200
@@ -0,0 +1,74 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct A {
+  explicit constexpr A (int x) noexcept : a (x) {}
+  constexpr virtual int foo () const noexcept { return a; }
+  constexpr virtual ~A () {}
+  int a;
+};
+struct B : public A {
+  explicit constexpr B (int x) noexcept : A (x) {}
+  constexpr int foo () const noexcept override { return a | 0x10; }
+};
+struct C : public A {
+  explicit constexpr C (int x) noexcept : A (x) {}
+};
+struct D : public A {
+  explicit constexpr D (int x) noexcept : A (x) {}
+};
+struct E {
+  constexpr E () noexcept : e (0) {}
+  explicit constexpr E (int x) noexcept : e (x) {}
+  int e;
+};
+struct F : public E, public B {
+  explicit constexpr F (int x) noexcept : B (x) {}
+};
+struct G : public E, public C {
+  explicit constexpr G (int x) noexcept : C (x) {}
+};
+struct H : public E, public D {
+  explicit constexpr H (int x) noexcept : D (x) {}
+};
+
+consteval int
+bar (void (*fn) ())
+{
+  int r = 0;
+  try
+    {
+      fn ();
+    }
+  catch (C *a)
+    {
+      r = a->foo () | 0x20;
+      delete a;
+    }
+  catch (const C *b)
+    {
+      r = b->foo () | 0x60;
+      delete b;
+    }
+  catch (A *c)
+    {
+      r = c->foo ();
+      delete c;
+    }
+  catch (const A *d)
+    {
+      r = d->foo () | 0x40;
+      delete d;
+    }
+  return r;
+}
+
+static_assert (bar ([] { throw new A { 1 }; }) == 1);
+static_assert (bar ([] { throw new B { 2 }; }) == 0x12);
+static_assert (bar ([] { throw new C { 3 }; }) == 0x23);
+static_assert (bar ([] { throw new D { 4 }; }) == 4);
+constexpr int a = bar ([] { throw new E { 5 }; });	// { dg-error "uncaught exception of type 'E\\\*'" }
+static_assert (bar ([] { throw new F { 6 }; }) == 0x16);
+static_assert (bar ([] { throw new G { 7 }; }) == 0x27);
+static_assert (bar ([] { throw new H { 8 }; }) == 8);
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh13.C.jj	2025-05-31 00:43:32.175996769 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh13.C	2025-05-31 00:43:32.175996769 +0200
@@ -0,0 +1,36 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+template <typename T>
+consteval T
+foo (T x)
+{
+  try
+    {
+      throw &x;
+    }
+  catch (void *ptr)		// { dg-message "for type 'void\\\*'" }
+    {
+      return *static_cast<T *> (ptr) | 0x10;
+    }
+  catch (const void *ptr)	// { dg-message "for type 'const void\\\*'" }
+    {
+      return *static_cast<const T *> (ptr) | 0x20;
+    }
+  catch (T *ptr)		// { dg-warning "exception of type 'T\\\*' will be caught by earlier handler" }
+    {				// { dg-warning "exception of type 'int\\\*' will be caught by earlier handler" "" { target *-*-* } .-1 }
+      return *ptr | 0x30;	// { dg-warning "exception of type 'long long unsigned int\\\*' will be caught by earlier handler" "" { target *-*-* } .-2 }
+    }
+  catch (const T *ptr)
+    {
+      return *ptr | 0x40;
+    }
+  catch (...)
+    {
+      return -1;
+    }
+}
+
+static_assert (foo (1) == 0x11);
+static_assert (foo (2ULL) == 0x12ULL);
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh14.C.jj	2025-05-31 00:43:32.177996742 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh14.C	2025-05-31 00:43:32.177996742 +0200
@@ -0,0 +1,42 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+template <typename T>
+constexpr T
+foo (T x, auto... y)
+{
+  const T z[] = { x, y... };
+  try
+    {
+      throw z;
+    }
+  catch (const T (&a)[4])
+    {
+      return T ();
+    }
+  catch (const T *b)
+    {
+      return b[0];
+    }
+  catch (...)
+    {
+      return T ();
+    }
+}
+
+void
+bar ()
+{
+}
+
+void
+baz ()
+{
+}
+
+static_assert (foo (42, 43, 44, 45, 46) == 42);
+static_assert (foo (43U, 44U, 45U, 46U) == 43U);
+static_assert (foo (44LL, 45LL) == 44LL);
+static_assert (foo (bar, baz, bar, baz) == bar);
+static_assert (foo (baz, bar) == baz);
--- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj	2025-05-31 00:43:13.847238377 +0200
+++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C	2025-05-31 00:43:32.177996742 +0200
@@ -624,13 +624,19 @@
 #endif
 
 #ifndef __cpp_pack_indexing
-# error "__cpp_pack_indexing"
+#  error "__cpp_pack_indexing"
 #elif __cpp_pack_indexing != 202311
 #  error "__cpp_pack_indexing != 202311"
 #endif
 
 #ifndef __cpp_pp_embed
-# error "__cpp_pp_embed"
+#  error "__cpp_pp_embed"
 #elif __cpp_pp_embed != 202502
 #  error "__cpp_pp_embed != 202502"
 #endif
+
+#ifndef __cpp_constexpr_exceptions
+#  error "__cpp_constexpr_exceptions"
+#elif __cpp_constexpr_exceptions != 202411
+#  error "__cpp_constexpr_exceptions != 202411"
+#endif
--- gcc/testsuite/g++.dg/cpp26/static_assert1.C.jj	2025-04-22 21:26:15.779920096 +0200
+++ gcc/testsuite/g++.dg/cpp26/static_assert1.C	2025-05-31 00:43:32.176996756 +0200
@@ -51,7 +51,7 @@ static_assert (true, H {});	// { dg-warn
 struct I { constexpr signed char size () const { return 0; }
 	   const char *data () const { return ""; } };
 static_assert (true, I {});	// { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
-struct J { constexpr int size () const { return j ? throw 1 : 0; }	// { dg-error "expression '<throw-expression>' is not a constant expression" }
+struct J { constexpr int size () const { return j ? throw 1 : 0; }	// { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23_down } }
 	   constexpr const char *data () const { return ""; };
 	   constexpr J (int x) : j (x) {}
 	   int j; };
@@ -60,6 +60,7 @@ static_assert (false, J (0));	// { dg-wa
 				// { dg-error "static assertion failed" "" { target *-*-* } .-1 }
 static_assert (false, J (1));	// { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
 				// { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target *-*-* } .-1 }
+				// { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
 struct K { constexpr operator int () { return 4; } };
 struct L { constexpr operator const char * () { return "test"; } };
 struct M { constexpr K size () const { return {}; }
@@ -261,10 +262,11 @@ namespace NN
 #if __cplusplus >= 201402L
   struct J {
     static constexpr int size () { return 0; }
-    static constexpr const char *data (int x = 0) { if (x) return nullptr; else throw 1; } }; // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++14 } }
+    static constexpr const char *data (int x = 0) { if (x) return nullptr; else throw 1; } }; // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target { c++14 && c++23_down } } }
   static_assert (true, J{});		// { dg-warning "'static_assert' with non-string message only available with" "" { target { c++14 && c++23_down } } }
   static_assert (false, J{});		// { dg-warning "'static_assert' with non-string message only available with" "" { target { c++14 && c++23_down } } }
 					// { dg-error "constexpr string 'data\\\(\\\)' must be a core constant expression" "" { target c++14 } .-1 }
+					// { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
 #endif
 #if __cpp_if_consteval >= 202106L
   struct K {
@@ -282,19 +284,21 @@ namespace NN
   static_assert (false, L{});		// { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
 					// { dg-error "static assertion failed: test" "" { target c++23 } .-1 }
   struct M {
-    static constexpr int size () { if consteval { throw 1; } else { return 4; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23 } }
+    static constexpr int size () { if consteval { throw 1; } else { return 4; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23_only } }
     static constexpr const char *data () { return "test"; }
   };
   static_assert (true, M{});		// { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
   static_assert (false, M{});		// { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
 					// { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target c++23 } .-1 }
+					// { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
   struct N {
     static constexpr int size () { return 4; }
-    static constexpr const char *data () { if consteval { throw 1; } else { return "test"; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23 } }
+    static constexpr const char *data () { if consteval { throw 1; } else { return "test"; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23_only } }
   };
   static_assert (true, N{});		// { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
   static_assert (false, N{});		// { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
 					// { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++23 } .-1 }
+					// { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
 #endif
   struct O { constexpr int operator () () const { return 12; } };
   struct P { constexpr const char *operator () () const { return "another test"; } };
--- gcc/testsuite/g++.dg/cpp2a/consteval34.C.jj	2023-12-05 19:52:29.911655231 +0100
+++ gcc/testsuite/g++.dg/cpp2a/consteval34.C	2025-05-31 00:43:32.177996742 +0200
@@ -1,42 +1,51 @@
 // { dg-do compile { target c++20 } }
 // Explicit { dg-require-effective-target exceptions_enabled } to avoid verify compiler messages FAILs for '-fno-exceptions'.
 
-consteval int bar (int i) { if (i != 1) throw 1; return 0; }	// { dg-error "is not a constant expression" }
+consteval int bar (int i) { if (i != 1) throw 1; return 0; }	// { dg-error "is not a constant expression" "" { target c++23_down } }
 
 constexpr int
 foo (bool b)
 {
-  return b ? bar (3) : 2; // { dg-message "in .constexpr. expansion" }
+  return b ? bar (3) : 2; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
 // { dg-error "call to consteval function" "" { target *-*-* } .-1 }
-}
+} // { dg-error "uncaught exception '1'" "" { target c++26 } }
 
 static_assert (foo (false) == 2);
 
-__extension__ constexpr int g1 = false ?: bar (2); // { dg-message "in .constexpr. expansion" }
-__extension__ constexpr int g2 = false ?: (1 + bar (2)); // { dg-message "in .constexpr. expansion" }
+__extension__ constexpr int g1 = false ?: bar (2); // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
+__extension__ constexpr int g2 = false ?: (1 + bar (2)); // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
 __extension__ constexpr int g3 = true ?: bar (2);
 __extension__ constexpr int g4 = true ?: (1 + bar (2));
-constexpr int g5 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" }
-constexpr int g6 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" }
+constexpr int g5 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
+constexpr int g6 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
 
 void
 g ()
 {
-  __extension__ int a1[bar(3)]; // { dg-message "in .constexpr. expansion" }
+  __extension__ int a1[bar(3)]; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
 // { dg-error "call to consteval function" "" { target *-*-* } .-1 }
   int a2[sizeof (bar(3))];
 
-  int a3 = false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" }
+  int a3 = false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
 // { dg-error "call to consteval function" "" { target *-*-* } .-1 }
-  a3 += false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
+  a3 += false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
 // { dg-error "call to consteval function" "" { target *-*-* } .-1 }
 
-  __extension__ int a4 = false ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" }
+  __extension__ int a4 = false ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
 // { dg-error "call to consteval function" "" { target *-*-* } .-1 }
-  __extension__ int a5 = true ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
+  __extension__ int a5 = true ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
 // { dg-error "call to consteval function" "" { target *-*-* } .-1 }
-  int a6 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
+  int a6 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
 // { dg-error "call to consteval function" "" { target *-*-* } .-1 }
-  int a7 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
+  int a7 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
 // { dg-error "call to consteval function" "" { target *-*-* } .-1 }
-}
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
+} // { dg-error "uncaught exception '1'" "" { target c++26 } }
--- gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C.jj	2023-12-05 19:52:29.910655245 +0100
+++ gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C	2025-05-31 00:43:32.178996729 +0200
@@ -4,13 +4,13 @@
 
 struct fixed_string {
   consteval int size(int n) const {
-    if (n < 0) throw; // { dg-error "not a constant" }
-    return n;
+    if (n < 0) throw; // { dg-error "not a constant" "" { target c++23_down } }
+    return n; // { dg-error "'void __cxa_rethrow\\\(\\\)' called with no caught exceptions active" "" { target c++26 } .-1 }
   }
 
   static consteval int size_static(int n) {
-    if (n < 0) throw; // { dg-error "not a constant" }
-    return n;
+    if (n < 0) throw; // { dg-error "not a constant" "" { target c++23_down } }
+    return n; // { dg-error "'void __cxa_rethrow\\\(\\\)' called with no caught exceptions active" "" { target c++26 } .-1 }
   }
 
   consteval void operator()() const { }
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C.jj	2025-05-31 00:43:13.848238364 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C	2025-05-31 00:43:32.178996729 +0200
@@ -4,6 +4,22 @@
 
 // From clang's constant-expression-cxx2a.cpp.
 
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+  struct exception {
+    constexpr exception () noexcept {}
+    constexpr virtual ~exception () noexcept {}
+    constexpr exception (const exception &) = default;
+    constexpr exception &operator= (const exception &) = default;
+    constexpr virtual const char *what () const noexcept { return "std::exception"; }
+  };
+  struct bad_cast : public exception {
+    constexpr virtual ~bad_cast () noexcept {}
+    constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+  };
+}
+#endif
+
 struct A2 { virtual void a2(); };
 struct A : A2 { virtual void a(); };
 struct B : A {}; 
@@ -26,31 +42,36 @@ static_assert(dynamic_cast<const A*>(sta
 static_assert(g.f == (void*)(F*)&g);
 static_assert(dynamic_cast<const void*>(static_cast<const D*>(&g)) == &g);
 
-constexpr int d_a = (dynamic_cast<const A&>(static_cast<const D&>(g)), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target *-*-* } .-1 }
-
+constexpr int d_a = (dynamic_cast<const A&>(static_cast<const D&>(g)), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 // Can navigate from A2 to its A...
 static_assert(&dynamic_cast<A&>((A2&)(B&)g) == &(A&)(B&)g);
 // ... and from B to its A ...
 static_assert(&dynamic_cast<A&>((B&)g) == &(A&)(B&)g);
 // ... but not from D.
-static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g); // { dg-error "non-constant condition for static assertion|reference .dynamic_cast. failed" }
-// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target *-*-* } .-1 }
-
+static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g); // { dg-error "non-constant condition for static assertion" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target c++23_down } .-1 }
+// { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } .-2 }
+// { dg-error "uncaught exception" "" { target c++26 } .-3 }
 // Can cast from A2 to sibling class D.
 static_assert(&dynamic_cast<D&>((A2&)(B&)g) == &(D&)g);
 
 // Cannot cast from private base E to derived class F.
-constexpr int e_f = (dynamic_cast<F&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target *-*-* } .-1 }
+constexpr int e_f = (dynamic_cast<F&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 
 // Cannot cast from B to private sibling E.
-constexpr int b_e = (dynamic_cast<E&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .E." "" { target *-*-* } .-1 }
+constexpr int b_e = (dynamic_cast<E&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .E." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 
 struct Unrelated { virtual void unrelated(); };
 
-constexpr int b_unrelated = (dynamic_cast<Unrelated&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .Unrelated." "" { target *-*-* } .-1 }
-constexpr int e_unrelated = (dynamic_cast<Unrelated&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target *-*-* } .-1 }
+constexpr int b_unrelated = (dynamic_cast<Unrelated&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .Unrelated." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr int e_unrelated = (dynamic_cast<Unrelated&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C.jj	2025-05-31 00:43:13.849238350 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C	2025-05-31 00:43:32.178996729 +0200
@@ -1,6 +1,22 @@
 // PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
 // { dg-do compile { target c++20 } }
 
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+  struct exception {
+    constexpr exception () noexcept {}
+    constexpr virtual ~exception () noexcept {}
+    constexpr exception (const exception &) = default;
+    constexpr exception &operator= (const exception &) = default;
+    constexpr virtual const char *what () const noexcept { return "std::exception"; }
+  };
+  struct bad_cast : public exception {
+    constexpr virtual ~bad_cast () noexcept {}
+    constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+  };
+}
+#endif
+
 // Private base.
 
 struct P1 { virtual void p1(); };
@@ -12,14 +28,16 @@ struct A : B, C, private P2 { virtual vo
 constexpr A a;
 
 // P1 is a non-public base of A.
-constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 
 // Don't error here.
 static_assert (dynamic_cast<B*>((P1*)&a) == nullptr);
 
-constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 
 static_assert (dynamic_cast<C*>((P1*)&a) == nullptr);
 static_assert (dynamic_cast<C*>((P2*)&a) == nullptr);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C.jj	2025-05-31 00:43:13.850238337 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C	2025-05-31 00:43:32.178996729 +0200
@@ -1,6 +1,22 @@
 // PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
 // { dg-do compile { target c++20 } }
 
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+  struct exception {
+    constexpr exception () noexcept {}
+    constexpr virtual ~exception () noexcept {}
+    constexpr exception (const exception &) = default;
+    constexpr exception &operator= (const exception &) = default;
+    constexpr virtual const char *what () const noexcept { return "std::exception"; }
+  };
+  struct bad_cast : public exception {
+    constexpr virtual ~bad_cast () noexcept {}
+    constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+  };
+}
+#endif
+
 // Protected base.
 
 struct P1 { virtual void p1(); };
@@ -12,14 +28,16 @@ struct A : B, C, protected P2 { virtual
 constexpr A a;
 
 // P1 is a non-public base of A.
-constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 
 // Don't error here.
 static_assert (dynamic_cast<B*>((P1*)&a) == nullptr);
 
-constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 
 static_assert (dynamic_cast<C*>((P1*)&a) == nullptr);
 static_assert (dynamic_cast<C*>((P2*)&a) == nullptr);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C.jj	2025-05-31 00:43:13.850238337 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C	2025-05-31 00:43:32.178996729 +0200
@@ -1,6 +1,22 @@
 // PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
 // { dg-do compile { target c++20 } }
 
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+  struct exception {
+    constexpr exception () noexcept {}
+    constexpr virtual ~exception () noexcept {}
+    constexpr exception (const exception &) = default;
+    constexpr exception &operator= (const exception &) = default;
+    constexpr virtual const char *what () const noexcept { return "std::exception"; }
+  };
+  struct bad_cast : public exception {
+    constexpr virtual ~bad_cast () noexcept {}
+    constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+  };
+}
+#endif
+
 // Unrelated type.
 
 struct B { virtual void b(); };
@@ -12,12 +28,15 @@ constexpr A a;
 
 struct U { virtual void u(); };
 
-constexpr bool b1 = (dynamic_cast<U&>((B&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .U." "" { target *-*-* } .-1 }
-constexpr bool b2 = (dynamic_cast<U&>((P1&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
-constexpr bool b3 = (dynamic_cast<U&>((P2&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+constexpr bool b1 = (dynamic_cast<U&>((B&)a), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .U." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b2 = (dynamic_cast<U&>((P1&)a), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b3 = (dynamic_cast<U&>((P2&)a), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 
 static_assert (dynamic_cast<U*>((B*)&a) == nullptr);
 static_assert (dynamic_cast<U*>((P1*)&a) == nullptr);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C.jj	2025-05-31 00:43:13.850238337 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C	2025-05-31 00:43:32.178996729 +0200
@@ -1,6 +1,22 @@
 // PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
 // { dg-do compile { target c++20 } }
 
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+  struct exception {
+    constexpr exception () noexcept {}
+    constexpr virtual ~exception () noexcept {}
+    constexpr exception (const exception &) = default;
+    constexpr exception &operator= (const exception &) = default;
+    constexpr virtual const char *what () const noexcept { return "std::exception"; }
+  };
+  struct bad_cast : public exception {
+    constexpr virtual ~bad_cast () noexcept {}
+    constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+  };
+}
+#endif
+
 // Ambiguous base.
 
 struct A { virtual void a(); };
@@ -11,7 +27,8 @@ struct E : B, C, D { virtual void d(); }
 
 constexpr E e;
 
-constexpr bool b1 = (dynamic_cast<A&>((D&)e), false); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message ".A. is an ambiguous base class of dynamic type .E. of its operand" "" { target *-*-* } .-1 }
+constexpr bool b1 = (dynamic_cast<A&>((D&)e), false); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message ".A. is an ambiguous base class of dynamic type .E. of its operand" "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 
 static_assert (dynamic_cast<A*>((D*)&e) == nullptr);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C.jj	2025-05-31 00:43:13.847238377 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C	2025-05-31 00:43:32.177996742 +0200
@@ -8,6 +8,22 @@
 // a pointer to or object of the constructor or destructor's own class or one
 // of its bases, the dynamic_cast results in undefined behavior.
 
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+  struct exception {
+    constexpr exception () noexcept {}
+    constexpr virtual ~exception () noexcept {}
+    constexpr exception (const exception &) = default;
+    constexpr exception &operator= (const exception &) = default;
+    constexpr virtual const char *what () const noexcept { return "std::exception"; }
+  };
+  struct bad_cast : public exception {
+    constexpr virtual ~bad_cast () noexcept {}
+    constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+  };
+}
+#endif
+
 struct V {
   virtual void f();
 };
@@ -19,7 +35,7 @@ struct B : V {
 };
 
 struct D : A, B {
-  constexpr D() : B((A*)this, this) { } // { dg-message "in 'constexpr' expansion of" }
+  constexpr D() : B((A*)this, this) { } // { dg-message "in 'constexpr' expansion of" "" { target c++23_down } }
 };
 
 constexpr B::B(V* v, A* a)
@@ -29,8 +45,9 @@ constexpr B::B(V* v, A* a)
   if (b != nullptr)
     __builtin_abort ();
 
-  B& br = dynamic_cast<B&>(*v); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .B." "" { target *-*-* } .-1 }
+  B& br = dynamic_cast<B&>(*v); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .B." "" { target c++23_down } .-1 }
 }
 
-constexpr D d; // { dg-message "in 'constexpr' expansion of" }
+constexpr D d; // { dg-message "in 'constexpr' expansion of" "" { target c++23_down } }
+// { dg-error "uncaught exception" "" { target c++26 } .-1 }
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C.jj	2025-05-31 00:43:13.848238364 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C	2025-05-31 00:43:32.177996742 +0200
@@ -4,6 +4,22 @@
 // Adopted from g++.old-deja/g++.other/dyncast1.C.
 // But use reference dynamic_cast.
 
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+  struct exception {
+    constexpr exception () noexcept {}
+    constexpr virtual ~exception () noexcept {}
+    constexpr exception (const exception &) = default;
+    constexpr exception &operator= (const exception &) = default;
+    constexpr virtual const char *what () const noexcept { return "std::exception"; }
+  };
+  struct bad_cast : public exception {
+    constexpr virtual ~bad_cast () noexcept {}
+    constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+  };
+}
+#endif
+
 // 1. downcast
 // 1.1. single inheritance case
 
@@ -21,27 +37,35 @@ class CCC : protected B {};
 class DDD : protected CCC {};
 
 constexpr D d;
-constexpr bool b01 = (dynamic_cast<D&> ((A&)d), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
-constexpr bool b02 = (dynamic_cast<D&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
+constexpr bool b01 = (dynamic_cast<D&> ((A&)d), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .D." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b02 = (dynamic_cast<D&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 static_assert (&d == &dynamic_cast<const D&> ((C&)d));
-constexpr bool b03 = (dynamic_cast<C&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
+constexpr bool b03 = (dynamic_cast<C&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 
 constexpr DD dd;
-constexpr bool b04 = (dynamic_cast<DD&> ((A&)dd), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DD." "" { target *-*-* } .-1 }
-constexpr bool b05 = (dynamic_cast<DD&> ((B&)dd), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DD." "" { target *-*-* } .-1 }
+constexpr bool b04 = (dynamic_cast<DD&> ((A&)dd), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DD." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b05 = (dynamic_cast<DD&> ((B&)dd), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DD." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 
 constexpr DDD ddd;
-constexpr bool b06 = (dynamic_cast<DDD&> ((A&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
-constexpr bool b07 = (dynamic_cast<DDD&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
-constexpr bool b08 = (dynamic_cast<CCC&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
+constexpr bool b06 = (dynamic_cast<DDD&> ((A&)ddd), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DDD." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b07 = (dynamic_cast<DDD&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b08 = (dynamic_cast<CCC&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 
 // 1.2. multiple inheritance case
 // 1.2.1. all bases are public
@@ -50,19 +74,23 @@ struct E : D, CC {};
 struct EE : CC, D {}; //Will search in reverse order.
 
 constexpr E e;
-constexpr bool b09 = (dynamic_cast<E&> ((A&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .E." "" { target *-*-* } .-1 }
-constexpr bool b10 = (dynamic_cast<E&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .E." "" { target *-*-* } .-1 }
+constexpr bool b09 = (dynamic_cast<E&> ((A&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .E." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b10 = (dynamic_cast<E&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .E." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 static_assert (&e == &dynamic_cast<E&> ((C&)(D&)e));
 static_assert (&e == &dynamic_cast<E&> ((B&)(CC&)e));
 static_assert (&(CC&)e == &dynamic_cast<CC&> ((B&)(CC&)e));
 
 constexpr EE ee;
-constexpr bool b11 = (dynamic_cast<EE&> ((A&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .EE." "" { target *-*-* } .-1 }
-constexpr bool b12 = (dynamic_cast<EE&> ((B&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .EE." "" { target *-*-* } .-1 }
+constexpr bool b11 = (dynamic_cast<EE&> ((A&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .EE." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b12 = (dynamic_cast<EE&> ((B&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .EE." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 static_assert (&ee == &dynamic_cast<EE&> ((C&)(D&)ee));
 static_assert (&ee == &dynamic_cast<EE&> ((B&)(CC&)ee));
 static_assert (&(CC&)ee == &dynamic_cast<CC&> ((B&)(CC&)ee));
@@ -78,14 +106,17 @@ constexpr X x;
 static_assert (&x == &dynamic_cast<X&>((B&)(CC&)(E&)x));
 
 constexpr XX xx;
-constexpr bool b13 = (dynamic_cast<XX&>((B&)(CC&)(E&)xx), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .XX." "" { target *-*-* } .-1 }
+constexpr bool b13 = (dynamic_cast<XX&>((B&)(CC&)(E&)xx), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .XX." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 
 constexpr Y y;
-constexpr bool b14 = (dynamic_cast<Y&>((B&)y), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .Y." "" { target *-*-* } .-1 }
-constexpr bool b15 = (dynamic_cast<Y&>((A&)(B&)y), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .Y." "" { target *-*-* } .-1 }
+constexpr bool b14 = (dynamic_cast<Y&>((B&)y), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .Y." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b15 = (dynamic_cast<Y&>((A&)(B&)y), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .Y." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 
 // 2. crosscast
 
@@ -93,13 +124,16 @@ struct J { virtual void j(); };
 struct K : CC, private J {}; 
 class KK : J, CC{};
 		
-constexpr bool b16 = (dynamic_cast<CC&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .CC." "" { target *-*-* } .-1 }
+constexpr bool b16 = (dynamic_cast<CC&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .CC." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 static_assert (&(CC&)e == &dynamic_cast<CC&> ((C&)(D&)e));
 
 constexpr K k;
-constexpr bool b17 = (dynamic_cast<J&> ((B&)k), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "dynamic type .K. of its operand does not have an unambiguous public base class .J." "" { target *-*-* } .-1 }
+constexpr bool b17 = (dynamic_cast<J&> ((B&)k), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "dynamic type .K. of its operand does not have an unambiguous public base class .J." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
 constexpr KK kk;
-constexpr bool b18 = (dynamic_cast<J&> ((CC&)kk), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const CC. of its operand is a non-public base class of dynamic type .KK." "" { target *-*-* } .-1 }
+constexpr bool b18 = (dynamic_cast<J&> ((CC&)kk), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const CC. of its operand is a non-public base class of dynamic type .KK." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic18.C.jj	2025-05-31 00:43:13.848238364 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic18.C	2025-05-31 00:43:32.177996742 +0200
@@ -2,6 +2,22 @@
 // { dg-do compile { target c++20 } }
 // Here 'b' doesn't point/refer to a public base of Derived.
 
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+  struct exception {
+    constexpr exception () noexcept {}
+    constexpr virtual ~exception () noexcept {}
+    constexpr exception (const exception &) = default;
+    constexpr exception &operator= (const exception &) = default;
+    constexpr virtual const char *what () const noexcept { return "std::exception"; }
+  };
+  struct bad_cast : public exception {
+    constexpr virtual ~bad_cast () noexcept {}
+    constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+  };
+}
+#endif
+
 struct Base {
     constexpr virtual ~Base(){}
 };
@@ -11,12 +27,12 @@ struct Derived: Base {
 };
 
 constexpr const Derived& cast(const Base& b) {
-    return dynamic_cast<const Derived&>(b); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "dynamic type .const Base. of its operand does not have a base class of type .Derived." "" { target *-*-* } .-1 }
+    return dynamic_cast<const Derived&>(b); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "dynamic type .const Base. of its operand does not have a base class of type .Derived." "" { target c++23_down } .-1 }
 }
 
 auto test() {
     static constexpr Base b;
-    constexpr auto res = cast(b);
+    constexpr auto res = cast(b);	// { dg-error "uncaught exception" "" { target c++26 } }
     return res;
 }
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new27.C.jj	2025-05-31 00:43:32.178996729 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new27.C	2025-05-31 00:43:32.178996729 +0200
@@ -0,0 +1,41 @@
+// { dg-do compile { target c++20 } }
+
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+  struct exception {
+    constexpr exception () noexcept {}
+    constexpr virtual ~exception () noexcept {}
+    constexpr exception (const exception &) = default;
+    constexpr exception &operator= (const exception &) = default;
+    constexpr virtual const char *what () const noexcept { return "std::exception"; }
+  };
+  struct bad_alloc : public exception {
+    constexpr virtual ~bad_alloc () noexcept {}
+    constexpr virtual const char *what () const noexcept { return "std::bad_alloc"; }
+  };
+  struct bad_array_new_length : public bad_alloc {
+    constexpr virtual ~bad_array_new_length () noexcept {}
+    constexpr virtual const char *what () const noexcept { return "std::bad_array_new_length"; }
+  };
+}
+#endif
+
+constexpr int
+foo (__SIZE_TYPE__ x, int y, int z)
+{
+  char (*a)[2] = new char[x][2];	// { dg-error "call to non-'constexpr' function 'void __cxa_throw_bad_array_new_length\\\(\\\)'" "" { target c++23_down } }
+  delete[] a;				// { dg-message "declared here" "" { target c++23_down } .-1 }
+  int *b = new int[y];			// { dg-error "call to non-'constexpr' function 'void __cxa_throw_bad_array_new_length\\\(\\\)'" "" { target c++23_down } }
+  delete[] b;
+  int *c = new int[z]{1, 2, 3};		// { dg-error "call to non-'constexpr' function 'void __cxa_throw_bad_array_new_length\\\(\\\)'" "" { target c++23_down } }
+  delete[] c;
+  return 0;
+}
+
+constexpr int a = foo (16, 2, 3);
+constexpr int b = foo (-64, 2, 3);	// { dg-message "in 'constexpr' expansion of" "" { target c++23_down } }
+					// { dg-error "uncaught exception" "" { target c++26 } .-1 }
+constexpr int c = foo (16, -1, 3);	// { dg-message "in 'constexpr' expansion of" "" { target c++23_down } }
+					// { dg-error "uncaught exception" "" { target c++26 } .-1 }
+constexpr int d = foo (16, 2, 1);	// { dg-message "in 'constexpr' expansion of" "" { target c++23_down } }
+					// { dg-error "uncaught exception" "" { target c++26 } .-1 }
--- gcc/testsuite/g++.dg/cpp2a/constexpr-typeid5.C.jj	2025-05-31 00:43:32.178996729 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-typeid5.C	2025-05-31 00:43:32.178996729 +0200
@@ -0,0 +1,20 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++17 } }
+
+#include <typeinfo>
+
+template <class T>
+constexpr bool foo ()
+{
+   bool r = false;
+   const std::type_info &s = typeid( (r = true), *(T *) 0);	// { dg-error "call to non-'constexpr' function 'void __cxa_bad_typeid\\\(\\\)'" "" { target c++23_down } }
+   return r;							// { dg-message "declared here" "" { target c++23_down } .-1 }
+}
+
+struct A {};
+struct B { virtual ~B () {} };
+
+static_assert (!foo <int> ());
+static_assert (!foo <A> ());
+constexpr bool a = foo <B> ();	// { dg-message "in 'constexpr' expansion of" "" { target c++23_down } }
+// { dg-error "uncaught exception" "" { target c++26 } .-1 }
--- libstdc++-v3/include/bits/version.def.jj	2025-05-31 00:43:13.852238311 +0200
+++ libstdc++-v3/include/bits/version.def	2025-05-31 00:43:32.180996703 +0200
@@ -1978,6 +1978,15 @@ ftms = {
   };
 };
 
+ftms = {
+  name = constexpr_exceptions;
+  values = {
+    v = 202502;
+    cxxmin = 26;
+    extra_cond = "__cpp_constexpr_exceptions >= 202411L";
+  };
+};
+
 // Standard test specifications.
 stds[97] = ">= 199711L";
 stds[03] = ">= 199711L";
--- libstdc++-v3/include/bits/version.h.jj	2025-05-31 00:43:13.852238311 +0200
+++ libstdc++-v3/include/bits/version.h	2025-05-31 00:43:32.180996703 +0200
@@ -2213,4 +2213,14 @@
 #endif /* !defined(__cpp_lib_indirect) && defined(__glibcxx_want_indirect) */
 #undef __glibcxx_want_indirect
 
+#if !defined(__cpp_lib_constexpr_exceptions)
+# if (__cplusplus >  202302L) && (__cpp_constexpr_exceptions >= 202411L)
+#  define __glibcxx_constexpr_exceptions 202502L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_constexpr_exceptions)
+#   define __cpp_lib_constexpr_exceptions 202502L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_constexpr_exceptions) && defined(__glibcxx_want_constexpr_exceptions) */
+#undef __glibcxx_want_constexpr_exceptions
+
 #undef __glibcxx_want_all
--- libstdc++-v3/libsupc++/exception.jj	2025-05-31 00:43:13.853238298 +0200
+++ libstdc++-v3/libsupc++/exception	2025-05-31 00:43:32.180996703 +0200
@@ -56,8 +56,16 @@ namespace std _GLIBCXX_VISIBILITY(defaul
   class bad_exception : public exception
   {
   public:
-    bad_exception() _GLIBCXX_USE_NOEXCEPT { }
+    _GLIBCXX26_CONSTEXPR bad_exception() _GLIBCXX_USE_NOEXCEPT { }
 
+#if __cplusplus >= 202400L
+    constexpr virtual ~bad_exception() _GLIBCXX_TXN_SAFE_DYN noexcept {}
+
+    constexpr virtual const char* what() const _GLIBCXX_TXN_SAFE_DYN noexcept
+    {
+      return "std::bad_exception";
+    }
+#else
     // This declaration is not useless:
     // http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
     virtual ~bad_exception() _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT;
@@ -65,6 +73,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
     // See comment in eh_exception.cc.
     virtual const char*
     what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT;
+#endif
   };
 
   /// If you write a replacement %terminate handler, it must be of this type.
--- libstdc++-v3/libsupc++/exception.h.jj	2025-05-31 00:43:13.853238298 +0200
+++ libstdc++-v3/libsupc++/exception.h	2025-05-31 00:43:32.179996716 +0200
@@ -61,19 +61,28 @@ namespace std _GLIBCXX_VISIBILITY(defaul
   class exception
   {
   public:
-    exception() _GLIBCXX_NOTHROW { }
+    _GLIBCXX26_CONSTEXPR exception() _GLIBCXX_NOTHROW { }
+#if __cplusplus >= 202400L
+    constexpr virtual ~exception() _GLIBCXX_TXN_SAFE_DYN noexcept {}
+#else
     virtual ~exception() _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW;
+#endif
 #if __cplusplus >= 201103L
-    exception(const exception&) = default;
-    exception& operator=(const exception&) = default;
-    exception(exception&&) = default;
-    exception& operator=(exception&&) = default;
+    _GLIBCXX26_CONSTEXPR exception(const exception&) = default;
+    _GLIBCXX26_CONSTEXPR exception& operator=(const exception&) = default;
+    _GLIBCXX26_CONSTEXPR exception(exception&&) = default;
+    _GLIBCXX26_CONSTEXPR exception& operator=(exception&&) = default;
 #endif
 
     /** Returns a C-style character string describing the general cause
      *  of the current error.  */
+#if __cplusplus >= 202400L
+    constexpr virtual const char*
+    what() const _GLIBCXX_TXN_SAFE_DYN noexcept { return "std::exception"; }
+#else
     virtual const char*
     what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW;
+#endif
   };
 
   /// @}
--- libstdc++-v3/libsupc++/exception_ptr.h.jj	2025-05-31 00:43:13.854238284 +0200
+++ libstdc++-v3/libsupc++/exception_ptr.h	2025-05-31 00:43:32.179996716 +0200
@@ -75,7 +75,8 @@ namespace std _GLIBCXX_VISIBILITY(defaul
   exception_ptr current_exception() _GLIBCXX_USE_NOEXCEPT;
 
   template<typename _Ex>
-  exception_ptr make_exception_ptr(_Ex) _GLIBCXX_USE_NOEXCEPT;
+  _GLIBCXX26_CONSTEXPR exception_ptr make_exception_ptr(_Ex)
+  _GLIBCXX_USE_NOEXCEPT;
 
   /// Throw the object pointed to by the exception_ptr.
   void rethrow_exception(exception_ptr) __attribute__ ((__noreturn__));
@@ -98,7 +99,25 @@ namespace std _GLIBCXX_VISIBILITY(defaul
     {
       void* _M_exception_object;
 
+#if __cplusplus >= 202400L
+      constexpr explicit exception_ptr(void* __e) noexcept
+      : _M_exception_object(__e)
+      {
+	if (_M_exception_object)
+	  {
+#if __cpp_if_consteval >= 202106L \
+  && _GLIBCXX_HAS_BUILTIN(__builtin_eh_ptr_adjust_ref)
+	    if consteval {
+	      __builtin_eh_ptr_adjust_ref(_M_exception_object, 1);
+	      return;
+	    }
+#endif
+	    _M_addref();
+	  }
+      }
+#else
       explicit exception_ptr(void* __e) _GLIBCXX_USE_NOEXCEPT;
+#endif
 
       void _M_addref() _GLIBCXX_USE_NOEXCEPT;
       void _M_release() _GLIBCXX_USE_NOEXCEPT;
@@ -108,19 +127,21 @@ namespace std _GLIBCXX_VISIBILITY(defaul
       friend exception_ptr std::current_exception() _GLIBCXX_USE_NOEXCEPT;
       friend void std::rethrow_exception(exception_ptr);
       template<typename _Ex>
-      friend exception_ptr std::make_exception_ptr(_Ex) _GLIBCXX_USE_NOEXCEPT;
+      friend _GLIBCXX26_CONSTEXPR exception_ptr std::make_exception_ptr(_Ex)
+      _GLIBCXX_USE_NOEXCEPT;
 
     public:
-      exception_ptr() _GLIBCXX_USE_NOEXCEPT;
+      _GLIBCXX26_CONSTEXPR exception_ptr() _GLIBCXX_USE_NOEXCEPT;
 
-      exception_ptr(const exception_ptr&) _GLIBCXX_USE_NOEXCEPT;
+      _GLIBCXX26_CONSTEXPR exception_ptr(const exception_ptr&)
+      _GLIBCXX_USE_NOEXCEPT;
 
 #if __cplusplus >= 201103L
-      exception_ptr(nullptr_t) noexcept
+      _GLIBCXX26_CONSTEXPR exception_ptr(nullptr_t) noexcept
       : _M_exception_object(nullptr)
       { }
 
-      exception_ptr(exception_ptr&& __o) noexcept
+      _GLIBCXX26_CONSTEXPR exception_ptr(exception_ptr&& __o) noexcept
       : _M_exception_object(__o._M_exception_object)
       { __o._M_exception_object = nullptr; }
 #endif
@@ -132,11 +153,11 @@ namespace std _GLIBCXX_VISIBILITY(defaul
       exception_ptr(__safe_bool) _GLIBCXX_USE_NOEXCEPT;
 #endif
 
-      exception_ptr&
+      _GLIBCXX26_CONSTEXPR exception_ptr&
       operator=(const exception_ptr&) _GLIBCXX_USE_NOEXCEPT;
 
 #if __cplusplus >= 201103L
-      exception_ptr&
+      _GLIBCXX26_CONSTEXPR exception_ptr&
       operator=(exception_ptr&& __o) noexcept
       {
         exception_ptr(static_cast<exception_ptr&&>(__o)).swap(*this);
@@ -144,9 +165,9 @@ namespace std _GLIBCXX_VISIBILITY(defaul
       }
 #endif
 
-      ~exception_ptr() _GLIBCXX_USE_NOEXCEPT;
+      _GLIBCXX26_CONSTEXPR ~exception_ptr() _GLIBCXX_USE_NOEXCEPT;
 
-      void
+      _GLIBCXX26_CONSTEXPR void
       swap(exception_ptr&) _GLIBCXX_USE_NOEXCEPT;
 
 #ifdef _GLIBCXX_EH_PTR_COMPAT
@@ -158,13 +179,13 @@ namespace std _GLIBCXX_VISIBILITY(defaul
 #endif
 
 #if __cplusplus >= 201103L
-      explicit operator bool() const noexcept
+      _GLIBCXX26_CONSTEXPR explicit operator bool() const noexcept
       { return _M_exception_object; }
 #endif
 
 #if __cpp_impl_three_way_comparison >= 201907L \
       && ! defined _GLIBCXX_EH_PTR_RELOPS_COMPAT
-      friend bool
+      _GLIBCXX26_CONSTEXPR friend bool
       operator==(const exception_ptr&, const exception_ptr&) noexcept = default;
 #else
       friend _GLIBCXX_EH_PTR_USED bool
@@ -184,31 +205,49 @@ namespace std _GLIBCXX_VISIBILITY(defaul
     };
 
     _GLIBCXX_EH_PTR_USED
-    inline
+    _GLIBCXX26_CONSTEXPR inline
     exception_ptr::exception_ptr() _GLIBCXX_USE_NOEXCEPT
     : _M_exception_object(0)
     { }
 
     _GLIBCXX_EH_PTR_USED
-    inline
+    _GLIBCXX26_CONSTEXPR inline
     exception_ptr::exception_ptr(const exception_ptr& __other)
     _GLIBCXX_USE_NOEXCEPT
     : _M_exception_object(__other._M_exception_object)
     {
       if (_M_exception_object)
-	_M_addref();
+	{
+#if __cpp_if_consteval >= 202106L \
+  && _GLIBCXX_HAS_BUILTIN(__builtin_eh_ptr_adjust_ref)
+	  if consteval {
+	    __builtin_eh_ptr_adjust_ref(_M_exception_object, 1);
+	    return;
+	  }
+#endif
+	  _M_addref();
+	}
     }
 
     _GLIBCXX_EH_PTR_USED
-    inline
+    _GLIBCXX26_CONSTEXPR inline
     exception_ptr::~exception_ptr() _GLIBCXX_USE_NOEXCEPT
     {
       if (_M_exception_object)
-	_M_release();
+	{
+#if __cpp_if_consteval >= 202106L \
+  && _GLIBCXX_HAS_BUILTIN(__builtin_eh_ptr_adjust_ref)
+	  if consteval {
+	    __builtin_eh_ptr_adjust_ref(_M_exception_object, -1);
+	    return;
+	  }
+#endif
+	  _M_release();
+	}
     }
 
     _GLIBCXX_EH_PTR_USED
-    inline exception_ptr&
+    _GLIBCXX26_CONSTEXPR inline exception_ptr&
     exception_ptr::operator=(const exception_ptr& __other) _GLIBCXX_USE_NOEXCEPT
     {
       exception_ptr(__other).swap(*this);
@@ -216,7 +255,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
     }
 
     _GLIBCXX_EH_PTR_USED
-    inline void
+    _GLIBCXX26_CONSTEXPR inline void
     exception_ptr::swap(exception_ptr &__other) _GLIBCXX_USE_NOEXCEPT
     {
       void *__tmp = _M_exception_object;
@@ -225,7 +264,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
     }
 
     /// @relates exception_ptr
-    inline void
+    _GLIBCXX26_CONSTEXPR inline void
     swap(exception_ptr& __lhs, exception_ptr& __rhs)
     { __lhs.swap(__rhs); }
 
@@ -244,9 +283,21 @@ namespace std _GLIBCXX_VISIBILITY(defaul
   /// Obtain an exception_ptr pointing to a copy of the supplied object.
 #if (__cplusplus >= 201103L && __cpp_rtti) || __cpp_exceptions
   template<typename _Ex>
-    exception_ptr
+    _GLIBCXX26_CONSTEXPR exception_ptr
     make_exception_ptr(_Ex __ex) _GLIBCXX_USE_NOEXCEPT
     {
+#if __cplusplus >= 202400L
+      if consteval {
+	try
+	  {
+	    throw __ex;
+	  }
+	catch(...)
+	  {
+	    return current_exception();
+	  }
+      }
+#endif
 #if __cplusplus >= 201103L && __cpp_rtti
       using _Ex2 = typename decay<_Ex>::type;
       void* __e = __cxxabiv1::__cxa_allocate_exception(sizeof(_Ex));
@@ -279,7 +330,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
   // instead of a working one compiled with RTTI and/or exceptions enabled.
   template<typename _Ex>
     __attribute__ ((__always_inline__))
-    inline exception_ptr
+    _GLIBCXX26_CONSTEXPR inline exception_ptr
     make_exception_ptr(_Ex) _GLIBCXX_USE_NOEXCEPT
     { return exception_ptr(); }
 #endif
--- libstdc++-v3/libsupc++/nested_exception.h.jj	2025-05-31 00:43:13.854238284 +0200
+++ libstdc++-v3/libsupc++/nested_exception.h	2025-05-31 00:43:32.180996703 +0200
@@ -62,17 +62,24 @@ namespace std _GLIBCXX_VISIBILITY(defaul
 
   public:
     /// The default constructor stores the current exception (if any).
+    _GLIBCXX26_CONSTEXPR
     nested_exception() noexcept : _M_ptr(current_exception()) { }
 
+    _GLIBCXX26_CONSTEXPR
     nested_exception(const nested_exception&) noexcept = default;
 
+    _GLIBCXX26_CONSTEXPR
     nested_exception& operator=(const nested_exception&) noexcept = default;
 
+#if __cplusplus >= 202400L
+    constexpr virtual ~nested_exception() noexcept {}
+#else
     virtual ~nested_exception() noexcept;
+#endif
 
     /// Rethrow the stored exception, or terminate if none was stored.
     [[noreturn]]
-    void
+    _GLIBCXX26_CONSTEXPR void
     rethrow_nested() const
     {
       if (_M_ptr)
@@ -81,7 +88,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
     }
 
     /// Access the stored exception.
-    exception_ptr
+    _GLIBCXX26_CONSTEXPR exception_ptr
     nested_ptr() const noexcept
     { return _M_ptr; }
   };
@@ -91,11 +98,11 @@ namespace std _GLIBCXX_VISIBILITY(defaul
   template<typename _Except>
     struct _Nested_exception : public _Except, public nested_exception
     {
-      explicit _Nested_exception(const _Except& __ex)
+      _GLIBCXX26_CONSTEXPR explicit _Nested_exception(const _Except& __ex)
       : _Except(__ex)
       { }
 
-      explicit _Nested_exception(_Except&& __ex)
+      _GLIBCXX26_CONSTEXPR explicit _Nested_exception(_Except&& __ex)
       : _Except(static_cast<_Except&&>(__ex))
       { }
     };
@@ -144,7 +151,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
    */
   template<typename _Tp>
     [[noreturn]]
-    inline void
+    _GLIBCXX26_CONSTEXPR inline void
     throw_with_nested(_Tp&& __t)
     {
       using _Up = typename decay<_Tp>::type;
@@ -204,7 +211,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
 # if ! __cpp_rtti
     [[__gnu__::__always_inline__]]
 #endif
-    inline void
+    _GLIBCXX26_CONSTEXPR inline void
     rethrow_if_nested(const _Ex& __ex)
     {
       const _Ex* __ptr = __builtin_addressof(__ex);
--- libstdc++-v3/libsupc++/new.jj	2025-05-31 00:43:13.855238271 +0200
+++ libstdc++-v3/libsupc++/new	2025-05-31 00:43:32.179996716 +0200
@@ -66,33 +66,51 @@ namespace std
   class bad_alloc : public exception
   {
   public:
-    bad_alloc() throw() { }
+    _GLIBCXX26_CONSTEXPR bad_alloc() throw() { }
 
 #if __cplusplus >= 201103L
-    bad_alloc(const bad_alloc&) = default;
-    bad_alloc& operator=(const bad_alloc&) = default;
+    _GLIBCXX26_CONSTEXPR bad_alloc(const bad_alloc&) = default;
+    _GLIBCXX26_CONSTEXPR bad_alloc& operator=(const bad_alloc&) = default;
 #endif
 
+#if __cplusplus >= 202400L
+    constexpr virtual ~bad_alloc() noexcept {}
+
+    constexpr virtual const char* what() const noexcept
+    {
+      return "std::bad_alloc";
+    }
+#else
     // This declaration is not useless:
     // http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
     virtual ~bad_alloc() throw();
 
     // See comment in eh_exception.cc.
     virtual const char* what() const throw();
+#endif
   };
 
 #if __cplusplus >= 201103L
   class bad_array_new_length : public bad_alloc
   {
   public:
-    bad_array_new_length() throw() { }
+    _GLIBCXX26_CONSTEXPR bad_array_new_length() throw() { }
 
+#if __cplusplus >= 202400L
+    constexpr virtual ~bad_array_new_length() noexcept {}
+
+    constexpr virtual const char* what() const noexcept
+    {
+      return "std::bad_array_new_length";
+    }
+#else
     // This declaration is not useless:
     // http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
     virtual ~bad_array_new_length() throw();
 
     // See comment in eh_exception.cc.
     virtual const char* what() const throw();
+#endif
   };
 #endif
 
--- libstdc++-v3/libsupc++/typeinfo.jj	2025-05-31 00:43:13.856238258 +0200
+++ libstdc++-v3/libsupc++/typeinfo	2025-05-31 00:43:32.180996703 +0200
@@ -224,14 +224,23 @@ namespace std
   class bad_cast : public exception
   {
   public:
-    bad_cast() _GLIBCXX_USE_NOEXCEPT { }
+    _GLIBCXX26_CONSTEXPR bad_cast() _GLIBCXX_USE_NOEXCEPT { }
 
+#if __cplusplus >= 202400L
+    constexpr virtual ~bad_cast() noexcept {}
+
+    constexpr virtual const char* what() const noexcept
+    {
+      return "std::bad_cast";
+    }
+#else
     // This declaration is not useless:
     // http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
     virtual ~bad_cast() _GLIBCXX_USE_NOEXCEPT;
 
     // See comment in eh_exception.cc.
     virtual const char* what() const _GLIBCXX_USE_NOEXCEPT;
+#endif
   };
 
   /**
@@ -241,14 +250,23 @@ namespace std
   class bad_typeid : public exception
   {
   public:
-    bad_typeid () _GLIBCXX_USE_NOEXCEPT { }
+    _GLIBCXX26_CONSTEXPR bad_typeid () _GLIBCXX_USE_NOEXCEPT { }
+
+#if __cplusplus >= 202400L
+    constexpr virtual ~bad_typeid() noexcept {}
 
+    constexpr virtual const char* what() const noexcept
+    {
+      return "std::bad_typeid";
+    }
+#else
     // This declaration is not useless:
     // http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
     virtual ~bad_typeid() _GLIBCXX_USE_NOEXCEPT;
 
     // See comment in eh_exception.cc.
     virtual const char* what() const _GLIBCXX_USE_NOEXCEPT;
+#endif
   };
 } // namespace std
 

	Jakub



More information about the Libstdc++ mailing list