https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117785

--- Comment #23 from GCC Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Jakub Jelinek <ja...@gcc.gnu.org>:

https://gcc.gnu.org/g:baaee10123db6cf896283175b345d535b225defb

commit r16-2183-gbaaee10123db6cf896283175b345d535b225defb
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Thu Jul 10 23:26:15 2025 +0200

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

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

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

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

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

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

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

    2025-07-10  Jakub Jelinek  <ja...@redhat.com>

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

Reply via email to