[PATCH] c++, libstdc++, v3: Implement C++26 P3068R5 - constexpr exceptions [PR117785]
Jakub Jelinek
jakub@redhat.com
Wed Jul 9 13:30:05 GMT 2025
On Tue, Jul 08, 2025 at 09:43:20PM -0400, Jason Merrill wrote:
> This will work for any class type with a what() method, which is different
> from vterminate.cc that only calls what() for a type derived from
> std::exception. I think it makes sense to be consistent with that; a what()
> from an unrelated class might mean something different.
Ok, changed + further test coverage for that added.
> > + {
> > + arg = ctx->global->caught_exceptions.pop ();
> > + if (arg == NULL_TREE || !VAR_P (arg))
> > + {
> > + gcc_assert (!ctx->global->caught_exceptions.is_empty ());
> > + arg = ctx->global->caught_exceptions.pop ();
>
> How is this be possible? begin_catch should have left us with a variable at
> the end? Should this be an error rather than a workaround?
I was worried about the ctor throwing or something like that, though that
seems to be wrapped with MUST_NOT_THROW and so probably shouldn't reach
here, at least it doesn't on the testsuite. Turned into error.
> > @@ -3066,7 +3810,12 @@ cxx_eval_call_expression (const constexp
> > return arg1;
> > }
> > else if (cxx_dynamic_cast_fn_p (fun))
> > - return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p);
> > + return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p,
> > + jump_target);
> > + else if (enum cxa_builtin kind = cxx_cxa_builtin_fn_p (fun))
> > + return cxx_eval_cxa_builtin_fn (ctx, t, kind, fun,
> > + non_constant_p, overflow_p,
> > + jump_target);
> > if (!ctx->quiet)
> > {
>
> If evaluating the arguments might throw, we can't give up at this point for
> a non-constexpr function.
I don't understand this comment, at least in connection with the above
snippet, that just handles the magic calls. If constant evaluation of their
arguments throws, e.g. cxx_eval_cxa_builtin_fn will just return:
for (int i = 0; i < nargs; ++i)
{
args[i] = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (call, i),
vc_prvalue, non_constant_p,
overflow_p, jump_target);
if (*non_constant_p)
return call;
if (*jump_target)
return NULL_TREE;
}
If you mean for constexpr calls, that is done in cxx_bind_parameters_in_call
and I can surely try to double check what will happen if argument evaluation
throw.
> > case CLEANUP_POINT_EXPR:
> > {
> > - auto_vec<tree, 2> cleanups;
> > + auto_vec<tree, 4> cleanups;
>
> What's the rationale for this increase?
See other mail, I've dropped this hunk from this patch and am willing
to deal with it incrementally.
Other comments I believe incorporated into the (so far lightly) tested new
patch, except for the following:
> > --- gcc/cp/decl.cc.jj 2025-05-31 00:43:13.835238535 +0200
> > +++ gcc/cp/decl.cc 2025-05-31 00:43:32.174996782 +0200
> > @@ -5070,6 +5070,18 @@ cxx_init_decl_processing (void)
> > BUILT_IN_FRONTEND, NULL, NULL_TREE);
> > set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF);
> > + if (cxx_dialect >= cxx26)
> > + {
> > + tree void_ptrintftype
> > + = build_function_type_list (void_type_node, ptr_type_node,
> > + integer_type_node, NULL_TREE);
> > + decl = add_builtin_function ("__builtin_eh_ptr_adjust_ref",
>
> Instead of this, I wonder about hijacking the exception_ptr constructor and
> destructor? That doesn't need to be in this patch, just a thought. Do we
> know if there's a clang/libc++ plan for constexpr exception_ptr yet?
Hana has some implementation on her branch, but I don't see progress in
trying to upstream that into LLVM.
The reason I've added a builtin is that unlike the __cxa_* functions or say
std::current_exception() etc. which are defined out of line (I think even in
libc++), most of the exception_ptr class cdtors and methods are defined
inline, for libstdc++ except for exception_ptr::_M_addref and
exception_ptr::_M_release which are out of line. The names of those are
very libstdc++ specific though, libc++ uses something else. Though the
operations (addref and release) are used in multiple places, e.g. in the
private ctor from void *, in copy ctor, and release from dtor, so figuring
out what exactly to hijack seems harder and less portable and future
standards can always add further methods to exception_ptr etc.
https://github.com/llvm/llvm-project/compare/main...hanickadot:llvm-project:hana/P3068-constexpr-exceptions#diff-58be87d6aa8658f15e1c1f3fa40acb71d078896d857cf98033b32f9fa294c320
is what I can see for exception_ptr, but whatever builtins they choose
in the end I guess depends on the upstreaming. And they can choose to use
the same ones as GCC or libc++ can conditionalize that.
2025-07-09 Jakub Jelinek <jakub@redhat.com>
PR c++/117785
gcc/c-family/
* c-cppbuiltin.cc (c_cpp_builtins): Predefine
__cpp_constexpr_exceptions=202411L for C++26.
gcc/cp/
* constexpr.cc: Implement C++26 P3068R5 - constexpr exceptions.
(class constexpr_global_ctx): Add caught_exceptions and
uncaught_exceptions members.
(constexpr_global_ctx::constexpr_global_ctx): Initialize
uncaught_exceptions.
(returns, breaks, continues, switches): Move earlier.
(throws): New function.
(exception_what_str, diagnose_std_terminate,
diagnose_uncaught_exception): New functions.
(enum cxa_builtin): New type.
(cxx_cxa_builtin_fn_p, cxx_eval_cxa_builtin_fn): New functions.
(cxx_eval_builtin_function_call): Add jump_target argument. Call
cxx_eval_cxa_builtin_fn for __builtin_eh_ptr_adjust_ref. Adjust
cxx_eval_constant_expression calls, if it results in jmp_target,
set *jump_target to it and return.
(cxx_bind_parameters_in_call): Add jump_target argument. Pass
it through to cxx_eval_constant_expression. If it sets *jump_target,
break.
(fold_operand): Adjust cxx_eval_constant_expression caller.
(cxx_eval_assert): Likewise. If it set jmp_target, return true.
(cxx_eval_internal_function): Add jump_target argument. Pass it
through to cxx_eval_constant_expression. Return early if
*jump_target after recursing on args.
(cxx_eval_dynamic_cast_fn): Likewise. Don't set reference_p for
C++26 with -fexceptions.
(cxx_eval_thunk_call): Add jump_target argument. Pass it through
to cxx_eval_constant_expression.
(cxx_set_object_constness): Likewise. Don't set TREE_READONLY if
throws (jump_target).
(cxx_eval_call_expression): Add jump_target argument. Pass it
through to cxx_eval_internal_function, cxx_eval_builtin_function_call,
cxx_eval_thunk_call, cxx_eval_dynamic_cast_fn and
cxx_set_object_constness. Pass it through also
cxx_eval_constant_expression on arguments, cxx_bind_parameters_in_call
and cxx_fold_indirect_ref and for those cases return early if
*jump_target. Call cxx_eval_cxa_builtin_fn for cxx_cxa_builtin_fn_p
functions. For cxx_eval_constant_expression on body, pass address of
cleared jmp_target automatic variable, if it throws propagate to
*jump_target and make it non-cacheable.
(cxx_eval_unary_expression): Add jump_target argument. Pass it
through to cxx_eval_constant_expression and return early if
*jump_target after the call.
(cxx_fold_pointer_plus_expression): Likewise.
(cxx_eval_binary_expression): Likewise and similarly for
cxx_fold_pointer_plus_expression call.
(cxx_eval_conditional_expression): Pass jump_target to
cxx_eval_constant_expression on first operand and return early if
*jump_target after the call.
(cxx_eval_vector_conditional_expression): Add jump_target argument.
Pass it through to cxx_eval_constant_expression for all 3 arguments
and return early if *jump_target after any of those calls.
(get_array_or_vector_nelts): Add jump_target argument. Pass it
through to cxx_eval_constant_expression.
(eval_and_check_array_index): Add jump_target argument. Pass it
through to cxx_eval_constant_expression calls and return early after
each of them if *jump_target.
(cxx_eval_array_reference): Likewise.
(cxx_eval_component_reference): Likewise.
(cxx_eval_bit_field_ref): Likewise.
(cxx_eval_bit_cast): Likewise. Assert CHECKING_P call doesn't
throw or return.
(cxx_eval_logical_expression): Add jump_target argument. Pass it
through to cxx_eval_constant_expression calls and return early after
each of them if *jump_target.
(cxx_eval_bare_aggregate): Likewise.
(cxx_eval_vec_init_1): Add jump_target argument. Pass it through
to cxx_eval_bare_aggregate and recursive call. Pass it through
to get_array_or_vector_nelts and cxx_eval_constant_expression
and return early after it if *jump_target.
(cxx_eval_vec_init): Add jump_target argument. Pass it through
to cxx_eval_constant_expression and cxx_eval_vec_init_1.
(cxx_union_active_member): Add jump_target argument. Pass it
through to cxx_eval_constant_expression and return early after it
if *jump_target.
(cxx_fold_indirect_ref_1): Add jump_target argument. Pass it
through to cxx_union_active_member and recursive calls.
(cxx_eval_indirect_ref): Add jump_target argument. Pass it through
to cxx_fold_indirect_ref_1 calls and to recursive call, in which
case return early after it if *jump_target.
(cxx_fold_indirect_ref): Add jump_target argument. Pass it through
to cxx_fold_indirect_ref and cxx_eval_constant_expression calls and
return early after those if *jump_target.
(cxx_eval_trinary_expression): Add jump_target argument. Pass it
through to cxx_eval_constant_expression calls and return early after
those if *jump_target.
(cxx_eval_store_expression): Add jump_target argument. Pass it
through to cxx_eval_constant_expression and eval_and_check_array_index
calls and return early after those if *jump_target.
(cxx_eval_increment_expression): Add jump_target argument. Pass it
through to cxx_eval_constant_expression calls and return early after
those if *jump_target.
(label_matches): Handle VAR_DECL case.
(cxx_eval_statement_list): Remove local_target variable and
!jump_target handling. Handle throws (jump_target) like returns or
breaks.
(cxx_eval_loop_expr): Remove local_target variable and !jump_target
handling. Pass it through to cxx_eval_constant_expression. Handle
throws (jump_target) like returns.
(cxx_eval_switch_expr): Pass jump_target through to
cxx_eval_constant_expression on cond, return early after it if
*jump_target.
(build_new_constexpr_heap_type): Add jump_target argument. Pass it
through to cxx_eval_constant_expression calls, return early after
those if *jump_target.
(cxx_eval_constant_expression): Make jump_target argument no longer
defaulted, don't test jump_target for NULL. Pass jump_target
through to recursive calls, cxx_eval_call_expression,
cxx_eval_store_expression, cxx_eval_indirect_ref,
cxx_eval_unary_expression, cxx_eval_binary_expression,
cxx_eval_logical_expression, cxx_eval_array_reference,
cxx_eval_component_reference, cxx_eval_bit_field_ref,
cxx_eval_vector_conditional_expression, cxx_eval_bare_aggregate,
cxx_eval_vec_init, cxx_eval_trinary_expression, cxx_fold_indirect_ref,
build_new_constexpr_heap_type, cxx_eval_increment_expression,
cxx_eval_bit_cast and return earlyu after some of those if
*jump_target as needed.
(cxx_eval_constant_expression) <case TARGET_EXPR>: For C++26 push
also CLEANUP_EH_ONLY cleanups, with NULL_TREE marker after them.
(cxx_eval_constant_expression) <case RETURN_EXPR>: Don't override
*jump_target if throws (jump_target).
(cxx_eval_constant_expression) <case TRY_CATCH_EXPR, case TRY_BLOCK,
case MUST_NOT_THROW_EXPR, case TRY_FINALLY_EXPR, case CLEANUP_STMT>:
Handle C++26 constant expressions.
(cxx_eval_constant_expression) <case CLEANUP_POINT_EXPR>: For C++26
with throws (jump_target) evaluate the CLEANUP_EH_ONLY cleanups as
well, and if not throws (jump_target) skip those. Set *jump_target
if some of the cleanups threw.
(cxx_eval_constant_expression) <case THROW_EXPR>: Recurse on operand
for C++26.
(cxx_eval_outermost_constant_expr): Diagnose uncaught exceptions both
from main expression and cleanups, diagnose also
break/continue/returns from the main expression. Handle
CLEANUP_EH_ONLY cleanup markers. Don't diagnose mutable poison stuff
if non_constant_p. Use different diagnostics for non-deleted heap
allocations if they were allocated by __cxa_allocate_exception.
(struct check_for_return_continue_data): Add could_throw field.
(check_for_return_continue): Handle AGGR_INIT_EXPR and CALL_EXPR and
set d->could_throw if they could throw.
(potential_constant_expression_1): For CALL_EXPR allow
cxx_dynamic_cast_fn_p calls. For C++26 set *jump_target to void_node
for calls that could throw. Adjust check_for_return_continue_data
initializers and set *jump_target to void_node if data.could_throw_p.
For C++26 recurse on THROW_EXPR argument. Add comment explaining
TRY_BLOCK handling with C++26 exceptions. Handle throws like returns
in some cases.
* cp-tree.h (MUST_NOT_THROW_NOEXCEPT_P, MUST_NOT_THROW_THROW_P,
MUST_NOT_THROW_CATCH_P, DECL_EXCEPTION_REFCOUNT): Define.
(DECL_LOCAL_DECL_P): Fix comment typo, VARIABLE_DECL -> VAR_DECL.
(enum cp_built_in_function): Add CP_BUILT_IN_EH_PTR_ADJUST_REF,
(handler_match_for_exception_type): Declare.
* call.cc (handler_match_for_exception_type): New function.
* except.cc (initialize_handler_parm): Set MUST_NOT_THROW_CATCH_P
on newly created MUST_NOT_THROW_EXPR.
(begin_eh_spec_block): Set MUST_NOT_THROW_NOEXCEPT_P.
(wrap_cleanups_r): Set MUST_NOT_THROW_THROW_P.
(build_throw): Add another TARGET_EXPR whose scope spans
until after the __cxa_throw call and copy pointer value from ptr
to it and use it in __cxa_throw argument.
* tree.cc (builtin_valid_in_constant_expr_p): Handle
CP_BUILT_IN_EH_PTR_ADJUST_REF.
* decl.cc (cxx_init_decl_processing): Initialize
__builtin_eh_ptr_adjust_ref FE builtin.
* pt.cc (tsubst_stmt) <case MUST_NOT_THROW_EXPR>: Copy the
MUST_NOT_THROW_NOEXCEPT_P, MUST_NOT_THROW_THROW_P and
MUST_NOT_THROW_CATCH_P flags.
* cp-gimplify.cc (cp_gimplify_expr) <case CALL_EXPR>: Error on
non-folded CP_BUILT_IN_EH_PTR_ADJUST_REF calls.
gcc/testsuite/
* g++.dg/cpp0x/constexpr-throw.C: Expect different diagnostics for
C++26.
* g++.dg/cpp1y/constexpr-84192.C: Expect different diagnostics.
* g++.dg/cpp1y/constexpr-throw.C: Expect different diagnostics for
C++26.
* g++.dg/cpp1z/constexpr-asm-5.C: Likewise.
* g++.dg/cpp26/constexpr-eh1.C: New test.
* g++.dg/cpp26/constexpr-eh2.C: New test.
* g++.dg/cpp26/constexpr-eh3.C: New test.
* g++.dg/cpp26/constexpr-eh4.C: New test.
* g++.dg/cpp26/constexpr-eh5.C: New test.
* g++.dg/cpp26/constexpr-eh6.C: New test.
* g++.dg/cpp26/constexpr-eh7.C: New test.
* g++.dg/cpp26/constexpr-eh8.C: New test.
* g++.dg/cpp26/constexpr-eh9.C: New test.
* g++.dg/cpp26/constexpr-eh10.C: New test.
* g++.dg/cpp26/constexpr-eh11.C: New test.
* g++.dg/cpp26/constexpr-eh12.C: New test.
* g++.dg/cpp26/constexpr-eh13.C: New test.
* g++.dg/cpp26/constexpr-eh14.C: New test.
* g++.dg/cpp26/feat-cxx26.C: Change formatting in __cpp_pack_indexing
and __cpp_pp_embed test. Add __cpp_constexpr_exceptions test.
* g++.dg/cpp26/static_assert1.C: Expect different diagnostics for
C++26.
* g++.dg/cpp2a/consteval34.C: Likewise.
* g++.dg/cpp2a/consteval-memfn1.C: Likewise.
* g++.dg/cpp2a/constexpr-dynamic4.C: For C++26 add std::exception and
std::bad_cast definitions and expect different diagnostics.
* g++.dg/cpp2a/constexpr-dynamic6.C: Likewise.
* g++.dg/cpp2a/constexpr-dynamic7.C: Likewise.
* g++.dg/cpp2a/constexpr-dynamic8.C: Likewise.
* g++.dg/cpp2a/constexpr-dynamic9.C: Likewise.
* g++.dg/cpp2a/constexpr-dynamic11.C: Likewise.
* g++.dg/cpp2a/constexpr-dynamic14.C: Likewise.
* g++.dg/cpp2a/constexpr-dynamic18.C: Likewise.
* g++.dg/cpp2a/constexpr-new27.C: New test.
* g++.dg/cpp2a/constexpr-typeid5.C: New test.
libstdc++-v3/
* include/bits/version.def (constexpr_exceptions): New.
* include/bits/version.h: Regenerate.
* libsupc++/exception (std::bad_exception::bad_exception): Add
_GLIBCXX26_CONSTEXPR.
(std::bad_exception::~bad_exception, std::bad_exception::what): For
C++26 add constexpr and define inline.
* libsupc++/exception.h (std::exception::exception,
std::exception::operator=): Add _GLIBCXX26_CONSTEXPR.
(std::exception::~exception, std::exception::what): For C++26 add
constexpr and define inline.
* libsupc++/exception_ptr.h (std::make_exception_ptr): Add
_GLIBCXX26_CONSTEXPR. For if consteval use just throw with
current_exception() in catch.
(std::exception_ptr::exception_ptr(void*)): For C++26 add constexpr
and define inline.
(std::exception_ptr::exception_ptr()): Add _GLIBCXX26_CONSTEXPR.
(std::exception_ptr::exception_ptr(const exception_ptr&)): Likewise.
Use __builtin_eh_ptr_adjust_ref if consteval and compiler has it
instead of _M_addref.
(std::exception_ptr::exception_ptr(nullptr_t)): Add
_GLIBCXX26_CONSTEXPR.
(std::exception_ptr::exception_ptr(exception_ptr&&)): Likewise.
(std::exception_ptr::operator=): Likewise.
(std::exception_ptr::~exception_ptr): Likewise. Use
__builtin_eh_ptr_adjust_ref if consteval and compiler has it
instead of _M_release.
(std::exception_ptr::swap): Add _GLIBCXX26_CONSTEXPR.
(std::exception_ptr::operator bool): Likewise.
(std::exception_ptr::operator==): Likewise.
* libsupc++/nested_exception.h
(std::nested_exception::nested_exception): Add _GLIBCXX26_CONSTEXPR.
(std::nested_exception::operator=): Likewise.
(std::nested_exception::~nested_exception): For C++26 add constexpr
and define inline.
(std::nested_exception::rethrow_if_nested): Add _GLIBCXX26_CONSTEXPR.
(std::nested_exception::nested_ptr): Likewise.
(std::_Nested_exception::_Nested_exception): Likewise.
(std::throw_with_nested, std::rethrow_if_nested): Likewise.
* libsupc++/new (std::bad_alloc::bad_alloc): Likewise.
(std::bad_alloc::operator=): Likewise.
(std::bad_alloc::~bad_alloc): For C++26 add constexpr and define
inline.
(std::bad_alloc::what): Likewise.
(std::bad_array_new_length::bad_array_new_length): Add
_GLIBCXX26_CONSTEXPR.
(std::bad_array_new_length::~bad_array_new_length): For C++26 add
constexpr and define inline.
(std::bad_array_new_length::what): Likewise.
* libsupc++/typeinfo (std::bad_cast::bad_cast): Add
_GLIBCXX26_CONSTEXPR.
(std::bad_cast::~bad_cast): For C++26 add constexpr and define inline.
(std::bad_cast::what): Likewise.
(std::bad_typeid::bad_typeid): Add _GLIBCXX26_CONSTEXPR.
(std::bad_typeid::~bad_typeid): For C++26 add constexpr and define
inline.
(std::bad_typeid::what): Likewise.
--- gcc/c-family/c-cppbuiltin.cc.jj 2025-07-08 19:06:41.569662820 +0200
+++ gcc/c-family/c-cppbuiltin.cc 2025-07-09 08:23:15.048898304 +0200
@@ -1087,6 +1087,7 @@ c_cpp_builtins (cpp_reader *pfile)
{
/* Set feature test macros for C++26. */
cpp_define (pfile, "__cpp_constexpr=202406L");
+ cpp_define (pfile, "__cpp_constexpr_exceptions=202411L");
cpp_define (pfile, "__cpp_static_assert=202306L");
cpp_define (pfile, "__cpp_placeholder_variables=202306L");
cpp_define (pfile, "__cpp_structured_bindings=202403L");
--- gcc/cp/constexpr.cc.jj 2025-07-09 13:30:46.086784510 +0200
+++ gcc/cp/constexpr.cc 2025-07-09 14:54:52.387804539 +0200
@@ -1184,6 +1184,10 @@ public:
/* Heap VAR_DECLs created during the evaluation of the outermost constant
expression. */
auto_vec<tree, 16> heap_vars;
+ /* Vector of caught exceptions, including exceptions still not active at
+ the start of a handler (those are immediately followed up by HANDLER_TYPE
+ until __cxa_begin_catch finishes). */
+ auto_vec<tree, 2> caught_exceptions;
/* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR. */
vec<tree> *cleanups;
/* If non-null, only allow modification of existing values of the variables
@@ -1191,10 +1195,13 @@ public:
hash_set<tree> *modifiable;
/* Number of heap VAR_DECL deallocations. */
unsigned heap_dealloc_count;
+ /* Number of uncaught exceptions. */
+ unsigned uncaught_exceptions;
+
/* Constructor. */
constexpr_global_ctx ()
: constexpr_ops_count (0), cleanups (NULL), modifiable (nullptr),
- heap_dealloc_count (0) {}
+ heap_dealloc_count (0), uncaught_exceptions (0) {}
bool is_outside_lifetime (tree t)
{
@@ -1308,6 +1315,48 @@ struct constexpr_ctx {
mce_value manifestly_const_eval;
};
+/* Predicates for the meaning of *jump_target. */
+
+static bool
+returns (tree *jump_target)
+{
+ return *jump_target && TREE_CODE (*jump_target) == RETURN_EXPR;
+}
+
+static bool
+breaks (tree *jump_target)
+{
+ return (*jump_target
+ && ((TREE_CODE (*jump_target) == LABEL_DECL
+ && LABEL_DECL_BREAK (*jump_target))
+ || TREE_CODE (*jump_target) == BREAK_STMT
+ || TREE_CODE (*jump_target) == EXIT_EXPR));
+}
+
+static bool
+continues (tree *jump_target)
+{
+ return (*jump_target
+ && ((TREE_CODE (*jump_target) == LABEL_DECL
+ && LABEL_DECL_CONTINUE (*jump_target))
+ || TREE_CODE (*jump_target) == CONTINUE_STMT));
+}
+
+static bool
+switches (tree *jump_target)
+{
+ return *jump_target && TREE_CODE (*jump_target) == INTEGER_CST;
+}
+
+static bool
+throws (tree *jump_target)
+{
+ /* void_node is for use in potential_constant_expression_1, otherwise
+ it should an artificial VAR_DECL created by constant evaluation
+ of __cxa_allocate_exception (). */
+ return (*jump_target && (VAR_P (*jump_target) || *jump_target == void_node));
+}
+
/* True if the constexpr relaxations afforded by P2280R4 for unknown
references and objects are in effect. */
@@ -1543,13 +1592,675 @@ enum value_cat {
};
static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
- value_cat, bool *, bool *, tree * = NULL);
+ value_cat, bool *, bool *, tree *);
static tree cxx_eval_bare_aggregate (const constexpr_ctx *, tree,
- value_cat, bool *, bool *);
+ value_cat, bool *, bool *, tree *);
static tree cxx_fold_indirect_ref (const constexpr_ctx *, location_t, tree, tree,
- bool * = NULL);
+ bool *, tree *);
static tree find_heap_var_refs (tree *, int *, void *);
+/* For exception object EXC if it has class type and usable what () method
+ which returns cv char * return the xmalloced string literal which it returns
+ if possible, otherwise return NULL. */
+
+static char *
+exception_what_str (const constexpr_ctx *ctx, tree exc)
+{
+ tree type = strip_array_types (TREE_TYPE (exc));
+ if (!CLASS_TYPE_P (type))
+ return NULL;
+ tree std_exception = lookup_qualified_name (std_node, "exception",
+ LOOK_want::NORMAL, false);
+ if (TREE_CODE (std_exception) != TYPE_DECL)
+ return NULL;
+ if (!CLASS_TYPE_P (TREE_TYPE (std_exception)))
+ return NULL;
+ base_kind b_kind;
+ tree binfo = lookup_base (type, TREE_TYPE (std_exception), ba_check, &b_kind,
+ tf_none);
+ if (binfo == NULL_TREE || binfo == error_mark_node)
+ return NULL;
+ if (type != TREE_TYPE (exc))
+ exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL);
+ tree call
+ = finish_class_member_access_expr (exc, get_identifier ("what"), false,
+ tf_none);
+ if (call == error_mark_node)
+ return NULL;
+ releasing_vec what_args;
+ call = finish_call_expr (call, &what_args, false, false, tf_none);
+ if (call == error_mark_node)
+ return NULL;
+ if (TREE_CODE (TREE_TYPE (call)) != POINTER_TYPE
+ || !INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (call)))
+ || !COMPLETE_TYPE_P (TREE_TYPE (TREE_TYPE (call)))
+ || !tree_int_cst_equal (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (call))),
+ TYPE_SIZE_UNIT (char_type_node))
+ || TYPE_PRECISION (TREE_TYPE (TREE_TYPE (call))) != BITS_PER_UNIT)
+ return NULL;
+ if (!potential_constant_expression (call))
+ return NULL;
+ bool non_constant_p = false, overflow_p = false;
+ tree jmp_target = NULL;
+ tree ptr = cxx_eval_constant_expression (ctx, call, vc_prvalue,
+ &non_constant_p, &overflow_p,
+ &jmp_target);
+ if (!throws (&jmp_target)
+ && !non_constant_p
+ && reduced_constant_expression_p (ptr))
+ if (const char *msg = c_getstr (ptr))
+ return xstrdup (msg);
+ auto_vec <char, 32> v;
+ for (unsigned i = 0; i < INT_MAX; ++i)
+ {
+ tree t = call;
+ if (i)
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr, size_int (i));
+ t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t);
+ non_constant_p = false;
+ overflow_p = false;
+ jmp_target = NULL;
+ tree t2 = cxx_eval_constant_expression (ctx, t, vc_prvalue,
+ &non_constant_p, &overflow_p,
+ &jmp_target);
+ if (throws (&jmp_target)
+ || non_constant_p
+ || !tree_fits_shwi_p (t2))
+ return NULL;
+ char c = tree_to_shwi (t2);
+ v.safe_push (c);
+ if (c == '\0')
+ break;
+ }
+ return xstrdup (v.address ());
+}
+
+/* Diagnose constant expression evaluation encountering call to
+ std::terminate due to exception EXC. */
+
+static void
+diagnose_std_terminate (location_t loc, const constexpr_ctx *ctx, tree exc)
+{
+ tree type = strip_array_types (TREE_TYPE (exc));
+ if (char *str = exception_what_str (ctx, exc))
+ {
+ error_at (loc, "%qs called after throwing an exception of type %qT; "
+ "%<what()%>: %qs", "std::terminate", type, str);
+ free (str);
+ }
+ else
+ {
+ if (type != TREE_TYPE (exc))
+ exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL);
+ bool non_constant_p = false, overflow_p = false;
+ tree jmp_target = NULL;
+ tree val = cxx_eval_constant_expression (ctx, exc, vc_prvalue,
+ &non_constant_p, &overflow_p,
+ &jmp_target);
+ if (!throws (&jmp_target)
+ && !non_constant_p
+ && reduced_constant_expression_p (val))
+ error_at (loc, "%qs called after throwing an exception %qE",
+ "std::terminate", val);
+ else
+ error_at (loc, "%qs called after throwing an exception of type %qT",
+ "std::terminate", type);
+ }
+}
+
+/* Diagnose constant expression evaluation encountering call to
+ uncaught exception EXC. */
+
+static void
+diagnose_uncaught_exception (location_t loc, const constexpr_ctx *ctx, tree exc)
+{
+ tree type = strip_array_types (TREE_TYPE (exc));
+ if (char *str = exception_what_str (ctx, exc))
+ {
+ error_at (loc, "uncaught exception of type %qT; %<what()%>: %qs", type, str);
+ free (str);
+ }
+ else
+ {
+ if (type != TREE_TYPE (exc))
+ exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL);
+ bool non_constant_p = false, overflow_p = false;
+ tree jmp_target = NULL;
+ tree val = cxx_eval_constant_expression (ctx, exc, vc_prvalue,
+ &non_constant_p, &overflow_p,
+ &jmp_target);
+ if (!throws (&jmp_target)
+ && !non_constant_p
+ && reduced_constant_expression_p (val))
+ error_at (loc, "uncaught exception %qE", val);
+ else
+ error_at (loc, "uncaught exception of type %qT", type);
+ }
+}
+
+/* Kinds of __cxa_* functions (and a few other EH related ones) we handle as
+ magic constexpr functions for C++26. */
+
+enum cxa_builtin {
+ CXA_NONE = 0,
+ CXA_ALLOCATE_EXCEPTION = 1,
+ CXA_FREE_EXCEPTION = 2,
+ CXA_THROW = 3,
+ CXA_BEGIN_CATCH = 4,
+ CXA_END_CATCH = 5,
+ CXA_RETHROW = 6,
+ CXA_GET_EXCEPTION_PTR = 7,
+ CXA_BAD_CAST = 8,
+ CXA_BAD_TYPEID = 9,
+ CXA_THROW_BAD_ARRAY_NEW_LENGTH = 10,
+ STD_UNCAUGHT_EXCEPTIONS = 11,
+ STD_CURRENT_EXCEPTION = 12,
+ STD_RETHROW_EXCEPTION = 13,
+ BUILTIN_EH_PTR_ADJUST_REF = 14
+};
+
+/* Return cxa_builtin if FNDECL is a __cxa_* function handled as
+ magic constexpr function for C++26. Return CXA_NONE otherwise. */
+
+static enum cxa_builtin
+cxx_cxa_builtin_fn_p (tree fndecl)
+{
+ if (cxx_dialect < cxx26)
+ return CXA_NONE;
+ if (DECL_LANGUAGE (fndecl) != lang_c)
+ {
+ if (!decl_in_std_namespace_p (fndecl))
+ return CXA_NONE;
+ if (id_equal (DECL_NAME (fndecl), "uncaught_exceptions"))
+ return STD_UNCAUGHT_EXCEPTIONS;
+ if (id_equal (DECL_NAME (fndecl), "current_exception"))
+ return STD_CURRENT_EXCEPTION;
+ if (id_equal (DECL_NAME (fndecl), "rethrow_exception"))
+ return STD_RETHROW_EXCEPTION;
+ return CXA_NONE;
+ }
+ if (!startswith (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "__cxa_"))
+ return CXA_NONE;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_allocate_exception"))
+ return CXA_ALLOCATE_EXCEPTION;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_free_exception"))
+ return CXA_FREE_EXCEPTION;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_throw"))
+ return CXA_THROW;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_begin_catch"))
+ return CXA_BEGIN_CATCH;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_end_catch"))
+ return CXA_END_CATCH;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_rethrow"))
+ return CXA_RETHROW;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_get_exception_ptr"))
+ return CXA_GET_EXCEPTION_PTR;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_bad_cast"))
+ return CXA_BAD_CAST;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_bad_typeid"))
+ return CXA_BAD_TYPEID;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_throw_bad_array_new_length"))
+ return CXA_THROW_BAD_ARRAY_NEW_LENGTH;
+ return CXA_NONE;
+}
+
+/* Helper function for cxx_eval_cxa_builtin_fn.
+ Check if ARG is a valid first argument of __cxa_throw or
+ __cxa_free_exception or __builtin_eh_ptr_adjust_ref. Return NULL_TREE if
+ not, otherwise return the artificial __cxa_allocate_exception allocated
+ VAR_DECL. FREE_EXC is true for __cxa_free_exception, false otherwise. */
+
+static tree
+cxa_check_throw_arg (tree arg, bool free_exc)
+{
+ STRIP_NOPS (arg);
+ if (TREE_CODE (arg) != ADDR_EXPR)
+ return NULL_TREE;
+ arg = TREE_OPERAND (arg, 0);
+ if (!VAR_P (arg)
+ || !DECL_ARTIFICIAL (arg)
+ || ((!free_exc || DECL_NAME (arg) != heap_uninit_identifier)
+ && DECL_NAME (arg) != heap_identifier)
+ || !DECL_LANG_SPECIFIC (arg))
+ return NULL_TREE;
+ return arg;
+}
+
+/* Helper function for cxx_eval_cxa_builtin_fn.
+ "Allocate" on the constexpr heap an exception object of TYPE
+ with REFCOUNT. */
+
+static tree
+cxa_allocate_exception (location_t loc, tree type, tree refcount)
+{
+ tree var = build_decl (loc, VAR_DECL, heap_uninit_identifier, type);
+ DECL_ARTIFICIAL (var) = 1;
+ retrofit_lang_decl (var);
+ DECL_EXCEPTION_REFCOUNT (var) = refcount;
+ return var;
+}
+
+/* Evaluate various __cxa_* calls as magic constexpr builtins for
+ C++26 constexpr exception support (P3068R5). */
+
+static tree
+cxx_eval_cxa_builtin_fn (const constexpr_ctx *ctx, tree call,
+ enum cxa_builtin kind, tree fndecl,
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
+{
+ int nargs = call_expr_nargs (call);
+ location_t loc = cp_expr_loc_or_input_loc (call);
+ tree args[4], arg;
+ if (nargs > 4)
+ {
+ invalid_nargs:
+ if (!ctx->quiet)
+ error_at (loc, "call to %qD function with incorrect"
+ "number of arguments", fndecl);
+ *non_constant_p = true;
+ return call;
+ }
+ if ((kind == CXA_BEGIN_CATCH || kind == CXA_GET_EXCEPTION_PTR)
+ && nargs == 1
+ && (arg = CALL_EXPR_ARG (call, 0))
+ && TREE_CODE (arg) == CALL_EXPR
+ && call_expr_nargs (arg) == 1
+ && integer_zerop (CALL_EXPR_ARG (arg, 0)))
+ if (tree fun = get_function_named_in_call (arg))
+ if (fndecl_built_in_p (fun, BUILT_IN_EH_POINTER))
+ {
+ if (ctx->global->caught_exceptions.length () < 2)
+ {
+ no_caught_exceptions:
+ if (!ctx->quiet)
+ error_at (loc, "%qD called with no caught exceptions pending",
+ fndecl);
+ *non_constant_p = true;
+ return call;
+ }
+ /* Both __cxa_get_exception_ptr (__builtin_eh_pointer (0))
+ and __cxa_begin_catch (__builtin_eh_pointer (0)) calls expect
+ ctx->global->caught_exceptions vector to end with
+ __cxa_allocate_exception created artificial VAR_DECL (the
+ exception object) followed by handler type, pushed by TRY_BLOCK
+ evaluation. The only difference between the functions is that
+ __cxa_begin_catch pops the handler type from the vector and keeps
+ the VAR_DECL last and decreases uncaught_exceptions. The
+ VAR_DECL after __cxa_begin_catch serves as the current exception
+ and is then popped in __cxa_end_catch evaluation. */
+ tree handler_type = ctx->global->caught_exceptions.last ();
+ if (handler_type && VAR_P (handler_type))
+ goto no_caught_exceptions;
+ unsigned idx = ctx->global->caught_exceptions.length () - 2;
+ arg = ctx->global->caught_exceptions[idx];
+ gcc_assert (VAR_P (arg));
+ if (kind == CXA_BEGIN_CATCH)
+ {
+ ctx->global->caught_exceptions.pop ();
+ --ctx->global->uncaught_exceptions;
+ }
+ if (handler_type == NULL_TREE)
+ /* __cxa_begin_catch used for catch (...). Just return
+ void. */
+ return void_node;
+ else if (POINTER_TYPE_P (handler_type))
+ {
+ /* __cxa_begin_catch used for catch of a pointer. */
+ if (TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE)
+ arg = build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (arg)), arg,
+ size_zero_node, NULL_TREE, NULL_TREE);
+ arg = cp_convert (handler_type, arg,
+ ctx->quiet ? tf_none : tf_warning_or_error);
+ if (arg == error_mark_node)
+ {
+ *non_constant_p = true;
+ return call;
+ }
+ }
+ else
+ {
+ /* __cxa_begin_catch used for catch of a non-pointer type. */
+ tree exc_type = strip_array_types (TREE_TYPE (arg));
+ tree exc_ptr_type = build_pointer_type (exc_type);
+ arg = build_fold_addr_expr_with_type (arg, exc_ptr_type);
+ if (CLASS_TYPE_P (handler_type))
+ {
+ tree ptr_type = build_pointer_type (handler_type);
+ arg = cp_convert (ptr_type, arg,
+ ctx->quiet ? tf_none
+ : tf_warning_or_error);
+ if (arg == error_mark_node)
+ {
+ *non_constant_p = true;
+ return call;
+ }
+ }
+ }
+ return cxx_eval_constant_expression (ctx, arg, vc_prvalue,
+ non_constant_p, overflow_p,
+ jump_target);
+ }
+ for (int i = 0; i < nargs; ++i)
+ {
+ args[i] = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (call, i),
+ vc_prvalue, non_constant_p,
+ overflow_p, jump_target);
+ if (*non_constant_p)
+ return call;
+ if (*jump_target)
+ return NULL_TREE;
+ }
+ switch (kind)
+ {
+ case CXA_ALLOCATE_EXCEPTION:
+ if (nargs != 1)
+ goto invalid_nargs;
+ if (!tree_fits_uhwi_p (args[0]))
+ {
+ if (!ctx->quiet)
+ error_at (loc, "cannot allocate exception: size not constant");
+ *non_constant_p = true;
+ return call;
+ }
+ else
+ {
+ tree type = build_array_type_nelts (char_type_node,
+ tree_to_uhwi (args[0]));
+ tree var = cxa_allocate_exception (loc, type, size_zero_node);
+ ctx->global->heap_vars.safe_push (var);
+ ctx->global->put_value (var, NULL_TREE);
+ return fold_convert (ptr_type_node, build_address (var));
+ }
+ case CXA_FREE_EXCEPTION:
+ if (nargs != 1)
+ goto invalid_nargs;
+ arg = cxa_check_throw_arg (args[0], true);
+ if (arg == NULL_TREE)
+ {
+ invalid_ptr:
+ if (!ctx->quiet)
+ error_at (loc, "first argument to %qD function not result of "
+ "%<__cxa_allocate_exception%>", fndecl);
+ *non_constant_p = true;
+ return call;
+ }
+ DECL_NAME (arg) = heap_deleted_identifier;
+ ctx->global->destroy_value (arg);
+ ctx->global->heap_dealloc_count++;
+ return void_node;
+ case CXA_THROW:
+ if (nargs != 3)
+ goto invalid_nargs;
+ arg = cxa_check_throw_arg (args[0], false);
+ if (arg == NULL_TREE)
+ goto invalid_ptr;
+ DECL_EXCEPTION_REFCOUNT (arg)
+ = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg),
+ size_one_node);
+ ++ctx->global->uncaught_exceptions;
+ *jump_target = arg;
+ return void_node;
+ case CXA_BEGIN_CATCH:
+ case CXA_GET_EXCEPTION_PTR:
+ goto invalid_nargs;
+ case CXA_END_CATCH:
+ if (nargs != 0)
+ goto invalid_nargs;
+ if (ctx->global->caught_exceptions.is_empty ())
+ {
+ no_active_exc:
+ if (!ctx->quiet)
+ error_at (loc, "%qD called with no caught exceptions active",
+ fndecl);
+ *non_constant_p = true;
+ return call;
+ }
+ else
+ {
+ arg = ctx->global->caught_exceptions.pop ();
+ if (arg == NULL_TREE || !VAR_P (arg))
+ goto no_active_exc;
+ free_except:
+ DECL_EXCEPTION_REFCOUNT (arg)
+ = size_binop (MINUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg),
+ size_one_node);
+ if (integer_zerop (DECL_EXCEPTION_REFCOUNT (arg)))
+ {
+ if (type_build_dtor_call (TREE_TYPE (arg)))
+ {
+ tree cleanup
+ = cxx_maybe_build_cleanup (arg, (ctx->quiet ? tf_none
+ : tf_warning_or_error));
+ if (cleanup == error_mark_node)
+ *non_constant_p = true;
+ tree jmp_target = NULL_TREE;
+ cxx_eval_constant_expression (ctx, cleanup, vc_discard,
+ non_constant_p, overflow_p,
+ &jmp_target);
+ if (throws (&jmp_target))
+ *jump_target = jmp_target;
+ }
+ DECL_NAME (arg) = heap_deleted_identifier;
+ ctx->global->destroy_value (arg);
+ ctx->global->heap_dealloc_count++;
+ }
+ }
+ return void_node;
+ case CXA_RETHROW:
+ if (nargs != 0)
+ goto invalid_nargs;
+ unsigned idx;
+ FOR_EACH_VEC_ELT_REVERSE (ctx->global->caught_exceptions, idx, arg)
+ if (arg == NULL_TREE || !VAR_P (arg))
+ --idx;
+ else
+ break;
+ if (arg == NULL_TREE)
+ {
+ if (!ctx->quiet)
+ error_at (loc, "%qD called with no caught exceptions active",
+ fndecl);
+ *non_constant_p = true;
+ return call;
+ }
+ DECL_EXCEPTION_REFCOUNT (arg)
+ = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), size_one_node);
+ ++ctx->global->uncaught_exceptions;
+ *jump_target = arg;
+ return void_node;
+ case CXA_BAD_CAST:
+ case CXA_BAD_TYPEID:
+ case CXA_THROW_BAD_ARRAY_NEW_LENGTH:
+ if (nargs != 0)
+ goto invalid_nargs;
+ else
+ {
+ tree name;
+ switch (kind)
+ {
+ case CXA_BAD_CAST:
+ name = get_identifier ("bad_cast");
+ break;
+ case CXA_BAD_TYPEID:
+ name = get_identifier ("bad_typeid");
+ break;
+ case CXA_THROW_BAD_ARRAY_NEW_LENGTH:
+ name = get_identifier ("bad_array_new_length");
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ tree decl = lookup_qualified_name (std_node, name);
+ if (TREE_CODE (decl) != TYPE_DECL
+ || !CLASS_TYPE_P (TREE_TYPE (decl))
+ || !type_build_ctor_call (TREE_TYPE (decl)))
+ {
+ if (!ctx->quiet)
+ error_at (loc, "%qD called without %<std::%D%> being defined",
+ fndecl, name);
+ *non_constant_p = true;
+ return call;
+ }
+ tree type = TREE_TYPE (decl);
+ tree var = cxa_allocate_exception (loc, type, size_one_node);
+ tree ctor
+ = build_special_member_call (var, complete_ctor_identifier,
+ NULL, type, LOOKUP_NORMAL,
+ ctx->quiet ? tf_none
+ : tf_warning_or_error);
+ if (ctor == error_mark_node)
+ {
+ *non_constant_p = true;
+ return call;
+ }
+ ctx->global->heap_vars.safe_push (var);
+ if (TREE_CONSTANT (ctor))
+ ctx->global->put_value (var, ctor);
+ else
+ {
+ ctx->global->put_value (var, NULL_TREE);
+ cxx_eval_constant_expression (ctx, ctor, vc_discard,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*non_constant_p)
+ return call;
+ if (throws (jump_target))
+ return NULL_TREE;
+ }
+ ++ctx->global->uncaught_exceptions;
+ *jump_target = var;
+ }
+ return void_node;
+ case STD_UNCAUGHT_EXCEPTIONS:
+ if (nargs != 0)
+ goto invalid_nargs;
+ /* Similarly to __builtin_is_constant_evaluated (), we don't
+ want to give a definite answer during mce_unknown evaluation,
+ because that might prevent evaluation later on when some
+ exceptions might be uncaught. But unlike that, we don't
+ want to constant fold it even during cp_fold, because at runtime
+ std::uncaught_exceptions () might still be non-zero. */
+ if (ctx->manifestly_const_eval != mce_true)
+ {
+ *non_constant_p = true;
+ return call;
+ }
+ return build_int_cst (integer_type_node,
+ ctx->global->uncaught_exceptions);
+ case STD_CURRENT_EXCEPTION:
+ if (nargs != 0)
+ goto invalid_nargs;
+ else
+ {
+ tree name = get_identifier ("exception_ptr");
+ tree decl = lookup_qualified_name (std_node, name);
+ tree fld;
+ if (TREE_CODE (decl) != TYPE_DECL
+ || !CLASS_TYPE_P (TREE_TYPE (decl))
+ || !COMPLETE_TYPE_P (TREE_TYPE (decl))
+ || !(fld = next_aggregate_field (TYPE_FIELDS (TREE_TYPE (decl))))
+ || DECL_ARTIFICIAL (fld)
+ || TREE_CODE (TREE_TYPE (fld)) != POINTER_TYPE
+ || next_aggregate_field (DECL_CHAIN (fld))
+ || !tree_int_cst_equal (TYPE_SIZE (TREE_TYPE (decl)),
+ TYPE_SIZE (TREE_TYPE (fld))))
+ {
+ if (!ctx->quiet)
+ error_at (loc, "%qD called without supportable %qs",
+ fndecl, "std::exception_ptr");
+ *non_constant_p = true;
+ return call;
+ }
+ FOR_EACH_VEC_ELT_REVERSE (ctx->global->caught_exceptions, idx, arg)
+ if (arg == NULL_TREE || !VAR_P (arg))
+ --idx;
+ else
+ break;
+ /* Similarly to __builtin_is_constant_evaluated (), we don't
+ want to give a definite answer during mce_unknown evaluation,
+ because that might prevent evaluation later on when some
+ exceptions might be current. But unlike that, we don't
+ want to constant fold it even during cp_fold, because at runtime
+ std::current_exception () might still be non-null. */
+ if (ctx->manifestly_const_eval != mce_true && arg == NULL_TREE)
+ {
+ *non_constant_p = true;
+ return call;
+ }
+ if (arg == NULL_TREE)
+ arg = build_zero_cst (TREE_TYPE (fld));
+ else
+ {
+ DECL_EXCEPTION_REFCOUNT (arg)
+ = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg),
+ size_one_node);
+ arg = fold_convert (ptr_type_node, build_address (arg));
+ }
+ return build_constructor_single (TREE_TYPE (decl), fld, arg);
+ }
+ case STD_RETHROW_EXCEPTION:
+ if (nargs != 1)
+ goto invalid_nargs;
+ if (TYPE_REF_P (TREE_TYPE (args[0])))
+ {
+ arg = args[0];
+ STRIP_NOPS (arg);
+ if (TREE_CODE (arg) == ADDR_EXPR)
+ {
+ args[0]
+ = cxx_eval_constant_expression (ctx, TREE_OPERAND (arg, 0),
+ vc_prvalue, non_constant_p,
+ overflow_p, jump_target);
+ if (*non_constant_p)
+ return call;
+ if (*jump_target)
+ return NULL_TREE;
+ }
+ }
+ if (TREE_CODE (args[0]) != CONSTRUCTOR
+ || CONSTRUCTOR_NELTS (args[0]) != 1)
+ {
+ invalid_std_rethrow:
+ if (!ctx->quiet)
+ error_at (loc, "%qD called with unexpected %qs argument",
+ fndecl, "std::exception_ptr");
+ *non_constant_p = true;
+ return void_node;
+ }
+ arg = cxa_check_throw_arg (CONSTRUCTOR_ELT (args[0], 0)->value, false);
+ if (arg == NULL_TREE)
+ goto invalid_std_rethrow;
+ DECL_EXCEPTION_REFCOUNT (arg)
+ = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), size_one_node);
+ ++ctx->global->uncaught_exceptions;
+ *jump_target = arg;
+ return void_node;
+ case BUILTIN_EH_PTR_ADJUST_REF:
+ if (nargs != 2)
+ goto invalid_nargs;
+ arg = cxa_check_throw_arg (args[0], false);
+ if (arg == NULL_TREE)
+ goto invalid_ptr;
+ if (integer_onep (args[1]))
+ DECL_EXCEPTION_REFCOUNT (arg)
+ = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg),
+ size_one_node);
+ else if (integer_minus_onep (args[1]))
+ goto free_except;
+ else
+ {
+ if (!ctx->quiet)
+ error_at (loc, "%qD called with second argument "
+ "other than 1 or -1", fndecl);
+ *non_constant_p = true;
+ }
+ return void_node;
+ default:
+ gcc_unreachable ();
+ }
+}
+
/* Attempt to evaluate T which represents a call to a builtin function.
We assume here that all builtin functions evaluate to scalar types
represented by _CST nodes. */
@@ -1557,7 +2268,8 @@ static tree find_heap_var_refs (tree *,
static tree
cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
const int nargs = call_expr_nargs (t);
tree *args = (tree *) alloca (nargs * sizeof (tree));
@@ -1603,6 +2315,12 @@ cxx_eval_builtin_function_call (const co
return fold_builtin_source_location (t);
}
+ if (fndecl_built_in_p (fun, CP_BUILT_IN_EH_PTR_ADJUST_REF,
+ BUILT_IN_FRONTEND))
+ return cxx_eval_cxa_builtin_fn (ctx, t, BUILTIN_EH_PTR_ADJUST_REF,
+ fun, non_constant_p, overflow_p,
+ jump_target);
+
int strops = 0;
int strret = 0;
if (fndecl_built_in_p (fun, BUILT_IN_NORMAL))
@@ -1677,8 +2395,14 @@ cxx_eval_builtin_function_call (const co
|| potential_constant_expression (arg))
{
bool dummy1 = false, dummy2 = false;
+ tree jmp_target = NULL_TREE;
arg = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
- &dummy1, &dummy2);
+ &dummy1, &dummy2, &jmp_target);
+ if (jmp_target)
+ {
+ *jump_target = jmp_target;
+ return NULL_TREE;
+ }
}
if (bi_const_p)
@@ -1767,7 +2491,8 @@ cxx_eval_builtin_function_call (const co
}
return cxx_eval_constant_expression (&new_ctx, new_call, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
}
/* TEMP is the constant value of a temporary object of type TYPE. Adjust
@@ -1882,7 +2607,8 @@ addr_of_non_const_var (tree *tp, int *wa
static tree
cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun,
tree orig_fun, bool *non_constant_p,
- bool *overflow_p, bool *non_constant_args)
+ bool *overflow_p, bool *non_constant_args,
+ tree *jump_target)
{
int nargs = call_expr_nargs (t);
tree parms = DECL_ARGUMENTS (fun);
@@ -1958,14 +2684,16 @@ cxx_bind_parameters_in_call (const const
/* Undo convert_for_arg_passing work here. */
x = convert_from_reference (x);
arg = cxx_eval_constant_expression (ctx, x, vc_glvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
}
else
/* Normally we would strip a TARGET_EXPR in an initialization context
such as this, but here we do the elision differently: we keep the
TARGET_EXPR, and use its CONSTRUCTOR as the value of the parm. */
arg = cxx_eval_constant_expression (ctx, x, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
/* Check we aren't dereferencing a null pointer when calling a non-static
member function, which is undefined behaviour. */
if (i == 0 && DECL_OBJECT_MEMBER_FUNCTION_P (fun)
@@ -1983,6 +2711,8 @@ cxx_bind_parameters_in_call (const const
/* Don't VERIFY_CONSTANT here. */
if (*non_constant_p && ctx->quiet)
break;
+ if (*jump_target)
+ break;
/* Just discard ellipsis args after checking their constantitude. */
if (!parms)
continue;
@@ -2094,9 +2824,10 @@ fold_operand (tree e, const constexpr_ct
if (ctx)
{
bool new_non_constant_p = false, new_overflow_p = false;
+ tree jmp_target = NULL_TREE;
e = cxx_eval_constant_expression (ctx, e, vc_prvalue,
&new_non_constant_p,
- &new_overflow_p);
+ &new_overflow_p, &jmp_target);
}
else
e = fold_non_dependent_expr (e, tf_none, /*manifestly_const_eval=*/true);
@@ -2183,7 +2914,7 @@ cxx_eval_assert (const constexpr_ctx *ct
if (*non_constant_p)
return true;
- tree eval;
+ tree eval, jmp_target = NULL_TREE;
if (!evaluated)
{
if (!potential_rvalue_constant_expression (arg))
@@ -2196,12 +2927,15 @@ cxx_eval_assert (const constexpr_ctx *ct
modifiable_tracker ms (new_ctx.global);
eval = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
&new_non_constant_p,
- &new_overflow_p);
+ &new_overflow_p, &jmp_target);
}
else
eval = cxx_eval_constant_expression (ctx, arg, vc_prvalue,
non_constant_p,
- overflow_p);
+ overflow_p, &jmp_target);
+ if (jmp_target)
+ return true;
+
if (!*non_constant_p && integer_zerop (eval))
{
if (!ctx->quiet)
@@ -2233,7 +2967,8 @@ cxx_eval_assert (const constexpr_ctx *ct
static tree
cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
enum tree_code opcode = ERROR_MARK;
@@ -2266,13 +3001,15 @@ cxx_eval_internal_function (const conste
case IFN_LAUNDER:
return cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
vc_prvalue, non_constant_p,
- overflow_p);
+ overflow_p, jump_target);
case IFN_VEC_CONVERT:
{
tree arg = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
vc_prvalue, non_constant_p,
- overflow_p);
+ overflow_p, jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (TREE_CODE (arg) == VECTOR_CST)
if (tree r = fold_const_call (CFN_VEC_CONVERT, TREE_TYPE (t), arg))
return r;
@@ -2290,10 +3027,13 @@ cxx_eval_internal_function (const conste
/* Evaluate constant arguments using OPCODE and return a complex
number containing the result and the overflow bit. */
tree arg0 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
tree arg1 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 1), lval,
- non_constant_p, overflow_p);
-
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
{
location_t loc = cp_expr_loc_or_input_loc (t);
@@ -2566,7 +3306,8 @@ get_component_with_type (tree path, tree
static tree
cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
/* T will be something like
__dynamic_cast ((B*) b, &_ZTI1B, &_ZTI1D, 8)
@@ -2585,19 +3326,26 @@ cxx_eval_dynamic_cast_fn (const constexp
/* TYPE can only be either T* or T&. We can't know which of these it
is by looking at TYPE, but OBJ will be "(T*) x" in the first case,
- and something like "(T*)(T&)(T*) x" in the second case. */
- bool reference_p = false;
+ and something like "(T*)(T&)(T*) x" in the second case.
+ This is true for the reference cases in C++ < 26 or when exceptions
+ aren't enabled, in that case we should diagnose errors. For C++26
+ with exceptions we should silently evaluate to null pointer and
+ let the callers call __cxa_bad_cast () later to throw an exception. */
+ bool fail_for_non_constant_p = false;
while (CONVERT_EXPR_P (obj) || TREE_CODE (obj) == SAVE_EXPR)
{
- reference_p |= TYPE_REF_P (TREE_TYPE (obj));
+ if (cxx_dialect < cxx26 || !flag_exceptions)
+ fail_for_non_constant_p |= TYPE_REF_P (TREE_TYPE (obj));
obj = TREE_OPERAND (obj, 0);
}
/* Evaluate the object so that we know its dynamic type. */
obj = cxx_eval_constant_expression (ctx, obj, vc_prvalue, non_constant_p,
- overflow_p);
+ overflow_p, jump_target);
if (*non_constant_p)
return call;
+ if (*jump_target)
+ return NULL_TREE;
/* For dynamic_cast from classes with virtual bases we can get something
like (virt_base *)(&d + 16) as OBJ. Try to convert that into
@@ -2609,7 +3357,7 @@ cxx_eval_dynamic_cast_fn (const constexp
if (TREE_CODE (objo) == POINTER_PLUS_EXPR)
{
objo = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (TREE_TYPE (obj)),
- obj);
+ obj, NULL, jump_target);
if (objo)
obj = build_fold_addr_expr (objo);
}
@@ -2625,7 +3373,7 @@ cxx_eval_dynamic_cast_fn (const constexp
? TREE_OPERAND (obj, 1) : obj))
if (TREE_CODE (t) != FIELD_DECL || !DECL_FIELD_IS_BASE (t))
{
- if (reference_p)
+ if (fail_for_non_constant_p)
{
if (!ctx->quiet)
{
@@ -2647,9 +3395,12 @@ cxx_eval_dynamic_cast_fn (const constexp
constructor or destructor's class. */
tree vtable = build_vfield_ref (obj, objtype);
vtable = cxx_eval_constant_expression (ctx, vtable, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
if (*non_constant_p)
return call;
+ if (*jump_target)
+ return NULL_TREE;
/* With -fsanitize=vptr, we initialize all vtable pointers to null,
so it's possible that we got a null pointer now. */
if (integer_zerop (vtable))
@@ -2681,7 +3432,7 @@ cxx_eval_dynamic_cast_fn (const constexp
/* If not accessible, give an error. */
if (t == error_mark_node)
{
- if (reference_p)
+ if (fail_for_non_constant_p)
{
if (!ctx->quiet)
{
@@ -2714,7 +3465,7 @@ cxx_eval_dynamic_cast_fn (const constexp
obj = get_component_with_type (obj, mdtype, NULL_TREE);
if (obj == error_mark_node)
{
- if (reference_p)
+ if (fail_for_non_constant_p)
{
if (!ctx->quiet)
{
@@ -2736,7 +3487,7 @@ cxx_eval_dynamic_cast_fn (const constexp
tree binfo = lookup_base (mdtype, type, ba_check, &b_kind, tf_none);
if (!binfo || binfo == error_mark_node)
{
- if (reference_p)
+ if (fail_for_non_constant_p)
{
if (!ctx->quiet)
{
@@ -2832,7 +3583,7 @@ replace_decl (tree *tp, tree decl, tree
static tree
cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p, tree *jump_target)
{
tree function = THUNK_TARGET (thunk_fndecl);
@@ -2875,7 +3626,8 @@ cxx_eval_thunk_call (const constexpr_ctx
new_call, offset);
return cxx_eval_constant_expression (ctx, new_call, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
}
/* If OBJECT is of const class type, evaluate it to a CONSTRUCTOR and set
@@ -2885,7 +3637,7 @@ cxx_eval_thunk_call (const constexpr_ctx
static void
cxx_set_object_constness (const constexpr_ctx *ctx, tree object,
bool readonly_p, bool *non_constant_p,
- bool *overflow_p)
+ bool *overflow_p, tree *jump_target)
{
if (CLASS_TYPE_P (TREE_TYPE (object))
&& CP_TYPE_CONST_P (TREE_TYPE (object)))
@@ -2893,8 +3645,11 @@ cxx_set_object_constness (const constexp
/* Subobjects might not be stored in ctx->global->values but we
can get its CONSTRUCTOR by evaluating *this. */
tree e = cxx_eval_constant_expression (ctx, object, vc_prvalue,
- non_constant_p, overflow_p);
- if (TREE_CODE (e) == CONSTRUCTOR && !*non_constant_p)
+ non_constant_p, overflow_p,
+ jump_target);
+ if (!*non_constant_p
+ && !throws (jump_target)
+ && TREE_CODE (e) == CONSTRUCTOR)
TREE_READONLY (e) = readonly_p;
}
}
@@ -2906,20 +3661,25 @@ cxx_set_object_constness (const constexp
static tree
cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
location_t loc = cp_expr_loc_or_input_loc (t);
tree fun = get_function_named_in_call (t);
if (fun == NULL_TREE)
return cxx_eval_internal_function (ctx, t, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
if (TREE_CODE (fun) != FUNCTION_DECL)
{
/* Might be a constexpr function pointer. */
fun = cxx_eval_constant_expression (ctx, fun, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
STRIP_NOPS (fun);
if (TREE_CODE (fun) == ADDR_EXPR)
fun = TREE_OPERAND (fun, 0);
@@ -2971,9 +3731,11 @@ cxx_eval_call_expression (const constexp
if (fndecl_built_in_p (fun))
return cxx_eval_builtin_function_call (ctx, t, fun,
- lval, non_constant_p, overflow_p);
+ lval, non_constant_p, overflow_p,
+ jump_target);
if (DECL_THUNK_P (fun))
- return cxx_eval_thunk_call (ctx, t, fun, lval, non_constant_p, overflow_p);
+ return cxx_eval_thunk_call (ctx, t, fun, lval, non_constant_p, overflow_p,
+ jump_target);
if (!maybe_constexpr_fn (fun))
{
if (TREE_CODE (t) == CALL_EXPR
@@ -2988,7 +3750,10 @@ cxx_eval_call_expression (const constexp
{
tree arg = CALL_EXPR_ARG (t, i);
arg = cxx_eval_constant_expression (ctx, arg, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
/* Deleting a non-constant pointer has a better error message
below. */
if (new_op_p || i != 0)
@@ -3103,7 +3868,10 @@ cxx_eval_call_expression (const constexp
{
tree arg = CALL_EXPR_ARG (t, i);
arg = cxx_eval_constant_expression (ctx, arg, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (i == 1)
arg1 = arg;
else
@@ -3113,7 +3881,12 @@ cxx_eval_call_expression (const constexp
return arg1;
}
else if (cxx_dynamic_cast_fn_p (fun))
- return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p);
+ return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p,
+ jump_target);
+ else if (enum cxa_builtin kind = cxx_cxa_builtin_fn_p (fun))
+ return cxx_eval_cxa_builtin_fn (ctx, t, kind, fun,
+ non_constant_p, overflow_p,
+ jump_target);
if (!ctx->quiet)
{
@@ -3158,7 +3931,8 @@ cxx_eval_call_expression (const constexp
constexpr_call new_call;
new_call.bindings
= cxx_bind_parameters_in_call (ctx, t, fun, orig_fun, non_constant_p,
- overflow_p, &non_constant_args);
+ overflow_p, &non_constant_args,
+ jump_target);
/* We build up the bindings list before we know whether we already have this
call cached. If we don't end up saving these bindings, ggc_free them when
@@ -3172,6 +3946,8 @@ cxx_eval_call_expression (const constexp
void preserve () { bindings = NULL; }
} fb (new_call.bindings);
+ if (*jump_target)
+ return NULL_TREE;
if (*non_constant_p)
return t;
@@ -3246,7 +4022,9 @@ cxx_eval_call_expression (const constexp
new_obj = TREE_VEC_ELT (new_call.bindings, 0);
bool empty_base = false;
new_obj = cxx_fold_indirect_ref (ctx, loc, DECL_CONTEXT (fun), new_obj,
- &empty_base);
+ &empty_base, jump_target);
+ if (*jump_target)
+ return NULL_TREE;
/* If we're initializing an empty class, don't set constness, because
cxx_fold_indirect_ref will return the wrong object to set constness
of. */
@@ -3395,7 +4173,7 @@ cxx_eval_call_expression (const constexp
semantics are no longer in effect; see [class.dtor]p5. */
if (new_obj && DECL_DESTRUCTOR_P (fun))
cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/false,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, jump_target);
/* If this is a constructor, we are beginning the lifetime of the
object we are initializing. */
@@ -3409,16 +4187,25 @@ cxx_eval_call_expression (const constexp
build_constructor (TREE_TYPE (new_obj),
NULL));
cxx_eval_constant_expression (ctx, activate,
- lval, non_constant_p, overflow_p);
+ lval, non_constant_p, overflow_p,
+ jump_target);
ggc_free (activate);
+ if (*jump_target)
+ return NULL_TREE;
}
- tree jump_target = NULL_TREE;
+ tree jmp_target = NULL_TREE;
cxx_eval_constant_expression (&call_ctx, body,
vc_discard, non_constant_p, overflow_p,
- &jump_target);
+ &jmp_target);
- if (DECL_CONSTRUCTOR_P (fun))
+ if (!*non_constant_p && throws (&jmp_target))
+ {
+ result = NULL_TREE;
+ cacheable = false;
+ *jump_target = jmp_target;
+ }
+ else if (DECL_CONSTRUCTOR_P (fun))
/* This can be null for a subobject constructor call, in
which case what we care about is the initialization
side-effects rather than the value. We could get at the
@@ -3446,7 +4233,7 @@ cxx_eval_call_expression (const constexp
marking the CONSTRUCTOR TREE_READONLY. */
if (new_obj && DECL_CONSTRUCTOR_P (fun))
cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/true,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, jump_target);
/* Remove the parms/result from the values map. */
destroy_value_checked (ctx, res, non_constant_p);
@@ -3506,7 +4293,13 @@ cxx_eval_call_expression (const constexp
cacheable = false;
result = cxx_eval_constant_expression (ctx, result, lval,
non_constant_p,
- overflow_p);
+ overflow_p,
+ jump_target);
+ if (*jump_target)
+ {
+ cacheable = false;
+ result = NULL_TREE;
+ }
}
}
@@ -3864,12 +4657,16 @@ cxx_eval_check_shift_p (location_t loc,
static tree
cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t,
bool /*lval*/,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
tree r;
tree orig_arg = TREE_OPERAND (t, 0);
tree arg = cxx_eval_constant_expression (ctx, orig_arg, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
VERIFY_CONSTANT (arg);
location_t loc = EXPR_LOCATION (t);
enum tree_code code = TREE_CODE (t);
@@ -3893,7 +4690,7 @@ cxx_eval_unary_expression (const constex
static tree
cxx_fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t,
tree lhs, tree rhs, bool *non_constant_p,
- bool *overflow_p)
+ bool *overflow_p, tree *jump_target)
{
STRIP_NOPS (lhs);
if (TREE_CODE (lhs) != ADDR_EXPR)
@@ -3915,9 +4712,12 @@ cxx_fold_pointer_plus_expression (const
t = fold_convert_loc (loc, ssizetype, TREE_OPERAND (lhs, 1));
tree nelts = array_type_nelts_top (TREE_TYPE (TREE_OPERAND (lhs, 0)));
nelts = cxx_eval_constant_expression (ctx, nelts, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
if (*non_constant_p)
return NULL_TREE;
+ if (*jump_target)
+ return NULL_TREE;
/* Don't fold an out-of-bound access. */
if (!tree_int_cst_le (t, nelts))
return NULL_TREE;
@@ -3937,7 +4737,8 @@ cxx_fold_pointer_plus_expression (const
t = cp_build_addr_expr (t, tf_warning_or_error);
t = cp_fold_convert (orig_type, t);
return cxx_eval_constant_expression (ctx, t, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
}
return NULL_TREE;
@@ -3981,22 +4782,29 @@ cxx_maybe_fold_addr_pointer_plus (tree t
static tree
cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
tree r = NULL_TREE;
tree orig_lhs = TREE_OPERAND (t, 0);
tree orig_rhs = TREE_OPERAND (t, 1);
tree lhs, rhs;
lhs = cxx_eval_constant_expression (ctx, orig_lhs, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
/* Don't VERIFY_CONSTANT here, it's unnecessary and will break pointer
subtraction. */
if (*non_constant_p)
return t;
+ if (*jump_target)
+ return NULL_TREE;
rhs = cxx_eval_constant_expression (ctx, orig_rhs, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
if (*non_constant_p)
return t;
+ if (*jump_target)
+ return NULL_TREE;
location_t loc = EXPR_LOCATION (t);
enum tree_code code = TREE_CODE (t);
@@ -4052,13 +4860,17 @@ cxx_eval_binary_expression (const conste
return t;
}
else if (code == POINTER_PLUS_EXPR)
- r = cxx_fold_pointer_plus_expression (ctx, t, lhs, rhs, non_constant_p,
- overflow_p);
+ {
+ r = cxx_fold_pointer_plus_expression (ctx, t, lhs, rhs, non_constant_p,
+ overflow_p, jump_target);
+ if (*jump_target)
+ return NULL_TREE;
+ }
else if (code == SPACESHIP_EXPR)
{
r = genericize_spaceship (loc, type, lhs, rhs);
return cxx_eval_constant_expression (ctx, r, lval, non_constant_p,
- overflow_p);
+ overflow_p, jump_target);
}
if (r == NULL_TREE)
@@ -4117,7 +4929,10 @@ cxx_eval_conditional_expression (const c
{
tree val = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
VERIFY_CONSTANT (val);
if (TREE_CODE (t) == IF_STMT && IF_STMT_CONSTEVAL_P (t))
{
@@ -4178,19 +4993,29 @@ cxx_eval_conditional_expression (const c
static tree
cxx_eval_vector_conditional_expression (const constexpr_ctx *ctx, tree t,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
tree arg1 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
VERIFY_CONSTANT (arg1);
tree arg2 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
VERIFY_CONSTANT (arg2);
tree arg3 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
VERIFY_CONSTANT (arg3);
location_t loc = EXPR_LOCATION (t);
tree type = TREE_TYPE (t);
@@ -4578,7 +5403,8 @@ diag_array_subscript (location_t loc, co
static tree
get_array_or_vector_nelts (const constexpr_ctx *ctx, tree type,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
tree nelts;
if (TREE_CODE (type) == ARRAY_TYPE)
@@ -4595,7 +5421,8 @@ get_array_or_vector_nelts (const constex
/* For VLAs, the number of elements won't be an integer constant. */
nelts = cxx_eval_constant_expression (ctx, nelts, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
return nelts;
}
@@ -4626,13 +5453,17 @@ extract_string_elt (tree string, unsigne
static tree
eval_and_check_array_index (const constexpr_ctx *ctx,
tree t, bool allow_one_past,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
location_t loc = cp_expr_loc_or_input_loc (t);
tree ary = TREE_OPERAND (t, 0);
t = TREE_OPERAND (t, 1);
tree index = cxx_eval_constant_expression (ctx, t, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
VERIFY_CONSTANT (index);
if (!tree_fits_shwi_p (index)
@@ -4644,7 +5475,9 @@ eval_and_check_array_index (const conste
}
tree nelts = get_array_or_vector_nelts (ctx, TREE_TYPE (ary), non_constant_p,
- overflow_p);
+ overflow_p, jump_target);
+ if (*jump_target)
+ return NULL_TREE;
VERIFY_CONSTANT (nelts);
if (allow_one_past
? !tree_int_cst_le (index, nelts)
@@ -4664,14 +5497,18 @@ eval_and_check_array_index (const conste
static tree
cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
tree oldary = TREE_OPERAND (t, 0);
tree ary = cxx_eval_constant_expression (ctx, oldary,
lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
if (*non_constant_p)
return t;
+ if (*jump_target)
+ return NULL_TREE;
if (!lval
&& TREE_CODE (ary) == VIEW_CONVERT_EXPR
&& VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (ary, 0)))
@@ -4681,9 +5518,12 @@ cxx_eval_array_reference (const constexp
tree oldidx = TREE_OPERAND (t, 1);
tree index = eval_and_check_array_index (ctx, t, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
if (*non_constant_p)
return t;
+ if (*jump_target)
+ return NULL_TREE;
if (lval && ary == oldary && index == oldidx)
return t;
@@ -4801,7 +5641,7 @@ cxx_eval_array_reference (const constexp
ctx = &new_ctx;
}
t = cxx_eval_constant_expression (ctx, val, lval, non_constant_p,
- overflow_p);
+ overflow_p, jump_target);
if (new_ctor && t != ctx->ctor)
free_constructor (ctx->ctor);
return t;
@@ -4813,7 +5653,8 @@ cxx_eval_array_reference (const constexp
static tree
cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
unsigned HOST_WIDE_INT i;
tree field;
@@ -4822,9 +5663,12 @@ cxx_eval_component_reference (const cons
tree orig_whole = TREE_OPERAND (t, 0);
tree whole = cxx_eval_constant_expression (ctx, orig_whole,
lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
if (*non_constant_p)
return t;
+ if (*jump_target)
+ return NULL_TREE;
if (INDIRECT_REF_P (whole)
&& integer_zerop (TREE_OPERAND (whole, 0)))
{
@@ -4933,7 +5777,8 @@ cxx_eval_component_reference (const cons
value = build_value_init (TREE_TYPE (t), tf_warning_or_error);
return cxx_eval_constant_expression (ctx, value,
lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
}
/* Subroutine of cxx_eval_constant_expression.
@@ -4943,7 +5788,8 @@ cxx_eval_component_reference (const cons
static tree
cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
tree orig_whole = TREE_OPERAND (t, 0);
tree retval, fldval, utype, mask;
@@ -4951,10 +5797,13 @@ cxx_eval_bit_field_ref (const constexpr_
HOST_WIDE_INT istart, isize;
tree whole = cxx_eval_constant_expression (ctx, orig_whole,
lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
tree start, field, value;
unsigned HOST_WIDE_INT i;
+ if (*jump_target)
+ return NULL_TREE;
if (whole == orig_whole)
return t;
/* Don't VERIFY_CONSTANT here; we only want to check that we got a
@@ -5235,7 +6084,7 @@ clear_uchar_or_std_byte_in_mask (locatio
static tree
cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
- bool *overflow_p)
+ bool *overflow_p, tree *jump_target)
{
if (check_bit_cast_type (ctx, EXPR_LOCATION (t), TREE_TYPE (t),
TREE_TYPE (t))
@@ -5249,9 +6098,12 @@ cxx_eval_bit_cast (const constexpr_ctx *
}
tree op = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
if (*non_constant_p)
return t;
+ if (*jump_target)
+ return NULL_TREE;
location_t loc = EXPR_LOCATION (t);
if (BITS_PER_UNIT != 8 || CHAR_BIT != 8)
@@ -5329,8 +6181,11 @@ cxx_eval_bit_cast (const constexpr_ctx *
if (CHECKING_P)
{
tree e = cxx_eval_bare_aggregate (ctx, r, vc_prvalue,
- non_constant_p, overflow_p);
- gcc_checking_assert (e == r);
+ non_constant_p, overflow_p,
+ jump_target);
+ gcc_checking_assert (e == r
+ && !throws (jump_target)
+ && !returns (jump_target));
r = e;
}
}
@@ -5371,19 +6226,24 @@ cxx_eval_bit_cast (const constexpr_ctx *
static tree
cxx_eval_logical_expression (const constexpr_ctx *ctx, tree t,
tree bailout_value, tree continue_value,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
tree r;
tree lhs = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
vc_prvalue, non_constant_p,
- overflow_p);
+ overflow_p, jump_target);
+ if (*jump_target)
+ return NULL_TREE;
VERIFY_CONSTANT (lhs);
if (tree_int_cst_equal (lhs, bailout_value))
return lhs;
gcc_assert (tree_int_cst_equal (lhs, continue_value));
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
vc_prvalue, non_constant_p,
- overflow_p);
+ overflow_p, jump_target);
+ if (*jump_target)
+ return NULL_TREE;
VERIFY_CONSTANT (r);
return r;
}
@@ -5540,7 +6400,8 @@ verify_ctor_sanity (const constexpr_ctx
static tree
cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (t);
bool changed = false;
@@ -5593,7 +6454,10 @@ cxx_eval_bare_aggregate (const constexpr
get_or_insert_ctor_field (ctx->ctor, index);
tree elt = cxx_eval_constant_expression (&new_ctx, value,
lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
/* Don't VERIFY_CONSTANT here. */
if (ctx->quiet && *non_constant_p)
break;
@@ -5683,7 +6547,8 @@ cxx_eval_bare_aggregate (const constexpr
static tree
cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
bool value_init, value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
tree elttype = TREE_TYPE (atype);
verify_ctor_sanity (ctx, atype);
@@ -5694,7 +6559,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx
if (init && TREE_CODE (init) == CONSTRUCTOR)
return cxx_eval_bare_aggregate (ctx, init, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, jump_target);
/* For the default constructor, build up a call to the default
constructor of the element type. We only need to handle class types
@@ -5731,7 +6596,9 @@ cxx_eval_vec_init_1 (const constexpr_ctx
}
tree nelts = get_array_or_vector_nelts (ctx, atype, non_constant_p,
- overflow_p);
+ overflow_p, jump_target);
+ if (*jump_target)
+ return NULL_TREE;
unsigned HOST_WIDE_INT max = tree_to_uhwi (nelts);
for (i = 0; i < max; ++i)
{
@@ -5757,9 +6624,9 @@ cxx_eval_vec_init_1 (const constexpr_ctx
}
else
eltinit = cp_build_array_ref (input_location, init, idx, complain);
- eltinit = cxx_eval_vec_init_1 (&new_ctx, elttype, eltinit, value_init,
- lval,
- non_constant_p, overflow_p);
+ eltinit = cxx_eval_vec_init_1 (&new_ctx, elttype, eltinit,
+ value_init, lval, non_constant_p,
+ overflow_p, jump_target);
}
else if (pre_init)
{
@@ -5773,7 +6640,8 @@ cxx_eval_vec_init_1 (const constexpr_ctx
/* Clarify what object is being initialized (118285). */
eltinit = build2 (INIT_EXPR, elttype, new_ctx.object, eltinit);
eltinit = cxx_eval_constant_expression (&new_ctx, eltinit, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
reuse = i == 0;
}
else
@@ -5789,8 +6657,11 @@ cxx_eval_vec_init_1 (const constexpr_ctx
/* Clarify what object is being initialized (118285). */
eltinit = build2 (INIT_EXPR, elttype, new_ctx.object, eltinit);
eltinit = cxx_eval_constant_expression (&new_ctx, eltinit, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
}
+ if (*jump_target)
+ return NULL_TREE;
if (*non_constant_p)
break;
if (no_slot)
@@ -5840,7 +6711,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx
static tree
cxx_eval_vec_init (const constexpr_ctx *ctx, tree t,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p, tree *jump_target)
{
tree atype = TREE_TYPE (t);
tree init = VEC_INIT_EXPR_INIT (t);
@@ -5872,10 +6743,10 @@ cxx_eval_vec_init (const constexpr_ctx *
}
init = expand_vec_init_expr (ctx->object, t, complain);
return cxx_eval_constant_expression (ctx, init, lval, non_constant_p,
- overflow_p);
+ overflow_p, jump_target);
}
tree r = cxx_eval_vec_init_1 (ctx, atype, init, value_init,
- lval, non_constant_p, overflow_p);
+ lval, non_constant_p, overflow_p, jump_target);
if (*non_constant_p)
return t;
else
@@ -5904,14 +6775,16 @@ same_type_ignoring_tlq_and_bounds_p (tre
otherwise return NULL_TREE. */
static tree
-cxx_union_active_member (const constexpr_ctx *ctx, tree t)
+cxx_union_active_member (const constexpr_ctx *ctx, tree t, tree *jump_target)
{
constexpr_ctx new_ctx = *ctx;
new_ctx.quiet = true;
bool non_constant_p = false, overflow_p = false;
tree ctor = cxx_eval_constant_expression (&new_ctx, t, vc_prvalue,
&non_constant_p,
- &overflow_p);
+ &overflow_p, jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (TREE_CODE (ctor) == CONSTRUCTOR
&& CONSTRUCTOR_NELTS (ctor) == 1
&& CONSTRUCTOR_ELT (ctor, 0)->index
@@ -5924,7 +6797,8 @@ cxx_union_active_member (const constexpr
static tree
cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type,
- tree op, unsigned HOST_WIDE_INT off, bool *empty_base)
+ tree op, unsigned HOST_WIDE_INT off, bool *empty_base,
+ tree *jump_target)
{
tree optype = TREE_TYPE (op);
unsigned HOST_WIDE_INT const_nunits;
@@ -5941,7 +6815,8 @@ cxx_fold_indirect_ref_1 (const constexpr
than pointer type. */
if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc,
strip_array_types (optype),
- op, off, empty_base))
+ op, off, empty_base,
+ jump_target))
return fold_convert (type, ret);
}
else if (TREE_CODE (optype) == COMPLEX_TYPE
@@ -5987,7 +6862,7 @@ cxx_fold_indirect_ref_1 (const constexpr
op = build4_loc (loc, ARRAY_REF, TREE_TYPE (optype), op, index,
NULL_TREE, NULL_TREE);
return cxx_fold_indirect_ref_1 (ctx, loc, type, op, rem,
- empty_base);
+ empty_base, jump_target);
}
}
/* ((foo *)&struct_with_foo_field)[x] => COMPONENT_REF */
@@ -5996,7 +6871,7 @@ cxx_fold_indirect_ref_1 (const constexpr
{
if (TREE_CODE (optype) == UNION_TYPE)
/* For unions prefer the currently active member. */
- if (tree field = cxx_union_active_member (ctx, op))
+ if (tree field = cxx_union_active_member (ctx, op, jump_target))
{
unsigned HOST_WIDE_INT el_sz
= tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
@@ -6005,7 +6880,8 @@ cxx_fold_indirect_ref_1 (const constexpr
tree cop = build3 (COMPONENT_REF, TREE_TYPE (field),
op, field, NULL_TREE);
if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc, type, cop,
- off, empty_base))
+ off, empty_base,
+ jump_target))
return ret;
}
}
@@ -6050,7 +6926,8 @@ cxx_fold_indirect_ref_1 (const constexpr
op, field, NULL_TREE);
if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc, type, cop,
off - upos,
- empty_base))
+ empty_base,
+ jump_target))
return ret;
}
}
@@ -6070,7 +6947,7 @@ cxx_fold_indirect_ref_1 (const constexpr
static tree
cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type,
- tree op0, bool *empty_base /* = NULL*/)
+ tree op0, bool *empty_base, tree *jump_target)
{
tree sub = op0;
tree subtype;
@@ -6152,7 +7029,8 @@ cxx_fold_indirect_ref (const constexpr_c
tree off = integer_zero_node;
canonicalize_obj_off (op, off);
return cxx_fold_indirect_ref_1 (ctx, loc, type, op,
- tree_to_uhwi (off), empty_base);
+ tree_to_uhwi (off), empty_base,
+ jump_target);
}
}
else if (TREE_CODE (sub) == POINTER_PLUS_EXPR
@@ -6167,7 +7045,8 @@ cxx_fold_indirect_ref (const constexpr_c
tree obj = TREE_OPERAND (op00, 0);
canonicalize_obj_off (obj, off);
return cxx_fold_indirect_ref_1 (ctx, loc, type, obj,
- tree_to_uhwi (off), empty_base);
+ tree_to_uhwi (off), empty_base,
+ jump_target);
}
}
/* *(foo *)fooarrptr => (*fooarrptr)[0] */
@@ -6177,7 +7056,10 @@ cxx_fold_indirect_ref (const constexpr_c
tree type_domain;
tree min_val = size_zero_node;
tree newsub
- = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (subtype), sub, NULL);
+ = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (subtype), sub, NULL,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (newsub)
sub = newsub;
else
@@ -6195,7 +7077,8 @@ cxx_fold_indirect_ref (const constexpr_c
static tree
cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
tree orig_op0 = TREE_OPERAND (t, 0);
bool empty_base = false;
@@ -6213,13 +7096,17 @@ cxx_eval_indirect_ref (const constexpr_c
/* First try to simplify it directly. */
tree r = cxx_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t),
- orig_op0, &empty_base);
+ orig_op0, &empty_base, jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (!r)
{
/* If that didn't work, evaluate the operand first. */
tree op0 = cxx_eval_constant_expression (ctx, orig_op0,
vc_prvalue, non_constant_p,
- overflow_p);
+ overflow_p, jump_target);
+ if (*jump_target)
+ return NULL_TREE;
/* Don't VERIFY_CONSTANT here. */
if (*non_constant_p)
return t;
@@ -6233,7 +7120,9 @@ cxx_eval_indirect_ref (const constexpr_c
}
r = cxx_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t), op0,
- &empty_base);
+ &empty_base, jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (r == NULL_TREE)
{
/* We couldn't fold to a constant value. Make sure it's not
@@ -6263,7 +7152,10 @@ cxx_eval_indirect_ref (const constexpr_c
}
r = cxx_eval_constant_expression (ctx, r,
- lval, non_constant_p, overflow_p);
+ lval, non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (*non_constant_p)
return t;
@@ -6373,7 +7265,8 @@ non_const_var_error (location_t loc, tre
static tree
cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
int i;
tree args[3];
@@ -6383,7 +7276,10 @@ cxx_eval_trinary_expression (const const
{
args[i] = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, i),
lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
VERIFY_CONSTANT (args[i]);
}
@@ -6505,7 +7401,8 @@ modifying_const_object_p (tree_code code
static tree
cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
constexpr_ctx new_ctx = *ctx;
@@ -6531,7 +7428,10 @@ cxx_eval_store_expression (const constex
if (!SCALAR_TYPE_P (type))
new_ctx.ctor = new_ctx.object = NULL_TREE;
init = cxx_eval_constant_expression (&new_ctx, init, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (*non_constant_p)
return t;
}
@@ -6543,8 +7443,11 @@ cxx_eval_store_expression (const constex
as a whole; otherwise, only evaluate the innermost piece to avoid
building up unnecessary *_REFs. */
target = cxx_eval_constant_expression (ctx, target, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
evaluated = true;
+ if (*jump_target)
+ return NULL_TREE;
if (*non_constant_p)
return t;
}
@@ -6570,7 +7473,10 @@ cxx_eval_store_expression (const constex
if (TREE_CODE (probe) == ARRAY_REF)
{
elt = eval_and_check_array_index (ctx, probe, false,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (*non_constant_p)
return t;
}
@@ -6627,8 +7533,11 @@ cxx_eval_store_expression (const constex
break;
}
probe = cxx_eval_constant_expression (ctx, probe, vc_glvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
evaluated = true;
+ if (*jump_target)
+ return NULL_TREE;
if (*non_constant_p)
return t;
}
@@ -6972,7 +7881,10 @@ cxx_eval_store_expression (const constex
if (tree tinit = TARGET_EXPR_INITIAL (init))
init = tinit;
init = cxx_eval_constant_expression (&new_ctx, init, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
/* The hash table might have moved since the get earlier, and the
initializer might have mutated the underlying CONSTRUCTORs, so we must
recompute VALP. */
@@ -7098,7 +8010,8 @@ cxx_eval_store_expression (const constex
static tree
cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
enum tree_code code = TREE_CODE (t);
tree type = TREE_TYPE (t);
@@ -7112,12 +8025,18 @@ cxx_eval_increment_expression (const con
/* The operand as an lvalue. */
op = cxx_eval_constant_expression (ctx, op, vc_glvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
/* The operand as an rvalue. */
tree val
= cxx_eval_constant_expression (ctx, op, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
/* Don't VERIFY_CONSTANT if this might be dealing with a pointer to
a local array in a constexpr function. */
bool ptr = INDIRECT_TYPE_P (TREE_TYPE (val));
@@ -7156,8 +8075,11 @@ cxx_eval_increment_expression (const con
tree store = build2_loc (cp_expr_loc_or_loc (t, input_location),
MODIFY_EXPR, type, op, mod);
mod = cxx_eval_constant_expression (ctx, store, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
ggc_free (store);
+ if (*jump_target)
+ return NULL_TREE;
if (*non_constant_p)
return t;
@@ -7171,42 +8093,6 @@ cxx_eval_increment_expression (const con
return val;
}
-/* Predicates for the meaning of *jump_target. */
-
-static bool
-returns (tree *jump_target)
-{
- return *jump_target
- && TREE_CODE (*jump_target) == RETURN_EXPR;
-}
-
-static bool
-breaks (tree *jump_target)
-{
- return *jump_target
- && ((TREE_CODE (*jump_target) == LABEL_DECL
- && LABEL_DECL_BREAK (*jump_target))
- || TREE_CODE (*jump_target) == BREAK_STMT
- || TREE_CODE (*jump_target) == EXIT_EXPR);
-}
-
-static bool
-continues (tree *jump_target)
-{
- return *jump_target
- && ((TREE_CODE (*jump_target) == LABEL_DECL
- && LABEL_DECL_CONTINUE (*jump_target))
- || TREE_CODE (*jump_target) == CONTINUE_STMT);
-
-}
-
-static bool
-switches (tree *jump_target)
-{
- return *jump_target
- && TREE_CODE (*jump_target) == INTEGER_CST;
-}
-
/* Subroutine of cxx_eval_statement_list. Determine whether the statement
STMT matches *jump_target. If we're looking for a case label and we see
the default label, note it in ctx->css_state. */
@@ -7254,6 +8140,11 @@ label_matches (const constexpr_ctx *ctx,
breaks (jump_target) or continues (jump_target). */
break;
+ case VAR_DECL:
+ /* Uncaught exception. This is handled by TRY_BLOCK evaluation
+ and other places by testing throws (jump_target). */
+ break;
+
default:
gcc_unreachable ();
}
@@ -7268,15 +8159,9 @@ cxx_eval_statement_list (const constexpr
bool *non_constant_p, bool *overflow_p,
tree *jump_target)
{
- tree local_target;
/* In a statement-expression we want to return the last value.
For empty statement expression return void_node. */
tree r = void_node;
- if (!jump_target)
- {
- local_target = NULL_TREE;
- jump_target = &local_target;
- }
for (tree_stmt_iterator i = tsi_start (t); !tsi_end_p (i); ++i)
{
tree stmt = *i;
@@ -7304,18 +8189,11 @@ cxx_eval_statement_list (const constexpr
jump_target);
if (*non_constant_p)
break;
- if (returns (jump_target) || breaks (jump_target))
+ if (returns (jump_target)
+ || breaks (jump_target)
+ || throws (jump_target))
break;
}
- if (*jump_target && jump_target == &local_target)
- {
- /* We aren't communicating the jump to our caller, so give up. We don't
- need to support evaluation of jumps out of statement-exprs. */
- if (!ctx->quiet)
- error_at (cp_expr_loc_or_input_loc (r),
- "statement is not a constant expression");
- *non_constant_p = true;
- }
return r;
}
@@ -7327,13 +8205,6 @@ cxx_eval_loop_expr (const constexpr_ctx
bool *non_constant_p, bool *overflow_p,
tree *jump_target)
{
- tree local_target;
- if (!jump_target)
- {
- local_target = NULL_TREE;
- jump_target = &local_target;
- }
-
tree body, cond = NULL_TREE, expr = NULL_TREE;
tree cond_prep = NULL_TREE, cond_cleanup = NULL_TREE;
unsigned cond_cleanup_depth = 0;
@@ -7389,7 +8260,7 @@ cxx_eval_loop_expr (const constexpr_ctx
tree c;
FOR_EACH_VEC_ELT_REVERSE (cleanups, i, c)
cxx_eval_constant_expression (ctx, c, vc_discard, non_constant_p,
- overflow_p);
+ overflow_p, jump_target);
}
if (cond_prep)
for (tree decl = BIND_EXPR_VARS (cond_prep);
@@ -7484,7 +8355,8 @@ cxx_eval_loop_expr (const constexpr_ctx
if (*non_constant_p
|| returns (jump_target)
|| breaks (jump_target)
- || continues (jump_target))
+ || continues (jump_target)
+ || throws (jump_target))
{
depth = 1;
break;
@@ -7531,6 +8403,7 @@ cxx_eval_loop_expr (const constexpr_ctx
&& !breaks (jump_target)
&& !continues (jump_target)
&& (!switches (jump_target) || count == 0)
+ && !throws (jump_target)
&& !*non_constant_p);
cleanup_cond ();
@@ -7549,7 +8422,10 @@ cxx_eval_switch_expr (const constexpr_ct
tree cond
= TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_COND (t) : SWITCH_COND (t);
cond = cxx_eval_constant_expression (ctx, cond, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
VERIFY_CONSTANT (cond);
if (TREE_CODE (cond) != INTEGER_CST)
{
@@ -7682,7 +8558,8 @@ maybe_warn_about_constant_value (locatio
static tree
build_new_constexpr_heap_type (const constexpr_ctx *ctx, tree elt_type,
tree cookie_size, tree full_size, tree arg_size,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
gcc_assert (cookie_size == NULL_TREE || tree_fits_uhwi_p (cookie_size));
gcc_assert (tree_fits_uhwi_p (full_size));
@@ -7718,13 +8595,17 @@ build_new_constexpr_heap_type (const con
if (integer_zerop (op0))
arg_size
= cxx_eval_constant_expression (ctx, op1, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
else if (integer_zerop (op1))
arg_size
= cxx_eval_constant_expression (ctx, op0, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
else
arg_size = NULL_TREE;
+ if (*jump_target)
+ return NULL_TREE;
}
else
arg_size = NULL_TREE;
@@ -7754,9 +8635,9 @@ static tree
cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
value_cat lval,
bool *non_constant_p, bool *overflow_p,
- tree *jump_target /* = NULL */)
+ tree *jump_target)
{
- if (jump_target && *jump_target)
+ if (*jump_target)
{
/* If we are jumping, ignore all statements/expressions except those
that could have LABEL_EXPR or CASE_LABEL_EXPR in their bodies. */
@@ -7880,7 +8761,7 @@ cxx_eval_constant_expression (const cons
r = convert_from_reference (r);
}
return cxx_eval_constant_expression (ctx, r, lval, non_constant_p,
- overflow_p);
+ overflow_p, jump_target);
}
/* fall through */
case CONST_DECL:
@@ -7958,7 +8839,10 @@ cxx_eval_constant_expression (const cons
r = v;
if (TREE_ADDRESSABLE (TREE_TYPE (t)))
r = cxx_eval_constant_expression (ctx, r, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
}
else if (lval)
/* Defer in case this is only used for its type. */;
@@ -7991,7 +8875,7 @@ cxx_eval_constant_expression (const cons
case CALL_EXPR:
case AGGR_INIT_EXPR:
r = cxx_eval_call_expression (ctx, t, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, jump_target);
break;
case DECL_EXPR:
@@ -8055,7 +8939,10 @@ cxx_eval_constant_expression (const cons
if (tree init = DECL_INITIAL (r))
{
init = cxx_eval_constant_expression (ctx, init, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
/* Don't share a CONSTRUCTOR that might be changed. */
init = unshare_constructor (init);
/* Remember that a constant object's constructor has already
@@ -8125,9 +9012,12 @@ cxx_eval_constant_expression (const cons
/* Pass vc_prvalue because this indicates
initialization of a temporary. */
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
if (*non_constant_p)
break;
+ if (*jump_target)
+ return NULL_TREE;
if (!is_complex)
{
r = unshare_constructor (r);
@@ -8135,8 +9025,15 @@ cxx_eval_constant_expression (const cons
r = adjust_temp_type (type, r);
ctx->global->put_value (slot, r);
}
- if (TARGET_EXPR_CLEANUP (t) && !CLEANUP_EH_ONLY (t))
- ctx->global->cleanups->safe_push (TARGET_EXPR_CLEANUP (t));
+ if (TARGET_EXPR_CLEANUP (t)
+ && (!CLEANUP_EH_ONLY (t) || cxx_dialect >= cxx26))
+ {
+ ctx->global->cleanups->safe_push (TARGET_EXPR_CLEANUP (t));
+ /* Mark CLEANUP_EH_ONLY cleanups by pushing NULL_TREE after
+ them. */
+ if (CLEANUP_EH_ONLY (t))
+ ctx->global->cleanups->safe_push (NULL_TREE);
+ }
if (ctx->save_exprs)
ctx->save_exprs->safe_push (slot);
if (lval)
@@ -8150,33 +9047,28 @@ cxx_eval_constant_expression (const cons
case MODIFY_EXPR:
gcc_assert (jump_target == NULL || *jump_target == NULL_TREE);
r = cxx_eval_store_expression (ctx, t, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, jump_target);
break;
case SCOPE_REF:
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
case RETURN_EXPR:
if (TREE_OPERAND (t, 0) != NULL_TREE)
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
lval,
- non_constant_p, overflow_p);
- /* FALLTHRU */
+ non_constant_p, overflow_p,
+ jump_target);
+ if (!throws (jump_target))
+ *jump_target = t;
+ break;
case BREAK_STMT:
case CONTINUE_STMT:
- if (jump_target)
- *jump_target = t;
- else
- {
- /* Can happen with ({ return true; }) && false; passed to
- maybe_constant_value. There is nothing to jump over in this
- case, and the bug will be diagnosed later. */
- gcc_assert (ctx->quiet);
- *non_constant_p = true;
- }
+ *jump_target = t;
break;
case SAVE_EXPR:
@@ -8185,9 +9077,10 @@ cxx_eval_constant_expression (const cons
r = v;
else
{
- r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue,
- non_constant_p, overflow_p);
- if (*non_constant_p)
+ r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
+ vc_prvalue, non_constant_p,
+ overflow_p, jump_target);
+ if (*non_constant_p || *jump_target)
break;
ctx->global->put_value (t, r);
if (ctx->save_exprs)
@@ -8195,16 +9088,7 @@ cxx_eval_constant_expression (const cons
}
break;
- case TRY_CATCH_EXPR:
- if (TREE_OPERAND (t, 0) == NULL_TREE)
- {
- r = void_node;
- break;
- }
- /* FALLTHRU */
case NON_LVALUE_EXPR:
- case TRY_BLOCK:
- case MUST_NOT_THROW_EXPR:
case EXPR_STMT:
case EH_SPEC_BLOCK:
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
@@ -8213,6 +9097,42 @@ cxx_eval_constant_expression (const cons
jump_target);
break;
+ case TRY_BLOCK:
+ r = cxx_eval_constant_expression (ctx, TRY_STMTS (t), lval,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (!*non_constant_p && throws (jump_target))
+ if (tree h = TRY_HANDLERS (t))
+ {
+ tree type = strip_array_types (TREE_TYPE (*jump_target));
+ if (TREE_CODE (h) == STATEMENT_LIST)
+ {
+ for (tree stmt : tsi_range (h))
+ if (TREE_CODE (stmt) == HANDLER
+ && handler_match_for_exception_type (stmt, type))
+ {
+ h = stmt;
+ break;
+ }
+ if (TREE_CODE (h) == STATEMENT_LIST)
+ h = NULL_TREE;
+ }
+ else if (TREE_CODE (h) != HANDLER
+ || !handler_match_for_exception_type (h, type))
+ h = NULL_TREE;
+ if (h)
+ {
+ gcc_assert (VAR_P (*jump_target));
+ ctx->global->caught_exceptions.safe_push (*jump_target);
+ ctx->global->caught_exceptions.safe_push (HANDLER_TYPE (h));
+ *jump_target = NULL_TREE;
+ r = cxx_eval_constant_expression (ctx, HANDLER_BODY (h),
+ vc_discard, non_constant_p,
+ overflow_p, jump_target);
+ }
+ }
+ break;
+
case CLEANUP_POINT_EXPR:
{
auto_vec<tree, 2> cleanups;
@@ -8230,47 +9150,189 @@ cxx_eval_constant_expression (const cons
ctx->global->cleanups = prev_cleanups;
unsigned int i;
- tree cleanup;
+ tree cleanup, jmp_target = NULL_TREE;
+ bool eh = throws (jump_target);
/* Evaluate the cleanups. */
FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup)
- cxx_eval_constant_expression (&new_ctx, cleanup, vc_discard,
- non_constant_p, overflow_p);
+ if (cleanup == NULL_TREE)
+ {
+ /* NULL_TREE cleanup is a marker that before it is
+ CLEANUP_EH_ONLY cleanup. Skip the cleanup before it
+ if the body didn't throw. */
+ if (!eh)
+ --i;
+ }
+ else
+ cxx_eval_constant_expression (&new_ctx, cleanup, vc_discard,
+ non_constant_p, overflow_p,
+ &jmp_target);
/* Forget SAVE_EXPRs and TARGET_EXPRs created by this
full-expression. */
for (tree save_expr : save_exprs)
destroy_value_checked (ctx, save_expr, non_constant_p);
+ if (throws (&jmp_target))
+ *jump_target = jmp_target;
}
break;
+ case MUST_NOT_THROW_EXPR:
+ r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
+ lval,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (throws (jump_target))
+ {
+ /* [except.handle]/7 - If the search for a handler exits the
+ function body of a function with a non-throwing exception
+ specification, the function std::terminate is invoked. */
+ if (!ctx->quiet)
+ {
+ auto_diagnostic_group d;
+ diagnose_std_terminate (loc, ctx, *jump_target);
+ if (MUST_NOT_THROW_NOEXCEPT_P (t)
+ && ctx->call
+ && ctx->call->fundef)
+ inform (loc, "uncaught exception exited from %<noexcept%> "
+ "function %qD",
+ ctx->call->fundef->decl);
+ else if (MUST_NOT_THROW_THROW_P (t))
+ inform (loc, "destructor exited with an exception after "
+ "initializing the exception object");
+ else if (MUST_NOT_THROW_CATCH_P (t))
+ inform (loc, "constructor exited with another exception while "
+ "entering handler");
+ }
+ *non_constant_p = true;
+ *jump_target = NULL_TREE;
+ r = NULL_TREE;
+ }
+ break;
+
+ case TRY_CATCH_EXPR:
+ if (TREE_OPERAND (t, 0) == NULL_TREE)
+ {
+ r = void_node;
+ break;
+ }
+ r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (!*non_constant_p && throws (jump_target))
+ {
+ tree jmp_target = NULL_TREE;
+ cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard,
+ non_constant_p, overflow_p,
+ &jmp_target);
+ if (throws (&jmp_target))
+ {
+ if (throws (jump_target))
+ {
+ /* [except.throw]/9 - If the exception handling mechanism
+ handling an uncaught exception directly invokes a function
+ that exits via an exception, the function std::terminate is
+ invoked. */
+ if (!ctx->quiet)
+ {
+ auto_diagnostic_group d;
+ diagnose_std_terminate (loc, ctx, *jump_target);
+ inform (loc, "destructor exited with an exception");
+ }
+ *non_constant_p = true;
+ *jump_target = NULL_TREE;
+ r = NULL_TREE;
+ }
+ else
+ *jump_target = jmp_target;
+ }
+ }
+ break;
+
case TRY_FINALLY_EXPR:
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
non_constant_p, overflow_p,
jump_target);
if (!*non_constant_p)
- /* Also evaluate the cleanup. */
- cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard,
- non_constant_p, overflow_p);
+ {
+ tree jmp_target = NULL_TREE;
+ /* Also evaluate the cleanup. */
+ if (TREE_CODE (TREE_OPERAND (t, 1)) == EH_ELSE_EXPR
+ && throws (jump_target))
+ cxx_eval_constant_expression (ctx,
+ TREE_OPERAND (TREE_OPERAND (t, 1),
+ 1), vc_discard,
+ non_constant_p, overflow_p,
+ &jmp_target);
+ else
+ cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard,
+ non_constant_p, overflow_p,
+ &jmp_target);
+ if (throws (&jmp_target))
+ {
+ if (throws (jump_target))
+ {
+ /* [except.throw]/9 - If the exception handling mechanism
+ handling an uncaught exception directly invokes a function
+ that exits via an exception, the function std::terminate is
+ invoked. */
+ if (!ctx->quiet)
+ {
+ auto_diagnostic_group d;
+ diagnose_std_terminate (loc, ctx, *jump_target);
+ inform (loc, "destructor exited with an exception");
+ }
+ *non_constant_p = true;
+ *jump_target = NULL_TREE;
+ r = NULL_TREE;
+ }
+ else
+ *jump_target = jmp_target;
+ }
+ }
break;
case EH_ELSE_EXPR:
/* Evaluate any cleanup that applies to non-EH exits. */
cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_discard,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
- /* We do not have constexpr exceptions yet, so skip the EH path. */
+ /* The EH path is handled in TRY_FINALLY_EXPR handling above. */
break;
case CLEANUP_STMT:
r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
non_constant_p, overflow_p,
jump_target);
- if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
+ if ((!CLEANUP_EH_ONLY (t) || throws (jump_target)) && !*non_constant_p)
{
iloc_sentinel ils (loc);
+ tree jmp_target = NULL_TREE;
/* Also evaluate the cleanup. */
cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), vc_discard,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ &jmp_target);
+ if (throws (&jmp_target))
+ {
+ if (throws (jump_target))
+ {
+ /* [except.throw]/9 - If the exception handling mechanism
+ handling an uncaught exception directly invokes a function
+ that exits via an exception, the function std::terminate is
+ invoked. */
+ if (!ctx->quiet)
+ {
+ auto_diagnostic_group d;
+ diagnose_std_terminate (loc, ctx, *jump_target);
+ inform (loc, "destructor exited with an exception");
+ }
+ *non_constant_p = true;
+ *jump_target = NULL_TREE;
+ r = NULL_TREE;
+ }
+ else
+ *jump_target = jmp_target;
+ }
}
break;
@@ -8280,14 +9342,18 @@ cxx_eval_constant_expression (const cons
case MEM_REF:
case INDIRECT_REF:
r = cxx_eval_indirect_ref (ctx, t, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
case ADDR_EXPR:
{
tree oldop = TREE_OPERAND (t, 0);
tree op = cxx_eval_constant_expression (ctx, oldop, vc_glvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
/* Don't VERIFY_CONSTANT here. */
if (*non_constant_p)
return t;
@@ -8307,7 +9373,10 @@ cxx_eval_constant_expression (const cons
if (lval)
{
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (r == error_mark_node)
;
else if (r == TREE_OPERAND (t, 0) || lval == vc_discard)
@@ -8328,7 +9397,8 @@ cxx_eval_constant_expression (const cons
case FIXED_CONVERT_EXPR:
case VEC_DUPLICATE_EXPR:
r = cxx_eval_unary_expression (ctx, t, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
case SIZEOF_EXPR:
@@ -8366,6 +9436,8 @@ cxx_eval_constant_expression (const cons
cxx_eval_constant_expression (ctx, op0, vc_discard,
non_constant_p, overflow_p,
jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (*non_constant_p)
return t;
op1 = TREE_OPERAND (t, 1);
@@ -8418,7 +9490,8 @@ cxx_eval_constant_expression (const cons
case RANGE_EXPR:
case COMPLEX_EXPR:
r = cxx_eval_binary_expression (ctx, t, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
/* fold can introduce non-IF versions of these; still treat them as
@@ -8427,19 +9500,22 @@ cxx_eval_constant_expression (const cons
case TRUTH_ANDIF_EXPR:
r = cxx_eval_logical_expression (ctx, t, boolean_false_node,
boolean_true_node,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
case TRUTH_OR_EXPR:
case TRUTH_ORIF_EXPR:
r = cxx_eval_logical_expression (ctx, t, boolean_true_node,
boolean_false_node,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
case ARRAY_REF:
r = cxx_eval_array_reference (ctx, t, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
case COMPONENT_REF:
@@ -8454,17 +9530,19 @@ cxx_eval_constant_expression (const cons
return t;
}
r = cxx_eval_component_reference (ctx, t, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
case BIT_FIELD_REF:
r = cxx_eval_bit_field_ref (ctx, t, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
case COND_EXPR:
case IF_STMT:
- if (jump_target && *jump_target)
+ if (*jump_target)
{
tree orig_jump = *jump_target;
tree arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 1))
@@ -8502,7 +9580,7 @@ cxx_eval_constant_expression (const cons
break;
case VEC_COND_EXPR:
r = cxx_eval_vector_conditional_expression (ctx, t, non_constant_p,
- overflow_p);
+ overflow_p, jump_target);
break;
case CONSTRUCTOR:
@@ -8514,7 +9592,7 @@ cxx_eval_constant_expression (const cons
return t;
}
r = cxx_eval_bare_aggregate (ctx, t, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, jump_target);
break;
case VEC_INIT_EXPR:
@@ -8524,12 +9602,13 @@ cxx_eval_constant_expression (const cons
or xvalue of the same type, meaning direct-initialization from the
corresponding member. */
r = cxx_eval_vec_init (ctx, t, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, jump_target);
break;
case VEC_PERM_EXPR:
r = cxx_eval_trinary_expression (ctx, t, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
case PAREN_EXPR:
@@ -8537,7 +9616,8 @@ cxx_eval_constant_expression (const cons
/* A PAREN_EXPR resulting from __builtin_assoc_barrier has no effect in
constant expressions since it's unaffected by -fassociative-math. */
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
case NOP_EXPR:
@@ -8561,7 +9641,10 @@ cxx_eval_constant_expression (const cons
? vc_discard
: tcode == VIEW_CONVERT_EXPR
? lval : vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (*non_constant_p)
return t;
tree type = TREE_TYPE (t);
@@ -8618,7 +9701,10 @@ cxx_eval_constant_expression (const cons
{
if (integer_zerop (sop))
return build_int_cst (type, 0);
- r = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (type), sop);
+ r = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (type), sop,
+ NULL, jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (r)
{
r = build1 (ADDR_EXPR, type, r);
@@ -8745,10 +9831,14 @@ cxx_eval_constant_expression (const cons
if (cxx_replaceable_global_alloc_fn (fun)
&& IDENTIFIER_NEW_OP_P (DECL_NAME (fun)))
arg_size = CALL_EXPR_ARG (oldop, 0);
- TREE_TYPE (var)
+ tree new_type
= build_new_constexpr_heap_type (ctx, elt_type, cookie_size,
var_size, arg_size,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
+ TREE_TYPE (var) = new_type;
TREE_TYPE (TREE_OPERAND (op, 0))
= build_pointer_type (TREE_TYPE (var));
}
@@ -8787,7 +9877,10 @@ cxx_eval_constant_expression (const cons
tree op = cxx_eval_constant_expression (ctx, oldop,
lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
if (*non_constant_p)
return t;
r = fold_convert (TREE_TYPE (t), op);
@@ -8824,14 +9917,20 @@ cxx_eval_constant_expression (const cons
case PREDECREMENT_EXPR:
case POSTDECREMENT_EXPR:
return cxx_eval_increment_expression (ctx, t,
- lval, non_constant_p, overflow_p);
+ lval, non_constant_p, overflow_p,
+ jump_target);
+ case THROW_EXPR:
+ if (cxx_dialect >= cxx26)
+ return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
+ non_constant_p, overflow_p,
+ jump_target);
+ /* FALLTHROUGH */
case LAMBDA_EXPR:
case NEW_EXPR:
case VEC_NEW_EXPR:
case DELETE_EXPR:
case VEC_DELETE_EXPR:
- case THROW_EXPR:
case MODOP_EXPR:
/* GCC internal stuff. */
case VA_ARG_EXPR:
@@ -8845,7 +9944,8 @@ cxx_eval_constant_expression (const cons
case OBJ_TYPE_REF:
/* Virtual function lookup. We don't need to do anything fancy. */
return cxx_eval_constant_expression (ctx, OBJ_TYPE_REF_EXPR (t),
- lval, non_constant_p, overflow_p);
+ lval, non_constant_p, overflow_p,
+ jump_target);
case PLACEHOLDER_EXPR:
/* Use of the value or address of the current object. */
@@ -8855,7 +9955,8 @@ cxx_eval_constant_expression (const cons
return ctor;
else
return cxx_eval_constant_expression (ctx, ctor, lval,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
}
/* A placeholder without a referent. We can get here when
checking whether NSDMIs are noexcept, or in massage_init_elt;
@@ -8868,7 +9969,10 @@ cxx_eval_constant_expression (const cons
{
tree cond = TREE_OPERAND (t, 0);
cond = cxx_eval_constant_expression (ctx, cond, vc_prvalue,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*jump_target)
+ return NULL_TREE;
VERIFY_CONSTANT (cond);
if (integer_nonzerop (cond))
*jump_target = t;
@@ -8980,7 +10084,7 @@ cxx_eval_constant_expression (const cons
*non_constant_p = true;
return t;
}
- r = cxx_eval_bit_cast (ctx, t, non_constant_p, overflow_p);
+ r = cxx_eval_bit_cast (ctx, t, non_constant_p, overflow_p, jump_target);
break;
case OMP_PARALLEL:
@@ -9299,8 +10403,34 @@ cxx_eval_outermost_constant_expr (tree t
if (manifestly_const_eval == mce_true)
instantiate_constexpr_fns (r);
+ tree jmp_target = NULL_TREE;
r = cxx_eval_constant_expression (&ctx, r, vc_prvalue,
- &non_constant_p, &overflow_p);
+ &non_constant_p, &overflow_p,
+ &jmp_target);
+ if (throws (&jmp_target) && !non_constant_p)
+ {
+ if (!ctx.quiet)
+ diagnose_uncaught_exception (input_location, &ctx, jmp_target);
+ non_constant_p = true;
+ jmp_target = NULL_TREE;
+ r = t;
+ }
+ else if (!non_constant_p && jmp_target)
+ {
+ non_constant_p = true;
+ if (!ctx.quiet)
+ {
+ if (breaks (&jmp_target))
+ error ("%<break%> outside of a loop or %<switch%>");
+ else if (continues (&jmp_target))
+ error ("%<continue%> outside of a loop");
+ else if (returns (&jmp_target))
+ error ("%<return%> in a statement expression");
+ else
+ gcc_unreachable ();
+ }
+ r = t;
+ }
/* If we got a non-simple TARGET_EXPR, the initializer was a sequence
of statements, and the result ought to be stored in ctx.ctor. */
@@ -9309,15 +10439,31 @@ cxx_eval_outermost_constant_expr (tree t
unsigned int i;
tree cleanup;
+ jmp_target = NULL_TREE;
/* Evaluate the cleanups. */
FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup)
- cxx_eval_constant_expression (&ctx, cleanup, vc_discard,
- &non_constant_p, &overflow_p);
+ if (cleanup == NULL_TREE)
+ /* NULL_TREE cleanup is a marker that before it is
+ CLEANUP_EH_ONLY cleanup. Skip the cleanup before it. */
+ --i;
+ else
+ cxx_eval_constant_expression (&ctx, cleanup, vc_discard,
+ &non_constant_p, &overflow_p,
+ &jmp_target);
+ if (throws (&jmp_target) && !non_constant_p)
+ {
+ if (!ctx.quiet)
+ diagnose_uncaught_exception (input_location, &ctx, jmp_target);
+ non_constant_p = true;
+ r = t;
+ }
/* Mutable logic is a bit tricky: we want to allow initialization of
constexpr variables with mutable members, but we can't copy those
members to another constexpr variable. */
- if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_MUTABLE_POISON (r))
+ if (!non_constant_p
+ && TREE_CODE (r) == CONSTRUCTOR
+ && CONSTRUCTOR_MUTABLE_POISON (r))
{
if (!allow_non_constant)
error ("%qE is not a constant expression because it refers to "
@@ -9335,8 +10481,13 @@ cxx_eval_outermost_constant_expr (tree t
{
if (!allow_non_constant && !non_constant_p)
{
- error ("%qE is not a constant expression because it refers to "
- "a result of %<operator new%>", t);
+ if (DECL_LANG_SPECIFIC (heap_var))
+ error ("%qE is not a constant expression because it refers to "
+ "exception object allocated with "
+ "%<__cxa_allocate_exception%>", t);
+ else
+ error ("%qE is not a constant expression because it refers to "
+ "a result of %<operator new%>", t);
inform (DECL_SOURCE_LOCATION (heap_var), "allocated here");
}
r = t;
@@ -9949,11 +11100,13 @@ struct check_for_return_continue_data {
hash_set<tree> *pset;
tree continue_stmt;
tree break_stmt;
+ bool could_throw;
};
/* Helper function for potential_constant_expression_1 SWITCH_STMT handling,
called through cp_walk_tree. Return the first RETURN_EXPR found, or note
- the first CONTINUE_STMT and/or BREAK_STMT if RETURN_EXPR is not found. */
+ the first CONTINUE_STMT and/or BREAK_STMT if RETURN_EXPR is not found.
+ For C++26 also note presence of possibly throwing calls. */
static tree
check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
{
@@ -10038,6 +11191,23 @@ check_for_return_continue (tree *tp, int
case CONSTRUCTOR:
break;
+ case AGGR_INIT_EXPR:
+ case CALL_EXPR:
+ /* In C++26 a function could throw. */
+ if (cxx_dialect >= cxx26 && flag_exceptions && cp_get_callee (t))
+ {
+ tree callee = cp_get_callee (t);
+ tree callee_fn = cp_get_fndecl_from_callee (callee, false);
+ if (!flag_enforce_eh_specs
+ || type_dependent_expression_p (callee)
+ || !POINTER_TYPE_P (TREE_TYPE (callee))
+ || (!type_noexcept_p (TREE_TYPE (TREE_TYPE (callee)))
+ && (callee_fn == NULL_TREE
+ || !TREE_NOTHROW (callee_fn))))
+ d->could_throw = true;
+ }
+ break;
+
default:
if (!EXPR_P (t))
*walk_subtrees = 0;
@@ -10243,7 +11413,8 @@ potential_constant_expression_1 (tree t,
|| TREE_CODE (t) != CALL_EXPR
|| current_function_decl == NULL_TREE
|| !is_std_construct_at (current_function_decl))
- && !cxx_dynamic_cast_fn_p (fun))
+ && !cxx_dynamic_cast_fn_p (fun)
+ && !cxx_cxa_builtin_fn_p (fun))
{
if ((flags & tf_error)
&& constexpr_error (loc, fundef_p,
@@ -10290,6 +11461,22 @@ potential_constant_expression_1 (tree t,
jump_target))
return false;
}
+ /* In C++26 a function could throw. */
+ if (cxx_dialect >= cxx26
+ && flag_exceptions
+ && *jump_target == NULL_TREE
+ && cp_get_callee (t))
+ {
+ tree callee = cp_get_callee (t);
+ tree callee_fn = cp_get_fndecl_from_callee (callee, false);
+ if (!flag_enforce_eh_specs
+ || type_dependent_expression_p (callee)
+ || !POINTER_TYPE_P (TREE_TYPE (callee))
+ || (!type_noexcept_p (TREE_TYPE (TREE_TYPE (callee)))
+ && (callee_fn == NULL_TREE
+ || !TREE_NOTHROW (callee_fn))))
+ *jump_target = void_node;
+ }
return true;
}
@@ -10512,11 +11699,13 @@ potential_constant_expression_1 (tree t,
a return. */
hash_set<tree> pset;
check_for_return_continue_data data = { &pset, NULL_TREE,
- NULL_TREE };
+ NULL_TREE, false };
if (tree ret_expr
= cp_walk_tree (&FOR_BODY (t), check_for_return_continue,
&data, &pset))
*jump_target = ret_expr;
+ if (data.could_throw)
+ *jump_target = void_node;
return true;
}
}
@@ -10556,11 +11745,13 @@ potential_constant_expression_1 (tree t,
a return. */
hash_set<tree> pset;
check_for_return_continue_data data = { &pset, NULL_TREE,
- NULL_TREE };
+ NULL_TREE, false };
if (tree ret_expr
= cp_walk_tree (&WHILE_BODY (t), check_for_return_continue,
&data, &pset))
*jump_target = ret_expr;
+ if (data.could_throw)
+ *jump_target = void_node;
return true;
}
if (!RECUR (WHILE_BODY (t), any))
@@ -10584,7 +11775,7 @@ potential_constant_expression_1 (tree t,
{
hash_set<tree> pset;
check_for_return_continue_data data = { &pset, NULL_TREE,
- NULL_TREE };
+ NULL_TREE, false };
if (tree ret_expr
= cp_walk_tree (&SWITCH_STMT_BODY (t), check_for_return_continue,
&data, &pset))
@@ -10593,6 +11784,8 @@ potential_constant_expression_1 (tree t,
else if (data.continue_stmt)
/* The switch can't return, but might continue. */
*jump_target = data.continue_stmt;
+ if (data.could_throw)
+ *jump_target = void_node;
}
return true;
@@ -10622,7 +11815,6 @@ potential_constant_expression_1 (tree t,
case DYNAMIC_CAST_EXPR:
case PSEUDO_DTOR_EXPR:
- case THROW_EXPR:
case OMP_PARALLEL:
case OMP_TASK:
case OMP_FOR:
@@ -10678,6 +11870,11 @@ potential_constant_expression_1 (tree t,
constant. */
return true;
+ case THROW_EXPR:
+ if (cxx_dialect < cxx26)
+ goto fail;
+ return RECUR (TREE_OPERAND (t, 0), rval);
+
case ASM_EXPR:
if (flags & tf_error)
inline_asm_in_constexpr_error (loc, fundef_p);
@@ -10806,6 +12003,22 @@ potential_constant_expression_1 (tree t,
case CLEANUP_POINT_EXPR:
case MUST_NOT_THROW_EXPR:
case TRY_CATCH_EXPR:
+ /* Even for C++26 handle TRY_BLOCK conservatively, if we detect the
+ body could throw, even with catch (...) among handlers we'd need
+ to analyze them in detail if they couldn't rethrow it. More
+ importantly though, throws (jump_target) is just conservative,
+ and there could be e.g.
+ try
+ {
+ possibly_throwing_fn (args);
+ break;
+ }
+ catch (...)
+ {
+ }
+ or continue or return instead of break. So, clearing *jump_target
+ because we see catch (...) handler might mean we missed break
+ etc. */
case TRY_BLOCK:
case EH_SPEC_BLOCK:
case EXPR_STMT:
@@ -11047,9 +12260,9 @@ potential_constant_expression_1 (tree t,
want_rval, strict, now, fundef_p,
tf_none, &this_jump_target))
{
- if (returns (&this_jump_target))
+ if (returns (&this_jump_target) || throws (&this_jump_target))
*jump_target = this_jump_target;
- else if (!returns (jump_target))
+ else if (!returns (jump_target) && !throws (jump_target))
{
if (breaks (&this_jump_target)
|| continues (&this_jump_target))
@@ -11061,7 +12274,8 @@ potential_constant_expression_1 (tree t,
couldn't return, break or continue. */
hash_set<tree> pset;
check_for_return_continue_data data = { &pset, NULL_TREE,
- NULL_TREE };
+ NULL_TREE,
+ false };
if (tree ret_expr
= cp_walk_tree (&TREE_OPERAND (t, 2),
check_for_return_continue, &data,
@@ -11074,6 +12288,8 @@ potential_constant_expression_1 (tree t,
else if (data.break_stmt)
*jump_target = data.break_stmt;
}
+ if (data.could_throw)
+ *jump_target = void_node;
}
}
return true;
--- gcc/cp/cp-tree.h.jj 2025-07-09 08:16:50.883079418 +0200
+++ gcc/cp/cp-tree.h 2025-07-09 12:18:10.996187712 +0200
@@ -452,6 +452,7 @@ extern GTY(()) tree cp_global_trees[CPTI
contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
RETURN_EXPR_LOCAL_ADDR_P (in RETURN_EXPR)
PACK_INDEX_PARENTHESIZED_P (in PACK_INDEX_*)
+ MUST_NOT_THROW_NOEXCEPT_P (in MUST_NOT_THROW_EXPR)
1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE)
TI_PENDING_TEMPLATE_FLAG.
TEMPLATE_PARMS_FOR_INLINE.
@@ -472,6 +473,7 @@ extern GTY(()) tree cp_global_trees[CPTI
BIND_EXPR_VEC_DTOR (in BIND_EXPR)
ATOMIC_CONSTR_EXPR_FROM_CONCEPT_P (in ATOMIC_CONSTR)
STATIC_INIT_DECOMP_BASE_P (in the TREE_LIST for {static,tls}_aggregates)
+ MUST_NOT_THROW_THROW_P (in MUST_NOT_THROW_EXPR)
2: IDENTIFIER_KIND_BIT_2 (in IDENTIFIER_NODE)
ICS_THIS_FLAG (in _CONV)
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL)
@@ -493,6 +495,7 @@ extern GTY(()) tree cp_global_trees[CPTI
contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
STATIC_INIT_DECOMP_NONBASE_P (in the TREE_LIST
for {static,tls}_aggregates)
+ MUST_NOT_THROW_CATCH_P (in MUST_NOT_THROW_EXPR)
3: IMPLICIT_RVALUE_P (in NON_LVALUE_EXPR or STATIC_CAST_EXPR)
ICS_BAD_FLAG (in _CONV)
FN_TRY_BLOCK_P (in TRY_BLOCK)
@@ -3016,6 +3019,8 @@ struct GTY(()) lang_decl_min {
In a lambda-capture proxy VAR_DECL, this is DECL_CAPTURED_VARIABLE.
In a function-scope TREE_STATIC VAR_DECL or IMPLICIT_TYPEDEF_P TYPE_DECL,
this is DECL_DISCRIMINATOR.
+ In constexpr exception artificial VAR_DECL, this is
+ DECL_EXCEPTION_REFCOUNT.
In a DECL_LOCAL_DECL_P decl, this is the namespace decl it aliases.
Otherwise, in a class-scope DECL, this is DECL_ACCESS. */
tree access;
@@ -4470,6 +4475,23 @@ get_vec_init_expr (tree t)
#define MUST_NOT_THROW_COND(NODE) \
TREE_OPERAND (MUST_NOT_THROW_EXPR_CHECK (NODE), 1)
+/* Reasons why MUST_NOT_THROW_EXPR has been created. */
+
+/* Indicates MUST_NOT_THROW_EXPR has been created to wrap body of
+ a noexcept function. */
+#define MUST_NOT_THROW_NOEXCEPT_P(NODE) \
+ TREE_LANG_FLAG_0 (MUST_NOT_THROW_EXPR_CHECK (NODE))
+
+/* Indicates MUST_NOT_THROW_EXPR has been created to wrap construction of
+ exception object during throw. */
+#define MUST_NOT_THROW_THROW_P(NODE) \
+ TREE_LANG_FLAG_1 (MUST_NOT_THROW_EXPR_CHECK (NODE))
+
+/* Indicates MUST_NOT_THROW_EXPR has been created to wrap construction of
+ handler parameter during catch. */
+#define MUST_NOT_THROW_CATCH_P(NODE) \
+ TREE_LANG_FLAG_2 (MUST_NOT_THROW_EXPR_CHECK (NODE))
+
/* The TYPE_MAIN_DECL for a class template type is a TYPE_DECL, not a
TEMPLATE_DECL. This macro determines whether or not a given class
type is really a template type, as opposed to an instantiation or
@@ -4512,7 +4534,7 @@ get_vec_init_expr (tree t)
#define TYPE_CONTAINS_VPTR_P(NODE) \
(TYPE_POLYMORPHIC_P (NODE) || CLASSTYPE_VBASECLASSES (NODE))
-/* Nonzero if NODE is a FUNCTION_DECL or VARIABLE_DECL (for a decl
+/* Nonzero if NODE is a FUNCTION_DECL or VAR_DECL (for a decl
with namespace scope) declared in a local scope. */
#define DECL_LOCAL_DECL_P(NODE) \
DECL_LANG_FLAG_0 (VAR_OR_FUNCTION_DECL_CHECK (NODE))
@@ -5153,6 +5175,10 @@ get_vec_init_expr (tree t)
protected_access_node will appear in the DECL_ACCESS for the node. */
#define DECL_ACCESS(NODE) (LANG_DECL_MIN_CHECK (NODE)->access)
+/* In artificial VAR_DECL created by cxa_allocate_exception
+ this is reference count. */
+#define DECL_EXCEPTION_REFCOUNT(NODE) (LANG_DECL_MIN_CHECK (NODE)->access)
+
/* Nonzero if the FUNCTION_DECL is a global constructor. */
#define DECL_GLOBAL_CTOR_P(NODE) \
(LANG_DECL_FN_CHECK (NODE)->global_ctor_p)
@@ -6813,6 +6839,7 @@ enum cp_built_in_function {
CP_BUILT_IN_IS_CORRESPONDING_MEMBER,
CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
CP_BUILT_IN_SOURCE_LOCATION,
+ CP_BUILT_IN_EH_PTR_ADJUST_REF,
CP_BUILT_IN_LAST
};
@@ -6993,6 +7020,7 @@ extern bool type_has_extended_temps (tr
extern tree strip_top_quals (tree);
extern bool reference_related_p (tree, tree);
extern bool reference_compatible_p (tree, tree);
+extern bool handler_match_for_exception_type (tree, tree);
extern int remaining_arguments (tree);
extern tree build_implicit_conv_flags (tree, tree, int);
extern tree perform_implicit_conversion (tree, tree, tsubst_flags_t);
--- gcc/cp/call.cc.jj 2025-06-02 11:00:06.027527030 +0200
+++ gcc/cp/call.cc 2025-07-09 08:23:15.056898196 +0200
@@ -1723,6 +1723,56 @@ involves_qualification_conversion_p (tre
return false;
}
+/* Return true if HANDLER is a match for exception object with EXCEPT_TYPE as
+ per [except.handle]/3. */
+
+bool
+handler_match_for_exception_type (tree handler, tree except_type)
+{
+ tree handler_type = HANDLER_TYPE (handler);
+ if (handler_type == NULL_TREE)
+ return true; /* ... */
+ if (same_type_ignoring_top_level_qualifiers_p (handler_type, except_type))
+ return true;
+ if (CLASS_TYPE_P (except_type) && CLASS_TYPE_P (handler_type))
+ {
+ base_kind b_kind;
+ tree binfo = lookup_base (except_type, handler_type, ba_check, &b_kind,
+ tf_none);
+ if (binfo && binfo != error_mark_node)
+ return true;
+ }
+ if (TYPE_PTR_P (handler_type) || TYPE_PTRDATAMEM_P (handler_type))
+ {
+ if (TREE_CODE (except_type) == NULLPTR_TYPE)
+ return true;
+ if ((TYPE_PTR_P (handler_type) && TYPE_PTR_P (except_type))
+ || (TYPE_PTRDATAMEM_P (handler_type)
+ && TYPE_PTRDATAMEM_P (except_type)))
+ {
+ conversion *conv
+ = standard_conversion (handler_type, except_type, NULL_TREE,
+ /*c_cast_p=*/false, 0, tf_none);
+ if (conv && !conv->bad_p)
+ {
+ for (conversion *t = conv; t; t = next_conversion (t))
+ switch (t->kind)
+ {
+ case ck_ptr:
+ case ck_fnptr:
+ case ck_qual:
+ case ck_identity:
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
/* A reference of the indicated TYPE is being bound directly to the
expression represented by the implicit conversion sequence CONV.
Return a conversion sequence for this binding. */
--- gcc/cp/except.cc.jj 2025-06-02 11:00:06.120525824 +0200
+++ gcc/cp/except.cc 2025-07-09 12:48:35.755414128 +0200
@@ -367,6 +367,8 @@ initialize_handler_parm (tree decl, tree
MUST_NOT_THROW_EXPR. */
init = fold_build_cleanup_point_expr (TREE_TYPE (init), init);
init = build_must_not_throw_expr (init, NULL_TREE);
+ if (init && TREE_CODE (init) == MUST_NOT_THROW_EXPR)
+ MUST_NOT_THROW_CATCH_P (init) = 1;
}
decl = pushdecl (decl);
@@ -523,6 +525,7 @@ begin_eh_spec_block (void)
r = build_stmt (spec_location, MUST_NOT_THROW_EXPR,
NULL_TREE, NULL_TREE);
TREE_SIDE_EFFECTS (r) = 1;
+ MUST_NOT_THROW_NOEXCEPT_P (r) = 1;
}
else
r = build_stmt (spec_location, EH_SPEC_BLOCK, NULL_TREE, NULL_TREE);
@@ -614,6 +617,7 @@ wrap_cleanups_r (tree *tp, int *walk_sub
{
cleanup = build2 (MUST_NOT_THROW_EXPR, void_type_node, cleanup,
NULL_TREE);
+ MUST_NOT_THROW_THROW_P (cleanup) = 1;
TARGET_EXPR_CLEANUP (exp) = cleanup;
}
@@ -712,6 +716,11 @@ build_throw (location_t loc, tree exp, t
allocate_expr = do_allocate_exception (temp_type);
if (allocate_expr == error_mark_node)
return error_mark_node;
+ /* Copy ptr inside of the CLEANUP_POINT_EXPR
+ added below to a TARGET_EXPR slot added outside of it,
+ otherwise during constant evaluation of throw expression
+ we'd diagnose accessing ptr outside of its lifetime. */
+ tree ptr_copy = get_internal_target_expr (null_pointer_node);
allocate_expr = get_internal_target_expr (allocate_expr);
ptr = TARGET_EXPR_SLOT (allocate_expr);
TARGET_EXPR_CLEANUP (allocate_expr) = do_free_exception (ptr);
@@ -763,10 +772,17 @@ build_throw (location_t loc, tree exp, t
/* Prepend the allocation. */
exp = build2 (COMPOUND_EXPR, TREE_TYPE (exp), allocate_expr, exp);
+ exp = build2 (COMPOUND_EXPR, void_type_node, exp,
+ build2 (MODIFY_EXPR, void_type_node,
+ TARGET_EXPR_SLOT (ptr_copy), ptr));
+ ptr = TARGET_EXPR_SLOT (ptr_copy);
+
/* Force all the cleanups to be evaluated here so that we don't have
to do them during unwinding. */
exp = build1 (CLEANUP_POINT_EXPR, void_type_node, exp);
+ exp = build2 (COMPOUND_EXPR, TREE_TYPE (exp), ptr_copy, exp);
+
throw_type = build_eh_type_type (prepare_eh_type (TREE_TYPE (object)));
cleanup = NULL_TREE;
--- gcc/cp/tree.cc.jj 2025-06-17 13:18:52.927091924 +0200
+++ gcc/cp/tree.cc 2025-07-09 08:23:15.057898182 +0200
@@ -488,6 +488,7 @@ builtin_valid_in_constant_expr_p (const_
case CP_BUILT_IN_SOURCE_LOCATION:
case CP_BUILT_IN_IS_CORRESPONDING_MEMBER:
case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
+ case CP_BUILT_IN_EH_PTR_ADJUST_REF:
return true;
default:
break;
--- gcc/cp/decl.cc.jj 2025-07-09 08:16:50.886079378 +0200
+++ gcc/cp/decl.cc 2025-07-09 08:23:15.059898155 +0200
@@ -5082,6 +5082,18 @@ cxx_init_decl_processing (void)
BUILT_IN_FRONTEND, NULL, NULL_TREE);
set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF);
+ if (cxx_dialect >= cxx26)
+ {
+ tree void_ptrintftype
+ = build_function_type_list (void_type_node, ptr_type_node,
+ integer_type_node, NULL_TREE);
+ decl = add_builtin_function ("__builtin_eh_ptr_adjust_ref",
+ void_ptrintftype,
+ CP_BUILT_IN_EH_PTR_ADJUST_REF,
+ BUILT_IN_FRONTEND, NULL, NULL_TREE);
+ set_call_expr_flags (decl, ECF_NOTHROW | ECF_LEAF);
+ }
+
integer_two_node = build_int_cst (NULL_TREE, 2);
/* Guess at the initial static decls size. */
--- gcc/cp/pt.cc.jj 2025-07-09 08:16:50.901079175 +0200
+++ gcc/cp/pt.cc 2025-07-09 08:23:15.063898102 +0200
@@ -20147,7 +20147,14 @@ tsubst_stmt (tree t, tree args, tsubst_f
{
tree op0 = RECUR (TREE_OPERAND (t, 0));
tree cond = RECUR (MUST_NOT_THROW_COND (t));
- RETURN (build_must_not_throw_expr (op0, cond));
+ stmt = build_must_not_throw_expr (op0, cond);
+ if (stmt && TREE_CODE (stmt) == MUST_NOT_THROW_EXPR)
+ {
+ MUST_NOT_THROW_NOEXCEPT_P (stmt) = MUST_NOT_THROW_NOEXCEPT_P (t);
+ MUST_NOT_THROW_THROW_P (stmt) = MUST_NOT_THROW_THROW_P (t);
+ MUST_NOT_THROW_CATCH_P (stmt) = MUST_NOT_THROW_CATCH_P (t);
+ }
+ RETURN (stmt);
}
case EXPR_PACK_EXPANSION:
--- gcc/cp/cp-gimplify.cc.jj 2025-06-27 23:34:21.856899744 +0200
+++ gcc/cp/cp-gimplify.cc 2025-07-09 08:23:15.063898102 +0200
@@ -889,6 +889,12 @@ cp_gimplify_expr (tree *expr_p, gimple_s
(EXPR_LOCATION (*expr_p), call_expr_nargs (*expr_p),
&CALL_EXPR_ARG (*expr_p, 0));
break;
+ case CP_BUILT_IN_EH_PTR_ADJUST_REF:
+ error_at (EXPR_LOCATION (*expr_p),
+ "%qs used outside of constant expressions",
+ "__builtin_eh_ptr_adjust_ref");
+ *expr_p = void_node;
+ break;
default:
break;
}
--- gcc/testsuite/g++.dg/cpp0x/constexpr-throw.C.jj 2025-06-02 11:00:06.205524722 +0200
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-throw.C 2025-07-09 08:23:15.081897859 +0200
@@ -2,8 +2,8 @@
// Explicit { dg-require-effective-target exceptions_enabled } to avoid verify compiler messages FAILs for '-fno-exceptions'.
constexpr int may_throw(bool decide) {
- return decide ? 42 : throw -1; // { dg-error "throw" }
+ return decide ? 42 : throw -1; // { dg-error "throw" "" { target c++23_down } }
}
-constexpr int x = may_throw(false); // { dg-message "may_throw" }
-constexpr int y = may_throw(true);
+constexpr int x = may_throw(false); // { dg-message "may_throw" "" { target c++23_down } }
+constexpr int y = may_throw(true); // { dg-error "uncaught exception '-1'" "" { target c++26 } .-1 }
--- gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C.jj 2020-01-12 11:54:37.113402848 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C 2025-07-09 08:23:15.090897738 +0200
@@ -12,7 +12,7 @@ void
f2 ()
{
for (;;)
- constexpr bool b = ({ break; false; }) && false; // { dg-error "is not a constant expression" }
+ constexpr bool b = ({ break; false; }) && false; // { dg-error "'break' outside of a loop or 'switch'" }
}
constexpr bool
--- gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C.jj 2025-06-02 11:00:06.214524605 +0200
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C 2025-07-09 08:23:15.091897724 +0200
@@ -27,8 +27,8 @@ constexpr int fun(int n) {
case 0:
return 1;
default:
- throw; // { dg-error "not a constant expression" }
- }
+ throw; // { dg-error "not a constant expression" "" { target c++23_down } }
+ } // { dg-error "'void __cxa_rethrow\\\(\\\)' called with no caught exceptions active" "" { target c++26 } .-1 }
}
static_assert(fun(0), "");
--- gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C.jj 2025-06-02 11:00:06.217524566 +0200
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C 2025-07-09 08:23:15.098897630 +0200
@@ -18,7 +18,7 @@ struct H { short size () const { return
constexpr const char *data () const { return ""; } };
struct I { constexpr signed char size () const { return 0; }
const char *data () const { return ""; } };
-struct J { constexpr int size () const { return j ? throw 1 : 0; } // { dg-error "expression '<throw-expression>' is not a constant expression" }
+struct J { constexpr int size () const { return j ? throw 1 : 0; } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23_down } }
constexpr const char *data () const { return ""; };
constexpr J (int x) : j (x) {}
int j; };
@@ -114,6 +114,7 @@ foo ()
asm ((J (0)));
asm ("" :: (J (1)) (1)); // { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" }
+ // { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
asm ((M {}));
#if __cpp_constexpr_dynamic_alloc >= 201907L
asm ((N {})); // { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++20 } }
@@ -188,6 +189,7 @@ bar ()
asm ((J (0)));
asm ("" :: (J (1)) (1)); // { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" }
+ // { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
asm ((M {}));
#if __cpp_constexpr_dynamic_alloc >= 201907L
asm ((N {})); // { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++20 } }
@@ -272,7 +274,7 @@ namespace NN
#if __cplusplus >= 201402L
struct J {
static constexpr int size () { return 0; }
- static constexpr const char *data (int x = 0) { if (x) return nullptr; else throw 1; } }; // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++14 } }
+ static constexpr const char *data (int x = 0) { if (x) return nullptr; else throw 1; } }; // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target { c++14 && c++23_down } } }
#endif
#if __cpp_if_consteval >= 202106L
struct K {
@@ -284,12 +286,12 @@ namespace NN
static constexpr const char *data () { if consteval { return "test"; } else { throw 1; } }
};
struct M {
- static constexpr int size () { if consteval { throw 1; } else { return 4; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23 } }
+ static constexpr int size () { if consteval { throw 1; } else { return 4; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23_only } }
static constexpr const char *data () { return "test"; }
};
struct N {
static constexpr int size () { return 4; }
- static constexpr const char *data () { if consteval { throw 1; } else { return "test"; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23 } }
+ static constexpr const char *data () { if consteval { throw 1; } else { return "test"; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23_only } }
};
#endif
struct O { constexpr int operator () () const { return 12; } };
@@ -318,12 +320,15 @@ namespace NN
asm ((I {}));
#if __cplusplus >= 201402L
asm ((J {})); // { dg-error "constexpr string 'data\\\(\\\)' must be a core constant expression" "" { target c++14 } }
+ // { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
#endif
#if __cpp_if_consteval >= 202106L
asm ((K {}));
asm ((L {}));
asm ((M {})); // { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target c++23 } }
+ // { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
asm ((N {})); // { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++23 } }
+ // { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
#endif
asm ((Q {}));
asm ((R {}));
@@ -348,12 +353,15 @@ namespace NN
asm ((I {}));
#if __cplusplus >= 201402L
asm ((J {})); // { dg-error "constexpr string 'data\\\(\\\)' must be a core constant expression" "" { target c++14 } }
+ // { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
#endif
#if __cpp_if_consteval >= 202106L
asm ((K {}));
asm ((L {}));
asm ((M {})); // { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target c++23 } }
+ // { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
asm ((N {})); // { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++23 } }
+ // { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
#endif
asm ((Q {}));
asm ((R {}));
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh1.C.jj 2025-07-09 08:23:15.110897468 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh1.C 2025-07-09 08:23:15.110897468 +0200
@@ -0,0 +1,140 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct S {
+ constexpr S () : s (new int (0)) {}
+ constexpr S (int x) : s (new int (x)) {}
+ constexpr S (const S &x) : s (new int (*x.s)) {}
+ constexpr ~S () { delete s; }
+ int *s;
+};
+struct T : public S {
+ constexpr T () : S () {}
+ constexpr T (int x) : S (x) {}
+ constexpr T (const T &x) : S (*x.s) {}
+ constexpr ~T () {}
+};
+struct U : public T {
+ constexpr U () : T () {}
+ constexpr U (int x) : T (x) {}
+ constexpr U (const U &x) : T (*x.s) {}
+ constexpr ~U () {}
+};
+struct V : public T {
+ constexpr V () : T () {}
+ constexpr V (int x) : T (x) {}
+ constexpr V (const U &x) : T (*x.s) {}
+ constexpr ~V () {}
+};
+
+template <typename X>
+constexpr int
+foo (X x)
+{
+ try { throw x; }
+ catch (int &a) { return 42 + a; }
+ catch (const unsigned b) { return 43 + b; }
+ catch (const long &c) { return 44 + c; }
+ catch (bool d) { return 45 + d; }
+ catch (const U &e) { return 46 + *e.s; }
+ catch (const T &f) { return 47 + *f.s; }
+ catch (S g) { return 48 + *g.s; }
+ catch (int *const &h) { return 49; }
+ catch (long long *) { return 50; }
+ catch (const S *const &) { return 51; }
+ catch (...) { return 52; }
+}
+
+template <typename X>
+constexpr int
+bar (const X &x)
+{
+ throw x;
+}
+
+template <typename X>
+constexpr int
+baz (const X &x)
+{
+ try
+ {
+ try { bar (x); }
+ catch (int &a) { a += 80; throw; }
+ catch (long b) { b += 80; throw; }
+ catch (U &c) { c.s[0] += 82; throw; }
+ catch (V d) { d.s[0] += 83; throw; }
+ }
+ catch (int a) { return 42 + a; }
+ catch (const long &b) { return 43 + b; }
+ catch (S &c) { return 44 + c.s[0]; }
+ catch (long long d) { return 45 + d; }
+ catch (...) { return -1; }
+}
+
+constexpr int
+qux (int x, bool y = true)
+{
+ try
+ {
+ switch (x)
+ {
+ case 0: throw 42; break;
+ case 1: x = y ? throw 43 : 5; break;
+ case 2: x = -(throw 44, 6); break;
+ case 3: x = x + (throw 45, 7); break;
+ case 4: x = (throw 46, 8) + x; break;
+ case 5: x = (throw 47, y) ? 4 : 5; break;
+ case 6: x += (throw 48, y); break;
+ case 7: x = (double) (throw 49, y); break;
+ case 8: x = foo ((throw 50, x)); break;
+ default: break;
+ }
+ }
+ catch (int a) { return a; }
+ return -1;
+}
+
+constexpr int
+corge ()
+{
+ try { throw 0; }
+ catch (int *const &h) { return 49; }
+ catch (long long *) { return 50; }
+ catch (const S *const &) { return 51; }
+ catch (...) { return 52; }
+}
+
+static_assert (foo (12) == 54);
+static_assert (foo (12U) == 55);
+static_assert (foo (12L) == 56);
+static_assert (foo (false) == 45);
+static_assert (foo (true) == 46);
+static_assert (foo (U (12)) == 58);
+static_assert (foo (T (20)) == 67);
+static_assert (foo (S (30)) == 78);
+static_assert (foo (nullptr) == 49);
+static_assert (foo ((int *)nullptr) == 49);
+static_assert (foo ((long long *)nullptr) == 50);
+static_assert (foo ((const S *)nullptr) == 51);
+static_assert (foo ((const T *)nullptr) == 51);
+static_assert (foo ((const U *)nullptr) == 51);
+static_assert (foo (12ULL) == 52);
+static_assert (baz (5) == 127);
+static_assert (baz (6L) == 49);
+static_assert (baz (U (25)) == 151);
+static_assert (baz (V (26)) == 70);
+static_assert (baz (T (27)) == 71);
+static_assert (baz (S (28)) == 72);
+static_assert (baz (7LL) == 52);
+static_assert (baz (8ULL) == -1);
+static_assert (qux (0) == 42);
+static_assert (qux (1) == 43);
+static_assert (qux (2) == 44);
+static_assert (qux (3) == 45);
+static_assert (qux (4) == 46);
+static_assert (qux (5) == 47);
+static_assert (qux (6) == 48);
+static_assert (qux (7) == 49);
+static_assert (qux (8) == 50);
+static_assert (corge () == 52);
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh2.C.jj 2025-07-09 08:23:15.111897454 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh2.C 2025-07-09 08:23:15.111897454 +0200
@@ -0,0 +1,112 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct S {
+ constexpr S () : s (0) {}
+ constexpr S (int x) : s (x) { if (x == 42) throw 42; }
+ constexpr S (const S &x) : s (x.s) {}
+ constexpr ~S () noexcept (false) { if (s == 41) throw 41; }
+ constexpr const char *what () const noexcept { return "S"; }
+ int s;
+};
+struct T : public S {
+ constexpr T () {}
+ constexpr T (int x) : S (x) {}
+ constexpr T (const T &x) : S (x.s) {}
+ constexpr ~T () {}
+ constexpr const char *what () const noexcept { return "T"; }
+};
+struct U {
+ constexpr U () : u (0) {}
+ constexpr U (int x) : u (x) {}
+ constexpr U (const S &x) : u (0) {}
+ constexpr U (const U &x) : u (x.u) { if (u == 42) throw 43; }
+ constexpr ~U () {}
+ constexpr const char *what () const noexcept { return "U"; }
+ int u;
+};
+
+constexpr int
+foo (int x)
+{
+ if (x == 1)
+ throw 43;
+ return x;
+}
+
+constexpr int
+bar (int x) noexcept // { dg-error "'std::terminate' called" }
+{ // { dg-message "uncaught exception exited from 'noexcept' function 'constexpr int bar\\\(int\\\)'" "" { target *-*-* } .-1 }
+ return foo (x);
+}
+
+constexpr int
+baz (int x)
+{
+ switch (x)
+ {
+ case 0: throw 1; break;
+ case 1: try { x = bar (x); } catch (...) {} break; // { dg-message "in 'constexpr' expansion of" }
+ case 2: throw S (2); break;
+ case 3: try { throw S (42); } catch (int a) { if (a != 42) throw -1; } break;
+ case 4: try { S s (41); throw 2; } catch (...) {} break; // { dg-error "'std::terminate' called" }
+ case 5: return 5; // { dg-message "destructor exited with an exception" "" { target *-*-* } .-1 }
+ case 6:
+ try
+ {
+ throw S (5);
+ }
+ catch (const T &) {}
+ catch (int) {}
+ catch (const bool &) {}
+ catch (const T **const &) {}
+ break;
+ case 7: try { constexpr int y = foo (2); } catch (...) {} break;
+ case 8:
+ try
+ {
+ try
+ {
+ throw U ();
+ }
+ catch (U &u)
+ {
+ u.u = 42;
+ throw;
+ }
+ }
+ catch (U u) // { dg-error "'std::terminate' called" }
+ { // { dg-message "constructor exited with another exception while entering handler" "" { target *-*-* } .-1 }
+ }
+ break;
+ case 9:
+ try
+ {
+ throw U (S (41)); // { dg-error "'std::terminate' called" }
+ } // { dg-message "destructor exited with an exception" "" { target *-*-* } .-1 }
+ catch (...)
+ {
+ }
+ break;
+ }
+ return -1;
+}
+
+constexpr int
+qux (int x)
+{
+ try { constexpr int y = foo (1); } catch (...) {} // { dg-error "uncaught exception" }
+ return 0;
+}
+
+constexpr int a = baz (0); // { dg-error "uncaught exception" }
+constexpr int b = baz (1); // { dg-message "in 'constexpr' expansion of" }
+constexpr int c = baz (2); // { dg-error "uncaught exception" }
+constexpr int d = baz (3);
+constexpr int e = baz (4); // { dg-message "in 'constexpr' expansion of" }
+constexpr int f = baz (5);
+constexpr int g = baz (6); // { dg-error "uncaught exception" }
+constexpr int h = baz (7);
+constexpr int i = baz (8); // { dg-message "in 'constexpr' expansion of" }
+constexpr int j = baz (9); // { dg-message "in 'constexpr' expansion of" }
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh3.C.jj 2025-07-09 08:23:15.111897454 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh3.C 2025-07-09 08:23:15.111897454 +0200
@@ -0,0 +1,442 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+#include <exception>
+#include <new>
+#include <typeinfo>
+
+constexpr std::exception a;
+constexpr const char *b = a.what ();
+constexpr std::bad_exception c;
+constexpr const char *d = c.what ();
+constexpr std::bad_alloc e;
+constexpr const char *f = e.what ();
+constexpr std::bad_array_new_length g;
+constexpr const char *h = g.what ();
+constexpr std::bad_cast i;
+constexpr const char *j = i.what ();
+constexpr std::bad_typeid k;
+constexpr const char *l = k.what ();
+constexpr std::exception_ptr m = nullptr;
+static_assert (m == nullptr);
+constexpr std::exception_ptr n = std::current_exception ();
+static_assert (n == nullptr);
+constexpr std::exception_ptr o;
+static_assert (o == nullptr);
+constexpr std::nested_exception p;
+static_assert (p.nested_ptr () == nullptr);
+
+struct A { virtual ~A () {} };
+struct B { virtual void b (); };
+struct C { virtual void c (); };
+struct D : private B { virtual void d (); };
+struct E { virtual void e (); };
+struct F : D, E, private C { virtual void f (); };
+struct G { constexpr G () { if (std::uncaught_exceptions () != 0) asm (""); } };
+struct H { constexpr H () : h (0) {} constexpr ~H () { if (std::uncaught_exceptions () != h) asm (""); } int h; };
+struct I : std::nested_exception { };
+struct J { virtual ~J () noexcept = default; };
+struct K final { };
+struct L : J, std::nested_exception { };
+struct M { };
+struct N : I, L { };
+struct O : private std::nested_exception { };
+
+constexpr int
+foo (int x)
+{
+ if (std::uncaught_exceptions () != 0)
+ return -1;
+ switch (x)
+ {
+ case 0:
+ try
+ {
+ const std::type_info &s = typeid (*(A *) 0);
+ return -1;
+ }
+ catch (const std::bad_typeid &x)
+ {
+ if (std::uncaught_exceptions () != 0)
+ return -1;
+ const char *p = x.what ();
+ return 1;
+ }
+ catch (...)
+ {
+ return -1;
+ }
+ break;
+ case 1:
+ try
+ {
+ static constexpr F f;
+ D &d = dynamic_cast<D &>((B &) f);
+ return -1;
+ }
+ catch (std::bad_cast x)
+ {
+ const char *p = x.what ();
+ return 2;
+ }
+ catch (...)
+ {
+ return -1;
+ }
+ break;
+ case 2:
+ try
+ {
+ H h;
+ h.h = 1;
+ if (std::current_exception () != nullptr)
+ return -1;
+ throw G ();
+ }
+ catch (const G &g)
+ {
+ if (std::uncaught_exceptions () != 0)
+ return -1;
+ if (std::current_exception () == nullptr)
+ return -1;
+ return 3;
+ }
+ catch (...)
+ {
+ return -1;
+ }
+ break;
+ case 3:
+ try
+ {
+ decltype (sizeof 0) x = -64;
+ char (*a)[2] = new char[x][2];
+ delete[] a;
+ }
+ catch (std::bad_array_new_length x)
+ {
+ return 4;
+ }
+ break;
+ case 4:
+ try
+ {
+ int y = -1;
+ int *a = new int[y];
+ delete[] a;
+ }
+ catch (const std::bad_array_new_length &)
+ {
+ return 5;
+ }
+ break;
+ case 5:
+ try
+ {
+ int z = 1;
+ int *a = new int[z]{1, 2, 3};
+ delete[] a;
+ }
+ catch (std::bad_array_new_length &)
+ {
+ return 6;
+ }
+ break;
+ case 6:
+ {
+ std::exception_ptr b, d;
+ if (b != nullptr || d != nullptr)
+ return -1;
+ try
+ {
+ throw 1;
+ }
+ catch (int a)
+ {
+ if (a != 1)
+ return -1;
+ b = std::current_exception ();
+ if (b == nullptr)
+ return -1;
+ try
+ {
+ throw 2L;
+ }
+ catch (long int c)
+ {
+ if (c != 2L)
+ return -1;
+ d = std::current_exception ();
+ if (d == nullptr || b == d)
+ return -1;
+ }
+ if (std::current_exception () != b)
+ return -1;
+ }
+ if (std::current_exception () != nullptr)
+ return -1;
+ try
+ {
+ std::rethrow_exception (d);
+ }
+ catch (long int &e)
+ {
+ if (e != 2L)
+ return -1;
+ try
+ {
+ std::rethrow_exception (b);
+ }
+ catch (const int &f)
+ {
+ if (f != 1)
+ return -1;
+ try
+ {
+ std::rethrow_exception (d);
+ }
+ catch (const long int g)
+ {
+ if (g != 2L)
+ return -1;
+ try
+ {
+ std::rethrow_exception (b);
+ }
+ catch (int h)
+ {
+ if (h != 1)
+ return -1;
+ std::exception_ptr i (b);
+ std::exception_ptr j;
+ if (j != nullptr || i == nullptr || i != b || bool (j))
+ return -1;
+ j = i;
+ if (j != b || !bool (j))
+ return -1;
+ j = nullptr;
+ std::swap (i, j);
+ if (j == nullptr || j != b || i != nullptr)
+ return -1;
+ }
+ }
+ }
+ }
+ return 7;
+ }
+ case 7:
+ {
+ std::exception_ptr a = std::make_exception_ptr (42);
+ std::exception_ptr b = std::make_exception_ptr (std::exception ());
+ std::exception_ptr c
+ = std::make_exception_ptr (std::bad_array_new_length ());
+ try
+ {
+ std::rethrow_exception (a);
+ }
+ catch (int d)
+ {
+ if (d != 42)
+ return -1;
+ try
+ {
+ std::rethrow_exception (b);
+ }
+ catch (const std::exception &e)
+ {
+ const char *f = e.what ();
+ try
+ {
+ std::rethrow_exception (c);
+ }
+ catch (const std::bad_alloc &g)
+ {
+ try
+ {
+ throw;
+ }
+ catch (const std::bad_array_new_length &h)
+ {
+ const char *i = h.what ();
+ const char *j = g.what ();
+ }
+ }
+ }
+ }
+ return 8;
+ }
+ case 8:
+ {
+ std::nested_exception a;
+ if (a.nested_ptr () != nullptr)
+ return -1;
+ try
+ {
+ std::nested_exception b;
+ if (b.nested_ptr () != nullptr)
+ return -1;
+ throw 42;
+ }
+ catch (...)
+ {
+ std::nested_exception c;
+ if (c.nested_ptr () != std::current_exception ())
+ return -1;
+ std::nested_exception d = c;
+ if (d.nested_ptr () != c.nested_ptr ())
+ return -1;
+ c = d;
+ try
+ {
+ c.rethrow_nested ();
+ }
+ catch (const int &e)
+ {
+ if (e != 42)
+ return -1;
+ }
+ }
+ return 9;
+ }
+ case 9:
+ try
+ {
+ std::throw_with_nested (I ());
+ }
+ catch (const std::nested_exception &a)
+ {
+ if (a.nested_ptr () != nullptr)
+ return -1;
+ try
+ {
+ throw;
+ }
+ catch (const I &)
+ {
+ return 10;
+ }
+ }
+ return -1;
+ case 10:
+ try
+ {
+ std::throw_with_nested (J ());
+ }
+ catch (const std::nested_exception &a)
+ {
+ if (a.nested_ptr () != nullptr)
+ return -1;
+ try
+ {
+ throw;
+ }
+ catch (const J &)
+ {
+ return 11;
+ }
+ }
+ return -1;
+ case 11:
+ try
+ {
+ std::throw_with_nested (K ());
+ }
+ catch (const std::nested_exception &)
+ {
+ return -1;
+ }
+ catch (const K &)
+ {
+ return 12;
+ }
+ return -1;
+ case 12:
+ try
+ {
+ throw 42;
+ }
+ catch (...)
+ {
+ I a;
+ try
+ {
+ std::rethrow_if_nested (a);
+ }
+ catch (const int &b)
+ {
+ if (b == 42)
+ return 13;
+ }
+ }
+ return -1;
+ case 13:
+ try
+ {
+ throw J ();
+ }
+ catch (const J &a)
+ {
+ std::rethrow_if_nested (a);
+ return 14;
+ }
+ return -1;
+ case 14:
+ try
+ {
+ throw 42;
+ }
+ catch (...)
+ {
+ try
+ {
+ throw L ();
+ }
+ catch (const J &a)
+ {
+ try
+ {
+ std::rethrow_if_nested (a);
+ }
+ catch (const int &b)
+ {
+ if (b == 42)
+ return 15;
+ }
+ }
+ }
+ return -1;
+ case 15:
+ {
+ std::rethrow_if_nested (1);
+ M m;
+ std::rethrow_if_nested (m);
+ N n;
+ std::rethrow_if_nested (n);
+ O o;
+ std::rethrow_if_nested (o);
+ return 16;
+ }
+ default:
+ break;
+ }
+ return -1;
+}
+
+static_assert (foo (0) == 1);
+static_assert (foo (1) == 2);
+static_assert (foo (2) == 3);
+static_assert (foo (3) == 4);
+static_assert (foo (4) == 5);
+static_assert (foo (5) == 6);
+static_assert (foo (6) == 7);
+static_assert (foo (7) == 8);
+static_assert (foo (8) == 9);
+static_assert (foo (9) == 10);
+static_assert (foo (10) == 11);
+static_assert (foo (11) == 12);
+static_assert (foo (12) == 13);
+static_assert (foo (13) == 14);
+static_assert (foo (14) == 15);
+static_assert (foo (15) == 16);
+static_assert (std::uncaught_exceptions () == 0);
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh4.C.jj 2025-07-09 08:23:15.111897454 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh4.C 2025-07-09 08:23:15.111897454 +0200
@@ -0,0 +1,72 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct A { virtual ~A () {} };
+struct B { virtual void b (); };
+struct C { virtual void c (); };
+struct D : private B { virtual void d (); };
+struct E { virtual void e (); };
+struct F : D, E, private C { virtual void f (); };
+
+constexpr int
+foo (int x)
+{
+ switch (x)
+ {
+ case 1:
+ try
+ {
+ static constexpr F f;
+ D &d = dynamic_cast<D &>((B &) f); // { dg-error "called without 'std::bad_cast' being defined" }
+ return -1;
+ }
+ catch (...)
+ {
+ return -1;
+ }
+ break;
+ case 3:
+ try
+ {
+ decltype (sizeof 0) x = -64;
+ char (*a)[2] = new char[x][2]; // { dg-error "called without 'std::bad_array_new_length' being defined" }
+ delete[] a;
+ }
+ catch (...)
+ {
+ return -1;
+ }
+ break;
+ case 4:
+ try
+ {
+ int y = -1;
+ int *a = new int[y]; // { dg-error "called without 'std::bad_array_new_length' being defined" }
+ delete[] a;
+ }
+ catch (...)
+ {
+ return -1;
+ }
+ break;
+ case 5:
+ try
+ {
+ int z = 1;
+ int *a = new int[z]{1, 2, 3}; // { dg-error "called without 'std::bad_array_new_length' being defined" }
+ delete[] a;
+ }
+ catch (...)
+ {
+ return -1;
+ }
+ break;
+ }
+ return -1;
+}
+
+constexpr int a = foo (1); // { dg-message "in 'constexpr' expansion of" }
+constexpr int b = foo (3); // { dg-message "in 'constexpr' expansion of" }
+constexpr int c = foo (4); // { dg-message "in 'constexpr' expansion of" }
+constexpr int d = foo (5); // { dg-message "in 'constexpr' expansion of" }
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh5.C.jj 2025-07-09 08:23:15.112897441 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh5.C 2025-07-09 08:23:15.111897454 +0200
@@ -0,0 +1,55 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+constexpr void
+foo ()
+{
+ throw 1;
+}
+
+void
+bar ()
+{
+}
+
+constexpr void
+baz ()
+{
+ foo ();
+ bar ();
+}
+
+constexpr void
+qux ()
+{
+ if consteval {
+ throw 2;
+ }
+ bar ();
+}
+
+constexpr bool
+corge ()
+{
+ try
+ {
+ baz ();
+ }
+ catch (int a)
+ {
+ if (a != 1)
+ return false;
+ try
+ {
+ qux ();
+ }
+ catch (int b)
+ {
+ return b == 2;
+ }
+ }
+ return false;
+}
+
+static_assert (corge ());
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh6.C.jj 2025-07-09 08:23:15.112897441 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh6.C 2025-07-09 08:23:15.112897441 +0200
@@ -0,0 +1,134 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct S {
+ constexpr S () : s (0) {}
+ constexpr S (int x) : s (x) {}
+ constexpr S (const S &x) : s (x.s) {}
+ constexpr ~S () {}
+ int s;
+};
+struct T {
+ constexpr T () : t (0) {}
+ constexpr T (int x) : t (x) {}
+ constexpr T (const T &x) : t (x.t) {}
+ constexpr ~T () {}
+ int t;
+};
+struct U : public S, public T {
+ constexpr U () : S (0), T (0) {}
+ constexpr U (int x, int y) : S (x), T (y) {}
+ constexpr U (const U &x) : S (x.s), T (x.t) {}
+ constexpr ~U () {}
+};
+
+constexpr bool
+foo ()
+{
+ try
+ {
+ throw U (1, 2);
+ }
+ catch (const U &x)
+ {
+ if (x.s != 1 || x.t != 2)
+ return false;
+ try
+ {
+ throw;
+ }
+ catch (const S &y)
+ {
+ if (y.s != 1)
+ return false;
+ try
+ {
+ throw;
+ }
+ catch (const T &z)
+ {
+ if (z.t != 2)
+ return false;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+constexpr bool
+bar ()
+{
+ try
+ {
+ throw U (1, 2);
+ }
+ catch (U &x)
+ {
+ if (x.s != 1 || x.t != 2)
+ return false;
+ try
+ {
+ x.s = 3;
+ x.t = 4;
+ throw;
+ }
+ catch (S &y)
+ {
+ if (y.s != 3)
+ return false;
+ try
+ {
+ throw;
+ }
+ catch (T &z)
+ {
+ if (z.t != 4)
+ return false;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+constexpr bool
+baz ()
+{
+ try
+ {
+ throw U (1, 2);
+ }
+ catch (U x)
+ {
+ if (x.s != 1 || x.t != 2)
+ return false;
+ try
+ {
+ x.s = 3;
+ x.t = 4;
+ throw;
+ }
+ catch (S y)
+ {
+ if (y.s != 1)
+ return false;
+ try
+ {
+ throw;
+ }
+ catch (T z)
+ {
+ if (z.t != 2)
+ return false;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static_assert (foo ());
+static_assert (bar ());
+static_assert (baz ());
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh7.C.jj 2025-07-09 08:23:15.112897441 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh7.C 2025-07-09 08:23:15.112897441 +0200
@@ -0,0 +1,151 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+constexpr char p[] = "hello";
+constexpr const char *q[] = { &p[0], &p[3] };
+constexpr const char *const *r = &q[0];
+const char *s[] = { &p[0], &p[3] };
+constexpr const char **t = &s[0];
+
+constexpr bool
+foo ()
+{
+ try
+ {
+ throw t;
+ }
+ catch (const char **const &x)
+ {
+ if (x != t)
+ return false;
+ try
+ {
+ throw;
+ }
+ catch (const char **&y)
+ {
+ if (y != t)
+ return false;
+ try
+ {
+ throw;
+ }
+ catch (const char **z)
+ {
+ if (z != t)
+ return false;
+ try
+ {
+ throw;
+ }
+ catch (const char *const *const &v)
+ {
+ if (v != (const char *const *) t)
+ return false;
+ try
+ {
+ throw;
+ }
+ catch (const char *const *w)
+ {
+ if (w != (const char *const *) t)
+ return false;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+constexpr bool
+bar ()
+{
+ try
+ {
+ throw nullptr;
+ }
+ catch (const char **const &x)
+ {
+ if (x != nullptr)
+ return false;
+ try
+ {
+ throw;
+ }
+ catch (const char **&y)
+ {
+ if (y != nullptr)
+ return false;
+ try
+ {
+ throw;
+ }
+ catch (const char **z)
+ {
+ if (z != nullptr)
+ return false;
+ try
+ {
+ throw;
+ }
+ catch (const char *const *const &v)
+ {
+ if (v != nullptr)
+ return false;
+ try
+ {
+ throw;
+ }
+ catch (const char *const *w)
+ {
+ if (w != nullptr)
+ return false;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+constexpr bool
+baz ()
+{
+ try
+ {
+ throw r;
+ }
+ catch (const char *const *const &x)
+ {
+ if (x != r || **x != 'h')
+ return false;
+ try
+ {
+ throw;
+ }
+ catch (const char *const *&y)
+ {
+ if (y != r || **y != 'h')
+ return false;
+ try
+ {
+ throw;
+ }
+ catch (const char *const *z)
+ {
+ if (z != r || **z != 'h')
+ return false;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static_assert (foo ());
+static_assert (bar ());
+static_assert (baz ());
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh8.C.jj 2025-07-09 08:23:15.112897441 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh8.C 2025-07-09 08:23:15.112897441 +0200
@@ -0,0 +1,36 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+#include <exception>
+
+constexpr std::exception_ptr
+foo ()
+{
+ try
+ {
+ throw 42;
+ }
+ catch (...)
+ {
+ return std::current_exception ();
+ }
+}
+
+constexpr bool
+bar ()
+{
+ try
+ {
+ std::rethrow_exception (foo ());
+ }
+ catch (const int &a)
+ {
+ return a == 42;
+ }
+ return false;
+}
+
+static_assert (bar ());
+constexpr std::exception_ptr a = foo (); // { dg-error "is not a constant expression because it refers to exception object allocated with '__cxa_allocate_exception'" }
+constexpr std::exception_ptr b = std::make_exception_ptr (42ULL); // { dg-error "is not a constant expression because it refers to exception object allocated with '__cxa_allocate_exception'" }
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh9.C.jj 2025-07-09 08:23:15.113897427 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh9.C 2025-07-09 10:03:05.906144072 +0200
@@ -0,0 +1,127 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+namespace std
+{
+ struct exception
+ {
+ constexpr exception () noexcept { }
+ constexpr virtual ~exception () noexcept {}
+ constexpr exception (const exception &) = default;
+ constexpr exception &operator= (const exception &) = default;
+ constexpr exception (exception &&) = default;
+ constexpr exception &operator= (exception &&) = default;
+ constexpr virtual const char *what () const noexcept
+ { return "std::exception"; }
+ };
+}
+
+struct S : public std::exception {
+ constexpr S () : s (0) {}
+ constexpr S (int x) : s (x) {}
+ constexpr S (const S &x) : s (x.s) {}
+ constexpr virtual ~S () {}
+ constexpr virtual const char *what () noexcept { return "this is S"; }
+ int s;
+};
+struct T : public std::exception {
+ constexpr T () : s (new char[1]), t (0) { s[0] = '\0'; }
+ constexpr T (const char *p, int q) : s (new char[q + 1]), t (q)
+ {
+ for (int i = 0; i <= t; ++i)
+ s[i] = p[i];
+ }
+ constexpr T (const T &x) : s (new char[x.t + 1]), t (x.t)
+ {
+ for (int i = 0; i <= t; ++i)
+ s[i] = x.s[i];
+ }
+ constexpr virtual ~T () { delete[] s; }
+ constexpr virtual const char *what () noexcept { return s; }
+ char *s;
+ int t;
+};
+struct U {
+ constexpr U () : x (0), y (0), z (0) {}
+ constexpr U (int a, long b, unsigned long long c) : x (a), y (b), z (c) {}
+ constexpr U (const U &u) = default;
+ int x;
+ long y;
+ unsigned long long z;
+};
+struct V {
+ constexpr V () : v (0) {}
+ constexpr V (int x) : v (x) {}
+ constexpr V (const V &x) : v (x.v) {}
+ constexpr virtual ~V () {}
+ constexpr virtual const char *what () noexcept { return "this is V"; }
+ int v;
+};
+
+constexpr int
+foo (int x)
+{
+ if (x == 1)
+ throw S (42);
+ else if (x == 2)
+ throw T ("hello, world", sizeof ("hello, world") - 1);
+ else if (x == 3)
+ throw U (1, -2L, 42ULL);
+ else if (x == 4)
+ throw 42;
+ else if (x == 5)
+ throw 1.0;
+ else if (x == 6)
+ throw V (42);
+ else
+ return 42;
+}
+
+constexpr int
+bar (int x) noexcept
+// { dg-error "'std::terminate' called after throwing an exception of type 'S'; 'what\\\(\\\)': 'this is S'" "" { target *-*-* } .-1 }
+// { dg-message "uncaught exception exited from 'noexcept' function 'constexpr int bar\\\(int\\\)'" "" { target *-*-* } .-2 }
+// { dg-error "'std::terminate' called after throwing an exception of type 'T'; 'what\\\(\\\)': 'hello, world'" "" { target *-*-* } .-3 }
+// { dg-error "'std::terminate' called after throwing an exception 'U\\\{1, -2, 42\\\}'" "" { target *-*-* } .-4 }
+// { dg-error "'std::terminate' called after throwing an exception '42'" "" { target *-*-* } .-5 }
+// { dg-error "'std::terminate' called after throwing an exception '1\\\.0e\\\+0'" "" { target *-*-* } .-6 }
+// { dg-error "'std::terminate' called after throwing an exception 'V\\\{\[^\n\r]*42\\\}" "" { target *-*-* } .-7 }
+{
+ return foo (x);
+}
+
+constexpr int
+baz (int x)
+{
+ try
+ {
+ return foo (x);
+ }
+ catch (...)
+ {
+ return -1;
+ }
+}
+
+static_assert (bar (0) == 42);
+constexpr int a = bar (1); // { dg-message "in 'constexpr' expansion of" }
+constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" }
+constexpr int c = bar (3); // { dg-message "in 'constexpr' expansion of" }
+constexpr int d = bar (4); // { dg-message "in 'constexpr' expansion of" }
+constexpr int e = bar (5); // { dg-message "in 'constexpr' expansion of" }
+constexpr int f = bar (6); // { dg-message "in 'constexpr' expansion of" }
+static_assert (baz (0) == 42);
+static_assert (baz (1) == -1);
+static_assert (baz (2) == -1);
+static_assert (baz (3) == -1);
+static_assert (baz (4) == -1);
+static_assert (baz (5) == -1);
+static_assert (baz (6) == -1);
+static_assert (foo (0) == 42);
+constexpr int g = foo (1); // { dg-error "uncaught exception of type 'S'; 'what\\\(\\\)': 'this is S'" }
+constexpr int h = foo (2); // { dg-error "uncaught exception of type 'T'; 'what\\\(\\\)': 'hello, world'" }
+constexpr int i = foo (3); // { dg-error "uncaught exception 'U\\\{1, -2, 42\\\}'" }
+constexpr int j = foo (4); // { dg-error "uncaught exception '42'" }
+constexpr int k = foo (5); // { dg-error "uncaught exception '1\\\.0e\\\+0'" }
+constexpr int l = foo (6); // { dg-error "uncaught exception 'V\\\{\[^\n\r]*42\\\}'" }
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh10.C.jj 2025-07-09 08:23:15.113897427 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh10.C 2025-07-09 08:23:15.113897427 +0200
@@ -0,0 +1,110 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct S {
+};
+struct T {
+ constexpr ~T () noexcept (false) { throw S {}; }
+};
+struct U {
+ int u;
+};
+struct V {
+ int v;
+ constexpr V (int x)
+ try : v { x }
+ {
+ if (v > 42)
+ throw U { 42 };
+ }
+ catch (U &u)
+ {
+ --u.u;
+ }
+};
+struct W {
+ constexpr ~W () { ++w; }
+ int &w;
+};
+struct X : public V {
+ constexpr X (int x)
+ try : V(x)
+ {
+ }
+ catch (U &u)
+ {
+ --u.u;
+ }
+};
+
+constexpr int
+foo (bool x)
+{
+ try
+ {
+ T t; // { dg-error "'std::terminate' called after throwing an exception '42'" }
+ if (x) // { dg-message "destructor exited with an exception" "" { target *-*-* } .-1 }
+ throw 42;
+ return 10;
+ }
+ catch (S)
+ {
+ return 11;
+ }
+}
+
+constexpr int
+bar ()
+{
+ V v { 42 };
+ try
+ {
+ V w { 43 };
+ }
+ catch (const U &u)
+ {
+ if (u.u == 41)
+ return 44;
+ }
+ return -1;
+}
+
+constexpr int
+baz ()
+{
+ int i = 42;
+ try
+ {
+ W w { i };
+ throw S ();
+ }
+ catch (...)
+ {
+ if (i == 43)
+ return 42;
+ }
+ return -1;
+}
+
+constexpr int
+qux ()
+{
+ X v { 42 };
+ try
+ {
+ X w { 43 };
+ }
+ catch (const U &u)
+ {
+ if (u.u == 40)
+ return 48;
+ }
+ return -1;
+}
+
+static_assert (foo (false) == 11);
+constexpr int a = foo (true); // { dg-message "in 'constexpr' expansion of" }
+static_assert (bar () == 44);
+static_assert (baz () == 42);
+static_assert (qux () == 48);
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh11.C.jj 2025-07-09 08:23:15.113897427 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh11.C 2025-07-09 08:23:15.113897427 +0200
@@ -0,0 +1,69 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct A {
+ explicit constexpr A (int x) noexcept : a (x) {}
+ constexpr virtual int foo () const noexcept { return a; }
+ constexpr virtual ~A () {}
+ int a;
+};
+struct B : public A {
+ explicit constexpr B (int x) noexcept : A (x) {}
+ constexpr int foo () const noexcept override { return a | 0x10; }
+};
+struct C : public A {
+ explicit constexpr C (int x) noexcept : A (x) {}
+};
+struct D : public A {
+ explicit constexpr D (int x) noexcept : A (x) {}
+};
+struct E {
+ constexpr E () noexcept : e (0) {}
+ explicit constexpr E (int x) noexcept : e (x) {}
+ int e;
+};
+struct F : public E, public B {
+ explicit constexpr F (int x) noexcept : B (x) {}
+};
+struct G : public E, public C {
+ explicit constexpr G (int x) noexcept : C (x) {}
+};
+struct H : public E, public D {
+ explicit constexpr H (int x) noexcept : D (x) {}
+};
+
+consteval int
+bar (void (*fn) ())
+{
+ try
+ {
+ fn ();
+ }
+ catch (C &a)
+ {
+ return a.foo () | 0x20;
+ }
+ catch (const C &b) // { dg-warning "exception of type 'C' will be caught by earlier handler" }
+ {
+ return b.foo () | 0x60;
+ }
+ catch (A &c)
+ {
+ return c.foo ();
+ }
+ catch (const A &d) // { dg-warning "exception of type 'A' will be caught by earlier handler" }
+ {
+ return d.foo () | 0x40;
+ }
+ return -1;
+}
+
+static_assert (bar ([] { throw A { 1 }; }) == 1);
+static_assert (bar ([] { throw B { 2 }; }) == 0x12);
+static_assert (bar ([] { throw C { 3 }; }) == 0x23);
+static_assert (bar ([] { throw D { 4 }; }) == 4);
+constexpr int a = bar ([] { throw E { 5 }; }); // { dg-error "uncaught exception 'E\\\{5\\\}'" }
+static_assert (bar ([] { throw F { 6 }; }) == 0x16);
+static_assert (bar ([] { throw G { 7 }; }) == 0x27);
+static_assert (bar ([] { throw H { 8 }; }) == 8);
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh12.C.jj 2025-07-09 08:23:15.113897427 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh12.C 2025-07-09 08:23:15.113897427 +0200
@@ -0,0 +1,74 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+struct A {
+ explicit constexpr A (int x) noexcept : a (x) {}
+ constexpr virtual int foo () const noexcept { return a; }
+ constexpr virtual ~A () {}
+ int a;
+};
+struct B : public A {
+ explicit constexpr B (int x) noexcept : A (x) {}
+ constexpr int foo () const noexcept override { return a | 0x10; }
+};
+struct C : public A {
+ explicit constexpr C (int x) noexcept : A (x) {}
+};
+struct D : public A {
+ explicit constexpr D (int x) noexcept : A (x) {}
+};
+struct E {
+ constexpr E () noexcept : e (0) {}
+ explicit constexpr E (int x) noexcept : e (x) {}
+ int e;
+};
+struct F : public E, public B {
+ explicit constexpr F (int x) noexcept : B (x) {}
+};
+struct G : public E, public C {
+ explicit constexpr G (int x) noexcept : C (x) {}
+};
+struct H : public E, public D {
+ explicit constexpr H (int x) noexcept : D (x) {}
+};
+
+consteval int
+bar (void (*fn) ())
+{
+ int r = 0;
+ try
+ {
+ fn ();
+ }
+ catch (C *a)
+ {
+ r = a->foo () | 0x20;
+ delete a;
+ }
+ catch (const C *b)
+ {
+ r = b->foo () | 0x60;
+ delete b;
+ }
+ catch (A *c)
+ {
+ r = c->foo ();
+ delete c;
+ }
+ catch (const A *d)
+ {
+ r = d->foo () | 0x40;
+ delete d;
+ }
+ return r;
+}
+
+static_assert (bar ([] { throw new A { 1 }; }) == 1);
+static_assert (bar ([] { throw new B { 2 }; }) == 0x12);
+static_assert (bar ([] { throw new C { 3 }; }) == 0x23);
+static_assert (bar ([] { throw new D { 4 }; }) == 4);
+constexpr int a = bar ([] { throw new E { 5 }; }); // { dg-error "uncaught exception of type 'E\\\*'" }
+static_assert (bar ([] { throw new F { 6 }; }) == 0x16);
+static_assert (bar ([] { throw new G { 7 }; }) == 0x27);
+static_assert (bar ([] { throw new H { 8 }; }) == 8);
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh13.C.jj 2025-07-09 08:23:15.113897427 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh13.C 2025-07-09 08:23:15.113897427 +0200
@@ -0,0 +1,36 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+template <typename T>
+consteval T
+foo (T x)
+{
+ try
+ {
+ throw &x;
+ }
+ catch (void *ptr) // { dg-message "for type 'void\\\*'" }
+ {
+ return *static_cast<T *> (ptr) | 0x10;
+ }
+ catch (const void *ptr) // { dg-message "for type 'const void\\\*'" }
+ {
+ return *static_cast<const T *> (ptr) | 0x20;
+ }
+ catch (T *ptr) // { dg-warning "exception of type 'T\\\*' will be caught by earlier handler" }
+ { // { dg-warning "exception of type 'int\\\*' will be caught by earlier handler" "" { target *-*-* } .-1 }
+ return *ptr | 0x30; // { dg-warning "exception of type 'long long unsigned int\\\*' will be caught by earlier handler" "" { target *-*-* } .-2 }
+ }
+ catch (const T *ptr)
+ {
+ return *ptr | 0x40;
+ }
+ catch (...)
+ {
+ return -1;
+ }
+}
+
+static_assert (foo (1) == 0x11);
+static_assert (foo (2ULL) == 0x12ULL);
--- gcc/testsuite/g++.dg/cpp26/constexpr-eh14.C.jj 2025-07-09 08:23:15.113897427 +0200
+++ gcc/testsuite/g++.dg/cpp26/constexpr-eh14.C 2025-07-09 08:23:15.113897427 +0200
@@ -0,0 +1,42 @@
+// C++26 P3068R5 - Allowing exception throwing in constant-evaluation
+// { dg-do compile { target c++26 } }
+// { dg-require-effective-target exceptions_enabled }
+
+template <typename T>
+constexpr T
+foo (T x, auto... y)
+{
+ const T z[] = { x, y... };
+ try
+ {
+ throw z;
+ }
+ catch (const T (&a)[4])
+ {
+ return T ();
+ }
+ catch (const T *b)
+ {
+ return b[0];
+ }
+ catch (...)
+ {
+ return T ();
+ }
+}
+
+void
+bar ()
+{
+}
+
+void
+baz ()
+{
+}
+
+static_assert (foo (42, 43, 44, 45, 46) == 42);
+static_assert (foo (43U, 44U, 45U, 46U) == 43U);
+static_assert (foo (44LL, 45LL) == 44LL);
+static_assert (foo (bar, baz, bar, baz) == bar);
+static_assert (foo (baz, bar) == baz);
--- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj 2025-07-09 08:23:15.125897265 +0200
+++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 2025-07-09 08:27:59.517063090 +0200
@@ -624,19 +624,25 @@
#endif
#ifndef __cpp_pack_indexing
-# error "__cpp_pack_indexing"
+# error "__cpp_pack_indexing"
#elif __cpp_pack_indexing != 202311
# error "__cpp_pack_indexing != 202311"
#endif
#ifndef __cpp_pp_embed
-# error "__cpp_pp_embed"
+# error "__cpp_pp_embed"
#elif __cpp_pp_embed != 202502
# error "__cpp_pp_embed != 202502"
#endif
#ifndef __cpp_constexpr_virtual_inheritance
-# error "__cpp_constexpr_virtual_inheritance"
+# error "__cpp_constexpr_virtual_inheritance"
#elif __cpp_constexpr_virtual_inheritance != 202506
# error "__cpp_constexpr_virtual_inheritance != 202506"
#endif
+
+#ifndef __cpp_constexpr_exceptions
+# error "__cpp_constexpr_exceptions"
+#elif __cpp_constexpr_exceptions != 202411
+# error "__cpp_constexpr_exceptions != 202411"
+#endif
--- gcc/testsuite/g++.dg/cpp26/static_assert1.C.jj 2025-06-02 11:00:06.220524528 +0200
+++ gcc/testsuite/g++.dg/cpp26/static_assert1.C 2025-07-09 08:23:15.141897050 +0200
@@ -51,7 +51,7 @@ static_assert (true, H {}); // { dg-warn
struct I { constexpr signed char size () const { return 0; }
const char *data () const { return ""; } };
static_assert (true, I {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
-struct J { constexpr int size () const { return j ? throw 1 : 0; } // { dg-error "expression '<throw-expression>' is not a constant expression" }
+struct J { constexpr int size () const { return j ? throw 1 : 0; } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23_down } }
constexpr const char *data () const { return ""; };
constexpr J (int x) : j (x) {}
int j; };
@@ -60,6 +60,7 @@ static_assert (false, J (0)); // { dg-wa
// { dg-error "static assertion failed" "" { target *-*-* } .-1 }
static_assert (false, J (1)); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
// { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target *-*-* } .-1 }
+ // { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
struct K { constexpr operator int () { return 4; } };
struct L { constexpr operator const char * () { return "test"; } };
struct M { constexpr K size () const { return {}; }
@@ -261,10 +262,11 @@ namespace NN
#if __cplusplus >= 201402L
struct J {
static constexpr int size () { return 0; }
- static constexpr const char *data (int x = 0) { if (x) return nullptr; else throw 1; } }; // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++14 } }
+ static constexpr const char *data (int x = 0) { if (x) return nullptr; else throw 1; } }; // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target { c++14 && c++23_down } } }
static_assert (true, J{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++14 && c++23_down } } }
static_assert (false, J{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++14 && c++23_down } } }
// { dg-error "constexpr string 'data\\\(\\\)' must be a core constant expression" "" { target c++14 } .-1 }
+ // { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
#endif
#if __cpp_if_consteval >= 202106L
struct K {
@@ -282,19 +284,21 @@ namespace NN
static_assert (false, L{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
// { dg-error "static assertion failed: test" "" { target c++23 } .-1 }
struct M {
- static constexpr int size () { if consteval { throw 1; } else { return 4; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23 } }
+ static constexpr int size () { if consteval { throw 1; } else { return 4; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23_only } }
static constexpr const char *data () { return "test"; }
};
static_assert (true, M{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
static_assert (false, M{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
// { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target c++23 } .-1 }
+ // { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
struct N {
static constexpr int size () { return 4; }
- static constexpr const char *data () { if consteval { throw 1; } else { return "test"; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23 } }
+ static constexpr const char *data () { if consteval { throw 1; } else { return "test"; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23_only } }
};
static_assert (true, N{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
static_assert (false, N{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
// { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++23 } .-1 }
+ // { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
#endif
struct O { constexpr int operator () () const { return 12; } };
struct P { constexpr const char *operator () () const { return "another test"; } };
--- gcc/testsuite/g++.dg/cpp2a/consteval34.C.jj 2025-06-02 11:00:06.222524502 +0200
+++ gcc/testsuite/g++.dg/cpp2a/consteval34.C 2025-07-09 08:23:15.154896874 +0200
@@ -1,42 +1,51 @@
// { dg-do compile { target c++20 } }
// Explicit { dg-require-effective-target exceptions_enabled } to avoid verify compiler messages FAILs for '-fno-exceptions'.
-consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is not a constant expression" }
+consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is not a constant expression" "" { target c++23_down } }
constexpr int
foo (bool b)
{
- return b ? bar (3) : 2; // { dg-message "in .constexpr. expansion" }
+ return b ? bar (3) : 2; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
-}
+} // { dg-error "uncaught exception '1'" "" { target c++26 } }
static_assert (foo (false) == 2);
-__extension__ constexpr int g1 = false ?: bar (2); // { dg-message "in .constexpr. expansion" }
-__extension__ constexpr int g2 = false ?: (1 + bar (2)); // { dg-message "in .constexpr. expansion" }
+__extension__ constexpr int g1 = false ?: bar (2); // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
+__extension__ constexpr int g2 = false ?: (1 + bar (2)); // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
__extension__ constexpr int g3 = true ?: bar (2);
__extension__ constexpr int g4 = true ?: (1 + bar (2));
-constexpr int g5 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" }
-constexpr int g6 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" }
+constexpr int g5 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
+constexpr int g6 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-1 }
void
g ()
{
- __extension__ int a1[bar(3)]; // { dg-message "in .constexpr. expansion" }
+ __extension__ int a1[bar(3)]; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
int a2[sizeof (bar(3))];
- int a3 = false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" }
+ int a3 = false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
- a3 += false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
+ a3 += false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
- __extension__ int a4 = false ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" }
+ __extension__ int a4 = false ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
- __extension__ int a5 = true ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
+ __extension__ int a5 = true ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
- int a6 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
+ int a6 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
- int a7 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" }
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
+ int a7 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" "" { target c++23_down } }
// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
-}
+// { dg-error "uncaught exception '1'" "" { target c++26 } .-2 }
+} // { dg-error "uncaught exception '1'" "" { target c++26 } }
--- gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C.jj 2025-06-02 11:00:06.222524502 +0200
+++ gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C 2025-07-09 08:23:15.155896861 +0200
@@ -4,13 +4,13 @@
struct fixed_string {
consteval int size(int n) const {
- if (n < 0) throw; // { dg-error "not a constant" }
- return n;
+ if (n < 0) throw; // { dg-error "not a constant" "" { target c++23_down } }
+ return n; // { dg-error "'void __cxa_rethrow\\\(\\\)' called with no caught exceptions active" "" { target c++26 } .-1 }
}
static consteval int size_static(int n) {
- if (n < 0) throw; // { dg-error "not a constant" }
- return n;
+ if (n < 0) throw; // { dg-error "not a constant" "" { target c++23_down } }
+ return n; // { dg-error "'void __cxa_rethrow\\\(\\\)' called with no caught exceptions active" "" { target c++26 } .-1 }
}
consteval void operator()() const { }
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C.jj 2025-06-02 11:00:06.223524489 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C 2025-07-09 08:23:15.155896861 +0200
@@ -4,6 +4,22 @@
// From clang's constant-expression-cxx2a.cpp.
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+ struct exception {
+ constexpr exception () noexcept {}
+ constexpr virtual ~exception () noexcept {}
+ constexpr exception (const exception &) = default;
+ constexpr exception &operator= (const exception &) = default;
+ constexpr virtual const char *what () const noexcept { return "std::exception"; }
+ };
+ struct bad_cast : public exception {
+ constexpr virtual ~bad_cast () noexcept {}
+ constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+ };
+}
+#endif
+
struct A2 { virtual void a2(); };
struct A : A2 { virtual void a(); };
struct B : A {};
@@ -26,31 +42,36 @@ static_assert(dynamic_cast<const A*>(sta
static_assert(g.f == (void*)(F*)&g);
static_assert(dynamic_cast<const void*>(static_cast<const D*>(&g)) == &g);
-constexpr int d_a = (dynamic_cast<const A&>(static_cast<const D&>(g)), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target *-*-* } .-1 }
-
+constexpr int d_a = (dynamic_cast<const A&>(static_cast<const D&>(g)), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
// Can navigate from A2 to its A...
static_assert(&dynamic_cast<A&>((A2&)(B&)g) == &(A&)(B&)g);
// ... and from B to its A ...
static_assert(&dynamic_cast<A&>((B&)g) == &(A&)(B&)g);
// ... but not from D.
-static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g); // { dg-error "non-constant condition for static assertion|reference .dynamic_cast. failed" }
-// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target *-*-* } .-1 }
-
+static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g); // { dg-error "non-constant condition for static assertion" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target c++23_down } .-1 }
+// { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } .-2 }
+// { dg-error "uncaught exception" "" { target c++26 } .-3 }
// Can cast from A2 to sibling class D.
static_assert(&dynamic_cast<D&>((A2&)(B&)g) == &(D&)g);
// Cannot cast from private base E to derived class F.
-constexpr int e_f = (dynamic_cast<F&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target *-*-* } .-1 }
+constexpr int e_f = (dynamic_cast<F&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
// Cannot cast from B to private sibling E.
-constexpr int b_e = (dynamic_cast<E&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .E." "" { target *-*-* } .-1 }
+constexpr int b_e = (dynamic_cast<E&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .E." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
struct Unrelated { virtual void unrelated(); };
-constexpr int b_unrelated = (dynamic_cast<Unrelated&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .Unrelated." "" { target *-*-* } .-1 }
-constexpr int e_unrelated = (dynamic_cast<Unrelated&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target *-*-* } .-1 }
+constexpr int b_unrelated = (dynamic_cast<Unrelated&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .Unrelated." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr int e_unrelated = (dynamic_cast<Unrelated&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C.jj 2025-06-02 11:00:06.223524489 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C 2025-07-09 08:23:15.159896807 +0200
@@ -1,6 +1,22 @@
// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
// { dg-do compile { target c++20 } }
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+ struct exception {
+ constexpr exception () noexcept {}
+ constexpr virtual ~exception () noexcept {}
+ constexpr exception (const exception &) = default;
+ constexpr exception &operator= (const exception &) = default;
+ constexpr virtual const char *what () const noexcept { return "std::exception"; }
+ };
+ struct bad_cast : public exception {
+ constexpr virtual ~bad_cast () noexcept {}
+ constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+ };
+}
+#endif
+
// Private base.
struct P1 { virtual void p1(); };
@@ -12,14 +28,16 @@ struct A : B, C, private P2 { virtual vo
constexpr A a;
// P1 is a non-public base of A.
-constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
// Don't error here.
static_assert (dynamic_cast<B*>((P1*)&a) == nullptr);
-constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
static_assert (dynamic_cast<C*>((P1*)&a) == nullptr);
static_assert (dynamic_cast<C*>((P2*)&a) == nullptr);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C.jj 2025-06-02 11:00:06.223524489 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C 2025-07-09 08:23:15.159896807 +0200
@@ -1,6 +1,22 @@
// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
// { dg-do compile { target c++20 } }
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+ struct exception {
+ constexpr exception () noexcept {}
+ constexpr virtual ~exception () noexcept {}
+ constexpr exception (const exception &) = default;
+ constexpr exception &operator= (const exception &) = default;
+ constexpr virtual const char *what () const noexcept { return "std::exception"; }
+ };
+ struct bad_cast : public exception {
+ constexpr virtual ~bad_cast () noexcept {}
+ constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+ };
+}
+#endif
+
// Protected base.
struct P1 { virtual void p1(); };
@@ -12,14 +28,16 @@ struct A : B, C, protected P2 { virtual
constexpr A a;
// P1 is a non-public base of A.
-constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
// Don't error here.
static_assert (dynamic_cast<B*>((P1*)&a) == nullptr);
-constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
static_assert (dynamic_cast<C*>((P1*)&a) == nullptr);
static_assert (dynamic_cast<C*>((P2*)&a) == nullptr);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C.jj 2025-06-02 11:00:06.223524489 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C 2025-07-09 08:23:15.167896699 +0200
@@ -1,6 +1,22 @@
// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
// { dg-do compile { target c++20 } }
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+ struct exception {
+ constexpr exception () noexcept {}
+ constexpr virtual ~exception () noexcept {}
+ constexpr exception (const exception &) = default;
+ constexpr exception &operator= (const exception &) = default;
+ constexpr virtual const char *what () const noexcept { return "std::exception"; }
+ };
+ struct bad_cast : public exception {
+ constexpr virtual ~bad_cast () noexcept {}
+ constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+ };
+}
+#endif
+
// Unrelated type.
struct B { virtual void b(); };
@@ -12,12 +28,15 @@ constexpr A a;
struct U { virtual void u(); };
-constexpr bool b1 = (dynamic_cast<U&>((B&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .U." "" { target *-*-* } .-1 }
-constexpr bool b2 = (dynamic_cast<U&>((P1&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
-constexpr bool b3 = (dynamic_cast<U&>((P2&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+constexpr bool b1 = (dynamic_cast<U&>((B&)a), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .U." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b2 = (dynamic_cast<U&>((P1&)a), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b3 = (dynamic_cast<U&>((P2&)a), 0); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
static_assert (dynamic_cast<U*>((B*)&a) == nullptr);
static_assert (dynamic_cast<U*>((P1*)&a) == nullptr);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C.jj 2025-06-02 11:00:06.223524489 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C 2025-07-09 08:23:15.167896699 +0200
@@ -1,6 +1,22 @@
// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
// { dg-do compile { target c++20 } }
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+ struct exception {
+ constexpr exception () noexcept {}
+ constexpr virtual ~exception () noexcept {}
+ constexpr exception (const exception &) = default;
+ constexpr exception &operator= (const exception &) = default;
+ constexpr virtual const char *what () const noexcept { return "std::exception"; }
+ };
+ struct bad_cast : public exception {
+ constexpr virtual ~bad_cast () noexcept {}
+ constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+ };
+}
+#endif
+
// Ambiguous base.
struct A { virtual void a(); };
@@ -11,7 +27,8 @@ struct E : B, C, D { virtual void d(); }
constexpr E e;
-constexpr bool b1 = (dynamic_cast<A&>((D&)e), false); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message ".A. is an ambiguous base class of dynamic type .E. of its operand" "" { target *-*-* } .-1 }
+constexpr bool b1 = (dynamic_cast<A&>((D&)e), false); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message ".A. is an ambiguous base class of dynamic type .E. of its operand" "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
static_assert (dynamic_cast<A*>((D*)&e) == nullptr);
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C.jj 2025-06-02 11:00:06.222524502 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C 2025-07-09 08:23:15.168896686 +0200
@@ -8,6 +8,22 @@
// a pointer to or object of the constructor or destructor's own class or one
// of its bases, the dynamic_cast results in undefined behavior.
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+ struct exception {
+ constexpr exception () noexcept {}
+ constexpr virtual ~exception () noexcept {}
+ constexpr exception (const exception &) = default;
+ constexpr exception &operator= (const exception &) = default;
+ constexpr virtual const char *what () const noexcept { return "std::exception"; }
+ };
+ struct bad_cast : public exception {
+ constexpr virtual ~bad_cast () noexcept {}
+ constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+ };
+}
+#endif
+
struct V {
virtual void f();
};
@@ -19,7 +35,7 @@ struct B : V {
};
struct D : A, B {
- constexpr D() : B((A*)this, this) { } // { dg-message "in 'constexpr' expansion of" }
+ constexpr D() : B((A*)this, this) { } // { dg-message "in 'constexpr' expansion of" "" { target c++23_down } }
};
constexpr B::B(V* v, A* a)
@@ -29,8 +45,9 @@ constexpr B::B(V* v, A* a)
if (b != nullptr)
__builtin_abort ();
- B& br = dynamic_cast<B&>(*v); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .B." "" { target *-*-* } .-1 }
+ B& br = dynamic_cast<B&>(*v); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .B." "" { target c++23_down } .-1 }
}
-constexpr D d; // { dg-message "in 'constexpr' expansion of" }
+constexpr D d; // { dg-message "in 'constexpr' expansion of" "" { target c++23_down } }
+// { dg-error "uncaught exception" "" { target c++26 } .-1 }
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C.jj 2025-06-02 11:00:06.222524502 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C 2025-07-09 08:23:15.172896632 +0200
@@ -4,6 +4,22 @@
// Adopted from g++.old-deja/g++.other/dyncast1.C.
// But use reference dynamic_cast.
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+ struct exception {
+ constexpr exception () noexcept {}
+ constexpr virtual ~exception () noexcept {}
+ constexpr exception (const exception &) = default;
+ constexpr exception &operator= (const exception &) = default;
+ constexpr virtual const char *what () const noexcept { return "std::exception"; }
+ };
+ struct bad_cast : public exception {
+ constexpr virtual ~bad_cast () noexcept {}
+ constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+ };
+}
+#endif
+
// 1. downcast
// 1.1. single inheritance case
@@ -21,27 +37,35 @@ class CCC : protected B {};
class DDD : protected CCC {};
constexpr D d;
-constexpr bool b01 = (dynamic_cast<D&> ((A&)d), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
-constexpr bool b02 = (dynamic_cast<D&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
+constexpr bool b01 = (dynamic_cast<D&> ((A&)d), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .D." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b02 = (dynamic_cast<D&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
static_assert (&d == &dynamic_cast<const D&> ((C&)d));
-constexpr bool b03 = (dynamic_cast<C&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
+constexpr bool b03 = (dynamic_cast<C&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
constexpr DD dd;
-constexpr bool b04 = (dynamic_cast<DD&> ((A&)dd), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DD." "" { target *-*-* } .-1 }
-constexpr bool b05 = (dynamic_cast<DD&> ((B&)dd), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DD." "" { target *-*-* } .-1 }
+constexpr bool b04 = (dynamic_cast<DD&> ((A&)dd), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DD." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b05 = (dynamic_cast<DD&> ((B&)dd), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DD." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
constexpr DDD ddd;
-constexpr bool b06 = (dynamic_cast<DDD&> ((A&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
-constexpr bool b07 = (dynamic_cast<DDD&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
-constexpr bool b08 = (dynamic_cast<CCC&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
+constexpr bool b06 = (dynamic_cast<DDD&> ((A&)ddd), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DDD." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b07 = (dynamic_cast<DDD&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b08 = (dynamic_cast<CCC&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
// 1.2. multiple inheritance case
// 1.2.1. all bases are public
@@ -50,19 +74,23 @@ struct E : D, CC {};
struct EE : CC, D {}; //Will search in reverse order.
constexpr E e;
-constexpr bool b09 = (dynamic_cast<E&> ((A&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .E." "" { target *-*-* } .-1 }
-constexpr bool b10 = (dynamic_cast<E&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .E." "" { target *-*-* } .-1 }
+constexpr bool b09 = (dynamic_cast<E&> ((A&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .E." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b10 = (dynamic_cast<E&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .E." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
static_assert (&e == &dynamic_cast<E&> ((C&)(D&)e));
static_assert (&e == &dynamic_cast<E&> ((B&)(CC&)e));
static_assert (&(CC&)e == &dynamic_cast<CC&> ((B&)(CC&)e));
constexpr EE ee;
-constexpr bool b11 = (dynamic_cast<EE&> ((A&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .EE." "" { target *-*-* } .-1 }
-constexpr bool b12 = (dynamic_cast<EE&> ((B&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .EE." "" { target *-*-* } .-1 }
+constexpr bool b11 = (dynamic_cast<EE&> ((A&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .EE." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b12 = (dynamic_cast<EE&> ((B&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .EE." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
static_assert (&ee == &dynamic_cast<EE&> ((C&)(D&)ee));
static_assert (&ee == &dynamic_cast<EE&> ((B&)(CC&)ee));
static_assert (&(CC&)ee == &dynamic_cast<CC&> ((B&)(CC&)ee));
@@ -78,14 +106,17 @@ constexpr X x;
static_assert (&x == &dynamic_cast<X&>((B&)(CC&)(E&)x));
constexpr XX xx;
-constexpr bool b13 = (dynamic_cast<XX&>((B&)(CC&)(E&)xx), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .XX." "" { target *-*-* } .-1 }
+constexpr bool b13 = (dynamic_cast<XX&>((B&)(CC&)(E&)xx), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .XX." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
constexpr Y y;
-constexpr bool b14 = (dynamic_cast<Y&>((B&)y), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .Y." "" { target *-*-* } .-1 }
-constexpr bool b15 = (dynamic_cast<Y&>((A&)(B&)y), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .Y." "" { target *-*-* } .-1 }
+constexpr bool b14 = (dynamic_cast<Y&>((B&)y), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .Y." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
+constexpr bool b15 = (dynamic_cast<Y&>((A&)(B&)y), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .Y." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
// 2. crosscast
@@ -93,13 +124,16 @@ struct J { virtual void j(); };
struct K : CC, private J {};
class KK : J, CC{};
-constexpr bool b16 = (dynamic_cast<CC&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .CC." "" { target *-*-* } .-1 }
+constexpr bool b16 = (dynamic_cast<CC&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .CC." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
static_assert (&(CC&)e == &dynamic_cast<CC&> ((C&)(D&)e));
constexpr K k;
-constexpr bool b17 = (dynamic_cast<J&> ((B&)k), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "dynamic type .K. of its operand does not have an unambiguous public base class .J." "" { target *-*-* } .-1 }
+constexpr bool b17 = (dynamic_cast<J&> ((B&)k), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "dynamic type .K. of its operand does not have an unambiguous public base class .J." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
constexpr KK kk;
-constexpr bool b18 = (dynamic_cast<J&> ((CC&)kk), true); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "static type .const CC. of its operand is a non-public base class of dynamic type .KK." "" { target *-*-* } .-1 }
+constexpr bool b18 = (dynamic_cast<J&> ((CC&)kk), true); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "static type .const CC. of its operand is a non-public base class of dynamic type .KK." "" { target c++23_down } .-1 }
+// { dg-error "uncaught exception" "" { target c++26 } .-2 }
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic18.C.jj 2025-06-02 11:00:06.223524489 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic18.C 2025-07-09 08:23:15.173896619 +0200
@@ -2,6 +2,22 @@
// { dg-do compile { target c++20 } }
// Here 'b' doesn't point/refer to a public base of Derived.
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+ struct exception {
+ constexpr exception () noexcept {}
+ constexpr virtual ~exception () noexcept {}
+ constexpr exception (const exception &) = default;
+ constexpr exception &operator= (const exception &) = default;
+ constexpr virtual const char *what () const noexcept { return "std::exception"; }
+ };
+ struct bad_cast : public exception {
+ constexpr virtual ~bad_cast () noexcept {}
+ constexpr virtual const char *what () const noexcept { return "std::bad_cast"; }
+ };
+}
+#endif
+
struct Base {
constexpr virtual ~Base(){}
};
@@ -11,12 +27,12 @@ struct Derived: Base {
};
constexpr const Derived& cast(const Base& b) {
- return dynamic_cast<const Derived&>(b); // { dg-error "reference .dynamic_cast. failed" }
-// { dg-message "dynamic type .const Base. of its operand does not have a base class of type .Derived." "" { target *-*-* } .-1 }
+ return dynamic_cast<const Derived&>(b); // { dg-error "reference .dynamic_cast. failed" "" { target c++23_down } }
+// { dg-message "dynamic type .const Base. of its operand does not have a base class of type .Derived." "" { target c++23_down } .-1 }
}
auto test() {
static constexpr Base b;
- constexpr auto res = cast(b);
+ constexpr auto res = cast(b); // { dg-error "uncaught exception" "" { target c++26 } }
return res;
}
--- gcc/testsuite/g++.dg/cpp2a/constexpr-new27.C.jj 2025-07-09 08:23:15.173896619 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-new27.C 2025-07-09 08:23:15.173896619 +0200
@@ -0,0 +1,41 @@
+// { dg-do compile { target c++20 } }
+
+#if __cpp_constexpr_exceptions >= 202411L
+namespace std {
+ struct exception {
+ constexpr exception () noexcept {}
+ constexpr virtual ~exception () noexcept {}
+ constexpr exception (const exception &) = default;
+ constexpr exception &operator= (const exception &) = default;
+ constexpr virtual const char *what () const noexcept { return "std::exception"; }
+ };
+ struct bad_alloc : public exception {
+ constexpr virtual ~bad_alloc () noexcept {}
+ constexpr virtual const char *what () const noexcept { return "std::bad_alloc"; }
+ };
+ struct bad_array_new_length : public bad_alloc {
+ constexpr virtual ~bad_array_new_length () noexcept {}
+ constexpr virtual const char *what () const noexcept { return "std::bad_array_new_length"; }
+ };
+}
+#endif
+
+constexpr int
+foo (__SIZE_TYPE__ x, int y, int z)
+{
+ char (*a)[2] = new char[x][2]; // { dg-error "call to non-'constexpr' function 'void __cxa_throw_bad_array_new_length\\\(\\\)'" "" { target c++23_down } }
+ delete[] a; // { dg-message "declared here" "" { target c++23_down } .-1 }
+ int *b = new int[y]; // { dg-error "call to non-'constexpr' function 'void __cxa_throw_bad_array_new_length\\\(\\\)'" "" { target c++23_down } }
+ delete[] b;
+ int *c = new int[z]{1, 2, 3}; // { dg-error "call to non-'constexpr' function 'void __cxa_throw_bad_array_new_length\\\(\\\)'" "" { target c++23_down } }
+ delete[] c;
+ return 0;
+}
+
+constexpr int a = foo (16, 2, 3);
+constexpr int b = foo (-64, 2, 3); // { dg-message "in 'constexpr' expansion of" "" { target c++23_down } }
+ // { dg-error "uncaught exception" "" { target c++26 } .-1 }
+constexpr int c = foo (16, -1, 3); // { dg-message "in 'constexpr' expansion of" "" { target c++23_down } }
+ // { dg-error "uncaught exception" "" { target c++26 } .-1 }
+constexpr int d = foo (16, 2, 1); // { dg-message "in 'constexpr' expansion of" "" { target c++23_down } }
+ // { dg-error "uncaught exception" "" { target c++26 } .-1 }
--- gcc/testsuite/g++.dg/cpp2a/constexpr-typeid5.C.jj 2025-07-09 08:23:15.173896619 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-typeid5.C 2025-07-09 08:23:15.173896619 +0200
@@ -0,0 +1,20 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++17 } }
+
+#include <typeinfo>
+
+template <class T>
+constexpr bool foo ()
+{
+ bool r = false;
+ const std::type_info &s = typeid( (r = true), *(T *) 0); // { dg-error "call to non-'constexpr' function 'void __cxa_bad_typeid\\\(\\\)'" "" { target c++23_down } }
+ return r; // { dg-message "declared here" "" { target c++23_down } .-1 }
+}
+
+struct A {};
+struct B { virtual ~B () {} };
+
+static_assert (!foo <int> ());
+static_assert (!foo <A> ());
+constexpr bool a = foo <B> (); // { dg-message "in 'constexpr' expansion of" "" { target c++23_down } }
+// { dg-error "uncaught exception" "" { target c++26 } .-1 }
--- libstdc++-v3/include/bits/version.def.jj 2025-07-08 19:06:48.209575804 +0200
+++ libstdc++-v3/include/bits/version.def 2025-07-09 08:23:15.174896605 +0200
@@ -2041,6 +2041,15 @@ ftms = {
};
};
+ftms = {
+ name = constexpr_exceptions;
+ values = {
+ v = 202502;
+ cxxmin = 26;
+ extra_cond = "__cpp_constexpr_exceptions >= 202411L";
+ };
+};
+
// Standard test specifications.
stds[97] = ">= 199711L";
stds[03] = ">= 199711L";
--- libstdc++-v3/include/bits/version.h.jj 2025-07-08 19:06:48.210575791 +0200
+++ libstdc++-v3/include/bits/version.h 2025-07-09 08:23:15.175896592 +0200
@@ -2289,4 +2289,14 @@
#endif /* !defined(__cpp_lib_bitset) && defined(__glibcxx_want_bitset) */
#undef __glibcxx_want_bitset
+#if !defined(__cpp_lib_constexpr_exceptions)
+# if (__cplusplus > 202302L) && (__cpp_constexpr_exceptions >= 202411L)
+# define __glibcxx_constexpr_exceptions 202502L
+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_constexpr_exceptions)
+# define __cpp_lib_constexpr_exceptions 202502L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_constexpr_exceptions) && defined(__glibcxx_want_constexpr_exceptions) */
+#undef __glibcxx_want_constexpr_exceptions
+
#undef __glibcxx_want_all
--- libstdc++-v3/libsupc++/exception.jj 2025-06-26 16:18:01.051039568 +0200
+++ libstdc++-v3/libsupc++/exception 2025-07-09 08:23:15.175896592 +0200
@@ -57,8 +57,16 @@ namespace std _GLIBCXX_VISIBILITY(defaul
class bad_exception : public exception
{
public:
- bad_exception() _GLIBCXX_USE_NOEXCEPT { }
+ _GLIBCXX26_CONSTEXPR bad_exception() _GLIBCXX_USE_NOEXCEPT { }
+#if __cplusplus >= 202400L
+ constexpr virtual ~bad_exception() _GLIBCXX_TXN_SAFE_DYN noexcept {}
+
+ constexpr virtual const char* what() const _GLIBCXX_TXN_SAFE_DYN noexcept
+ {
+ return "std::bad_exception";
+ }
+#else
// This declaration is not useless:
// http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
virtual ~bad_exception() _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT;
@@ -66,6 +74,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
// See comment in eh_exception.cc.
virtual const char*
what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT;
+#endif
};
/// If you write a replacement %terminate handler, it must be of this type.
--- libstdc++-v3/libsupc++/exception.h.jj 2025-06-02 11:00:06.267523918 +0200
+++ libstdc++-v3/libsupc++/exception.h 2025-07-09 08:23:15.175896592 +0200
@@ -61,19 +61,28 @@ namespace std _GLIBCXX_VISIBILITY(defaul
class exception
{
public:
- exception() _GLIBCXX_NOTHROW { }
+ _GLIBCXX26_CONSTEXPR exception() _GLIBCXX_NOTHROW { }
+#if __cplusplus >= 202400L
+ constexpr virtual ~exception() _GLIBCXX_TXN_SAFE_DYN noexcept {}
+#else
virtual ~exception() _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW;
+#endif
#if __cplusplus >= 201103L
- exception(const exception&) = default;
- exception& operator=(const exception&) = default;
- exception(exception&&) = default;
- exception& operator=(exception&&) = default;
+ _GLIBCXX26_CONSTEXPR exception(const exception&) = default;
+ _GLIBCXX26_CONSTEXPR exception& operator=(const exception&) = default;
+ _GLIBCXX26_CONSTEXPR exception(exception&&) = default;
+ _GLIBCXX26_CONSTEXPR exception& operator=(exception&&) = default;
#endif
/** Returns a C-style character string describing the general cause
* of the current error. */
+#if __cplusplus >= 202400L
+ constexpr virtual const char*
+ what() const _GLIBCXX_TXN_SAFE_DYN noexcept { return "std::exception"; }
+#else
virtual const char*
what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW;
+#endif
};
/// @}
--- libstdc++-v3/libsupc++/exception_ptr.h.jj 2025-06-26 16:18:01.060039456 +0200
+++ libstdc++-v3/libsupc++/exception_ptr.h 2025-07-09 08:26:22.229374720 +0200
@@ -75,7 +75,8 @@ namespace std _GLIBCXX_VISIBILITY(defaul
exception_ptr current_exception() _GLIBCXX_USE_NOEXCEPT;
template<typename _Ex>
- exception_ptr make_exception_ptr(_Ex) _GLIBCXX_USE_NOEXCEPT;
+ _GLIBCXX26_CONSTEXPR exception_ptr make_exception_ptr(_Ex)
+ _GLIBCXX_USE_NOEXCEPT;
/// Throw the object pointed to by the exception_ptr.
void rethrow_exception(exception_ptr) __attribute__ ((__noreturn__));
@@ -105,7 +106,25 @@ namespace std _GLIBCXX_VISIBILITY(defaul
{
void* _M_exception_object;
+#if __cplusplus >= 202400L
+ constexpr explicit exception_ptr(void* __e) noexcept
+ : _M_exception_object(__e)
+ {
+ if (_M_exception_object)
+ {
+#if __cpp_if_consteval >= 202106L \
+ && _GLIBCXX_HAS_BUILTIN(__builtin_eh_ptr_adjust_ref)
+ if consteval {
+ __builtin_eh_ptr_adjust_ref(_M_exception_object, 1);
+ return;
+ }
+#endif
+ _M_addref();
+ }
+ }
+#else
explicit exception_ptr(void* __e) _GLIBCXX_USE_NOEXCEPT;
+#endif
void _M_addref() _GLIBCXX_USE_NOEXCEPT;
void _M_release() _GLIBCXX_USE_NOEXCEPT;
@@ -115,7 +134,8 @@ namespace std _GLIBCXX_VISIBILITY(defaul
friend exception_ptr std::current_exception() _GLIBCXX_USE_NOEXCEPT;
friend void std::rethrow_exception(exception_ptr);
template<typename _Ex>
- friend exception_ptr std::make_exception_ptr(_Ex) _GLIBCXX_USE_NOEXCEPT;
+ friend _GLIBCXX26_CONSTEXPR exception_ptr std::make_exception_ptr(_Ex)
+ _GLIBCXX_USE_NOEXCEPT;
#if __cpp_lib_exception_ptr_cast >= 202506L
template<typename _Ex>
friend const _Ex* std::exception_ptr_cast(const exception_ptr&) noexcept;
@@ -125,16 +145,17 @@ namespace std _GLIBCXX_VISIBILITY(defaul
_GLIBCXX_USE_NOEXCEPT;
public:
- exception_ptr() _GLIBCXX_USE_NOEXCEPT;
+ _GLIBCXX26_CONSTEXPR exception_ptr() _GLIBCXX_USE_NOEXCEPT;
- exception_ptr(const exception_ptr&) _GLIBCXX_USE_NOEXCEPT;
+ _GLIBCXX26_CONSTEXPR exception_ptr(const exception_ptr&)
+ _GLIBCXX_USE_NOEXCEPT;
#if __cplusplus >= 201103L
- exception_ptr(nullptr_t) noexcept
+ _GLIBCXX26_CONSTEXPR exception_ptr(nullptr_t) noexcept
: _M_exception_object(nullptr)
{ }
- exception_ptr(exception_ptr&& __o) noexcept
+ _GLIBCXX26_CONSTEXPR exception_ptr(exception_ptr&& __o) noexcept
: _M_exception_object(__o._M_exception_object)
{ __o._M_exception_object = nullptr; }
#endif
@@ -146,11 +167,11 @@ namespace std _GLIBCXX_VISIBILITY(defaul
exception_ptr(__safe_bool) _GLIBCXX_USE_NOEXCEPT;
#endif
- exception_ptr&
+ _GLIBCXX26_CONSTEXPR exception_ptr&
operator=(const exception_ptr&) _GLIBCXX_USE_NOEXCEPT;
#if __cplusplus >= 201103L
- exception_ptr&
+ _GLIBCXX26_CONSTEXPR exception_ptr&
operator=(exception_ptr&& __o) noexcept
{
exception_ptr(static_cast<exception_ptr&&>(__o)).swap(*this);
@@ -158,9 +179,9 @@ namespace std _GLIBCXX_VISIBILITY(defaul
}
#endif
- ~exception_ptr() _GLIBCXX_USE_NOEXCEPT;
+ _GLIBCXX26_CONSTEXPR ~exception_ptr() _GLIBCXX_USE_NOEXCEPT;
- void
+ _GLIBCXX26_CONSTEXPR void
swap(exception_ptr&) _GLIBCXX_USE_NOEXCEPT;
#ifdef _GLIBCXX_EH_PTR_COMPAT
@@ -172,13 +193,13 @@ namespace std _GLIBCXX_VISIBILITY(defaul
#endif
#if __cplusplus >= 201103L
- explicit operator bool() const noexcept
+ _GLIBCXX26_CONSTEXPR explicit operator bool() const noexcept
{ return _M_exception_object; }
#endif
#if __cpp_impl_three_way_comparison >= 201907L \
&& ! defined _GLIBCXX_EH_PTR_RELOPS_COMPAT
- friend bool
+ _GLIBCXX26_CONSTEXPR friend bool
operator==(const exception_ptr&, const exception_ptr&) noexcept = default;
#else
friend _GLIBCXX_EH_PTR_USED bool
@@ -198,31 +219,49 @@ namespace std _GLIBCXX_VISIBILITY(defaul
};
_GLIBCXX_EH_PTR_USED
- inline
+ _GLIBCXX26_CONSTEXPR inline
exception_ptr::exception_ptr() _GLIBCXX_USE_NOEXCEPT
: _M_exception_object(0)
{ }
_GLIBCXX_EH_PTR_USED
- inline
+ _GLIBCXX26_CONSTEXPR inline
exception_ptr::exception_ptr(const exception_ptr& __other)
_GLIBCXX_USE_NOEXCEPT
: _M_exception_object(__other._M_exception_object)
{
if (_M_exception_object)
- _M_addref();
+ {
+#if __cpp_if_consteval >= 202106L \
+ && _GLIBCXX_HAS_BUILTIN(__builtin_eh_ptr_adjust_ref)
+ if consteval {
+ __builtin_eh_ptr_adjust_ref(_M_exception_object, 1);
+ return;
+ }
+#endif
+ _M_addref();
+ }
}
_GLIBCXX_EH_PTR_USED
- inline
+ _GLIBCXX26_CONSTEXPR inline
exception_ptr::~exception_ptr() _GLIBCXX_USE_NOEXCEPT
{
if (_M_exception_object)
- _M_release();
+ {
+#if __cpp_if_consteval >= 202106L \
+ && _GLIBCXX_HAS_BUILTIN(__builtin_eh_ptr_adjust_ref)
+ if consteval {
+ __builtin_eh_ptr_adjust_ref(_M_exception_object, -1);
+ return;
+ }
+#endif
+ _M_release();
+ }
}
_GLIBCXX_EH_PTR_USED
- inline exception_ptr&
+ _GLIBCXX26_CONSTEXPR inline exception_ptr&
exception_ptr::operator=(const exception_ptr& __other) _GLIBCXX_USE_NOEXCEPT
{
exception_ptr(__other).swap(*this);
@@ -230,7 +269,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
}
_GLIBCXX_EH_PTR_USED
- inline void
+ _GLIBCXX26_CONSTEXPR inline void
exception_ptr::swap(exception_ptr &__other) _GLIBCXX_USE_NOEXCEPT
{
void *__tmp = _M_exception_object;
@@ -239,7 +278,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
}
/// @relates exception_ptr
- inline void
+ _GLIBCXX26_CONSTEXPR inline void
swap(exception_ptr& __lhs, exception_ptr& __rhs)
{ __lhs.swap(__rhs); }
@@ -258,9 +297,21 @@ namespace std _GLIBCXX_VISIBILITY(defaul
/// Obtain an exception_ptr pointing to a copy of the supplied object.
#if (__cplusplus >= 201103L && __cpp_rtti) || __cpp_exceptions
template<typename _Ex>
- exception_ptr
+ _GLIBCXX26_CONSTEXPR exception_ptr
make_exception_ptr(_Ex __ex) _GLIBCXX_USE_NOEXCEPT
{
+#if __cplusplus >= 202400L
+ if consteval {
+ try
+ {
+ throw __ex;
+ }
+ catch(...)
+ {
+ return current_exception();
+ }
+ }
+#endif
#if __cplusplus >= 201103L && __cpp_rtti
using _Ex2 = typename decay<_Ex>::type;
void* __e = __cxxabiv1::__cxa_allocate_exception(sizeof(_Ex));
@@ -293,7 +344,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
// instead of a working one compiled with RTTI and/or exceptions enabled.
template<typename _Ex>
__attribute__ ((__always_inline__))
- inline exception_ptr
+ _GLIBCXX26_CONSTEXPR inline exception_ptr
make_exception_ptr(_Ex) _GLIBCXX_USE_NOEXCEPT
{ return exception_ptr(); }
#endif
--- libstdc++-v3/libsupc++/nested_exception.h.jj 2025-06-02 11:00:06.267523918 +0200
+++ libstdc++-v3/libsupc++/nested_exception.h 2025-07-09 08:23:15.176896578 +0200
@@ -62,17 +62,24 @@ namespace std _GLIBCXX_VISIBILITY(defaul
public:
/// The default constructor stores the current exception (if any).
+ _GLIBCXX26_CONSTEXPR
nested_exception() noexcept : _M_ptr(current_exception()) { }
+ _GLIBCXX26_CONSTEXPR
nested_exception(const nested_exception&) noexcept = default;
+ _GLIBCXX26_CONSTEXPR
nested_exception& operator=(const nested_exception&) noexcept = default;
+#if __cplusplus >= 202400L
+ constexpr virtual ~nested_exception() noexcept {}
+#else
virtual ~nested_exception() noexcept;
+#endif
/// Rethrow the stored exception, or terminate if none was stored.
[[noreturn]]
- void
+ _GLIBCXX26_CONSTEXPR void
rethrow_nested() const
{
if (_M_ptr)
@@ -81,7 +88,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
}
/// Access the stored exception.
- exception_ptr
+ _GLIBCXX26_CONSTEXPR exception_ptr
nested_ptr() const noexcept
{ return _M_ptr; }
};
@@ -91,11 +98,11 @@ namespace std _GLIBCXX_VISIBILITY(defaul
template<typename _Except>
struct _Nested_exception : public _Except, public nested_exception
{
- explicit _Nested_exception(const _Except& __ex)
+ _GLIBCXX26_CONSTEXPR explicit _Nested_exception(const _Except& __ex)
: _Except(__ex)
{ }
- explicit _Nested_exception(_Except&& __ex)
+ _GLIBCXX26_CONSTEXPR explicit _Nested_exception(_Except&& __ex)
: _Except(static_cast<_Except&&>(__ex))
{ }
};
@@ -144,7 +151,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
*/
template<typename _Tp>
[[noreturn]]
- inline void
+ _GLIBCXX26_CONSTEXPR inline void
throw_with_nested(_Tp&& __t)
{
using _Up = typename decay<_Tp>::type;
@@ -204,7 +211,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
# if ! __cpp_rtti
[[__gnu__::__always_inline__]]
#endif
- inline void
+ _GLIBCXX26_CONSTEXPR inline void
rethrow_if_nested(const _Ex& __ex)
{
const _Ex* __ptr = __builtin_addressof(__ex);
--- libstdc++-v3/libsupc++/new.jj 2025-06-02 11:00:06.268523905 +0200
+++ libstdc++-v3/libsupc++/new 2025-07-09 08:23:15.177896565 +0200
@@ -66,33 +66,51 @@ namespace std
class bad_alloc : public exception
{
public:
- bad_alloc() throw() { }
+ _GLIBCXX26_CONSTEXPR bad_alloc() throw() { }
#if __cplusplus >= 201103L
- bad_alloc(const bad_alloc&) = default;
- bad_alloc& operator=(const bad_alloc&) = default;
+ _GLIBCXX26_CONSTEXPR bad_alloc(const bad_alloc&) = default;
+ _GLIBCXX26_CONSTEXPR bad_alloc& operator=(const bad_alloc&) = default;
#endif
+#if __cplusplus >= 202400L
+ constexpr virtual ~bad_alloc() noexcept {}
+
+ constexpr virtual const char* what() const noexcept
+ {
+ return "std::bad_alloc";
+ }
+#else
// This declaration is not useless:
// http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
virtual ~bad_alloc() throw();
// See comment in eh_exception.cc.
virtual const char* what() const throw();
+#endif
};
#if __cplusplus >= 201103L
class bad_array_new_length : public bad_alloc
{
public:
- bad_array_new_length() throw() { }
+ _GLIBCXX26_CONSTEXPR bad_array_new_length() throw() { }
+#if __cplusplus >= 202400L
+ constexpr virtual ~bad_array_new_length() noexcept {}
+
+ constexpr virtual const char* what() const noexcept
+ {
+ return "std::bad_array_new_length";
+ }
+#else
// This declaration is not useless:
// http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
virtual ~bad_array_new_length() throw();
// See comment in eh_exception.cc.
virtual const char* what() const throw();
+#endif
};
#endif
--- libstdc++-v3/libsupc++/typeinfo.jj 2025-06-02 11:00:06.269523892 +0200
+++ libstdc++-v3/libsupc++/typeinfo 2025-07-09 08:23:15.177896565 +0200
@@ -224,14 +224,23 @@ namespace std
class bad_cast : public exception
{
public:
- bad_cast() _GLIBCXX_USE_NOEXCEPT { }
+ _GLIBCXX26_CONSTEXPR bad_cast() _GLIBCXX_USE_NOEXCEPT { }
+#if __cplusplus >= 202400L
+ constexpr virtual ~bad_cast() noexcept {}
+
+ constexpr virtual const char* what() const noexcept
+ {
+ return "std::bad_cast";
+ }
+#else
// This declaration is not useless:
// http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
virtual ~bad_cast() _GLIBCXX_USE_NOEXCEPT;
// See comment in eh_exception.cc.
virtual const char* what() const _GLIBCXX_USE_NOEXCEPT;
+#endif
};
/**
@@ -241,14 +250,23 @@ namespace std
class bad_typeid : public exception
{
public:
- bad_typeid () _GLIBCXX_USE_NOEXCEPT { }
+ _GLIBCXX26_CONSTEXPR bad_typeid () _GLIBCXX_USE_NOEXCEPT { }
+
+#if __cplusplus >= 202400L
+ constexpr virtual ~bad_typeid() noexcept {}
+ constexpr virtual const char* what() const noexcept
+ {
+ return "std::bad_typeid";
+ }
+#else
// This declaration is not useless:
// http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
virtual ~bad_typeid() _GLIBCXX_USE_NOEXCEPT;
// See comment in eh_exception.cc.
virtual const char* what() const _GLIBCXX_USE_NOEXCEPT;
+#endif
};
} // namespace std
Jakub
More information about the Libstdc++
mailing list