]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++, libstdc++: Implement C++26 P3068R5 - constexpr exceptions [PR117785]
authorJakub Jelinek <jakub@redhat.com>
Thu, 10 Jul 2025 21:26:15 +0000 (23:26 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Thu, 10 Jul 2025 21:32:06 +0000 (23:32 +0200)
The following patch implements the C++26 P3068R5 - constexpr exceptions
paper.

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

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

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

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

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

2025-07-10  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.  For C++26 don't diagnose
calls to non-constexpr functions before cxx_bind_parameters_in_call
could report some argument throwing an exception.
(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.
(merge_jump_target): New function.
(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.
(callee_might_throw): New function.
(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.  For C++26 if call to non-constexpr call
is seen, try to evaluate arguments first and if they could throw,
don't diagnose call to non-constexpr function nor return false.
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-ellipsis2.C: Expect different diagnostics for
C++26.
* g++.dg/cpp0x/constexpr-throw.C: Likewise.
* 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/constexpr-eh15.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.

51 files changed:
gcc/c-family/c-cppbuiltin.cc
gcc/cp/call.cc
gcc/cp/constexpr.cc
gcc/cp/cp-gimplify.cc
gcc/cp/cp-tree.h
gcc/cp/decl.cc
gcc/cp/except.cc
gcc/cp/pt.cc
gcc/cp/tree.cc
gcc/testsuite/g++.dg/cpp0x/constexpr-ellipsis2.C
gcc/testsuite/g++.dg/cpp0x/constexpr-throw.C
gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C
gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C
gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C
gcc/testsuite/g++.dg/cpp26/constexpr-eh1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-eh10.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-eh11.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-eh12.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-eh13.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-eh14.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-eh15.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-eh2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-eh3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-eh4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-eh5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-eh6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-eh7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-eh8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-eh9.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
gcc/testsuite/g++.dg/cpp26/static_assert1.C
gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C
gcc/testsuite/g++.dg/cpp2a/consteval34.C
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic18.C
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C
gcc/testsuite/g++.dg/cpp2a/constexpr-new27.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/constexpr-typeid5.C [new file with mode: 0644]
libstdc++-v3/include/bits/version.def
libstdc++-v3/include/bits/version.h
libstdc++-v3/libsupc++/exception
libstdc++-v3/libsupc++/exception.h
libstdc++-v3/libsupc++/exception_ptr.h
libstdc++-v3/libsupc++/nested_exception.h
libstdc++-v3/libsupc++/new
libstdc++-v3/libsupc++/typeinfo

index 459fd86bb39d8a5e2adcebb6c26b0ce2ea9ae441..0c84020300fb61dc827f3e025c240c3a742b1c63 100644 (file)
@@ -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");
index 2c3ef3dfc35dce3b4707f2759db2bd37aa789cfc..26296255b1ece139a02a3136dcac8e7dcc041438 100644 (file)
@@ -1723,6 +1723,56 @@ involves_qualification_conversion_p (tree to, tree from)
   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.  */
index f9066bc79322e1210894ae26d6ddca2b4536ef88..eb19784dbbaad9a012a2a4a4f608e19f90cc0055 100644 (file)
@@ -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,672 @@ 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)
+    return NULL;
+  if (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);
+      gcc_assert (!throws (&jmp_target) && !non_constant_p);
+      if (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);
+      gcc_assert (!throws (&jmp_target) && !non_constant_p);
+      if (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, const constexpr_ctx *ctx, 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;
+  ctx->global->heap_vars.safe_push (var);
+  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)
+           /* Used for catch (...).  Just return void.  */
+           return void_node;
+         else if (POINTER_TYPE_P (handler_type))
+           {
+             /* 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
+           {
+             /* 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, ctx, type, size_zero_node);
+         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, ctx, 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;
+           }
+         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 to null 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 +2265,8 @@ static tree find_heap_var_refs (tree *, int *, void *);
 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 +2312,12 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
       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 +2392,14 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
          || 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 +2488,8 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
     }
 
   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 +2604,8 @@ addr_of_non_const_var (tree *tp, int *walk_subtrees, void *data)
 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 +2681,16 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun,
          /* 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 +2708,8 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun,
       /* 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 +2821,10 @@ fold_operand (tree e, const constexpr_ctx *ctx)
   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 +2911,7 @@ cxx_eval_assert (const constexpr_ctx *ctx, tree arg, const char *msg,
   if (*non_constant_p)
     return true;
 
-  tree eval;
+  tree eval, jmp_target = NULL_TREE;
   if (!evaluated)
     {
       if (!potential_rvalue_constant_expression (arg))
@@ -2196,12 +2924,15 @@ cxx_eval_assert (const constexpr_ctx *ctx, tree arg, const char *msg,
       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 +2964,8 @@ cxx_eval_assert (const constexpr_ctx *ctx, tree arg, const char *msg,
 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 +2998,15 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
     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 +3024,13 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
   /* 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 +3303,8 @@ get_component_with_type (tree path, tree type, tree stop)
 
 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 +3323,26 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
 
   /* 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 +3354,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
       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 +3370,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
                ? 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 +3392,12 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
      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 +3429,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
       /* 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 +3462,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
   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 +3484,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
   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 +3580,7 @@ replace_decl (tree *tp, tree decl, tree replacement)
 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 +3623,8 @@ cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl,
                       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 +3634,7 @@ cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl,
 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 +3642,11 @@ cxx_set_object_constness (const constexpr_ctx *ctx, tree object,
       /* 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 +3658,25 @@ cxx_set_object_constness (const constexpr_ctx *ctx, tree object,
 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 +3728,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 
   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);
+  bool non_constexpr_call = false;
   if (!maybe_constexpr_fn (fun))
     {
       if (TREE_CODE (t) == CALL_EXPR
@@ -2988,7 +3748,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
            {
              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 +3866,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
            {
              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,16 +3879,31 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
          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)
+      /* Calls to non-constexpr functions can be diagnosed right away
+        before C++26, though in C++26 evaluation of the arguments might
+        throw and if caught it could be still constant expression.
+        So for C++26 this is diagnosed only after
+        cxx_bind_parameters_in_call.  */
+      if (cxx_dialect >= cxx26)
+       non_constexpr_call = true;
+      else
        {
-         if (!lambda_static_thunk_p (fun))
-           error_at (loc, "call to non-%<constexpr%> function %qD", fun);
-         explain_invalid_constexpr_fn (fun);
+         if (!ctx->quiet)
+           {
+             if (!lambda_static_thunk_p (fun))
+               error_at (loc, "call to non-%<constexpr%> function %qD", fun);
+             explain_invalid_constexpr_fn (fun);
+           }
+         *non_constant_p = true;
+         return t;
        }
-      *non_constant_p = true;
-      return t;
     }
 
   constexpr_ctx new_ctx = *ctx;
@@ -3158,7 +3939,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
   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,8 +3954,21 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
     void preserve () { bindings = NULL; }
   } fb (new_call.bindings);
 
+  if (*jump_target)
+    return NULL_TREE;
   if (*non_constant_p)
     return t;
+  if (non_constexpr_call)
+    {
+      if (!ctx->quiet)
+       {
+         if (!lambda_static_thunk_p (fun))
+           error_at (loc, "call to non-%<constexpr%> function %qD", fun);
+         explain_invalid_constexpr_fn (fun);
+       }
+      *non_constant_p = true;
+      return t;
+    }
 
   /* We can't defer instantiating the function any longer.  */
   if (!DECL_INITIAL (fun)
@@ -3246,7 +4041,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
       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 +4192,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
             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 +4206,25 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
                                      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 +4252,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
             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 +4312,13 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
                    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 +4676,16 @@ cxx_eval_check_shift_p (location_t loc, const constexpr_ctx *ctx,
 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 +4709,7 @@ cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t,
 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 +4731,12 @@ cxx_fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t,
       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 +4756,8 @@ cxx_fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t,
       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 +4801,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 +4879,17 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
       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 +4948,10 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
 {
   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 +5012,29 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
 
 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 +5422,8 @@ diag_array_subscript (location_t loc, const constexpr_ctx *ctx, tree array, tree
 
 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 +5440,8 @@ get_array_or_vector_nelts (const constexpr_ctx *ctx, tree type,
 
   /* 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 +5472,17 @@ extract_string_elt (tree string, unsigned chars_per_elt, unsigned index)
 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 +5494,9 @@ eval_and_check_array_index (const constexpr_ctx *ctx,
     }
 
   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 +5516,18 @@ eval_and_check_array_index (const constexpr_ctx *ctx,
 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 +5537,12 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 
   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 +5660,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
       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 +5672,8 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 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 +5682,12 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
   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 +5796,8 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
   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 +5807,8 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
 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 +5816,13 @@ cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t,
   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 +6103,7 @@ clear_uchar_or_std_byte_in_mask (location_t loc, tree t, unsigned char *mask)
 
 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 +6117,12 @@ cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
     }
 
   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 +6200,9 @@ cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
          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 && !*jump_target);
              r = e;
            }
        }
@@ -5371,19 +6243,24 @@ cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
 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 +6417,8 @@ verify_ctor_sanity (const constexpr_ctx *ctx, tree type)
 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 +6471,10 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
        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 +6564,8 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
 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 +6576,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
 
   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 +6613,9 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
     }
 
   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 +6641,9 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
            }
          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 +6657,8 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
            /* 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 +6674,11 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
            /* 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 +6728,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
 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 +6760,10 @@ cxx_eval_vec_init (const constexpr_ctx *ctx, tree t,
        }
       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 +6792,16 @@ same_type_ignoring_tlq_and_bounds_p (tree type1, tree type2)
    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 +6814,8 @@ cxx_union_active_member (const constexpr_ctx *ctx, tree t)
 
 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 +6832,8 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type,
         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 +6879,7 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type,
          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 +6888,7 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type,
     {
       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 +6897,8 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type,
                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 +6943,8 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type,
                                   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 +6964,7 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type,
 
 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 +7046,8 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type,
          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 +7062,8 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type,
          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 +7073,10 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type,
       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 +7094,8 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type,
 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 +7113,17 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
 
   /* 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 +7137,9 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
        }
 
       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 +7169,10 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
     }
 
   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 +7282,8 @@ non_const_var_error (location_t loc, tree r, bool fundef_p)
 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 +7293,10 @@ cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t,
     {
       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 +7418,8 @@ modifying_const_object_p (tree_code code, tree obj, bool mutable_p)
 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 +7445,10 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
       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 +7460,11 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
         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 +7490,10 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
            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 +7550,11 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
                  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 +7898,10 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
        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 +8027,8 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 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 +8042,18 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
 
   /* 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 +8092,11 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
   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 +8110,6 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
     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 +8157,11 @@ label_matches (const constexpr_ctx *ctx, tree *jump_target, tree stmt)
         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 +8176,9 @@ cxx_eval_statement_list (const constexpr_ctx *ctx, tree t,
                         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 +8206,11 @@ cxx_eval_statement_list (const constexpr_ctx *ctx, tree t,
                                        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 +8222,6 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
                    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 +8277,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
        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 +8372,8 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
                      if (*non_constant_p
                          || returns (jump_target)
                          || breaks (jump_target)
-                         || continues (jump_target))
+                         || continues (jump_target)
+                         || throws (jump_target))
                        {
                          depth = 1;
                          break;
@@ -7531,6 +8420,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
         && !breaks (jump_target)
         && !continues (jump_target)
         && (!switches (jump_target) || count == 0)
+        && !throws (jump_target)
         && !*non_constant_p);
 
   cleanup_cond ();
@@ -7549,7 +8439,10 @@ cxx_eval_switch_expr (const constexpr_ctx *ctx, tree t,
   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 +8575,8 @@ maybe_warn_about_constant_value (location_t loc, tree decl)
 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 +8612,17 @@ build_new_constexpr_heap_type (const constexpr_ctx *ctx, tree elt_type,
          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;
@@ -7745,6 +8643,38 @@ build_new_constexpr_heap_type (const constexpr_ctx *ctx, tree elt_type,
   return build_new_constexpr_heap_type (elt_type, cookie_size, itype2);
 }
 
+/* Handle the case when a cleanup of some expression throws.  JMP_TARGET
+   indicates whether the cleanup threw or not, *JUMP_TARGET indicates whether
+   the expression which needed the cleanup threw.  If both threw, diagnose
+   it and return NULL, otherwise return R.  If only the cleanup threw, set
+   *JUMP_TARGET to the exception object from the cleanup.  */
+
+static tree
+merge_jump_target (location_t loc, const constexpr_ctx *ctx, tree r,
+                  bool *non_constant_p, tree *jump_target, tree jmp_target)
+{
+  if (!throws (&jmp_target))
+    return r;
+  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;
+      return NULL_TREE;
+    }
+  *jump_target = jmp_target;
+  return r;
+}
+
 /* Attempt to reduce the expression T to a constant value.
    On failure, issue diagnostic and return error_mark_node.  */
 /* FIXME unify with c_fully_fold */
@@ -7754,9 +8684,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 +8810,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
                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 +8888,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
          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 +8924,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     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 +8988,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
        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 +9061,12 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
        /* 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 +9074,15 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
            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 +9096,28 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     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 +9126,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
        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 +9137,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
        }
       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 +9146,42 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
                                        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 +9199,132 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 
        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);
+         r = merge_jump_target (loc, ctx, r, non_constant_p, 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);
+         r = merge_jump_target (loc, ctx, r, non_constant_p, 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);
+         r = merge_jump_target (loc, ctx, r, non_constant_p, jump_target,
+                                jmp_target);
        }
       break;
 
@@ -8280,14 +9334,18 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     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 +9365,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       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 +9389,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     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 +9428,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
            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 +9482,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     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 +9492,22 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     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 +9522,19 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
          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 +9572,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       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 +9584,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
            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 +9594,13 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
         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 +9608,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       /* 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 +9633,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
                                                ? 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 +9693,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
              {
                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 +9823,14 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
                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 +9869,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 
        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 +9909,20 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     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 +9936,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     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 +9947,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
            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 +9961,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       {
        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 +10076,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
          *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 +10395,34 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
 
   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 +10431,31 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
 
   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 +10473,13 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
        {
          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;
@@ -9917,6 +11060,24 @@ cxx_constant_init (tree t, tree decl)
   return maybe_constant_init_1 (t, decl, false, mce_true);
 }
 
+/* Return true if CALL_EXPR T might throw during constant evaluation.  */
+
+static bool
+callee_might_throw (tree t)
+{
+  if (cxx_dialect < cxx26 || !flag_exceptions)
+    return false;
+  tree callee = cp_get_callee (t);
+  if (callee == NULL_TREE)
+    return false;
+  tree callee_fn = cp_get_fndecl_from_callee (callee, false);
+  return (!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))));
+}
+
 #if 0
 /* FIXME see ADDR_EXPR section in potential_constant_expression_1.  */
 /* Return true if the object referred to by REF has automatic or thread
@@ -9949,11 +11110,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 +11201,13 @@ check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
     case CONSTRUCTOR:
       break;
 
+    case AGGR_INIT_EXPR:
+    case CALL_EXPR:
+      /* In C++26 a function could throw.  */
+      if (callee_might_throw (t))
+       d->could_throw = true;
+      break;
+
     default:
       if (!EXPR_P (t))
        *walk_subtrees = 0;
@@ -10243,8 +11413,27 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
                        || 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))
                  {
+                   /* In C++26 evaluation of the function arguments might
+                      throw and in that case it is irrelevant whether
+                      fun is constexpr or not.  */
+                   if (cxx_dialect >= cxx26)
+                     for (; i < nargs; ++i)
+                       {
+                         tree x = get_nth_callarg (t, i);
+                         bool rv = processing_template_decl ? any : rval;
+                         bool sub_now = false;
+                         if (!potential_constant_expression_1 (x, rv, strict,
+                                                               sub_now,
+                                                               fundef_p,
+                                                               flags,
+                                                               jump_target))
+                           return false;
+                         if (throws (jump_target))
+                           return true;
+                       }
                    if ((flags & tf_error)
                        && constexpr_error (loc, fundef_p,
                                            "call to non-%<constexpr%> "
@@ -10289,7 +11478,12 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
                                                  sub_now, fundef_p, flags,
                                                  jump_target))
              return false;
+           if (throws (jump_target))
+             return true;
           }
+       /* In C++26 a function could throw.  */
+       if (*jump_target == NULL_TREE && callee_might_throw (t))
+         *jump_target = void_node;
         return true;
       }
 
