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

Jakub Jelinek jakub@redhat.com
Wed Jul 9 13:30:05 GMT 2025


On Tue, Jul 08, 2025 at 09:43:20PM -0400, Jason Merrill wrote:
> This will work for any class type with a what() method, which is different
> from vterminate.cc that only calls what() for a type derived from
> std::exception.  I think it makes sense to be consistent with that; a what()
> from an unrelated class might mean something different.

Ok, changed + further test coverage for that added.

> > +	{
> > +	  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 ();
> 
> How is this be possible?  begin_catch should have left us with a variable at
> the end?  Should this be an error rather than a workaround?

I was worried about the ctor throwing or something like that, though that
seems to be wrapped with MUST_NOT_THROW and so probably shouldn't reach
here, at least it doesn't on the testsuite.  Turned into error.

> > @@ -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)
> >   	{
> 
> If evaluating the arguments might throw, we can't give up at this point for
> a non-constexpr function.

I don't understand this comment, at least in connection with the above
snippet, that just handles the magic calls.  If constant evaluation of their
arguments throws, e.g. cxx_eval_cxa_builtin_fn will just return:
  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;
    }
If you mean for constexpr calls, that is done in cxx_bind_parameters_in_call
and I can surely try to double check what will happen if argument evaluation
throw.

> >       case CLEANUP_POINT_EXPR:
> >         {
> > -	auto_vec<tree, 2> cleanups;
> > +	auto_vec<tree, 4> cleanups;
> 
> What's the rationale for this increase?

See other mail, I've dropped this hunk from this patch and am willing
to deal with it incrementally.

Other comments I believe incorporated into the (so far lightly) tested new
patch, except for the following:

> > --- 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",
> 
> Instead of this, I wonder about hijacking the exception_ptr constructor and
> destructor?  That doesn't need to be in this patch, just a thought. Do we
> know if there's a clang/libc++ plan for constexpr exception_ptr yet?

Hana has some implementation on her branch, but I don't see progress in
trying to upstream that into LLVM.
The reason I've added a builtin is that unlike the __cxa_* functions or say
std::current_exception() etc. which are defined out of line (I think even in
libc++), most of the exception_ptr class cdtors and methods are defined
inline, for libstdc++ except for exception_ptr::_M_addref and
exception_ptr::_M_release which are out of line.  The names of those are
very libstdc++ specific though, libc++ uses something else.  Though the
operations (addref and release) are used in multiple places, e.g. in the
private ctor from void *, in copy ctor, and release from dtor, so figuring
out what exactly to hijack seems harder and less portable and future
standards can always add further methods to exception_ptr etc.
https://github.com/llvm/llvm-project/compare/main...hanickadot:llvm-project:hana/P3068-constexpr-exceptions#diff-58be87d6aa8658f15e1c1f3fa40acb71d078896d857cf98033b32f9fa294c320
is what I can see for exception_ptr, but whatever builtins they choose
in the end I guess depends on the upstreaming.  And they can choose to use
the same ones as GCC or libc++ can conditionalize that.

2025-07-09  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>: 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): 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-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-07-08 19:06:41.569662820 +0200
+++ gcc/c-family/c-cppbuiltin.cc	2025-07-09 08:23:15.048898304 +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-07-09 13:30:46.086784510 +0200
+++ gcc/cp/constexpr.cc	2025-07-09 14:54:52.387804539 +0200
@@ -1184,6 +1184,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
@@ -1191,10 +1195,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)
   {
@@ -1308,6 +1315,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.  */
 
@@ -1543,13 +1592,675 @@ 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;
+  tree std_exception = lookup_qualified_name (std_node, "exception",
+					      LOOK_want::NORMAL, false);
+  if (TREE_CODE (std_exception) != TYPE_DECL)
+    return NULL;
+  if (!CLASS_TYPE_P (TREE_TYPE (std_exception)))
+    return NULL;
+  base_kind b_kind;
+  tree binfo = lookup_base (type, TREE_TYPE (std_exception), ba_check, &b_kind,
+			    tf_none);
+  if (binfo == NULL_TREE || binfo == error_mark_node)
+    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 (ptr), ptr, 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;
+}
+
+/* Helper function for cxx_eval_cxa_builtin_fn.
+   Check if ARG is a valid first argument of __cxa_throw or
+   __cxa_free_exception or __builtin_eh_ptr_adjust_ref.  Return NULL_TREE if
+   not, otherwise return the artificial __cxa_allocate_exception allocated
+   VAR_DECL.  FREE_EXC is true for __cxa_free_exception, false otherwise.  */
+
+static tree
+cxa_check_throw_arg (tree arg, bool free_exc)
+{
+  STRIP_NOPS (arg);
+  if (TREE_CODE (arg) != ADDR_EXPR)
+    return NULL_TREE;
+  arg = TREE_OPERAND (arg, 0);
+  if (!VAR_P (arg)
+      || !DECL_ARTIFICIAL (arg)
+      || ((!free_exc || DECL_NAME (arg) != heap_uninit_identifier)
+	  && DECL_NAME (arg) != heap_identifier)
+      || !DECL_LANG_SPECIFIC (arg))
+    return NULL_TREE;
+  return arg;
+}
+
+/* Helper function for cxx_eval_cxa_builtin_fn.
+   "Allocate" on the constexpr heap an exception object of TYPE
+   with REFCOUNT.  */
+
+static tree
+cxa_allocate_exception (location_t loc, tree type, tree refcount)
+{
+  tree var = build_decl (loc, VAR_DECL, heap_uninit_identifier, type);
+  DECL_ARTIFICIAL (var) = 1;
+  retrofit_lang_decl (var);
+  DECL_EXCEPTION_REFCOUNT (var) = refcount;
+  return var;
+}
+
+/* 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;
+	    }
+	  /* Both __cxa_get_exception_ptr (__builtin_eh_pointer (0))
+	     and __cxa_begin_catch (__builtin_eh_pointer (0)) calls expect
+	     ctx->global->caught_exceptions vector to end with
+	     __cxa_allocate_exception created artificial VAR_DECL (the
+	     exception object) followed by handler type, pushed by TRY_BLOCK
+	     evaluation.  The only difference between the functions is that
+	     __cxa_begin_catch pops the handler type from the vector and keeps
+	     the VAR_DECL last and decreases uncaught_exceptions.  The
+	     VAR_DECL after __cxa_begin_catch serves as the current exception
+	     and is then popped in __cxa_end_catch evaluation.  */
+	  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_convert (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 = cxa_allocate_exception (loc, type, 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;
+      arg = cxa_check_throw_arg (args[0], true);
+      if (arg == NULL_TREE)
+	{
+	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;
+	}
+      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;
+      arg = cxa_check_throw_arg (args[0], false);
+      if (arg == NULL_TREE)
+	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 ())
+	{
+	no_active_exc:
+	  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))
+	    goto no_active_exc;
+	free_except:
+	  DECL_EXCEPTION_REFCOUNT (arg)
+	    = size_binop (MINUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg),
+			  size_one_node);
+	  if (integer_zerop (DECL_EXCEPTION_REFCOUNT (arg)))
+	    {
+	      if (type_build_dtor_call (TREE_TYPE (arg)))
+		{
+		  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 = cxa_allocate_exception (loc, type, 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 unlike 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 unlike 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_true && 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 unexpected %qs argument",
+		      fndecl, "std::exception_ptr");
+	  *non_constant_p = true;
+	  return void_node;
+	}
+      arg = cxa_check_throw_arg (CONSTRUCTOR_ELT (args[0], 0)->value, false);
+      if (arg == NULL_TREE)
+	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;
+      arg = cxa_check_throw_arg (args[0], false);
+      if (arg == NULL_TREE)
+	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.  */
@@ -1557,7 +2268,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));
@@ -1603,6 +2315,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))
@@ -1677,8 +2395,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)
@@ -1767,7 +2491,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
@@ -1882,7 +2607,8 @@ addr_of_non_const_var (tree *tp, int *wa
 static tree
 cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun,
 			     tree orig_fun, bool *non_constant_p,
-			     bool *overflow_p, bool *non_constant_args)
+			     bool *overflow_p, bool *non_constant_args,
+			     tree *jump_target)
 {
   int nargs = call_expr_nargs (t);
   tree parms = DECL_ARGUMENTS (fun);
@@ -1958,14 +2684,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)
@@ -1983,6 +2711,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;
@@ -2094,9 +2824,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);
@@ -2183,7 +2914,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))
@@ -2196,12 +2927,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)
@@ -2233,7 +2967,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;
 
