On Thu, 10 Jul 2025 at 09:06, Jakub Jelinek wrote: > > On Wed, Jul 09, 2025 at 06:45:41PM -0400, Jason Merrill wrote: > > > + && reduced_constant_expression_p (val)) > > > > And a value doesn't need to be constant to be printable, we should be able > > to print it unconditionally. > > Sure, the question is if printing non-constant value is better for users. > The change to do unconditionally the %qE results in > /usr/src/gcc/gcc/testsuite/g++.dg/cpp26/constexpr-eh12.C:71:49: error: > uncaught exception '(E*)(& heap )' > while previously it was > /usr/src/gcc/gcc/testsuite/g++.dg/cpp26/constexpr-eh12.C:71:49: error: > uncaught exception of type 'E*' > I've kept the conditional for now but if you really want that change, can > remove it > in the 2 spots and tweak constexpr-eh12.C test's expectations. > > > Is there a reason not to add it to heap_vars here? > > In the earlier patch I had: > > > > + case CXA_ALLOCATE_EXCEPTION: > ... > > > + tree var = cxa_allocate_exception (loc, type, size_zero_node); > > > + ctx->global->heap_vars.safe_push (var); > > > + ctx->global->put_value (var, NULL_TREE); > vs. > > > + case CXA_BAD_CAST: > > > + case CXA_BAD_TYPEID: > > > + case CXA_THROW_BAD_ARRAY_NEW_LENGTH: > ... > > > + 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); > > and so it wasn't added to heap_vars if build_special_member_call failed. > But thinking about it now, we really don't care about what is in heap_vars > if *non_constant_p, so I've made the change. > > The rest incorporated into the following version of the patch, passes > GXX_TESTSUITE_STDS=98,11,14,17,20,23,26 make check-g++ > RUNTESTFLAGS="--target_board=unix/-fpie dg.exp='constexpr-eh* > constexpr-asm-5.C static_assert1.C feat-cxx26.C constexpr-throw.C > constexpr-84192.C constexpr-dynamic*.C consteval34.C constexpr-new27.C > consteval-memfn1.C constexpr-typeid5.C constexpr-ellipsis*'" > so far. > > 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. >
I'm seeing some unexpected libstdc++ test failures since this commit (confirmed by bisection). $ export GLIBCXX_TESTSUITE_STDS=26 $ make check RUNTESTFLAGS="conformance.exp=25_algo*debug/constexpr*neg.cc --target_board=unix/-D_GLIBCXX_DEBUG" FAIL: 25_algorithms/copy/debug/constexpr_neg.cc -std=gnu++26 (test for errors, line ) FAIL: 25_algorithms/copy/debug/constexpr_neg.cc -std=gnu++26 (test for excess errors) FAIL: 25_algorithms/copy_backward/debug/constexpr_neg.cc -std=gnu++26 (test for excess errors) FAIL: 25_algorithms/equal/debug/constexpr_neg.cc -std=gnu++26 (test for errors, line ) FAIL: 25_algorithms/equal/debug/constexpr_neg.cc -std=gnu++26 (test for excess errors) FAIL: 25_algorithms/lower_bound/debug/constexpr_partitioned_neg.cc -std=gnu++26 (test for excess errors) FAIL: 25_algorithms/lower_bound/debug/constexpr_partitioned_pred_neg.cc -std=gnu++26 (test for excess errors) FAIL: 25_algorithms/lower_bound/debug/constexpr_valid_range_neg.cc -std=gnu++26 (test for excess errors) FAIL: 25_algorithms/upper_bound/debug/constexpr_partitioned_neg.cc -std=gnu++26 (test for excess errors) FAIL: 25_algorithms/upper_bound/debug/constexpr_partitioned_pred_neg.cc -std=gnu++26 (test for excess errors) FAIL: 25_algorithms/upper_bound/debug/constexpr_valid_range_neg.cc -std=gnu++26 (test for excess errors) The difference is in the diagnostic we get from the expansion of this macro: #define _GLIBCXX_DEBUG_VERIFY_AT_F(_Cond,_ErrMsg,_File,_Line,_Func) \ do { \ if (__builtin_expect(!bool(_Cond), false)) \ __gnu_debug::_Error_formatter::_S_at(_File, _Line, _Func) \ ._ErrMsg._M_error(); \ } while (false) The dg-error lines in the tests expect a "non-constant condition" error and the location to match: // { dg-error "_Error_formatter::_M_error()" "" { target *-*-* } 0 } but now the error points to the _Error_formatter::_S_at(...) function. The diagnostic only changed for C++26, and I'm not sure why (the _S_at function isn't constexpr, so why wasn't it failing there before?) Is this change in diagnostics expected? I already have a patch to make the necessary changes to the tests, but I wanted to understand why it is required. The fixes are all of this form: -// { dg-error "_Error_formatter::_M_error()" "" { target *-*-* } 0 } +// { dg-error "_Error_formatter::(_M_error|_S_at)" "" { target *-*-* } 0 }