@@ -10512,11 +11706,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
                 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 +11752,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
             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 +11782,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
        {
          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 +11791,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
          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 +11822,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
 
     case DYNAMIC_CAST_EXPR:
     case PSEUDO_DTOR_EXPR:
-    case THROW_EXPR:
     case OMP_PARALLEL:
     case OMP_TASK:
     case OMP_FOR:
@@ -10678,6 +11877,11 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
         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 +12010,22 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
     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 +12267,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
                                               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 +12281,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
                         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 +12295,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
                          else if (data.break_stmt)
                            *jump_target = data.break_stmt;
                        }
+                     if (data.could_throw)
+                       *jump_target = void_node;
                    }
                }
              return true;
index ce69bd6030c780b098b27cf53b2c38d99a77e8c2..882a943391cbbae1dc1834df97654f1524f5b6f7 100644 (file)
@@ -889,6 +889,12 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
                        (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;
              }
index 3b92d9af6e1cadfcd092197dd7d5b30a40c0ff3a..43705733d5147ccec9cc0d79fb10faa793ee3972 100644 (file)
@@ -452,6 +452,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       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_MAX];
       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_MAX];
       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               (tree);
 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);
index 0e6afbe565274b4d455bb2d2fce705292e26bf34..664dbbec279650996f022b320042095ead3a1e32 100644 (file)
@@ -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.  */
index a9d8e2ffb57ade7b5fd7e7bb28815dab2421fc2a..a7f35e4314d4ee2c5e11f3300b308327335f6294 100644 (file)
@@ -367,6 +367,8 @@ initialize_handler_parm (tree decl, tree exp)
         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_subtrees, void * /*data*/)
     {
       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, tsubst_flags_t complain)
       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, tsubst_flags_t complain)
       /* 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;
index 40ce987a6e81727a6afa38c136106c6f5ea5a4eb..8f3822c05a44f735095268b1b3b7dff2dacf1219 100644 (file)
@@ -20147,7 +20147,14 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
       {
        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:
index 5863b6878f028262cd47946c45ac9ab4fd53b4a1..accce0ec26202d66aefc262b3119d80a072c6447 100644 (file)
@@ -488,6 +488,7 @@ builtin_valid_in_constant_expr_p (const_tree decl)
          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;
index b6a5323b0f906dced9c74e7974f137c2b1ae712b..c473257451c31b2e403222c5f9ef4b268c6d6a34 100644 (file)
@@ -9,4 +9,6 @@ struct A
 
 constexpr int ellipsis(...) { return 1; }
 
-static_assert(ellipsis(A().empty()), "Error"); // { dg-error "non-constant condition|empty" }
+static_assert(ellipsis(A().empty()), "Error"); // { dg-error "non-constant condition" }
+// { dg-error "call to non-'constexpr' function 'bool A::empty\\\(\\\)'" "" { target c++23_down } .-1 }
+// { dg-error "temporary of non-literal type 'A' in a constant expression" "" { target c++26 } .-2 }
index 9c49fa4e9e85131b0cddfccc53c58e79ee9c495e..9abf4a3982f67251920ebaead47c40243d84c691 100644 (file)
@@ -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 }
index f7439d80b3967bc59d3c3db97bdc442446ab2bcc..18efd23398fc45ca161cfc6275505f4cc836a3e7 100644 (file)
@@ -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
index b5fa6caeb576a755547c6a6908795f4da3f583c1..75e2fb82829308ade6cee3ddcbc82d07860d25bf 100644 (file)
@@ -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), "");
index 35beb27f7d5306d7489291724308ee52a9a60303..2c69b3b6f7a512547f7d1eb367b8acb3822cf616 100644 (file)
@@ -18,7 +18,7 @@ struct H { short size () const { return 0; }
           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 {}));
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh1.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh1.C
new file mode 100644 (file)
index 0000000..9eed3aa
--- /dev/null
@@ -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);
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh10.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh10.C
new file mode 100644 (file)
index 0000000..a86cc4d
--- /dev/null
@@ -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);
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh11.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh11.C
new file mode 100644 (file)
index 0000000..287e066
--- /dev/null
@@ -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);
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh12.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh12.C
new file mode 100644 (file)
index 0000000..4a85f15
--- /dev/null
@@ -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);
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh13.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh13.C
new file mode 100644 (file)
index 0000000..d62771c
--- /dev/null
@@ -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);
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh14.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh14.C
new file mode 100644 (file)
index 0000000..3e52f2b
--- /dev/null
@@ -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);
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh15.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh15.C
new file mode 100644 (file)
index 0000000..3dea461
--- /dev/null
@@ -0,0 +1,39 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+int
+foo (int x, int y)
+{
+  return x + y;
+}
+
+constexpr int
+bar (int x)
+{
+  if (x < 0)
+    throw x;
+  return x;
+}
+
+constexpr int
+baz (int x, int y)
+{
+  return foo (bar (x), bar (y));
+}
+
+constexpr int
+qux (int x, int y)
+{
+  try
+    {
+      return baz (x, y);
+    }
+  catch (int)
+    {
+      return 42;
+    }
+}
+
+static_assert (qux (12, -1) == 42);
+static_assert (qux (-7, 12) == 42);
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh2.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh2.C
new file mode 100644 (file)
index 0000000..8bd6b6f
--- /dev/null
@@ -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" }
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh3.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh3.C
new file mode 100644 (file)
index 0000000..f844d11
--- /dev/null
@@ -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);
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh4.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh4.C
new file mode 100644 (file)
index 0000000..24118ca
--- /dev/null
@@ -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" }
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh5.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh5.C
new file mode 100644 (file)
index 0000000..512aa34
--- /dev/null
@@ -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 ());
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh6.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh6.C
new file mode 100644 (file)
index 0000000..6fd9462
--- /dev/null
@@ -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 ());
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh7.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh7.C
new file mode 100644 (file)
index 0000000..6bdf0c3
--- /dev/null
@@ -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 ());
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh8.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh8.C
new file mode 100644 (file)
index 0000000..2560364
--- /dev/null
@@ -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'" }
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh9.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh9.C
new file mode 100644 (file)
index 0000000..fd39fe0
--- /dev/null
@@ -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\\\}'" }
index e4ffc357fcead09f85c4212bf6cc8b48f934a9c0..97bd36be0d3587d213cec07794ba80ea1afda335 100644 (file)
 #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
index 1d0e6f2840f12ffc502dc6ffe29240025a7d8e79..a6eab3cd69120fa88d746a324c3c9a88d0e2858f 100644 (file)
@@ -51,7 +51,7 @@ static_assert (true, H {});   // { dg-warning "'static_assert' with non-string mes
 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-warning "'static_assert' with non-string m
                                // { 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"; } };
index ca923519f98a644902a1fa83389ac7ead5574b1c..17a391bd84c789741a772c9ab25b5f9b43ba9c3f 100644 (file)
@@ -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 { }
index 7562f403f741a87a800de2e9ff5401e37bb40fdd..73bcf77297c56689657c8c678db8dea2cdb114c0 100644 (file)
@@ -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 } }
index 9ee93c334e9d8867aac1772287780c79ca4c83a3..12a071a85e590a7b30510d25a5b0057ada61f6d2 100644 (file)
@@ -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 }
index cc1cadae3bc00669d699518d1a8e1529d61cc132..f966db45e9212afd2ddeb1a010153560ec64d0bb 100644 (file)
@@ -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 }
index 25d98c2b661f31fa00456ec820e4bc116fe9c00a..112ff8a249cca067b47b2a9922c2e1f228e4ae17 100644 (file)
@@ -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;
 }
index da647bfca5e3ab98d60246c8d3b1fac037d368cc..a23713461aa99209c1a3c7f9c38a15090984517f 100644 (file)
@@ -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*>(static_cast<const C2*>(&g)) == nullptr);
 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 }
index d67c3072b5445c250f4343f351c09f8f23e41e2d..4434a1ae074b23997d3b69d0ca976546a6daa3b8 100644 (file)
@@ -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 void a(); };
 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);
index bc3efd05bccbd9eeb4705549d67d6dc11065a61b..d504efd102f8a0834569bd0110ae48d5b002b72c 100644 (file)
@@ -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 void a(); };
 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);
index 1958cae441db840955d50bb5f0acd5e5e061997c..2fc824258104fbe229afb6637f2bb8355d2618f9 100644 (file)
@@ -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);
index 7d42ffa5a78f51f1c77d5e71b050b9ecf0e8d57e..3a50dde82724838515a7325fbd464150e202b122 100644 (file)
@@ -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);
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new27.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new27.C
new file mode 100644 (file)
index 0000000..a26fc7e
--- /dev/null
@@ -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 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-typeid5.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-typeid5.C
new file mode 100644 (file)
index 0000000..567383d
--- /dev/null
@@ -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 }
index 31385b53107a10c98d6a905e470c928b748eacf3..df58e7018d81cf53c875bc9d4da34d1f484a290b 100644 (file)
@@ -2050,6 +2050,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";
index aa53f299848f13f58d3e090988f61958c1e76bcc..1414dd78ddab9c636590ab7f0de42ba80cbe9d1b 100644 (file)
 #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
index 61d41315f9f1f987c73b43937b940c5e716669f4..25ce8d97e3156d379094677262bd7400501c10b2 100644 (file)
@@ -57,8 +57,16 @@ namespace std _GLIBCXX_VISIBILITY(default)
   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(default)
     // 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.
index f8bca9127bce22d50bd2614a81f89d54d4e73b1d..efb5fea5d86388c7a3976a7bfd568b05c84a15f6 100644 (file)
@@ -61,19 +61,28 @@ namespace std _GLIBCXX_VISIBILITY(default)
   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
   };
 
   /// @}
index a6ff8c0d6982a96047d3aa167c1208897b7e06ca..be516d7aae177b0cf192f15a4b7ecd1be719e3ea 100644 (file)
@@ -75,7 +75,8 @@ namespace std _GLIBCXX_VISIBILITY(default)
   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(default)
     {
       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(default)
       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(default)
        _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(default)
       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(default)
       }
 #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(default)
 #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(default)
     };
 
     _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(default)
     }
 
     _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(default)
     }
 
     /// @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(default)
   /// 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(default)
   // 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
index c9f63e5a56a205cce4fef23f8f138b29cab8a119..aff47bc3d7f9cb6148c86afb97ed4786178b67c6 100644 (file)
@@ -62,17 +62,24 @@ namespace std _GLIBCXX_VISIBILITY(default)
 
   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(default)
     }
 
     /// 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(default)
   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(default)
    */
   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(default)
 # 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);
index a5503733cdcd57ec038fc77259022b52cdc12077..fb36dae25a6d3720e5e7c2efdbd85940d57af5fa 100644 (file)
@@ -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
 
index 0a94b86bd74572973587cc8025e650f00602076c..a31be7c39eaf1733f9aa8f399bfa78cc3724737a 100644 (file)
@@ -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