@@ -2266,13 +3001,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;
@@ -2290,10 +3027,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);
@@ -2566,7 +3306,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)
@@ -2585,19 +3326,26 @@ cxx_eval_dynamic_cast_fn (const constexp
 
   /* TYPE can only be either T* or T&.  We can't know which of these it
      is by looking at TYPE, but OBJ will be "(T*) x" in the first case,
-     and something like "(T*)(T&)(T*) x" in the second case.  */
-  bool reference_p = false;
+     and something like "(T*)(T&)(T*) x" in the second case.  
+     This is true for the reference cases in C++ < 26 or when exceptions
+     aren't enabled, in that case we should diagnose errors.  For C++26
+     with exceptions we should silently evaluate to null pointer and
+     let the callers call __cxa_bad_cast () later to throw an exception.  */
+  bool fail_for_non_constant_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)
+	fail_for_non_constant_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;
 
   /* For dynamic_cast from classes with virtual bases we can get something
      like (virt_base *)(&d + 16) as OBJ.  Try to convert that into
@@ -2609,7 +3357,7 @@ cxx_eval_dynamic_cast_fn (const constexp
       if (TREE_CODE (objo) == POINTER_PLUS_EXPR)
 	{
 	  objo = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (TREE_TYPE (obj)),
-					obj);
+					obj, NULL, jump_target);
 	  if (objo)
 	    obj = build_fold_addr_expr (objo);
 	}
@@ -2625,7 +3373,7 @@ cxx_eval_dynamic_cast_fn (const constexp
 		? TREE_OPERAND (obj, 1) : obj))
     if (TREE_CODE (t) != FIELD_DECL || !DECL_FIELD_IS_BASE (t))
       {
-	if (reference_p)
+	if (fail_for_non_constant_p)
 	  {
 	    if (!ctx->quiet)
 	      {
@@ -2647,9 +3395,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))
@@ -2681,7 +3432,7 @@ cxx_eval_dynamic_cast_fn (const constexp
       /* If not accessible, give an error.  */
       if (t == error_mark_node)
 	{
-	  if (reference_p)
+	  if (fail_for_non_constant_p)
 	    {
 	      if (!ctx->quiet)
 		{
@@ -2714,7 +3465,7 @@ cxx_eval_dynamic_cast_fn (const constexp
   obj = get_component_with_type (obj, mdtype, NULL_TREE);
   if (obj == error_mark_node)
     {
-      if (reference_p)
+      if (fail_for_non_constant_p)
 	{
 	  if (!ctx->quiet)
 	    {
@@ -2736,7 +3487,7 @@ cxx_eval_dynamic_cast_fn (const constexp
   tree binfo = lookup_base (mdtype, type, ba_check, &b_kind, tf_none);
   if (!binfo || binfo == error_mark_node)
     {
-      if (reference_p)
+      if (fail_for_non_constant_p)
 	{
 	  if (!ctx->quiet)
 	    {
@@ -2832,7 +3583,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);
 
@@ -2875,7 +3626,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
@@ -2885,7 +3637,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)))
@@ -2893,8 +3645,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;
     }
 }
@@ -2906,20 +3661,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);
@@ -2971,9 +3731,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
@@ -2988,7 +3750,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)
@@ -3103,7 +3868,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
@@ -3113,7 +3881,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)
 	{
@@ -3158,7 +3931,8 @@ cxx_eval_call_expression (const constexp
   constexpr_call new_call;
   new_call.bindings
     = cxx_bind_parameters_in_call (ctx, t, fun, orig_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
@@ -3172,6 +3946,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;
 
@@ -3246,7 +4022,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.  */
@@ -3395,7 +4173,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.  */
@@ -3409,16 +4187,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
@@ -3446,7 +4233,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);
@@ -3506,7 +4293,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;
+		      }
 		  }
 	      }
 
@@ -3864,12 +4657,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);
@@ -3893,7 +4690,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)
@@ -3915,9 +4712,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;
@@ -3937,7 +4737,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;
@@ -3981,22 +4782,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);
@@ -4052,13 +4860,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)
@@ -4117,7 +4929,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))
     {
@@ -4178,19 +4993,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);
@@ -4578,7 +5403,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)
@@ -4595,7 +5421,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;
 }
 
@@ -4626,13 +5453,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)
@@ -4644,7 +5475,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)
@@ -4664,14 +5497,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)))
@@ -4681,9 +5518,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;
@@ -4801,7 +5641,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;
@@ -4813,7 +5653,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;
@@ -4822,9 +5663,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)))
     {
@@ -4933,7 +5777,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.
@@ -4943,7 +5788,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;
@@ -4951,10 +5797,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
@@ -5235,7 +6084,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))
@@ -5249,9 +6098,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)
@@ -5329,8 +6181,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;
 	    }
 	}
@@ -5371,19 +6226,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;
 }
@@ -5540,7 +6400,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;
@@ -5593,7 +6454,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;
@@ -5683,7 +6547,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);
@@ -5694,7 +6559,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
@@ -5731,7 +6596,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)
     {
@@ -5757,9 +6624,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)
 	{
@@ -5773,7 +6640,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
@@ -5789,8 +6657,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)
@@ -5840,7 +6711,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);
@@ -5872,10 +6743,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
@@ -5904,14 +6775,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
@@ -5924,7 +6797,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;
@@ -5941,7 +6815,8 @@ cxx_fold_indirect_ref_1 (const constexpr
 	 than pointer type.  */
       if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc,
 					      strip_array_types (optype),
-					      op, off, empty_base))
+					      op, off, empty_base,
+					      jump_target))
 	return fold_convert (type, ret);
     }
   else if (TREE_CODE (optype) == COMPLEX_TYPE
@@ -5987,7 +6862,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 */
@@ -5996,7 +6871,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)));
@@ -6005,7 +6880,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;
 	      }
 	  }
@@ -6050,7 +6926,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;
 	      }
 	  }
@@ -6070,7 +6947,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;
@@ -6152,7 +7029,8 @@ cxx_fold_indirect_ref (const constexpr_c
 	  tree off = integer_zero_node;
 	  canonicalize_obj_off (op, off);
 	  return cxx_fold_indirect_ref_1 (ctx, loc, type, op,
-					  tree_to_uhwi (off), empty_base);
+					  tree_to_uhwi (off), empty_base,
+					  jump_target);
 	}
     }
   else if (TREE_CODE (sub) == POINTER_PLUS_EXPR
@@ -6167,7 +7045,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] */
@@ -6177,7 +7056,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
@@ -6195,7 +7077,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;
@@ -6213,13 +7096,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;
@@ -6233,7 +7120,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
@@ -6263,7 +7152,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;
 
@@ -6373,7 +7265,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];
@@ -6383,7 +7276,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]);
     }
 
@@ -6505,7 +7401,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;
 
@@ -6531,7 +7428,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;
     }
@@ -6543,8 +7443,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;
     }
@@ -6570,7 +7473,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;
 	      }
@@ -6627,8 +7533,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;
 	    }
@@ -6972,7 +7881,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. */
@@ -7098,7 +8010,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);
@@ -7112,12 +8025,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));
@@ -7156,8 +8075,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;
 
@@ -7171,42 +8093,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.  */
@@ -7254,6 +8140,11 @@ label_matches (const constexpr_ctx *ctx,
 	 breaks (jump_target) or continues (jump_target).  */
       break;
 
+    case VAR_DECL:
+      /* Uncaught exception.  This is handled by TRY_BLOCK evaluation
+	 and other places by testing throws (jump_target).  */
+      break;
+
     default:
       gcc_unreachable ();
     }
@@ -7268,15 +8159,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;
@@ -7304,18 +8189,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;
 }
 
@@ -7327,13 +8205,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;
@@ -7389,7 +8260,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);
@@ -7484,7 +8355,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;
@@ -7531,6 +8403,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 ();
@@ -7549,7 +8422,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)
     {
@@ -7682,7 +8558,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));
@@ -7718,13 +8595,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;
@@ -7754,9 +8635,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.  */
@@ -7880,7 +8761,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:
@@ -7958,7 +8839,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.  */;
@@ -7991,7 +8875,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:
@@ -8055,7 +8939,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
@@ -8125,9 +9012,12 @@ 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 (*jump_target)
+	  return NULL_TREE;
 	if (!is_complex)
 	  {
 	    r = unshare_constructor (r);
@@ -8135,8 +9025,15 @@ 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 (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 (ctx->save_exprs)
 	  ctx->save_exprs->safe_push (slot);
 	if (lval)
@@ -8150,33 +9047,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:
@@ -8185,9 +9077,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)
@@ -8195,16 +9088,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),
@@ -8213,6 +9097,42 @@ 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;
+		r = cxx_eval_constant_expression (ctx, HANDLER_BODY (h),
+						  vc_discard, non_constant_p,
+						  overflow_p, jump_target);
+	      }
+	  }
+      break;
+
     case CLEANUP_POINT_EXPR:
       {
 	auto_vec<tree, 2> cleanups;
@@ -8230,47 +9150,189 @@ 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 after "
+			     "initializing the exception object");
+	      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);
+	  tree jmp_target = NULL_TREE;
 	  /* Also evaluate the cleanup.  */
 	  cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), vc_discard,
-					non_constant_p, overflow_p);
+					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;
 
@@ -8280,14 +9342,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;
@@ -8307,7 +9373,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)
@@ -8328,7 +9397,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:
@@ -8366,6 +9436,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);
@@ -8418,7 +9490,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
@@ -8427,19 +9500,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:
@@ -8454,17 +9530,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))
@@ -8502,7 +9580,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:
@@ -8514,7 +9592,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:
@@ -8524,12 +9602,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:
@@ -8537,7 +9616,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:
@@ -8561,7 +9641,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);
@@ -8618,7 +9701,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);
@@ -8745,10 +9831,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));
 	  }
@@ -8787,7 +9877,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);
@@ -8824,14 +9917,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:
@@ -8845,7 +9944,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.  */
@@ -8855,7 +9955,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;
@@ -8868,7 +9969,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;
@@ -8980,7 +10084,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:
@@ -9299,8 +10403,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.  */
@@ -9309,15 +10439,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 "
@@ -9335,8 +10481,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;
@@ -9949,11 +11100,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)
 {
@@ -10038,6 +11191,23 @@ 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);
+	  tree callee_fn = cp_get_fndecl_from_callee (callee, false);
+	  if (!flag_enforce_eh_specs
+	      || type_dependent_expression_p (callee)
+	      || !POINTER_TYPE_P (TREE_TYPE (callee))
+	      || (!type_noexcept_p (TREE_TYPE (TREE_TYPE (callee)))
+		  && (callee_fn == NULL_TREE
+		      || !TREE_NOTHROW (callee_fn))))
+	    d->could_throw = true;
+	}
+      break;
+
     default:
       if (!EXPR_P (t))
 	*walk_subtrees = 0;
@@ -10243,7 +11413,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,
@@ -10290,6 +11461,22 @@ 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);
+	    tree callee_fn = cp_get_fndecl_from_callee (callee, false);
+	    if (!flag_enforce_eh_specs
+		|| type_dependent_expression_p (callee)
+		|| !POINTER_TYPE_P (TREE_TYPE (callee))
+		|| (!type_noexcept_p (TREE_TYPE (TREE_TYPE (callee)))
+		    && (callee_fn == NULL_TREE
+			|| !TREE_NOTHROW (callee_fn))))
+	      *jump_target = void_node;
+	  }
         return true;
       }
 
@@ -10512,11 +11699,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;
 	    }
 	}
@@ -10556,11 +11745,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))
@@ -10584,7 +11775,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))
@@ -10593,6 +11784,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;
 
@@ -10622,7 +11815,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:
@@ -10678,6 +11870,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);
@@ -10806,6 +12003,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:
@@ -11047,9 +12260,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))
@@ -11061,7 +12274,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,
@@ -11074,6 +12288,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-07-09 08:16:50.883079418 +0200
+++ gcc/cp/cp-tree.h	2025-07-09 12:18:10.996187712 +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)
@@ -3016,6 +3019,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;
@@ -4470,6 +4475,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
@@ -4512,7 +4534,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))
@@ -5153,6 +5175,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 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)
@@ -6813,6 +6839,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
 };
 
@@ -6993,6 +7020,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-06-02 11:00:06.027527030 +0200
+++ gcc/cp/call.cc	2025-07-09 08:23:15.056898196 +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-06-02 11:00:06.120525824 +0200
+++ gcc/cp/except.cc	2025-07-09 12:48:35.755414128 +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,11 @@ 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;
+      /* 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.  */
+      tree 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 +772,17 @@ build_throw (location_t loc, tree exp, t
       /* Prepend the allocation.  */
       exp = build2 (COMPOUND_EXPR, TREE_TYPE (exp), allocate_expr, exp);
 
+      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);
 
+      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-06-17 13:18:52.927091924 +0200
+++ gcc/cp/tree.cc	2025-07-09 08:23:15.057898182 +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-07-09 08:16:50.886079378 +0200
+++ gcc/cp/decl.cc	2025-07-09 08:23:15.059898155 +0200
@@ -5082,6 +5082,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-07-09 08:16:50.901079175 +0200
+++ gcc/cp/pt.cc	2025-07-09 08:23:15.063898102 +0200
@@ -20147,7 +20147,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-06-27 23:34:21.856899744 +0200
+++ gcc/cp/cp-gimplify.cc	2025-07-09 08:23:15.063898102 +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-throw.C.jj	2025-06-02 11:00:06.205524722 +0200
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-throw.C	2025-07-09 08:23:15.081897859 +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-12 11:54:37.113402848 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C	2025-07-09 08:23:15.090897738 +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	2025-06-02 11:00:06.214524605 +0200
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C	2025-07-09 08:23:15.091897724 +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-06-02 11:00:06.217524566 +0200
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C	2025-07-09 08:23:15.098897630 +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-07-09 08:23:15.110897468 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh1.C	2025-07-09 08:23:15.110897468 +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-07-09 08:23:15.111897454 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh2.C	2025-07-09 08:23:15.111897454 +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-07-09 08:23:15.111897454 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh3.C	2025-07-09 08:23:15.111897454 +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-07-09 08:23:15.111897454 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh4.C	2025-07-09 08:23:15.111897454 +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-07-09 08:23:15.112897441 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh5.C	2025-07-09 08:23:15.111897454 +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-07-09 08:23:15.112897441 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh6.C	2025-07-09 08:23:15.112897441 +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-07-09 08:23:15.112897441 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh7.C	2025-07-09 08:23:15.112897441 +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-07-09 08:23:15.112897441 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh8.C	2025-07-09 08:23:15.112897441 +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-07-09 08:23:15.113897427 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh9.C	2025-07-09 10:03:05.906144072 +0200
@@ -0,0 +1,127 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+namespace std
+{
+  struct exception
+  {
+    constexpr exception () noexcept { }
+    constexpr virtual ~exception () noexcept {}
+    constexpr exception (const exception &) = default;
+    constexpr exception &operator= (const exception &) = default;
+    constexpr exception (exception &&) = default;
+    constexpr exception &operator= (exception &&) = default;
+    constexpr virtual const char *what () const noexcept
+    { return "std::exception"; }
+  };
+}
+
+struct S : public std::exception {
+  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 : public std::exception {
+  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;
+};
+struct V {
+  constexpr V () : v (0) {}
+  constexpr V (int x) : v (x) {}
+  constexpr V (const V &x) : v (x.v) {}
+  constexpr virtual ~V () {}
+  constexpr virtual const char *what () noexcept { return "this is V"; }
+  int v;
+};
+
+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 if (x == 6)
+    throw V (42);
+  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 }
+// { dg-error "'std::terminate' called after throwing an exception 'V\\\{\[^\n\r]*42\\\}" "" { target *-*-* } .-7 }
+{
+  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" }
+constexpr int f = bar (6);		// { 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 (baz (6) == -1);
+static_assert (foo (0) == 42);
+constexpr int g = foo (1);		// { dg-error "uncaught exception of type 'S'; 'what\\\(\\\)': 'this is S'" }
+constexpr int h = foo (2);		// { dg-error "uncaught exception of type 'T'; 'what\\\(\\\)': 'hello, world'" }
+constexpr int i = foo (3);		// { dg-error "uncaught exception 'U\\\{1, -2, 42\\\}'" }
+constexpr int j = foo (4);		// { dg-error "uncaught exception '42'" }
+constexpr int k = foo (5);		// { dg-error "uncaught exception '1\\\.0e\\\+0'" }
+constexpr int l = foo (6);		// { dg-error "uncaught exception 'V\\\{\[^\n\r]*42\\\}'" }
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh10.C.jj	2025-07-09 08:23:15.113897427 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh10.C	2025-07-09 08:23:15.113897427 +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-07-09 08:23:15.113897427 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh11.C	2025-07-09 08:23:15.113897427 +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-07-09 08:23:15.113897427 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh12.C	2025-07-09 08:23:15.113897427 +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-07-09 08:23:15.113897427 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh13.C	2025-07-09 08:23:15.113897427 +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-07-09 08:23:15.113897427 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh14.C	2025-07-09 08:23:15.113897427 +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-07-09 08:23:15.125897265 +0200
+++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C	2025-07-09 08:27:59.517063090 +0200
@@ -624,19 +624,25 @@
 #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_virtual_inheritance
-# error "__cpp_constexpr_virtual_inheritance"
+#  error "__cpp_constexpr_virtual_inheritance"
 #elif __cpp_constexpr_virtual_inheritance != 202506
 #  error "__cpp_constexpr_virtual_inheritance != 202506"
 #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-06-02 11:00:06.220524528 +0200
+++ gcc/testsuite/g++.dg/cpp26/static_assert1.C	2025-07-09 08:23:15.141897050 +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	2025-06-02 11:00:06.222524502 +0200
+++ gcc/testsuite/g++.dg/cpp2a/consteval34.C	2025-07-09 08:23:15.154896874 +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	2025-06-02 11:00:06.222524502 +0200
+++ gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C	2025-07-09 08:23:15.155896861 +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-06-02 11:00:06.223524489 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C	2025-07-09 08:23:15.155896861 +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-06-02 11:00:06.223524489 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C	2025-07-09 08:23:15.159896807 +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-06-02 11:00:06.223524489 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C	2025-07-09 08:23:15.159896807 +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-06-02 11:00:06.223524489 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C	2025-07-09 08:23:15.167896699 +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-06-02 11:00:06.223524489 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C	2025-07-09 08:23:15.167896699 +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-06-02 11:00:06.222524502 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C	2025-07-09 08:23:15.168896686 +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-06-02 11:00:06.222524502 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C	2025-07-09 08:23:15.172896632 +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-06-02 11:00:06.223524489 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic18.C	2025-07-09 08:23:15.173896619 +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-07-09 08:23:15.173896619 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new27.C	2025-07-09 08:23:15.173896619 +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-07-09 08:23:15.173896619 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-typeid5.C	2025-07-09 08:23:15.173896619 +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-07-08 19:06:48.209575804 +0200
+++ libstdc++-v3/include/bits/version.def	2025-07-09 08:23:15.174896605 +0200
@@ -2041,6 +2041,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-07-08 19:06:48.210575791 +0200
+++ libstdc++-v3/include/bits/version.h	2025-07-09 08:23:15.175896592 +0200
@@ -2289,4 +2289,14 @@
 #endif /* !defined(__cpp_lib_bitset) && defined(__glibcxx_want_bitset) */
 #undef __glibcxx_want_bitset
 
+#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-06-26 16:18:01.051039568 +0200
+++ libstdc++-v3/libsupc++/exception	2025-07-09 08:23:15.175896592 +0200
@@ -57,8 +57,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;
@@ -66,6 +74,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-06-02 11:00:06.267523918 +0200
+++ libstdc++-v3/libsupc++/exception.h	2025-07-09 08:23:15.175896592 +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-06-26 16:18:01.060039456 +0200
+++ libstdc++-v3/libsupc++/exception_ptr.h	2025-07-09 08:26:22.229374720 +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__));
@@ -105,7 +106,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;
@@ -115,7 +134,8 @@ 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;
 #if __cpp_lib_exception_ptr_cast >= 202506L
       template<typename _Ex>
       friend const _Ex* std::exception_ptr_cast(const exception_ptr&) noexcept;
@@ -125,16 +145,17 @@ namespace std _GLIBCXX_VISIBILITY(defaul
 	_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
@@ -146,11 +167,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);
@@ -158,9 +179,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
@@ -172,13 +193,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
@@ -198,31 +219,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);
@@ -230,7 +269,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;
@@ -239,7 +278,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); }
 
@@ -258,9 +297,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));
@@ -293,7 +344,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-06-02 11:00:06.267523918 +0200
+++ libstdc++-v3/libsupc++/nested_exception.h	2025-07-09 08:23:15.176896578 +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-06-02 11:00:06.268523905 +0200
+++ libstdc++-v3/libsupc++/new	2025-07-09 08:23:15.177896565 +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-06-02 11:00:06.269523892 +0200
+++ libstdc++-v3/libsupc++/typeinfo	2025-07-09 08:23:15.177896565 +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