diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc index 459fd86bb39d..0c84020300fb 100644 --- a/gcc/c-family/c-cppbuiltin.cc +++ b/gcc/c-family/c-cppbuiltin.cc @@ -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"); diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 2c3ef3dfc35d..26296255b1ec 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -1723,6 +1723,56 @@ involves_qualification_conversion_p (tree to, tree from) return false; } +/* Return true if HANDLER is a match for exception object with EXCEPT_TYPE as + per [except.handle]/3. */ + +bool +handler_match_for_exception_type (tree handler, tree except_type) +{ + tree handler_type = HANDLER_TYPE (handler); + if (handler_type == NULL_TREE) + return true; /* ... */ + if (same_type_ignoring_top_level_qualifiers_p (handler_type, except_type)) + return true; + if (CLASS_TYPE_P (except_type) && CLASS_TYPE_P (handler_type)) + { + base_kind b_kind; + tree binfo = lookup_base (except_type, handler_type, ba_check, &b_kind, + tf_none); + if (binfo && binfo != error_mark_node) + return true; + } + if (TYPE_PTR_P (handler_type) || TYPE_PTRDATAMEM_P (handler_type)) + { + if (TREE_CODE (except_type) == NULLPTR_TYPE) + return true; + if ((TYPE_PTR_P (handler_type) && TYPE_PTR_P (except_type)) + || (TYPE_PTRDATAMEM_P (handler_type) + && TYPE_PTRDATAMEM_P (except_type))) + { + conversion *conv + = standard_conversion (handler_type, except_type, NULL_TREE, + /*c_cast_p=*/false, 0, tf_none); + if (conv && !conv->bad_p) + { + for (conversion *t = conv; t; t = next_conversion (t)) + switch (t->kind) + { + case ck_ptr: + case ck_fnptr: + case ck_qual: + case ck_identity: + break; + default: + return false; + } + return true; + } + } + } + return false; +} + /* A reference of the indicated TYPE is being bound directly to the expression represented by the implicit conversion sequence CONV. Return a conversion sequence for this binding. */ diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index f9066bc79322..eb19784dbbaa 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -1184,6 +1184,10 @@ public: /* Heap VAR_DECLs created during the evaluation of the outermost constant expression. */ auto_vec 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 caught_exceptions; /* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR. */ vec *cleanups; /* If non-null, only allow modification of existing values of the variables @@ -1191,10 +1195,13 @@ public: hash_set *modifiable; /* Number of heap VAR_DECL deallocations. */ unsigned heap_dealloc_count; + /* Number of uncaught exceptions. */ + unsigned uncaught_exceptions; + /* Constructor. */ constexpr_global_ctx () : constexpr_ops_count (0), cleanups (NULL), modifiable (nullptr), - heap_dealloc_count (0) {} + heap_dealloc_count (0), uncaught_exceptions (0) {} bool is_outside_lifetime (tree t) { @@ -1308,6 +1315,48 @@ struct constexpr_ctx { mce_value manifestly_const_eval; }; +/* Predicates for the meaning of *jump_target. */ + +static bool +returns (tree *jump_target) +{ + return *jump_target && TREE_CODE (*jump_target) == RETURN_EXPR; +} + +static bool +breaks (tree *jump_target) +{ + return (*jump_target + && ((TREE_CODE (*jump_target) == LABEL_DECL + && LABEL_DECL_BREAK (*jump_target)) + || TREE_CODE (*jump_target) == BREAK_STMT + || TREE_CODE (*jump_target) == EXIT_EXPR)); +} + +static bool +continues (tree *jump_target) +{ + return (*jump_target + && ((TREE_CODE (*jump_target) == LABEL_DECL + && LABEL_DECL_CONTINUE (*jump_target)) + || TREE_CODE (*jump_target) == CONTINUE_STMT)); +} + +static bool +switches (tree *jump_target) +{ + return *jump_target && TREE_CODE (*jump_target) == INTEGER_CST; +} + +static bool +throws (tree *jump_target) +{ + /* void_node is for use in potential_constant_expression_1, otherwise + it should an artificial VAR_DECL created by constant evaluation + of __cxa_allocate_exception (). */ + return (*jump_target && (VAR_P (*jump_target) || *jump_target == void_node)); +} + /* True if the constexpr relaxations afforded by P2280R4 for unknown references and objects are in effect. */ @@ -1543,13 +1592,672 @@ enum value_cat { }; static tree cxx_eval_constant_expression (const constexpr_ctx *, tree, - value_cat, bool *, bool *, tree * = NULL); + value_cat, bool *, bool *, tree *); static tree cxx_eval_bare_aggregate (const constexpr_ctx *, tree, - value_cat, bool *, bool *); + value_cat, bool *, bool *, tree *); static tree cxx_fold_indirect_ref (const constexpr_ctx *, location_t, tree, tree, - bool * = NULL); + bool *, tree *); static tree find_heap_var_refs (tree *, int *, void *); +/* For exception object EXC if it has class type and usable what () method + which returns cv char * return the xmalloced string literal which it returns + if possible, otherwise return NULL. */ + +static char * +exception_what_str (const constexpr_ctx *ctx, tree exc) +{ + tree type = strip_array_types (TREE_TYPE (exc)); + if (!CLASS_TYPE_P (type)) + return NULL; + tree std_exception = lookup_qualified_name (std_node, "exception", + LOOK_want::NORMAL, false); + if (TREE_CODE (std_exception) != TYPE_DECL) + return NULL; + if (!CLASS_TYPE_P (TREE_TYPE (std_exception))) + return NULL; + base_kind b_kind; + tree binfo = lookup_base (type, TREE_TYPE (std_exception), ba_check, &b_kind, + tf_none); + if (binfo == NULL_TREE || binfo == error_mark_node) + return NULL; + if (type != TREE_TYPE (exc)) + exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL); + tree call + = finish_class_member_access_expr (exc, get_identifier ("what"), false, + tf_none); + if (call == error_mark_node) + return NULL; + releasing_vec what_args; + call = finish_call_expr (call, &what_args, false, false, tf_none); + if (call == error_mark_node) + return NULL; + if (TREE_CODE (TREE_TYPE (call)) != POINTER_TYPE + || !INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (call))) + || !COMPLETE_TYPE_P (TREE_TYPE (TREE_TYPE (call))) + || !tree_int_cst_equal (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (call))), + TYPE_SIZE_UNIT (char_type_node)) + || TYPE_PRECISION (TREE_TYPE (TREE_TYPE (call))) != BITS_PER_UNIT) + return NULL; + if (!potential_constant_expression (call)) + return NULL; + bool non_constant_p = false, overflow_p = false; + tree jmp_target = NULL; + tree ptr = cxx_eval_constant_expression (ctx, call, vc_prvalue, + &non_constant_p, &overflow_p, + &jmp_target); + if (throws (&jmp_target) || non_constant_p) + return NULL; + if (reduced_constant_expression_p (ptr)) + if (const char *msg = c_getstr (ptr)) + return xstrdup (msg); + auto_vec 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; " + "%: %qs", "std::terminate", type, str); + free (str); + } + else + { + if (type != TREE_TYPE (exc)) + exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL); + bool non_constant_p = false, overflow_p = false; + tree jmp_target = NULL; + tree val = cxx_eval_constant_expression (ctx, exc, vc_prvalue, + &non_constant_p, &overflow_p, + &jmp_target); + gcc_assert (!throws (&jmp_target) && !non_constant_p); + if (reduced_constant_expression_p (val)) + error_at (loc, "%qs called after throwing an exception %qE", + "std::terminate", val); + else + error_at (loc, "%qs called after throwing an exception of type %qT", + "std::terminate", type); + } +} + +/* Diagnose constant expression evaluation encountering call to + uncaught exception EXC. */ + +static void +diagnose_uncaught_exception (location_t loc, const constexpr_ctx *ctx, tree exc) +{ + tree type = strip_array_types (TREE_TYPE (exc)); + if (char *str = exception_what_str (ctx, exc)) + { + error_at (loc, "uncaught exception of type %qT; %: %qs", type, str); + free (str); + } + else + { + if (type != TREE_TYPE (exc)) + exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL); + bool non_constant_p = false, overflow_p = false; + tree jmp_target = NULL; + tree val = cxx_eval_constant_expression (ctx, exc, vc_prvalue, + &non_constant_p, &overflow_p, + &jmp_target); + gcc_assert (!throws (&jmp_target) && !non_constant_p); + if (reduced_constant_expression_p (val)) + error_at (loc, "uncaught exception %qE", val); + else + error_at (loc, "uncaught exception of type %qT", type); + } +} + +/* Kinds of __cxa_* functions (and a few other EH related ones) we handle as + magic constexpr functions for C++26. */ + +enum cxa_builtin { + CXA_NONE = 0, + CXA_ALLOCATE_EXCEPTION = 1, + CXA_FREE_EXCEPTION = 2, + CXA_THROW = 3, + CXA_BEGIN_CATCH = 4, + CXA_END_CATCH = 5, + CXA_RETHROW = 6, + CXA_GET_EXCEPTION_PTR = 7, + CXA_BAD_CAST = 8, + CXA_BAD_TYPEID = 9, + CXA_THROW_BAD_ARRAY_NEW_LENGTH = 10, + STD_UNCAUGHT_EXCEPTIONS = 11, + STD_CURRENT_EXCEPTION = 12, + STD_RETHROW_EXCEPTION = 13, + BUILTIN_EH_PTR_ADJUST_REF = 14 +}; + +/* Return cxa_builtin if FNDECL is a __cxa_* function handled as + magic constexpr function for C++26. Return CXA_NONE otherwise. */ + +static enum cxa_builtin +cxx_cxa_builtin_fn_p (tree fndecl) +{ + if (cxx_dialect < cxx26) + return CXA_NONE; + if (DECL_LANGUAGE (fndecl) != lang_c) + { + if (!decl_in_std_namespace_p (fndecl)) + return CXA_NONE; + if (id_equal (DECL_NAME (fndecl), "uncaught_exceptions")) + return STD_UNCAUGHT_EXCEPTIONS; + if (id_equal (DECL_NAME (fndecl), "current_exception")) + return STD_CURRENT_EXCEPTION; + if (id_equal (DECL_NAME (fndecl), "rethrow_exception")) + return STD_RETHROW_EXCEPTION; + return CXA_NONE; + } + if (!startswith (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "__cxa_")) + return CXA_NONE; + if (id_equal (DECL_NAME (fndecl), "__cxa_allocate_exception")) + return CXA_ALLOCATE_EXCEPTION; + if (id_equal (DECL_NAME (fndecl), "__cxa_free_exception")) + return CXA_FREE_EXCEPTION; + if (id_equal (DECL_NAME (fndecl), "__cxa_throw")) + return CXA_THROW; + if (id_equal (DECL_NAME (fndecl), "__cxa_begin_catch")) + return CXA_BEGIN_CATCH; + if (id_equal (DECL_NAME (fndecl), "__cxa_end_catch")) + return CXA_END_CATCH; + if (id_equal (DECL_NAME (fndecl), "__cxa_rethrow")) + return CXA_RETHROW; + if (id_equal (DECL_NAME (fndecl), "__cxa_get_exception_ptr")) + return CXA_GET_EXCEPTION_PTR; + if (id_equal (DECL_NAME (fndecl), "__cxa_bad_cast")) + return CXA_BAD_CAST; + if (id_equal (DECL_NAME (fndecl), "__cxa_bad_typeid")) + return CXA_BAD_TYPEID; + if (id_equal (DECL_NAME (fndecl), "__cxa_throw_bad_array_new_length")) + return CXA_THROW_BAD_ARRAY_NEW_LENGTH; + return CXA_NONE; +} + +/* Helper function for cxx_eval_cxa_builtin_fn. + Check if ARG is a valid first argument of __cxa_throw or + __cxa_free_exception or __builtin_eh_ptr_adjust_ref. Return NULL_TREE if + not, otherwise return the artificial __cxa_allocate_exception allocated + VAR_DECL. FREE_EXC is true for __cxa_free_exception, false otherwise. */ + +static tree +cxa_check_throw_arg (tree arg, bool free_exc) +{ + STRIP_NOPS (arg); + if (TREE_CODE (arg) != ADDR_EXPR) + return NULL_TREE; + arg = TREE_OPERAND (arg, 0); + if (!VAR_P (arg) + || !DECL_ARTIFICIAL (arg) + || ((!free_exc || DECL_NAME (arg) != heap_uninit_identifier) + && DECL_NAME (arg) != heap_identifier) + || !DECL_LANG_SPECIFIC (arg)) + return NULL_TREE; + return arg; +} + +/* Helper function for cxx_eval_cxa_builtin_fn. + "Allocate" on the constexpr heap an exception object of TYPE + with REFCOUNT. */ + +static tree +cxa_allocate_exception (location_t loc, const constexpr_ctx *ctx, tree type, + tree refcount) +{ + tree var = build_decl (loc, VAR_DECL, heap_uninit_identifier, type); + DECL_ARTIFICIAL (var) = 1; + retrofit_lang_decl (var); + DECL_EXCEPTION_REFCOUNT (var) = refcount; + ctx->global->heap_vars.safe_push (var); + return var; +} + +/* Evaluate various __cxa_* calls as magic constexpr builtins for + C++26 constexpr exception support (P3068R5). */ + +static tree +cxx_eval_cxa_builtin_fn (const constexpr_ctx *ctx, tree call, + enum cxa_builtin kind, tree fndecl, + bool *non_constant_p, bool *overflow_p, + tree *jump_target) +{ + int nargs = call_expr_nargs (call); + location_t loc = cp_expr_loc_or_input_loc (call); + tree args[4], arg; + if (nargs > 4) + { + invalid_nargs: + if (!ctx->quiet) + error_at (loc, "call to %qD function with incorrect" + "number of arguments", fndecl); + *non_constant_p = true; + return call; + } + if ((kind == CXA_BEGIN_CATCH || kind == CXA_GET_EXCEPTION_PTR) + && nargs == 1 + && (arg = CALL_EXPR_ARG (call, 0)) + && TREE_CODE (arg) == CALL_EXPR + && call_expr_nargs (arg) == 1 + && integer_zerop (CALL_EXPR_ARG (arg, 0))) + if (tree fun = get_function_named_in_call (arg)) + if (fndecl_built_in_p (fun, BUILT_IN_EH_POINTER)) + { + if (ctx->global->caught_exceptions.length () < 2) + { + no_caught_exceptions: + if (!ctx->quiet) + error_at (loc, "%qD called with no caught exceptions pending", + fndecl); + *non_constant_p = true; + return call; + } + /* Both __cxa_get_exception_ptr (__builtin_eh_pointer (0)) + and __cxa_begin_catch (__builtin_eh_pointer (0)) calls expect + ctx->global->caught_exceptions vector to end with + __cxa_allocate_exception created artificial VAR_DECL (the + exception object) followed by handler type, pushed by TRY_BLOCK + evaluation. The only difference between the functions is that + __cxa_begin_catch pops the handler type from the vector and keeps + the VAR_DECL last and decreases uncaught_exceptions. The + VAR_DECL after __cxa_begin_catch serves as the current exception + and is then popped in __cxa_end_catch evaluation. */ + tree handler_type = ctx->global->caught_exceptions.last (); + if (handler_type && VAR_P (handler_type)) + goto no_caught_exceptions; + unsigned idx = ctx->global->caught_exceptions.length () - 2; + arg = ctx->global->caught_exceptions[idx]; + gcc_assert (VAR_P (arg)); + if (kind == CXA_BEGIN_CATCH) + { + ctx->global->caught_exceptions.pop (); + --ctx->global->uncaught_exceptions; + } + if (handler_type == NULL_TREE) + /* Used for catch (...). Just return void. */ + return void_node; + else if (POINTER_TYPE_P (handler_type)) + { + /* Used for catch of a pointer. */ + if (TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE) + arg = build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (arg)), arg, + size_zero_node, NULL_TREE, NULL_TREE); + arg = cp_convert (handler_type, arg, + ctx->quiet ? tf_none : tf_warning_or_error); + if (arg == error_mark_node) + { + *non_constant_p = true; + return call; + } + } + else + { + /* Used for catch of a non-pointer type. */ + tree exc_type = strip_array_types (TREE_TYPE (arg)); + tree exc_ptr_type = build_pointer_type (exc_type); + arg = build_fold_addr_expr_with_type (arg, exc_ptr_type); + if (CLASS_TYPE_P (handler_type)) + { + tree ptr_type = build_pointer_type (handler_type); + arg = cp_convert (ptr_type, arg, + ctx->quiet ? tf_none + : tf_warning_or_error); + if (arg == error_mark_node) + { + *non_constant_p = true; + return call; + } + } + } + return cxx_eval_constant_expression (ctx, arg, vc_prvalue, + non_constant_p, overflow_p, + jump_target); + } + for (int i = 0; i < nargs; ++i) + { + args[i] = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (call, i), + vc_prvalue, non_constant_p, + overflow_p, jump_target); + if (*non_constant_p) + return call; + if (*jump_target) + return NULL_TREE; + } + switch (kind) + { + case CXA_ALLOCATE_EXCEPTION: + if (nargs != 1) + goto invalid_nargs; + if (!tree_fits_uhwi_p (args[0])) + { + if (!ctx->quiet) + error_at (loc, "cannot allocate exception: size not constant"); + *non_constant_p = true; + return call; + } + else + { + tree type = build_array_type_nelts (char_type_node, + tree_to_uhwi (args[0])); + tree var = cxa_allocate_exception (loc, ctx, type, size_zero_node); + ctx->global->put_value (var, NULL_TREE); + return fold_convert (ptr_type_node, build_address (var)); + } + case CXA_FREE_EXCEPTION: + if (nargs != 1) + goto invalid_nargs; + arg = cxa_check_throw_arg (args[0], true); + if (arg == NULL_TREE) + { + invalid_ptr: + if (!ctx->quiet) + error_at (loc, "first argument to %qD function not result of " + "%<__cxa_allocate_exception%>", fndecl); + *non_constant_p = true; + return call; + } + DECL_NAME (arg) = heap_deleted_identifier; + ctx->global->destroy_value (arg); + ctx->global->heap_dealloc_count++; + return void_node; + case CXA_THROW: + if (nargs != 3) + goto invalid_nargs; + arg = cxa_check_throw_arg (args[0], false); + if (arg == NULL_TREE) + goto invalid_ptr; + DECL_EXCEPTION_REFCOUNT (arg) + = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), + size_one_node); + ++ctx->global->uncaught_exceptions; + *jump_target = arg; + return void_node; + case CXA_BEGIN_CATCH: + case CXA_GET_EXCEPTION_PTR: + goto invalid_nargs; + case CXA_END_CATCH: + if (nargs != 0) + goto invalid_nargs; + if (ctx->global->caught_exceptions.is_empty ()) + { + no_active_exc: + if (!ctx->quiet) + error_at (loc, "%qD called with no caught exceptions active", + fndecl); + *non_constant_p = true; + return call; + } + else + { + arg = ctx->global->caught_exceptions.pop (); + if (arg == NULL_TREE || !VAR_P (arg)) + goto no_active_exc; + free_except: + DECL_EXCEPTION_REFCOUNT (arg) + = size_binop (MINUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), + size_one_node); + if (integer_zerop (DECL_EXCEPTION_REFCOUNT (arg))) + { + if (type_build_dtor_call (TREE_TYPE (arg))) + { + tree cleanup + = cxx_maybe_build_cleanup (arg, (ctx->quiet ? tf_none + : tf_warning_or_error)); + if (cleanup == error_mark_node) + *non_constant_p = true; + tree jmp_target = NULL_TREE; + cxx_eval_constant_expression (ctx, cleanup, vc_discard, + non_constant_p, overflow_p, + &jmp_target); + if (throws (&jmp_target)) + *jump_target = jmp_target; + } + DECL_NAME (arg) = heap_deleted_identifier; + ctx->global->destroy_value (arg); + ctx->global->heap_dealloc_count++; + } + } + return void_node; + case CXA_RETHROW: + if (nargs != 0) + goto invalid_nargs; + unsigned idx; + FOR_EACH_VEC_ELT_REVERSE (ctx->global->caught_exceptions, idx, arg) + if (arg == NULL_TREE || !VAR_P (arg)) + --idx; + else + break; + if (arg == NULL_TREE) + { + if (!ctx->quiet) + error_at (loc, "%qD called with no caught exceptions active", + fndecl); + *non_constant_p = true; + return call; + } + DECL_EXCEPTION_REFCOUNT (arg) + = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), size_one_node); + ++ctx->global->uncaught_exceptions; + *jump_target = arg; + return void_node; + case CXA_BAD_CAST: + case CXA_BAD_TYPEID: + case CXA_THROW_BAD_ARRAY_NEW_LENGTH: + if (nargs != 0) + goto invalid_nargs; + else + { + tree name; + switch (kind) + { + case CXA_BAD_CAST: + name = get_identifier ("bad_cast"); + break; + case CXA_BAD_TYPEID: + name = get_identifier ("bad_typeid"); + break; + case CXA_THROW_BAD_ARRAY_NEW_LENGTH: + name = get_identifier ("bad_array_new_length"); + break; + default: + gcc_unreachable (); + } + tree decl = lookup_qualified_name (std_node, name); + if (TREE_CODE (decl) != TYPE_DECL + || !CLASS_TYPE_P (TREE_TYPE (decl)) + || !type_build_ctor_call (TREE_TYPE (decl))) + { + if (!ctx->quiet) + error_at (loc, "%qD called without % being defined", + fndecl, name); + *non_constant_p = true; + return call; + } + tree type = TREE_TYPE (decl); + tree var = cxa_allocate_exception (loc, ctx, type, size_one_node); + tree ctor + = build_special_member_call (var, complete_ctor_identifier, + NULL, type, LOOKUP_NORMAL, + ctx->quiet ? tf_none + : tf_warning_or_error); + if (ctor == error_mark_node) + { + *non_constant_p = true; + return call; + } + if (TREE_CONSTANT (ctor)) + ctx->global->put_value (var, ctor); + else + { + ctx->global->put_value (var, NULL_TREE); + cxx_eval_constant_expression (ctx, ctor, vc_discard, + non_constant_p, overflow_p, + jump_target); + if (*non_constant_p) + return call; + if (throws (jump_target)) + return NULL_TREE; + } + ++ctx->global->uncaught_exceptions; + *jump_target = var; + } + return void_node; + case STD_UNCAUGHT_EXCEPTIONS: + if (nargs != 0) + goto invalid_nargs; + /* Similarly to __builtin_is_constant_evaluated (), we don't + want to give a definite answer during mce_unknown evaluation, + because that might prevent evaluation later on when some + exceptions might be uncaught. But unlike that, we don't + want to constant fold it even during cp_fold, because at runtime + std::uncaught_exceptions () might still be non-zero. */ + if (ctx->manifestly_const_eval != mce_true) + { + *non_constant_p = true; + return call; + } + return build_int_cst (integer_type_node, + ctx->global->uncaught_exceptions); + case STD_CURRENT_EXCEPTION: + if (nargs != 0) + goto invalid_nargs; + else + { + tree name = get_identifier ("exception_ptr"); + tree decl = lookup_qualified_name (std_node, name); + tree fld; + if (TREE_CODE (decl) != TYPE_DECL + || !CLASS_TYPE_P (TREE_TYPE (decl)) + || !COMPLETE_TYPE_P (TREE_TYPE (decl)) + || !(fld = next_aggregate_field (TYPE_FIELDS (TREE_TYPE (decl)))) + || DECL_ARTIFICIAL (fld) + || TREE_CODE (TREE_TYPE (fld)) != POINTER_TYPE + || next_aggregate_field (DECL_CHAIN (fld)) + || !tree_int_cst_equal (TYPE_SIZE (TREE_TYPE (decl)), + TYPE_SIZE (TREE_TYPE (fld)))) + { + if (!ctx->quiet) + error_at (loc, "%qD called without supportable %qs", + fndecl, "std::exception_ptr"); + *non_constant_p = true; + return call; + } + FOR_EACH_VEC_ELT_REVERSE (ctx->global->caught_exceptions, idx, arg) + if (arg == NULL_TREE || !VAR_P (arg)) + --idx; + else + break; + /* Similarly to __builtin_is_constant_evaluated (), we don't + want to give a definite answer during mce_unknown evaluation, + because that might prevent evaluation later on when some + exceptions might be current. But unlike that, we don't + want to constant fold it to null even during cp_fold, because + at runtime std::current_exception () might still be non-null. */ + if (ctx->manifestly_const_eval != mce_true && arg == NULL_TREE) + { + *non_constant_p = true; + return call; + } + if (arg == NULL_TREE) + arg = build_zero_cst (TREE_TYPE (fld)); + else + { + DECL_EXCEPTION_REFCOUNT (arg) + = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), + size_one_node); + arg = fold_convert (ptr_type_node, build_address (arg)); + } + return build_constructor_single (TREE_TYPE (decl), fld, arg); + } + case STD_RETHROW_EXCEPTION: + if (nargs != 1) + goto invalid_nargs; + if (TYPE_REF_P (TREE_TYPE (args[0]))) + { + arg = args[0]; + STRIP_NOPS (arg); + if (TREE_CODE (arg) == ADDR_EXPR) + { + args[0] + = cxx_eval_constant_expression (ctx, TREE_OPERAND (arg, 0), + vc_prvalue, non_constant_p, + overflow_p, jump_target); + if (*non_constant_p) + return call; + if (*jump_target) + return NULL_TREE; + } + } + if (TREE_CODE (args[0]) != CONSTRUCTOR + || CONSTRUCTOR_NELTS (args[0]) != 1) + { + invalid_std_rethrow: + if (!ctx->quiet) + error_at (loc, "%qD called with unexpected %qs argument", + fndecl, "std::exception_ptr"); + *non_constant_p = true; + return void_node; + } + arg = cxa_check_throw_arg (CONSTRUCTOR_ELT (args[0], 0)->value, false); + if (arg == NULL_TREE) + goto invalid_std_rethrow; + DECL_EXCEPTION_REFCOUNT (arg) + = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), size_one_node); + ++ctx->global->uncaught_exceptions; + *jump_target = arg; + return void_node; + case BUILTIN_EH_PTR_ADJUST_REF: + if (nargs != 2) + goto invalid_nargs; + arg = cxa_check_throw_arg (args[0], false); + if (arg == NULL_TREE) + goto invalid_ptr; + if (integer_onep (args[1])) + DECL_EXCEPTION_REFCOUNT (arg) + = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), + size_one_node); + else if (integer_minus_onep (args[1])) + goto free_except; + else + { + if (!ctx->quiet) + error_at (loc, "%qD called with second argument " + "other than 1 or -1", fndecl); + *non_constant_p = true; + } + return void_node; + default: + gcc_unreachable (); + } +} + /* Attempt to evaluate T which represents a call to a builtin function. We assume here that all builtin functions evaluate to scalar types represented by _CST nodes. */ @@ -1557,7 +2265,8 @@ static tree find_heap_var_refs (tree *, int *, void *); static tree cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { const int nargs = call_expr_nargs (t); tree *args = (tree *) alloca (nargs * sizeof (tree)); @@ -1603,6 +2312,12 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, return fold_builtin_source_location (t); } + if (fndecl_built_in_p (fun, CP_BUILT_IN_EH_PTR_ADJUST_REF, + BUILT_IN_FRONTEND)) + return cxx_eval_cxa_builtin_fn (ctx, t, BUILTIN_EH_PTR_ADJUST_REF, + fun, non_constant_p, overflow_p, + jump_target); + int strops = 0; int strret = 0; if (fndecl_built_in_p (fun, BUILT_IN_NORMAL)) @@ -1677,8 +2392,14 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, || potential_constant_expression (arg)) { bool dummy1 = false, dummy2 = false; + tree jmp_target = NULL_TREE; arg = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue, - &dummy1, &dummy2); + &dummy1, &dummy2, &jmp_target); + if (jmp_target) + { + *jump_target = jmp_target; + return NULL_TREE; + } } if (bi_const_p) @@ -1767,7 +2488,8 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, } return cxx_eval_constant_expression (&new_ctx, new_call, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); } /* TEMP is the constant value of a temporary object of type TYPE. Adjust @@ -1882,7 +2604,8 @@ addr_of_non_const_var (tree *tp, int *walk_subtrees, void *data) static tree cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun, tree orig_fun, bool *non_constant_p, - bool *overflow_p, bool *non_constant_args) + bool *overflow_p, bool *non_constant_args, + tree *jump_target) { int nargs = call_expr_nargs (t); tree parms = DECL_ARGUMENTS (fun); @@ -1958,14 +2681,16 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun, /* Undo convert_for_arg_passing work here. */ x = convert_from_reference (x); arg = cxx_eval_constant_expression (ctx, x, vc_glvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); } else /* Normally we would strip a TARGET_EXPR in an initialization context such as this, but here we do the elision differently: we keep the TARGET_EXPR, and use its CONSTRUCTOR as the value of the parm. */ arg = cxx_eval_constant_expression (ctx, x, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); /* Check we aren't dereferencing a null pointer when calling a non-static member function, which is undefined behaviour. */ if (i == 0 && DECL_OBJECT_MEMBER_FUNCTION_P (fun) @@ -1983,6 +2708,8 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun, /* Don't VERIFY_CONSTANT here. */ if (*non_constant_p && ctx->quiet) break; + if (*jump_target) + break; /* Just discard ellipsis args after checking their constantitude. */ if (!parms) continue; @@ -2094,9 +2821,10 @@ fold_operand (tree e, const constexpr_ctx *ctx) if (ctx) { bool new_non_constant_p = false, new_overflow_p = false; + tree jmp_target = NULL_TREE; e = cxx_eval_constant_expression (ctx, e, vc_prvalue, &new_non_constant_p, - &new_overflow_p); + &new_overflow_p, &jmp_target); } else e = fold_non_dependent_expr (e, tf_none, /*manifestly_const_eval=*/true); @@ -2183,7 +2911,7 @@ cxx_eval_assert (const constexpr_ctx *ctx, tree arg, const char *msg, if (*non_constant_p) return true; - tree eval; + tree eval, jmp_target = NULL_TREE; if (!evaluated) { if (!potential_rvalue_constant_expression (arg)) @@ -2196,12 +2924,15 @@ cxx_eval_assert (const constexpr_ctx *ctx, tree arg, const char *msg, modifiable_tracker ms (new_ctx.global); eval = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue, &new_non_constant_p, - &new_overflow_p); + &new_overflow_p, &jmp_target); } else eval = cxx_eval_constant_expression (ctx, arg, vc_prvalue, non_constant_p, - overflow_p); + overflow_p, &jmp_target); + if (jmp_target) + return true; + if (!*non_constant_p && integer_zerop (eval)) { if (!ctx->quiet) @@ -2233,7 +2964,8 @@ cxx_eval_assert (const constexpr_ctx *ctx, tree arg, const char *msg, static tree cxx_eval_internal_function (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { enum tree_code opcode = ERROR_MARK; @@ -2266,13 +2998,15 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t, case IFN_LAUNDER: return cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), vc_prvalue, non_constant_p, - overflow_p); + overflow_p, jump_target); case IFN_VEC_CONVERT: { tree arg = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), vc_prvalue, non_constant_p, - overflow_p); + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; if (TREE_CODE (arg) == VECTOR_CST) if (tree r = fold_const_call (CFN_VEC_CONVERT, TREE_TYPE (t), arg)) return r; @@ -2290,10 +3024,13 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t, /* Evaluate constant arguments using OPCODE and return a complex number containing the result and the overflow bit. */ tree arg0 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); tree arg1 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 1), lval, - non_constant_p, overflow_p); - + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) { location_t loc = cp_expr_loc_or_input_loc (t); @@ -2566,7 +3303,8 @@ get_component_with_type (tree path, tree type, tree stop) static tree cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { /* T will be something like __dynamic_cast ((B*) b, &_ZTI1B, &_ZTI1D, 8) @@ -2585,19 +3323,26 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, /* TYPE can only be either T* or T&. We can't know which of these it is by looking at TYPE, but OBJ will be "(T*) x" in the first case, - and something like "(T*)(T&)(T*) x" in the second case. */ - bool reference_p = false; + and something like "(T*)(T&)(T*) x" in the second case. + This is true for the reference cases in C++ < 26 or when exceptions + aren't enabled, in that case we should diagnose errors. For C++26 + with exceptions we should silently evaluate to null pointer and + let the callers call __cxa_bad_cast () later to throw an exception. */ + bool fail_for_non_constant_p = false; while (CONVERT_EXPR_P (obj) || TREE_CODE (obj) == SAVE_EXPR) { - reference_p |= TYPE_REF_P (TREE_TYPE (obj)); + if (cxx_dialect < cxx26 || !flag_exceptions) + fail_for_non_constant_p |= TYPE_REF_P (TREE_TYPE (obj)); obj = TREE_OPERAND (obj, 0); } /* Evaluate the object so that we know its dynamic type. */ obj = cxx_eval_constant_expression (ctx, obj, vc_prvalue, non_constant_p, - overflow_p); + overflow_p, jump_target); if (*non_constant_p) return call; + if (*jump_target) + return NULL_TREE; /* For dynamic_cast from classes with virtual bases we can get something like (virt_base *)(&d + 16) as OBJ. Try to convert that into @@ -2609,7 +3354,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, if (TREE_CODE (objo) == POINTER_PLUS_EXPR) { objo = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (TREE_TYPE (obj)), - obj); + obj, NULL, jump_target); if (objo) obj = build_fold_addr_expr (objo); } @@ -2625,7 +3370,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, ? TREE_OPERAND (obj, 1) : obj)) if (TREE_CODE (t) != FIELD_DECL || !DECL_FIELD_IS_BASE (t)) { - if (reference_p) + if (fail_for_non_constant_p) { if (!ctx->quiet) { @@ -2647,9 +3392,12 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, constructor or destructor's class. */ tree vtable = build_vfield_ref (obj, objtype); vtable = cxx_eval_constant_expression (ctx, vtable, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return call; + if (*jump_target) + return NULL_TREE; /* With -fsanitize=vptr, we initialize all vtable pointers to null, so it's possible that we got a null pointer now. */ if (integer_zerop (vtable)) @@ -2681,7 +3429,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, /* If not accessible, give an error. */ if (t == error_mark_node) { - if (reference_p) + if (fail_for_non_constant_p) { if (!ctx->quiet) { @@ -2714,7 +3462,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, obj = get_component_with_type (obj, mdtype, NULL_TREE); if (obj == error_mark_node) { - if (reference_p) + if (fail_for_non_constant_p) { if (!ctx->quiet) { @@ -2736,7 +3484,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, tree binfo = lookup_base (mdtype, type, ba_check, &b_kind, tf_none); if (!binfo || binfo == error_mark_node) { - if (reference_p) + if (fail_for_non_constant_p) { if (!ctx->quiet) { @@ -2832,7 +3580,7 @@ replace_decl (tree *tp, tree decl, tree replacement) static tree cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, tree *jump_target) { tree function = THUNK_TARGET (thunk_fndecl); @@ -2875,7 +3623,8 @@ cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl, new_call, offset); return cxx_eval_constant_expression (ctx, new_call, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); } /* If OBJECT is of const class type, evaluate it to a CONSTRUCTOR and set @@ -2885,7 +3634,7 @@ cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl, static void cxx_set_object_constness (const constexpr_ctx *ctx, tree object, bool readonly_p, bool *non_constant_p, - bool *overflow_p) + bool *overflow_p, tree *jump_target) { if (CLASS_TYPE_P (TREE_TYPE (object)) && CP_TYPE_CONST_P (TREE_TYPE (object))) @@ -2893,8 +3642,11 @@ cxx_set_object_constness (const constexpr_ctx *ctx, tree object, /* Subobjects might not be stored in ctx->global->values but we can get its CONSTRUCTOR by evaluating *this. */ tree e = cxx_eval_constant_expression (ctx, object, vc_prvalue, - non_constant_p, overflow_p); - if (TREE_CODE (e) == CONSTRUCTOR && !*non_constant_p) + non_constant_p, overflow_p, + jump_target); + if (!*non_constant_p + && !throws (jump_target) + && TREE_CODE (e) == CONSTRUCTOR) TREE_READONLY (e) = readonly_p; } } @@ -2906,20 +3658,25 @@ cxx_set_object_constness (const constexpr_ctx *ctx, tree object, static tree cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { location_t loc = cp_expr_loc_or_input_loc (t); tree fun = get_function_named_in_call (t); if (fun == NULL_TREE) return cxx_eval_internal_function (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (TREE_CODE (fun) != FUNCTION_DECL) { /* Might be a constexpr function pointer. */ fun = cxx_eval_constant_expression (ctx, fun, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; STRIP_NOPS (fun); if (TREE_CODE (fun) == ADDR_EXPR) fun = TREE_OPERAND (fun, 0); @@ -2971,9 +3728,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, if (fndecl_built_in_p (fun)) return cxx_eval_builtin_function_call (ctx, t, fun, - lval, non_constant_p, overflow_p); + lval, non_constant_p, overflow_p, + jump_target); if (DECL_THUNK_P (fun)) - return cxx_eval_thunk_call (ctx, t, fun, lval, non_constant_p, overflow_p); + return cxx_eval_thunk_call (ctx, t, fun, lval, non_constant_p, overflow_p, + jump_target); + bool non_constexpr_call = false; if (!maybe_constexpr_fn (fun)) { if (TREE_CODE (t) == CALL_EXPR @@ -2988,7 +3748,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, { tree arg = CALL_EXPR_ARG (t, i); arg = cxx_eval_constant_expression (ctx, arg, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; /* Deleting a non-constant pointer has a better error message below. */ if (new_op_p || i != 0) @@ -3103,7 +3866,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, { tree arg = CALL_EXPR_ARG (t, i); arg = cxx_eval_constant_expression (ctx, arg, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (i == 1) arg1 = arg; else @@ -3113,16 +3879,31 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, return arg1; } else if (cxx_dynamic_cast_fn_p (fun)) - return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p); + return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p, + jump_target); + else if (enum cxa_builtin kind = cxx_cxa_builtin_fn_p (fun)) + return cxx_eval_cxa_builtin_fn (ctx, t, kind, fun, + non_constant_p, overflow_p, + jump_target); - if (!ctx->quiet) + /* Calls to non-constexpr functions can be diagnosed right away + before C++26, though in C++26 evaluation of the arguments might + throw and if caught it could be still constant expression. + So for C++26 this is diagnosed only after + cxx_bind_parameters_in_call. */ + if (cxx_dialect >= cxx26) + non_constexpr_call = true; + else { - if (!lambda_static_thunk_p (fun)) - error_at (loc, "call to non-% function %qD", fun); - explain_invalid_constexpr_fn (fun); + if (!ctx->quiet) + { + if (!lambda_static_thunk_p (fun)) + error_at (loc, "call to non-% function %qD", fun); + explain_invalid_constexpr_fn (fun); + } + *non_constant_p = true; + return t; } - *non_constant_p = true; - return t; } constexpr_ctx new_ctx = *ctx; @@ -3158,7 +3939,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, constexpr_call new_call; new_call.bindings = cxx_bind_parameters_in_call (ctx, t, fun, orig_fun, non_constant_p, - overflow_p, &non_constant_args); + overflow_p, &non_constant_args, + jump_target); /* We build up the bindings list before we know whether we already have this call cached. If we don't end up saving these bindings, ggc_free them when @@ -3172,8 +3954,21 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, void preserve () { bindings = NULL; } } fb (new_call.bindings); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; + if (non_constexpr_call) + { + if (!ctx->quiet) + { + if (!lambda_static_thunk_p (fun)) + error_at (loc, "call to non-% function %qD", fun); + explain_invalid_constexpr_fn (fun); + } + *non_constant_p = true; + return t; + } /* We can't defer instantiating the function any longer. */ if (!DECL_INITIAL (fun) @@ -3246,7 +4041,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, new_obj = TREE_VEC_ELT (new_call.bindings, 0); bool empty_base = false; new_obj = cxx_fold_indirect_ref (ctx, loc, DECL_CONTEXT (fun), new_obj, - &empty_base); + &empty_base, jump_target); + if (*jump_target) + return NULL_TREE; /* If we're initializing an empty class, don't set constness, because cxx_fold_indirect_ref will return the wrong object to set constness of. */ @@ -3395,7 +4192,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, semantics are no longer in effect; see [class.dtor]p5. */ if (new_obj && DECL_DESTRUCTOR_P (fun)) cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/false, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); /* If this is a constructor, we are beginning the lifetime of the object we are initializing. */ @@ -3409,16 +4206,25 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, build_constructor (TREE_TYPE (new_obj), NULL)); cxx_eval_constant_expression (ctx, activate, - lval, non_constant_p, overflow_p); + lval, non_constant_p, overflow_p, + jump_target); ggc_free (activate); + if (*jump_target) + return NULL_TREE; } - tree jump_target = NULL_TREE; + tree jmp_target = NULL_TREE; cxx_eval_constant_expression (&call_ctx, body, vc_discard, non_constant_p, overflow_p, - &jump_target); + &jmp_target); - if (DECL_CONSTRUCTOR_P (fun)) + if (!*non_constant_p && throws (&jmp_target)) + { + result = NULL_TREE; + cacheable = false; + *jump_target = jmp_target; + } + else if (DECL_CONSTRUCTOR_P (fun)) /* This can be null for a subobject constructor call, in which case what we care about is the initialization side-effects rather than the value. We could get at the @@ -3446,7 +4252,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, marking the CONSTRUCTOR TREE_READONLY. */ if (new_obj && DECL_CONSTRUCTOR_P (fun)) cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/true, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); /* Remove the parms/result from the values map. */ destroy_value_checked (ctx, res, non_constant_p); @@ -3506,7 +4312,13 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, cacheable = false; result = cxx_eval_constant_expression (ctx, result, lval, non_constant_p, - overflow_p); + overflow_p, + jump_target); + if (*jump_target) + { + cacheable = false; + result = NULL_TREE; + } } } @@ -3864,12 +4676,16 @@ cxx_eval_check_shift_p (location_t loc, const constexpr_ctx *ctx, static tree cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t, bool /*lval*/, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree r; tree orig_arg = TREE_OPERAND (t, 0); tree arg = cxx_eval_constant_expression (ctx, orig_arg, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (arg); location_t loc = EXPR_LOCATION (t); enum tree_code code = TREE_CODE (t); @@ -3893,7 +4709,7 @@ cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t, static tree cxx_fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t, tree lhs, tree rhs, bool *non_constant_p, - bool *overflow_p) + bool *overflow_p, tree *jump_target) { STRIP_NOPS (lhs); if (TREE_CODE (lhs) != ADDR_EXPR) @@ -3915,9 +4731,12 @@ cxx_fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t, t = fold_convert_loc (loc, ssizetype, TREE_OPERAND (lhs, 1)); tree nelts = array_type_nelts_top (TREE_TYPE (TREE_OPERAND (lhs, 0))); nelts = cxx_eval_constant_expression (ctx, nelts, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return NULL_TREE; + if (*jump_target) + return NULL_TREE; /* Don't fold an out-of-bound access. */ if (!tree_int_cst_le (t, nelts)) return NULL_TREE; @@ -3937,7 +4756,8 @@ cxx_fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t, t = cp_build_addr_expr (t, tf_warning_or_error); t = cp_fold_convert (orig_type, t); return cxx_eval_constant_expression (ctx, t, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); } return NULL_TREE; @@ -3981,22 +4801,29 @@ cxx_maybe_fold_addr_pointer_plus (tree t) static tree cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree r = NULL_TREE; tree orig_lhs = TREE_OPERAND (t, 0); tree orig_rhs = TREE_OPERAND (t, 1); tree lhs, rhs; lhs = cxx_eval_constant_expression (ctx, orig_lhs, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); /* Don't VERIFY_CONSTANT here, it's unnecessary and will break pointer subtraction. */ if (*non_constant_p) return t; + if (*jump_target) + return NULL_TREE; rhs = cxx_eval_constant_expression (ctx, orig_rhs, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return t; + if (*jump_target) + return NULL_TREE; location_t loc = EXPR_LOCATION (t); enum tree_code code = TREE_CODE (t); @@ -4052,13 +4879,17 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, return t; } else if (code == POINTER_PLUS_EXPR) - r = cxx_fold_pointer_plus_expression (ctx, t, lhs, rhs, non_constant_p, - overflow_p); + { + r = cxx_fold_pointer_plus_expression (ctx, t, lhs, rhs, non_constant_p, + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; + } else if (code == SPACESHIP_EXPR) { r = genericize_spaceship (loc, type, lhs, rhs); return cxx_eval_constant_expression (ctx, r, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); } if (r == NULL_TREE) @@ -4117,7 +4948,10 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, { tree val = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (val); if (TREE_CODE (t) == IF_STMT && IF_STMT_CONSTEVAL_P (t)) { @@ -4178,19 +5012,29 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, static tree cxx_eval_vector_conditional_expression (const constexpr_ctx *ctx, tree t, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree arg1 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (arg1); tree arg2 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (arg2); tree arg3 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2), vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (arg3); location_t loc = EXPR_LOCATION (t); tree type = TREE_TYPE (t); @@ -4578,7 +5422,8 @@ diag_array_subscript (location_t loc, const constexpr_ctx *ctx, tree array, tree static tree get_array_or_vector_nelts (const constexpr_ctx *ctx, tree type, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree nelts; if (TREE_CODE (type) == ARRAY_TYPE) @@ -4595,7 +5440,8 @@ get_array_or_vector_nelts (const constexpr_ctx *ctx, tree type, /* For VLAs, the number of elements won't be an integer constant. */ nelts = cxx_eval_constant_expression (ctx, nelts, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); return nelts; } @@ -4626,13 +5472,17 @@ extract_string_elt (tree string, unsigned chars_per_elt, unsigned index) static tree eval_and_check_array_index (const constexpr_ctx *ctx, tree t, bool allow_one_past, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { location_t loc = cp_expr_loc_or_input_loc (t); tree ary = TREE_OPERAND (t, 0); t = TREE_OPERAND (t, 1); tree index = cxx_eval_constant_expression (ctx, t, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (index); if (!tree_fits_shwi_p (index) @@ -4644,7 +5494,9 @@ eval_and_check_array_index (const constexpr_ctx *ctx, } tree nelts = get_array_or_vector_nelts (ctx, TREE_TYPE (ary), non_constant_p, - overflow_p); + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (nelts); if (allow_one_past ? !tree_int_cst_le (index, nelts) @@ -4664,14 +5516,18 @@ eval_and_check_array_index (const constexpr_ctx *ctx, static tree cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree oldary = TREE_OPERAND (t, 0); tree ary = cxx_eval_constant_expression (ctx, oldary, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return t; + if (*jump_target) + return NULL_TREE; if (!lval && TREE_CODE (ary) == VIEW_CONVERT_EXPR && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (ary, 0))) @@ -4681,9 +5537,12 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, tree oldidx = TREE_OPERAND (t, 1); tree index = eval_and_check_array_index (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return t; + if (*jump_target) + return NULL_TREE; if (lval && ary == oldary && index == oldidx) return t; @@ -4801,7 +5660,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, ctx = &new_ctx; } t = cxx_eval_constant_expression (ctx, val, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); if (new_ctor && t != ctx->ctor) free_constructor (ctx->ctor); return t; @@ -4813,7 +5672,8 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, static tree cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { unsigned HOST_WIDE_INT i; tree field; @@ -4822,9 +5682,12 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, tree orig_whole = TREE_OPERAND (t, 0); tree whole = cxx_eval_constant_expression (ctx, orig_whole, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return t; + if (*jump_target) + return NULL_TREE; if (INDIRECT_REF_P (whole) && integer_zerop (TREE_OPERAND (whole, 0))) { @@ -4933,7 +5796,8 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, value = build_value_init (TREE_TYPE (t), tf_warning_or_error); return cxx_eval_constant_expression (ctx, value, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); } /* Subroutine of cxx_eval_constant_expression. @@ -4943,7 +5807,8 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, static tree cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree orig_whole = TREE_OPERAND (t, 0); tree retval, fldval, utype, mask; @@ -4951,10 +5816,13 @@ cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t, HOST_WIDE_INT istart, isize; tree whole = cxx_eval_constant_expression (ctx, orig_whole, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); tree start, field, value; unsigned HOST_WIDE_INT i; + if (*jump_target) + return NULL_TREE; if (whole == orig_whole) return t; /* Don't VERIFY_CONSTANT here; we only want to check that we got a @@ -5235,7 +6103,7 @@ clear_uchar_or_std_byte_in_mask (location_t loc, tree t, unsigned char *mask) static tree cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p, - bool *overflow_p) + bool *overflow_p, tree *jump_target) { if (check_bit_cast_type (ctx, EXPR_LOCATION (t), TREE_TYPE (t), TREE_TYPE (t)) @@ -5249,9 +6117,12 @@ cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p, } tree op = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return t; + if (*jump_target) + return NULL_TREE; location_t loc = EXPR_LOCATION (t); if (BITS_PER_UNIT != 8 || CHAR_BIT != 8) @@ -5329,8 +6200,9 @@ cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p, if (CHECKING_P) { tree e = cxx_eval_bare_aggregate (ctx, r, vc_prvalue, - non_constant_p, overflow_p); - gcc_checking_assert (e == r); + non_constant_p, overflow_p, + jump_target); + gcc_checking_assert (e == r && !*jump_target); r = e; } } @@ -5371,19 +6243,24 @@ cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p, static tree cxx_eval_logical_expression (const constexpr_ctx *ctx, tree t, tree bailout_value, tree continue_value, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree r; tree lhs = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue, non_constant_p, - overflow_p); + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (lhs); if (tree_int_cst_equal (lhs, bailout_value)) return lhs; gcc_assert (tree_int_cst_equal (lhs, continue_value)); r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_prvalue, non_constant_p, - overflow_p); + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (r); return r; } @@ -5540,7 +6417,8 @@ verify_ctor_sanity (const constexpr_ctx *ctx, tree type) static tree cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { vec *v = CONSTRUCTOR_ELTS (t); bool changed = false; @@ -5593,7 +6471,10 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, get_or_insert_ctor_field (ctx->ctor, index); tree elt = cxx_eval_constant_expression (&new_ctx, value, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; /* Don't VERIFY_CONSTANT here. */ if (ctx->quiet && *non_constant_p) break; @@ -5683,7 +6564,8 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, static tree cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, bool value_init, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree elttype = TREE_TYPE (atype); verify_ctor_sanity (ctx, atype); @@ -5694,7 +6576,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, if (init && TREE_CODE (init) == CONSTRUCTOR) return cxx_eval_bare_aggregate (ctx, init, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); /* For the default constructor, build up a call to the default constructor of the element type. We only need to handle class types @@ -5731,7 +6613,9 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, } tree nelts = get_array_or_vector_nelts (ctx, atype, non_constant_p, - overflow_p); + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; unsigned HOST_WIDE_INT max = tree_to_uhwi (nelts); for (i = 0; i < max; ++i) { @@ -5757,9 +6641,9 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, } else eltinit = cp_build_array_ref (input_location, init, idx, complain); - eltinit = cxx_eval_vec_init_1 (&new_ctx, elttype, eltinit, value_init, - lval, - non_constant_p, overflow_p); + eltinit = cxx_eval_vec_init_1 (&new_ctx, elttype, eltinit, + value_init, lval, non_constant_p, + overflow_p, jump_target); } else if (pre_init) { @@ -5773,7 +6657,8 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, /* Clarify what object is being initialized (118285). */ eltinit = build2 (INIT_EXPR, elttype, new_ctx.object, eltinit); eltinit = cxx_eval_constant_expression (&new_ctx, eltinit, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); reuse = i == 0; } else @@ -5789,8 +6674,11 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, /* Clarify what object is being initialized (118285). */ eltinit = build2 (INIT_EXPR, elttype, new_ctx.object, eltinit); eltinit = cxx_eval_constant_expression (&new_ctx, eltinit, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); } + if (*jump_target) + return NULL_TREE; if (*non_constant_p) break; if (no_slot) @@ -5840,7 +6728,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, static tree cxx_eval_vec_init (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, tree *jump_target) { tree atype = TREE_TYPE (t); tree init = VEC_INIT_EXPR_INIT (t); @@ -5872,10 +6760,10 @@ cxx_eval_vec_init (const constexpr_ctx *ctx, tree t, } init = expand_vec_init_expr (ctx->object, t, complain); return cxx_eval_constant_expression (ctx, init, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); } tree r = cxx_eval_vec_init_1 (ctx, atype, init, value_init, - lval, non_constant_p, overflow_p); + lval, non_constant_p, overflow_p, jump_target); if (*non_constant_p) return t; else @@ -5904,14 +6792,16 @@ same_type_ignoring_tlq_and_bounds_p (tree type1, tree type2) otherwise return NULL_TREE. */ static tree -cxx_union_active_member (const constexpr_ctx *ctx, tree t) +cxx_union_active_member (const constexpr_ctx *ctx, tree t, tree *jump_target) { constexpr_ctx new_ctx = *ctx; new_ctx.quiet = true; bool non_constant_p = false, overflow_p = false; tree ctor = cxx_eval_constant_expression (&new_ctx, t, vc_prvalue, &non_constant_p, - &overflow_p); + &overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; if (TREE_CODE (ctor) == CONSTRUCTOR && CONSTRUCTOR_NELTS (ctor) == 1 && CONSTRUCTOR_ELT (ctor, 0)->index @@ -5924,7 +6814,8 @@ cxx_union_active_member (const constexpr_ctx *ctx, tree t) static tree cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, - tree op, unsigned HOST_WIDE_INT off, bool *empty_base) + tree op, unsigned HOST_WIDE_INT off, bool *empty_base, + tree *jump_target) { tree optype = TREE_TYPE (op); unsigned HOST_WIDE_INT const_nunits; @@ -5941,7 +6832,8 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, than pointer type. */ if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc, strip_array_types (optype), - op, off, empty_base)) + op, off, empty_base, + jump_target)) return fold_convert (type, ret); } else if (TREE_CODE (optype) == COMPLEX_TYPE @@ -5987,7 +6879,7 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, op = build4_loc (loc, ARRAY_REF, TREE_TYPE (optype), op, index, NULL_TREE, NULL_TREE); return cxx_fold_indirect_ref_1 (ctx, loc, type, op, rem, - empty_base); + empty_base, jump_target); } } /* ((foo *)&struct_with_foo_field)[x] => COMPONENT_REF */ @@ -5996,7 +6888,7 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, { if (TREE_CODE (optype) == UNION_TYPE) /* For unions prefer the currently active member. */ - if (tree field = cxx_union_active_member (ctx, op)) + if (tree field = cxx_union_active_member (ctx, op, jump_target)) { unsigned HOST_WIDE_INT el_sz = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field))); @@ -6005,7 +6897,8 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, tree cop = build3 (COMPONENT_REF, TREE_TYPE (field), op, field, NULL_TREE); if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc, type, cop, - off, empty_base)) + off, empty_base, + jump_target)) return ret; } } @@ -6050,7 +6943,8 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, op, field, NULL_TREE); if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc, type, cop, off - upos, - empty_base)) + empty_base, + jump_target)) return ret; } } @@ -6070,7 +6964,7 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, static tree cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, - tree op0, bool *empty_base /* = NULL*/) + tree op0, bool *empty_base, tree *jump_target) { tree sub = op0; tree subtype; @@ -6152,7 +7046,8 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, tree off = integer_zero_node; canonicalize_obj_off (op, off); return cxx_fold_indirect_ref_1 (ctx, loc, type, op, - tree_to_uhwi (off), empty_base); + tree_to_uhwi (off), empty_base, + jump_target); } } else if (TREE_CODE (sub) == POINTER_PLUS_EXPR @@ -6167,7 +7062,8 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, tree obj = TREE_OPERAND (op00, 0); canonicalize_obj_off (obj, off); return cxx_fold_indirect_ref_1 (ctx, loc, type, obj, - tree_to_uhwi (off), empty_base); + tree_to_uhwi (off), empty_base, + jump_target); } } /* *(foo *)fooarrptr => (*fooarrptr)[0] */ @@ -6177,7 +7073,10 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, tree type_domain; tree min_val = size_zero_node; tree newsub - = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (subtype), sub, NULL); + = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (subtype), sub, NULL, + jump_target); + if (*jump_target) + return NULL_TREE; if (newsub) sub = newsub; else @@ -6195,7 +7094,8 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, static tree cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree orig_op0 = TREE_OPERAND (t, 0); bool empty_base = false; @@ -6213,13 +7113,17 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, /* First try to simplify it directly. */ tree r = cxx_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t), - orig_op0, &empty_base); + orig_op0, &empty_base, jump_target); + if (*jump_target) + return NULL_TREE; if (!r) { /* If that didn't work, evaluate the operand first. */ tree op0 = cxx_eval_constant_expression (ctx, orig_op0, vc_prvalue, non_constant_p, - overflow_p); + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; /* Don't VERIFY_CONSTANT here. */ if (*non_constant_p) return t; @@ -6233,7 +7137,9 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, } r = cxx_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t), op0, - &empty_base); + &empty_base, jump_target); + if (*jump_target) + return NULL_TREE; if (r == NULL_TREE) { /* We couldn't fold to a constant value. Make sure it's not @@ -6263,7 +7169,10 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, } r = cxx_eval_constant_expression (ctx, r, - lval, non_constant_p, overflow_p); + lval, non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; @@ -6373,7 +7282,8 @@ non_const_var_error (location_t loc, tree r, bool fundef_p) static tree cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { int i; tree args[3]; @@ -6383,7 +7293,10 @@ cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t, { args[i] = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, i), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (args[i]); } @@ -6505,7 +7418,8 @@ modifying_const_object_p (tree_code code, tree obj, bool mutable_p) static tree cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { constexpr_ctx new_ctx = *ctx; @@ -6531,7 +7445,10 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, if (!SCALAR_TYPE_P (type)) new_ctx.ctor = new_ctx.object = NULL_TREE; init = cxx_eval_constant_expression (&new_ctx, init, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; } @@ -6543,8 +7460,11 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, as a whole; otherwise, only evaluate the innermost piece to avoid building up unnecessary *_REFs. */ target = cxx_eval_constant_expression (ctx, target, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); evaluated = true; + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; } @@ -6570,7 +7490,10 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, if (TREE_CODE (probe) == ARRAY_REF) { elt = eval_and_check_array_index (ctx, probe, false, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; } @@ -6627,8 +7550,11 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, break; } probe = cxx_eval_constant_expression (ctx, probe, vc_glvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); evaluated = true; + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; } @@ -6972,7 +7898,10 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, if (tree tinit = TARGET_EXPR_INITIAL (init)) init = tinit; init = cxx_eval_constant_expression (&new_ctx, init, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; /* The hash table might have moved since the get earlier, and the initializer might have mutated the underlying CONSTRUCTORs, so we must recompute VALP. */ @@ -7098,7 +8027,8 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, static tree cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { enum tree_code code = TREE_CODE (t); tree type = TREE_TYPE (t); @@ -7112,12 +8042,18 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t, /* The operand as an lvalue. */ op = cxx_eval_constant_expression (ctx, op, vc_glvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; /* The operand as an rvalue. */ tree val = cxx_eval_constant_expression (ctx, op, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; /* Don't VERIFY_CONSTANT if this might be dealing with a pointer to a local array in a constexpr function. */ bool ptr = INDIRECT_TYPE_P (TREE_TYPE (val)); @@ -7156,8 +8092,11 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t, tree store = build2_loc (cp_expr_loc_or_loc (t, input_location), MODIFY_EXPR, type, op, mod); mod = cxx_eval_constant_expression (ctx, store, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); ggc_free (store); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; @@ -7171,42 +8110,6 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t, return val; } -/* Predicates for the meaning of *jump_target. */ - -static bool -returns (tree *jump_target) -{ - return *jump_target - && TREE_CODE (*jump_target) == RETURN_EXPR; -} - -static bool -breaks (tree *jump_target) -{ - return *jump_target - && ((TREE_CODE (*jump_target) == LABEL_DECL - && LABEL_DECL_BREAK (*jump_target)) - || TREE_CODE (*jump_target) == BREAK_STMT - || TREE_CODE (*jump_target) == EXIT_EXPR); -} - -static bool -continues (tree *jump_target) -{ - return *jump_target - && ((TREE_CODE (*jump_target) == LABEL_DECL - && LABEL_DECL_CONTINUE (*jump_target)) - || TREE_CODE (*jump_target) == CONTINUE_STMT); - -} - -static bool -switches (tree *jump_target) -{ - return *jump_target - && TREE_CODE (*jump_target) == INTEGER_CST; -} - /* Subroutine of cxx_eval_statement_list. Determine whether the statement STMT matches *jump_target. If we're looking for a case label and we see the default label, note it in ctx->css_state. */ @@ -7254,6 +8157,11 @@ label_matches (const constexpr_ctx *ctx, tree *jump_target, tree stmt) breaks (jump_target) or continues (jump_target). */ break; + case VAR_DECL: + /* Uncaught exception. This is handled by TRY_BLOCK evaluation + and other places by testing throws (jump_target). */ + break; + default: gcc_unreachable (); } @@ -7268,15 +8176,9 @@ cxx_eval_statement_list (const constexpr_ctx *ctx, tree t, bool *non_constant_p, bool *overflow_p, tree *jump_target) { - tree local_target; /* In a statement-expression we want to return the last value. For empty statement expression return void_node. */ tree r = void_node; - if (!jump_target) - { - local_target = NULL_TREE; - jump_target = &local_target; - } for (tree_stmt_iterator i = tsi_start (t); !tsi_end_p (i); ++i) { tree stmt = *i; @@ -7304,18 +8206,11 @@ cxx_eval_statement_list (const constexpr_ctx *ctx, tree t, jump_target); if (*non_constant_p) break; - if (returns (jump_target) || breaks (jump_target)) + if (returns (jump_target) + || breaks (jump_target) + || throws (jump_target)) break; } - if (*jump_target && jump_target == &local_target) - { - /* We aren't communicating the jump to our caller, so give up. We don't - need to support evaluation of jumps out of statement-exprs. */ - if (!ctx->quiet) - error_at (cp_expr_loc_or_input_loc (r), - "statement is not a constant expression"); - *non_constant_p = true; - } return r; } @@ -7327,13 +8222,6 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, bool *non_constant_p, bool *overflow_p, tree *jump_target) { - tree local_target; - if (!jump_target) - { - local_target = NULL_TREE; - jump_target = &local_target; - } - tree body, cond = NULL_TREE, expr = NULL_TREE; tree cond_prep = NULL_TREE, cond_cleanup = NULL_TREE; unsigned cond_cleanup_depth = 0; @@ -7389,7 +8277,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, tree c; FOR_EACH_VEC_ELT_REVERSE (cleanups, i, c) cxx_eval_constant_expression (ctx, c, vc_discard, non_constant_p, - overflow_p); + overflow_p, jump_target); } if (cond_prep) for (tree decl = BIND_EXPR_VARS (cond_prep); @@ -7484,7 +8372,8 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, if (*non_constant_p || returns (jump_target) || breaks (jump_target) - || continues (jump_target)) + || continues (jump_target) + || throws (jump_target)) { depth = 1; break; @@ -7531,6 +8420,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, && !breaks (jump_target) && !continues (jump_target) && (!switches (jump_target) || count == 0) + && !throws (jump_target) && !*non_constant_p); cleanup_cond (); @@ -7549,7 +8439,10 @@ cxx_eval_switch_expr (const constexpr_ctx *ctx, tree t, tree cond = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_COND (t) : SWITCH_COND (t); cond = cxx_eval_constant_expression (ctx, cond, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (cond); if (TREE_CODE (cond) != INTEGER_CST) { @@ -7682,7 +8575,8 @@ maybe_warn_about_constant_value (location_t loc, tree decl) static tree build_new_constexpr_heap_type (const constexpr_ctx *ctx, tree elt_type, tree cookie_size, tree full_size, tree arg_size, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { gcc_assert (cookie_size == NULL_TREE || tree_fits_uhwi_p (cookie_size)); gcc_assert (tree_fits_uhwi_p (full_size)); @@ -7718,13 +8612,17 @@ build_new_constexpr_heap_type (const constexpr_ctx *ctx, tree elt_type, if (integer_zerop (op0)) arg_size = cxx_eval_constant_expression (ctx, op1, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); else if (integer_zerop (op1)) arg_size = cxx_eval_constant_expression (ctx, op0, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); else arg_size = NULL_TREE; + if (*jump_target) + return NULL_TREE; } else arg_size = NULL_TREE; @@ -7745,6 +8643,38 @@ build_new_constexpr_heap_type (const constexpr_ctx *ctx, tree elt_type, return build_new_constexpr_heap_type (elt_type, cookie_size, itype2); } +/* Handle the case when a cleanup of some expression throws. JMP_TARGET + indicates whether the cleanup threw or not, *JUMP_TARGET indicates whether + the expression which needed the cleanup threw. If both threw, diagnose + it and return NULL, otherwise return R. If only the cleanup threw, set + *JUMP_TARGET to the exception object from the cleanup. */ + +static tree +merge_jump_target (location_t loc, const constexpr_ctx *ctx, tree r, + bool *non_constant_p, tree *jump_target, tree jmp_target) +{ + if (!throws (&jmp_target)) + return r; + if (throws (jump_target)) + { + /* [except.throw]/9 - If the exception handling mechanism + handling an uncaught exception directly invokes a function + that exits via an exception, the function std::terminate is + invoked. */ + if (!ctx->quiet) + { + auto_diagnostic_group d; + diagnose_std_terminate (loc, ctx, *jump_target); + inform (loc, "destructor exited with an exception"); + } + *non_constant_p = true; + *jump_target = NULL_TREE; + return NULL_TREE; + } + *jump_target = jmp_target; + return r; +} + /* Attempt to reduce the expression T to a constant value. On failure, issue diagnostic and return error_mark_node. */ /* FIXME unify with c_fully_fold */ @@ -7754,9 +8684,9 @@ static tree cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, value_cat lval, bool *non_constant_p, bool *overflow_p, - tree *jump_target /* = NULL */) + tree *jump_target) { - if (jump_target && *jump_target) + if (*jump_target) { /* If we are jumping, ignore all statements/expressions except those that could have LABEL_EXPR or CASE_LABEL_EXPR in their bodies. */ @@ -7880,7 +8810,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, r = convert_from_reference (r); } return cxx_eval_constant_expression (ctx, r, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); } /* fall through */ case CONST_DECL: @@ -7958,7 +8888,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, r = v; if (TREE_ADDRESSABLE (TREE_TYPE (t))) r = cxx_eval_constant_expression (ctx, r, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; } else if (lval) /* Defer in case this is only used for its type. */; @@ -7991,7 +8924,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case CALL_EXPR: case AGGR_INIT_EXPR: r = cxx_eval_call_expression (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); break; case DECL_EXPR: @@ -8055,7 +8988,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (tree init = DECL_INITIAL (r)) { init = cxx_eval_constant_expression (ctx, init, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; /* Don't share a CONSTRUCTOR that might be changed. */ init = unshare_constructor (init); /* Remember that a constant object's constructor has already @@ -8125,9 +9061,12 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, /* Pass vc_prvalue because this indicates initialization of a temporary. */ r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) break; + if (*jump_target) + return NULL_TREE; if (!is_complex) { r = unshare_constructor (r); @@ -8135,8 +9074,15 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, r = adjust_temp_type (type, r); ctx->global->put_value (slot, r); } - if (TARGET_EXPR_CLEANUP (t) && !CLEANUP_EH_ONLY (t)) - ctx->global->cleanups->safe_push (TARGET_EXPR_CLEANUP (t)); + if (TARGET_EXPR_CLEANUP (t) + && (!CLEANUP_EH_ONLY (t) || cxx_dialect >= cxx26)) + { + ctx->global->cleanups->safe_push (TARGET_EXPR_CLEANUP (t)); + /* Mark CLEANUP_EH_ONLY cleanups by pushing NULL_TREE after + them. */ + if (CLEANUP_EH_ONLY (t)) + ctx->global->cleanups->safe_push (NULL_TREE); + } if (ctx->save_exprs) ctx->save_exprs->safe_push (slot); if (lval) @@ -8150,33 +9096,28 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case MODIFY_EXPR: gcc_assert (jump_target == NULL || *jump_target == NULL_TREE); r = cxx_eval_store_expression (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); break; case SCOPE_REF: r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case RETURN_EXPR: if (TREE_OPERAND (t, 0) != NULL_TREE) r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, - non_constant_p, overflow_p); - /* FALLTHRU */ + non_constant_p, overflow_p, + jump_target); + if (!throws (jump_target)) + *jump_target = t; + break; case BREAK_STMT: case CONTINUE_STMT: - if (jump_target) - *jump_target = t; - else - { - /* Can happen with ({ return true; }) && false; passed to - maybe_constant_value. There is nothing to jump over in this - case, and the bug will be diagnosed later. */ - gcc_assert (ctx->quiet); - *non_constant_p = true; - } + *jump_target = t; break; case SAVE_EXPR: @@ -8185,9 +9126,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, r = v; else { - r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue, - non_constant_p, overflow_p); - if (*non_constant_p) + r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), + vc_prvalue, non_constant_p, + overflow_p, jump_target); + if (*non_constant_p || *jump_target) break; ctx->global->put_value (t, r); if (ctx->save_exprs) @@ -8195,16 +9137,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, } break; - case TRY_CATCH_EXPR: - if (TREE_OPERAND (t, 0) == NULL_TREE) - { - r = void_node; - break; - } - /* FALLTHRU */ case NON_LVALUE_EXPR: - case TRY_BLOCK: - case MUST_NOT_THROW_EXPR: case EXPR_STMT: case EH_SPEC_BLOCK: r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), @@ -8213,6 +9146,42 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, jump_target); break; + case TRY_BLOCK: + r = cxx_eval_constant_expression (ctx, TRY_STMTS (t), lval, + non_constant_p, overflow_p, + jump_target); + if (!*non_constant_p && throws (jump_target)) + if (tree h = TRY_HANDLERS (t)) + { + tree type = strip_array_types (TREE_TYPE (*jump_target)); + if (TREE_CODE (h) == STATEMENT_LIST) + { + for (tree stmt : tsi_range (h)) + if (TREE_CODE (stmt) == HANDLER + && handler_match_for_exception_type (stmt, type)) + { + h = stmt; + break; + } + if (TREE_CODE (h) == STATEMENT_LIST) + h = NULL_TREE; + } + else if (TREE_CODE (h) != HANDLER + || !handler_match_for_exception_type (h, type)) + h = NULL_TREE; + if (h) + { + gcc_assert (VAR_P (*jump_target)); + ctx->global->caught_exceptions.safe_push (*jump_target); + ctx->global->caught_exceptions.safe_push (HANDLER_TYPE (h)); + *jump_target = NULL_TREE; + r = cxx_eval_constant_expression (ctx, HANDLER_BODY (h), + vc_discard, non_constant_p, + overflow_p, jump_target); + } + } + break; + case CLEANUP_POINT_EXPR: { auto_vec cleanups; @@ -8230,47 +9199,132 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, ctx->global->cleanups = prev_cleanups; unsigned int i; - tree cleanup; + tree cleanup, jmp_target = NULL_TREE; + bool eh = throws (jump_target); /* Evaluate the cleanups. */ FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup) - cxx_eval_constant_expression (&new_ctx, cleanup, vc_discard, - non_constant_p, overflow_p); + if (cleanup == NULL_TREE) + { + /* NULL_TREE cleanup is a marker that before it is + CLEANUP_EH_ONLY cleanup. Skip the cleanup before it + if the body didn't throw. */ + if (!eh) + --i; + } + else + cxx_eval_constant_expression (&new_ctx, cleanup, vc_discard, + non_constant_p, overflow_p, + &jmp_target); /* Forget SAVE_EXPRs and TARGET_EXPRs created by this full-expression. */ for (tree save_expr : save_exprs) destroy_value_checked (ctx, save_expr, non_constant_p); + if (throws (&jmp_target)) + *jump_target = jmp_target; } break; + case MUST_NOT_THROW_EXPR: + r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), + lval, + non_constant_p, overflow_p, + jump_target); + if (throws (jump_target)) + { + /* [except.handle]/7 - If the search for a handler exits the + function body of a function with a non-throwing exception + specification, the function std::terminate is invoked. */ + if (!ctx->quiet) + { + auto_diagnostic_group d; + diagnose_std_terminate (loc, ctx, *jump_target); + if (MUST_NOT_THROW_NOEXCEPT_P (t) + && ctx->call + && ctx->call->fundef) + inform (loc, "uncaught exception exited from % " + "function %qD", + ctx->call->fundef->decl); + else if (MUST_NOT_THROW_THROW_P (t)) + inform (loc, "destructor exited with an exception after " + "initializing the exception object"); + else if (MUST_NOT_THROW_CATCH_P (t)) + inform (loc, "constructor exited with another exception while " + "entering handler"); + } + *non_constant_p = true; + *jump_target = NULL_TREE; + r = NULL_TREE; + } + break; + + case TRY_CATCH_EXPR: + if (TREE_OPERAND (t, 0) == NULL_TREE) + { + r = void_node; + break; + } + r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, + non_constant_p, overflow_p, + jump_target); + if (!*non_constant_p && throws (jump_target)) + { + tree jmp_target = NULL_TREE; + cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard, + non_constant_p, overflow_p, + &jmp_target); + r = merge_jump_target (loc, ctx, r, non_constant_p, jump_target, + jmp_target); + } + break; + case TRY_FINALLY_EXPR: r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, non_constant_p, overflow_p, jump_target); if (!*non_constant_p) - /* Also evaluate the cleanup. */ - cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard, - non_constant_p, overflow_p); + { + tree jmp_target = NULL_TREE; + /* Also evaluate the cleanup. */ + if (TREE_CODE (TREE_OPERAND (t, 1)) == EH_ELSE_EXPR + && throws (jump_target)) + cxx_eval_constant_expression (ctx, + TREE_OPERAND (TREE_OPERAND (t, 1), + 1), vc_discard, + non_constant_p, overflow_p, + &jmp_target); + else + cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard, + non_constant_p, overflow_p, + &jmp_target); + r = merge_jump_target (loc, ctx, r, non_constant_p, jump_target, + jmp_target); + } break; case EH_ELSE_EXPR: /* Evaluate any cleanup that applies to non-EH exits. */ cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_discard, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); - /* We do not have constexpr exceptions yet, so skip the EH path. */ + /* The EH path is handled in TRY_FINALLY_EXPR handling above. */ break; case CLEANUP_STMT: r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval, non_constant_p, overflow_p, jump_target); - if (!CLEANUP_EH_ONLY (t) && !*non_constant_p) + if ((!CLEANUP_EH_ONLY (t) || throws (jump_target)) && !*non_constant_p) { iloc_sentinel ils (loc); + tree jmp_target = NULL_TREE; /* Also evaluate the cleanup. */ cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), vc_discard, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + &jmp_target); + r = merge_jump_target (loc, ctx, r, non_constant_p, jump_target, + jmp_target); } break; @@ -8280,14 +9334,18 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case MEM_REF: case INDIRECT_REF: r = cxx_eval_indirect_ref (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case ADDR_EXPR: { tree oldop = TREE_OPERAND (t, 0); tree op = cxx_eval_constant_expression (ctx, oldop, vc_glvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; /* Don't VERIFY_CONSTANT here. */ if (*non_constant_p) return t; @@ -8307,7 +9365,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (lval) { r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (r == error_mark_node) ; else if (r == TREE_OPERAND (t, 0) || lval == vc_discard) @@ -8328,7 +9389,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case FIXED_CONVERT_EXPR: case VEC_DUPLICATE_EXPR: r = cxx_eval_unary_expression (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case SIZEOF_EXPR: @@ -8366,6 +9428,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, cxx_eval_constant_expression (ctx, op0, vc_discard, non_constant_p, overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; op1 = TREE_OPERAND (t, 1); @@ -8418,7 +9482,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case RANGE_EXPR: case COMPLEX_EXPR: r = cxx_eval_binary_expression (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; /* fold can introduce non-IF versions of these; still treat them as @@ -8427,19 +9492,22 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case TRUTH_ANDIF_EXPR: r = cxx_eval_logical_expression (ctx, t, boolean_false_node, boolean_true_node, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case TRUTH_OR_EXPR: case TRUTH_ORIF_EXPR: r = cxx_eval_logical_expression (ctx, t, boolean_true_node, boolean_false_node, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case ARRAY_REF: r = cxx_eval_array_reference (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case COMPONENT_REF: @@ -8454,17 +9522,19 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, return t; } r = cxx_eval_component_reference (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case BIT_FIELD_REF: r = cxx_eval_bit_field_ref (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case COND_EXPR: case IF_STMT: - if (jump_target && *jump_target) + if (*jump_target) { tree orig_jump = *jump_target; tree arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 1)) @@ -8502,7 +9572,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, break; case VEC_COND_EXPR: r = cxx_eval_vector_conditional_expression (ctx, t, non_constant_p, - overflow_p); + overflow_p, jump_target); break; case CONSTRUCTOR: @@ -8514,7 +9584,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, return t; } r = cxx_eval_bare_aggregate (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); break; case VEC_INIT_EXPR: @@ -8524,12 +9594,13 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, or xvalue of the same type, meaning direct-initialization from the corresponding member. */ r = cxx_eval_vec_init (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); break; case VEC_PERM_EXPR: r = cxx_eval_trinary_expression (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case PAREN_EXPR: @@ -8537,7 +9608,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, /* A PAREN_EXPR resulting from __builtin_assoc_barrier has no effect in constant expressions since it's unaffected by -fassociative-math. */ r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case NOP_EXPR: @@ -8561,7 +9633,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, ? vc_discard : tcode == VIEW_CONVERT_EXPR ? lval : vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; tree type = TREE_TYPE (t); @@ -8618,7 +9693,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, { if (integer_zerop (sop)) return build_int_cst (type, 0); - r = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (type), sop); + r = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (type), sop, + NULL, jump_target); + if (*jump_target) + return NULL_TREE; if (r) { r = build1 (ADDR_EXPR, type, r); @@ -8745,10 +9823,14 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (cxx_replaceable_global_alloc_fn (fun) && IDENTIFIER_NEW_OP_P (DECL_NAME (fun))) arg_size = CALL_EXPR_ARG (oldop, 0); - TREE_TYPE (var) + tree new_type = build_new_constexpr_heap_type (ctx, elt_type, cookie_size, var_size, arg_size, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + TREE_TYPE (var) = new_type; TREE_TYPE (TREE_OPERAND (op, 0)) = build_pointer_type (TREE_TYPE (var)); } @@ -8787,7 +9869,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, tree op = cxx_eval_constant_expression (ctx, oldop, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; r = fold_convert (TREE_TYPE (t), op); @@ -8824,14 +9909,20 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case PREDECREMENT_EXPR: case POSTDECREMENT_EXPR: return cxx_eval_increment_expression (ctx, t, - lval, non_constant_p, overflow_p); + lval, non_constant_p, overflow_p, + jump_target); + case THROW_EXPR: + if (cxx_dialect >= cxx26) + return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, + non_constant_p, overflow_p, + jump_target); + /* FALLTHROUGH */ case LAMBDA_EXPR: case NEW_EXPR: case VEC_NEW_EXPR: case DELETE_EXPR: case VEC_DELETE_EXPR: - case THROW_EXPR: case MODOP_EXPR: /* GCC internal stuff. */ case VA_ARG_EXPR: @@ -8845,7 +9936,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case OBJ_TYPE_REF: /* Virtual function lookup. We don't need to do anything fancy. */ return cxx_eval_constant_expression (ctx, OBJ_TYPE_REF_EXPR (t), - lval, non_constant_p, overflow_p); + lval, non_constant_p, overflow_p, + jump_target); case PLACEHOLDER_EXPR: /* Use of the value or address of the current object. */ @@ -8855,7 +9947,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, return ctor; else return cxx_eval_constant_expression (ctx, ctor, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); } /* A placeholder without a referent. We can get here when checking whether NSDMIs are noexcept, or in massage_init_elt; @@ -8868,7 +9961,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, { tree cond = TREE_OPERAND (t, 0); cond = cxx_eval_constant_expression (ctx, cond, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (cond); if (integer_nonzerop (cond)) *jump_target = t; @@ -8980,7 +10076,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, *non_constant_p = true; return t; } - r = cxx_eval_bit_cast (ctx, t, non_constant_p, overflow_p); + r = cxx_eval_bit_cast (ctx, t, non_constant_p, overflow_p, jump_target); break; case OMP_PARALLEL: @@ -9299,8 +10395,34 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, if (manifestly_const_eval == mce_true) instantiate_constexpr_fns (r); + tree jmp_target = NULL_TREE; r = cxx_eval_constant_expression (&ctx, r, vc_prvalue, - &non_constant_p, &overflow_p); + &non_constant_p, &overflow_p, + &jmp_target); + if (throws (&jmp_target) && !non_constant_p) + { + if (!ctx.quiet) + diagnose_uncaught_exception (input_location, &ctx, jmp_target); + non_constant_p = true; + jmp_target = NULL_TREE; + r = t; + } + else if (!non_constant_p && jmp_target) + { + non_constant_p = true; + if (!ctx.quiet) + { + if (breaks (&jmp_target)) + error ("% outside of a loop or %"); + else if (continues (&jmp_target)) + error ("% outside of a loop"); + else if (returns (&jmp_target)) + error ("% in a statement expression"); + else + gcc_unreachable (); + } + r = t; + } /* If we got a non-simple TARGET_EXPR, the initializer was a sequence of statements, and the result ought to be stored in ctx.ctor. */ @@ -9309,15 +10431,31 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, unsigned int i; tree cleanup; + jmp_target = NULL_TREE; /* Evaluate the cleanups. */ FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup) - cxx_eval_constant_expression (&ctx, cleanup, vc_discard, - &non_constant_p, &overflow_p); + if (cleanup == NULL_TREE) + /* NULL_TREE cleanup is a marker that before it is + CLEANUP_EH_ONLY cleanup. Skip the cleanup before it. */ + --i; + else + cxx_eval_constant_expression (&ctx, cleanup, vc_discard, + &non_constant_p, &overflow_p, + &jmp_target); + if (throws (&jmp_target) && !non_constant_p) + { + if (!ctx.quiet) + diagnose_uncaught_exception (input_location, &ctx, jmp_target); + non_constant_p = true; + r = t; + } /* Mutable logic is a bit tricky: we want to allow initialization of constexpr variables with mutable members, but we can't copy those members to another constexpr variable. */ - if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_MUTABLE_POISON (r)) + if (!non_constant_p + && TREE_CODE (r) == CONSTRUCTOR + && CONSTRUCTOR_MUTABLE_POISON (r)) { if (!allow_non_constant) error ("%qE is not a constant expression because it refers to " @@ -9335,8 +10473,13 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, { if (!allow_non_constant && !non_constant_p) { - error ("%qE is not a constant expression because it refers to " - "a result of %", 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 %", t); inform (DECL_SOURCE_LOCATION (heap_var), "allocated here"); } r = t; @@ -9917,6 +11060,24 @@ cxx_constant_init (tree t, tree decl) return maybe_constant_init_1 (t, decl, false, mce_true); } +/* Return true if CALL_EXPR T might throw during constant evaluation. */ + +static bool +callee_might_throw (tree t) +{ + if (cxx_dialect < cxx26 || !flag_exceptions) + return false; + tree callee = cp_get_callee (t); + if (callee == NULL_TREE) + return false; + tree callee_fn = cp_get_fndecl_from_callee (callee, false); + return (!flag_enforce_eh_specs + || type_dependent_expression_p (callee) + || !POINTER_TYPE_P (TREE_TYPE (callee)) + || (!type_noexcept_p (TREE_TYPE (TREE_TYPE (callee))) + && (callee_fn == NULL_TREE || !TREE_NOTHROW (callee_fn)))); +} + #if 0 /* FIXME see ADDR_EXPR section in potential_constant_expression_1. */ /* Return true if the object referred to by REF has automatic or thread @@ -9949,11 +11110,13 @@ struct check_for_return_continue_data { hash_set *pset; tree continue_stmt; tree break_stmt; + bool could_throw; }; /* Helper function for potential_constant_expression_1 SWITCH_STMT handling, called through cp_walk_tree. Return the first RETURN_EXPR found, or note - the first CONTINUE_STMT and/or BREAK_STMT if RETURN_EXPR is not found. */ + the first CONTINUE_STMT and/or BREAK_STMT if RETURN_EXPR is not found. + For C++26 also note presence of possibly throwing calls. */ static tree check_for_return_continue (tree *tp, int *walk_subtrees, void *data) { @@ -10038,6 +11201,13 @@ check_for_return_continue (tree *tp, int *walk_subtrees, void *data) case CONSTRUCTOR: break; + case AGGR_INIT_EXPR: + case CALL_EXPR: + /* In C++26 a function could throw. */ + if (callee_might_throw (t)) + d->could_throw = true; + break; + default: if (!EXPR_P (t)) *walk_subtrees = 0; @@ -10243,8 +11413,27 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, || TREE_CODE (t) != CALL_EXPR || current_function_decl == NULL_TREE || !is_std_construct_at (current_function_decl)) - && !cxx_dynamic_cast_fn_p (fun)) + && !cxx_dynamic_cast_fn_p (fun) + && !cxx_cxa_builtin_fn_p (fun)) { + /* In C++26 evaluation of the function arguments might + throw and in that case it is irrelevant whether + fun is constexpr or not. */ + if (cxx_dialect >= cxx26) + for (; i < nargs; ++i) + { + tree x = get_nth_callarg (t, i); + bool rv = processing_template_decl ? any : rval; + bool sub_now = false; + if (!potential_constant_expression_1 (x, rv, strict, + sub_now, + fundef_p, + flags, + jump_target)) + return false; + if (throws (jump_target)) + return true; + } if ((flags & tf_error) && constexpr_error (loc, fundef_p, "call to non-% " @@ -10289,7 +11478,12 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, sub_now, fundef_p, flags, jump_target)) return false; + if (throws (jump_target)) + return true; } + /* In C++26 a function could throw. */ + if (*jump_target == NULL_TREE && callee_might_throw (t)) + *jump_target = void_node; return true; } @@ -10512,11 +11706,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, a return. */ hash_set pset; check_for_return_continue_data data = { &pset, NULL_TREE, - NULL_TREE }; + NULL_TREE, false }; if (tree ret_expr = cp_walk_tree (&FOR_BODY (t), check_for_return_continue, &data, &pset)) *jump_target = ret_expr; + if (data.could_throw) + *jump_target = void_node; return true; } } @@ -10556,11 +11752,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, a return. */ hash_set pset; check_for_return_continue_data data = { &pset, NULL_TREE, - NULL_TREE }; + NULL_TREE, false }; if (tree ret_expr = cp_walk_tree (&WHILE_BODY (t), check_for_return_continue, &data, &pset)) *jump_target = ret_expr; + if (data.could_throw) + *jump_target = void_node; return true; } if (!RECUR (WHILE_BODY (t), any)) @@ -10584,7 +11782,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, { hash_set pset; check_for_return_continue_data data = { &pset, NULL_TREE, - NULL_TREE }; + NULL_TREE, false }; if (tree ret_expr = cp_walk_tree (&SWITCH_STMT_BODY (t), check_for_return_continue, &data, &pset)) @@ -10593,6 +11791,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, else if (data.continue_stmt) /* The switch can't return, but might continue. */ *jump_target = data.continue_stmt; + if (data.could_throw) + *jump_target = void_node; } return true; @@ -10622,7 +11822,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case DYNAMIC_CAST_EXPR: case PSEUDO_DTOR_EXPR: - case THROW_EXPR: case OMP_PARALLEL: case OMP_TASK: case OMP_FOR: @@ -10678,6 +11877,11 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, constant. */ return true; + case THROW_EXPR: + if (cxx_dialect < cxx26) + goto fail; + return RECUR (TREE_OPERAND (t, 0), rval); + case ASM_EXPR: if (flags & tf_error) inline_asm_in_constexpr_error (loc, fundef_p); @@ -10806,6 +12010,22 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case CLEANUP_POINT_EXPR: case MUST_NOT_THROW_EXPR: case TRY_CATCH_EXPR: + /* Even for C++26 handle TRY_BLOCK conservatively, if we detect the + body could throw, even with catch (...) among handlers we'd need + to analyze them in detail if they couldn't rethrow it. More + importantly though, throws (jump_target) is just conservative, + and there could be e.g. + try + { + possibly_throwing_fn (args); + break; + } + catch (...) + { + } + or continue or return instead of break. So, clearing *jump_target + because we see catch (...) handler might mean we missed break + etc. */ case TRY_BLOCK: case EH_SPEC_BLOCK: case EXPR_STMT: @@ -11047,9 +12267,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, want_rval, strict, now, fundef_p, tf_none, &this_jump_target)) { - if (returns (&this_jump_target)) + if (returns (&this_jump_target) || throws (&this_jump_target)) *jump_target = this_jump_target; - else if (!returns (jump_target)) + else if (!returns (jump_target) && !throws (jump_target)) { if (breaks (&this_jump_target) || continues (&this_jump_target)) @@ -11061,7 +12281,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, couldn't return, break or continue. */ hash_set pset; check_for_return_continue_data data = { &pset, NULL_TREE, - NULL_TREE }; + NULL_TREE, + false }; if (tree ret_expr = cp_walk_tree (&TREE_OPERAND (t, 2), check_for_return_continue, &data, @@ -11074,6 +12295,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, else if (data.break_stmt) *jump_target = data.break_stmt; } + if (data.could_throw) + *jump_target = void_node; } } return true; diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index ce69bd6030c7..882a943391cb 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -889,6 +889,12 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) (EXPR_LOCATION (*expr_p), call_expr_nargs (*expr_p), &CALL_EXPR_ARG (*expr_p, 0)); break; + case CP_BUILT_IN_EH_PTR_ADJUST_REF: + error_at (EXPR_LOCATION (*expr_p), + "%qs used outside of constant expressions", + "__builtin_eh_ptr_adjust_ref"); + *expr_p = void_node; + break; default: break; } diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 3b92d9af6e1c..43705733d514 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -452,6 +452,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT) RETURN_EXPR_LOCAL_ADDR_P (in RETURN_EXPR) PACK_INDEX_PARENTHESIZED_P (in PACK_INDEX_*) + MUST_NOT_THROW_NOEXCEPT_P (in MUST_NOT_THROW_EXPR) 1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE) TI_PENDING_TEMPLATE_FLAG. TEMPLATE_PARMS_FOR_INLINE. @@ -472,6 +473,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; BIND_EXPR_VEC_DTOR (in BIND_EXPR) ATOMIC_CONSTR_EXPR_FROM_CONCEPT_P (in ATOMIC_CONSTR) STATIC_INIT_DECOMP_BASE_P (in the TREE_LIST for {static,tls}_aggregates) + MUST_NOT_THROW_THROW_P (in MUST_NOT_THROW_EXPR) 2: IDENTIFIER_KIND_BIT_2 (in IDENTIFIER_NODE) ICS_THIS_FLAG (in _CONV) DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL) @@ -493,6 +495,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT) STATIC_INIT_DECOMP_NONBASE_P (in the TREE_LIST for {static,tls}_aggregates) + MUST_NOT_THROW_CATCH_P (in MUST_NOT_THROW_EXPR) 3: IMPLICIT_RVALUE_P (in NON_LVALUE_EXPR or STATIC_CAST_EXPR) ICS_BAD_FLAG (in _CONV) FN_TRY_BLOCK_P (in TRY_BLOCK) @@ -3016,6 +3019,8 @@ struct GTY(()) lang_decl_min { In a lambda-capture proxy VAR_DECL, this is DECL_CAPTURED_VARIABLE. In a function-scope TREE_STATIC VAR_DECL or IMPLICIT_TYPEDEF_P TYPE_DECL, this is DECL_DISCRIMINATOR. + In constexpr exception artificial VAR_DECL, this is + DECL_EXCEPTION_REFCOUNT. In a DECL_LOCAL_DECL_P decl, this is the namespace decl it aliases. Otherwise, in a class-scope DECL, this is DECL_ACCESS. */ tree access; @@ -4470,6 +4475,23 @@ get_vec_init_expr (tree t) #define MUST_NOT_THROW_COND(NODE) \ TREE_OPERAND (MUST_NOT_THROW_EXPR_CHECK (NODE), 1) +/* Reasons why MUST_NOT_THROW_EXPR has been created. */ + +/* Indicates MUST_NOT_THROW_EXPR has been created to wrap body of + a noexcept function. */ +#define MUST_NOT_THROW_NOEXCEPT_P(NODE) \ + TREE_LANG_FLAG_0 (MUST_NOT_THROW_EXPR_CHECK (NODE)) + +/* Indicates MUST_NOT_THROW_EXPR has been created to wrap construction of + exception object during throw. */ +#define MUST_NOT_THROW_THROW_P(NODE) \ + TREE_LANG_FLAG_1 (MUST_NOT_THROW_EXPR_CHECK (NODE)) + +/* Indicates MUST_NOT_THROW_EXPR has been created to wrap construction of + handler parameter during catch. */ +#define MUST_NOT_THROW_CATCH_P(NODE) \ + TREE_LANG_FLAG_2 (MUST_NOT_THROW_EXPR_CHECK (NODE)) + /* The TYPE_MAIN_DECL for a class template type is a TYPE_DECL, not a TEMPLATE_DECL. This macro determines whether or not a given class type is really a template type, as opposed to an instantiation or @@ -4512,7 +4534,7 @@ get_vec_init_expr (tree t) #define TYPE_CONTAINS_VPTR_P(NODE) \ (TYPE_POLYMORPHIC_P (NODE) || CLASSTYPE_VBASECLASSES (NODE)) -/* Nonzero if NODE is a FUNCTION_DECL or VARIABLE_DECL (for a decl +/* Nonzero if NODE is a FUNCTION_DECL or VAR_DECL (for a decl with namespace scope) declared in a local scope. */ #define DECL_LOCAL_DECL_P(NODE) \ DECL_LANG_FLAG_0 (VAR_OR_FUNCTION_DECL_CHECK (NODE)) @@ -5153,6 +5175,10 @@ get_vec_init_expr (tree t) protected_access_node will appear in the DECL_ACCESS for the node. */ #define DECL_ACCESS(NODE) (LANG_DECL_MIN_CHECK (NODE)->access) +/* In artificial VAR_DECL created by cxa_allocate_exception + this is reference count. */ +#define DECL_EXCEPTION_REFCOUNT(NODE) (LANG_DECL_MIN_CHECK (NODE)->access) + /* Nonzero if the FUNCTION_DECL is a global constructor. */ #define DECL_GLOBAL_CTOR_P(NODE) \ (LANG_DECL_FN_CHECK (NODE)->global_ctor_p) @@ -6813,6 +6839,7 @@ enum cp_built_in_function { CP_BUILT_IN_IS_CORRESPONDING_MEMBER, CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS, CP_BUILT_IN_SOURCE_LOCATION, + CP_BUILT_IN_EH_PTR_ADJUST_REF, CP_BUILT_IN_LAST }; @@ -6993,6 +7020,7 @@ extern bool type_has_extended_temps (tree); extern tree strip_top_quals (tree); extern bool reference_related_p (tree, tree); extern bool reference_compatible_p (tree, tree); +extern bool handler_match_for_exception_type (tree, tree); extern int remaining_arguments (tree); extern tree build_implicit_conv_flags (tree, tree, int); extern tree perform_implicit_conversion (tree, tree, tsubst_flags_t); diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 0e6afbe56527..664dbbec2796 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -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. */ diff --git a/gcc/cp/except.cc b/gcc/cp/except.cc index a9d8e2ffb57a..a7f35e4314d4 100644 --- a/gcc/cp/except.cc +++ b/gcc/cp/except.cc @@ -367,6 +367,8 @@ initialize_handler_parm (tree decl, tree exp) MUST_NOT_THROW_EXPR. */ init = fold_build_cleanup_point_expr (TREE_TYPE (init), init); init = build_must_not_throw_expr (init, NULL_TREE); + if (init && TREE_CODE (init) == MUST_NOT_THROW_EXPR) + MUST_NOT_THROW_CATCH_P (init) = 1; } decl = pushdecl (decl); @@ -523,6 +525,7 @@ begin_eh_spec_block (void) r = build_stmt (spec_location, MUST_NOT_THROW_EXPR, NULL_TREE, NULL_TREE); TREE_SIDE_EFFECTS (r) = 1; + MUST_NOT_THROW_NOEXCEPT_P (r) = 1; } else r = build_stmt (spec_location, EH_SPEC_BLOCK, NULL_TREE, NULL_TREE); @@ -614,6 +617,7 @@ wrap_cleanups_r (tree *tp, int *walk_subtrees, void * /*data*/) { cleanup = build2 (MUST_NOT_THROW_EXPR, void_type_node, cleanup, NULL_TREE); + MUST_NOT_THROW_THROW_P (cleanup) = 1; TARGET_EXPR_CLEANUP (exp) = cleanup; } @@ -712,6 +716,11 @@ build_throw (location_t loc, tree exp, tsubst_flags_t complain) allocate_expr = do_allocate_exception (temp_type); if (allocate_expr == error_mark_node) return error_mark_node; + /* Copy ptr inside of the CLEANUP_POINT_EXPR + added below to a TARGET_EXPR slot added outside of it, + otherwise during constant evaluation of throw expression + we'd diagnose accessing ptr outside of its lifetime. */ + tree ptr_copy = get_internal_target_expr (null_pointer_node); allocate_expr = get_internal_target_expr (allocate_expr); ptr = TARGET_EXPR_SLOT (allocate_expr); TARGET_EXPR_CLEANUP (allocate_expr) = do_free_exception (ptr); @@ -763,10 +772,17 @@ build_throw (location_t loc, tree exp, tsubst_flags_t complain) /* Prepend the allocation. */ exp = build2 (COMPOUND_EXPR, TREE_TYPE (exp), allocate_expr, exp); + exp = build2 (COMPOUND_EXPR, void_type_node, exp, + build2 (MODIFY_EXPR, void_type_node, + TARGET_EXPR_SLOT (ptr_copy), ptr)); + ptr = TARGET_EXPR_SLOT (ptr_copy); + /* Force all the cleanups to be evaluated here so that we don't have to do them during unwinding. */ exp = build1 (CLEANUP_POINT_EXPR, void_type_node, exp); + exp = build2 (COMPOUND_EXPR, TREE_TYPE (exp), ptr_copy, exp); + throw_type = build_eh_type_type (prepare_eh_type (TREE_TYPE (object))); cleanup = NULL_TREE; diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 40ce987a6e81..8f3822c05a44 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -20147,7 +20147,14 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl) { tree op0 = RECUR (TREE_OPERAND (t, 0)); tree cond = RECUR (MUST_NOT_THROW_COND (t)); - RETURN (build_must_not_throw_expr (op0, cond)); + stmt = build_must_not_throw_expr (op0, cond); + if (stmt && TREE_CODE (stmt) == MUST_NOT_THROW_EXPR) + { + MUST_NOT_THROW_NOEXCEPT_P (stmt) = MUST_NOT_THROW_NOEXCEPT_P (t); + MUST_NOT_THROW_THROW_P (stmt) = MUST_NOT_THROW_THROW_P (t); + MUST_NOT_THROW_CATCH_P (stmt) = MUST_NOT_THROW_CATCH_P (t); + } + RETURN (stmt); } case EXPR_PACK_EXPANSION: diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 5863b6878f02..accce0ec2620 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -488,6 +488,7 @@ builtin_valid_in_constant_expr_p (const_tree decl) case CP_BUILT_IN_SOURCE_LOCATION: case CP_BUILT_IN_IS_CORRESPONDING_MEMBER: case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS: + case CP_BUILT_IN_EH_PTR_ADJUST_REF: return true; default: break; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ellipsis2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ellipsis2.C index b6a5323b0f90..c473257451c3 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ellipsis2.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ellipsis2.C @@ -9,4 +9,6 @@ struct A constexpr int ellipsis(...) { return 1; } -static_assert(ellipsis(A().empty()), "Error"); // { dg-error "non-constant condition|empty" } +static_assert(ellipsis(A().empty()), "Error"); // { dg-error "non-constant condition" } +// { dg-error "call to non-'constexpr' function 'bool A::empty\\\(\\\)'" "" { target c++23_down } .-1 } +// { dg-error "temporary of non-literal type 'A' in a constant expression" "" { target c++26 } .-2 } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-throw.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-throw.C index 9c49fa4e9e85..9abf4a3982f6 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-throw.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-throw.C @@ -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 } diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C index f7439d80b396..18efd23398fc 100644 --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C @@ -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 diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C index b5fa6caeb576..75e2fb828293 100644 --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C @@ -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), ""); diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C index 35beb27f7d53..2c69b3b6f7a5 100644 --- a/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C +++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C @@ -18,7 +18,7 @@ struct H { short size () const { return 0; } constexpr const char *data () const { return ""; } }; struct I { constexpr signed char size () const { return 0; } const char *data () const { return ""; } }; -struct J { constexpr int size () const { return j ? throw 1 : 0; } // { dg-error "expression '' is not a constant expression" } +struct J { constexpr int size () const { return j ? throw 1 : 0; } // { dg-error "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 '' 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 '' 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 '' is not a constant expression" "" { target c++23 } } + static constexpr int size () { if consteval { throw 1; } else { return 4; } } // { dg-error "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 '' is not a constant expression" "" { target c++23 } } + static constexpr const char *data () { if consteval { throw 1; } else { return "test"; } } // { dg-error "expression '' is not a constant expression" "" { target c++23_only } } }; #endif struct O { constexpr int operator () () const { return 12; } }; @@ -318,12 +320,15 @@ namespace NN asm ((I {})); #if __cplusplus >= 201402L asm ((J {})); // { dg-error "constexpr string 'data\\\(\\\)' must be a core constant expression" "" { target c++14 } } + // { dg-error "uncaught exception '1'" "" { target c++26 } .-1 } #endif #if __cpp_if_consteval >= 202106L asm ((K {})); asm ((L {})); asm ((M {})); // { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target c++23 } } + // { dg-error "uncaught exception '1'" "" { target c++26 } .-1 } asm ((N {})); // { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++23 } } + // { dg-error "uncaught exception '1'" "" { target c++26 } .-1 } #endif asm ((Q {})); asm ((R {})); @@ -348,12 +353,15 @@ namespace NN asm ((I {})); #if __cplusplus >= 201402L asm ((J {})); // { dg-error "constexpr string 'data\\\(\\\)' must be a core constant expression" "" { target c++14 } } + // { dg-error "uncaught exception '1'" "" { target c++26 } .-1 } #endif #if __cpp_if_consteval >= 202106L asm ((K {})); asm ((L {})); asm ((M {})); // { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target c++23 } } + // { dg-error "uncaught exception '1'" "" { target c++26 } .-1 } asm ((N {})); // { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++23 } } + // { dg-error "uncaught exception '1'" "" { target c++26 } .-1 } #endif asm ((Q {})); asm ((R {})); diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh1.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh1.C new file mode 100644 index 000000000000..9eed3aa2cf1c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh1.C @@ -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 +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 +constexpr int +bar (const X &x) +{ + throw x; +} + +template +constexpr int +baz (const X &x) +{ + try + { + try { bar (x); } + catch (int &a) { a += 80; throw; } + catch (long b) { b += 80; throw; } + catch (U &c) { c.s[0] += 82; throw; } + catch (V d) { d.s[0] += 83; throw; } + } + catch (int a) { return 42 + a; } + catch (const long &b) { return 43 + b; } + catch (S &c) { return 44 + c.s[0]; } + catch (long long d) { return 45 + d; } + catch (...) { return -1; } +} + +constexpr int +qux (int x, bool y = true) +{ + try + { + switch (x) + { + case 0: throw 42; break; + case 1: x = y ? throw 43 : 5; break; + case 2: x = -(throw 44, 6); break; + case 3: x = x + (throw 45, 7); break; + case 4: x = (throw 46, 8) + x; break; + case 5: x = (throw 47, y) ? 4 : 5; break; + case 6: x += (throw 48, y); break; + case 7: x = (double) (throw 49, y); break; + case 8: x = foo ((throw 50, x)); break; + default: break; + } + } + catch (int a) { return a; } + return -1; +} + +constexpr int +corge () +{ + try { throw 0; } + catch (int *const &h) { return 49; } + catch (long long *) { return 50; } + catch (const S *const &) { return 51; } + catch (...) { return 52; } +} + +static_assert (foo (12) == 54); +static_assert (foo (12U) == 55); +static_assert (foo (12L) == 56); +static_assert (foo (false) == 45); +static_assert (foo (true) == 46); +static_assert (foo (U (12)) == 58); +static_assert (foo (T (20)) == 67); +static_assert (foo (S (30)) == 78); +static_assert (foo (nullptr) == 49); +static_assert (foo ((int *)nullptr) == 49); +static_assert (foo ((long long *)nullptr) == 50); +static_assert (foo ((const S *)nullptr) == 51); +static_assert (foo ((const T *)nullptr) == 51); +static_assert (foo ((const U *)nullptr) == 51); +static_assert (foo (12ULL) == 52); +static_assert (baz (5) == 127); +static_assert (baz (6L) == 49); +static_assert (baz (U (25)) == 151); +static_assert (baz (V (26)) == 70); +static_assert (baz (T (27)) == 71); +static_assert (baz (S (28)) == 72); +static_assert (baz (7LL) == 52); +static_assert (baz (8ULL) == -1); +static_assert (qux (0) == 42); +static_assert (qux (1) == 43); +static_assert (qux (2) == 44); +static_assert (qux (3) == 45); +static_assert (qux (4) == 46); +static_assert (qux (5) == 47); +static_assert (qux (6) == 48); +static_assert (qux (7) == 49); +static_assert (qux (8) == 50); +static_assert (corge () == 52); diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh10.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh10.C new file mode 100644 index 000000000000..a86cc4df13f6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh10.C @@ -0,0 +1,110 @@ +// C++26 P3068R5 - Allowing exception throwing in constant-evaluation +// { dg-do compile { target c++26 } } +// { dg-require-effective-target exceptions_enabled } + +struct S { +}; +struct T { + constexpr ~T () noexcept (false) { throw S {}; } +}; +struct U { + int u; +}; +struct V { + int v; + constexpr V (int x) + try : v { x } + { + if (v > 42) + throw U { 42 }; + } + catch (U &u) + { + --u.u; + } +}; +struct W { + constexpr ~W () { ++w; } + int &w; +}; +struct X : public V { + constexpr X (int x) + try : V(x) + { + } + catch (U &u) + { + --u.u; + } +}; + +constexpr int +foo (bool x) +{ + try + { + T t; // { dg-error "'std::terminate' called after throwing an exception '42'" } + if (x) // { dg-message "destructor exited with an exception" "" { target *-*-* } .-1 } + throw 42; + return 10; + } + catch (S) + { + return 11; + } +} + +constexpr int +bar () +{ + V v { 42 }; + try + { + V w { 43 }; + } + catch (const U &u) + { + if (u.u == 41) + return 44; + } + return -1; +} + +constexpr int +baz () +{ + int i = 42; + try + { + W w { i }; + throw S (); + } + catch (...) + { + if (i == 43) + return 42; + } + return -1; +} + +constexpr int +qux () +{ + X v { 42 }; + try + { + X w { 43 }; + } + catch (const U &u) + { + if (u.u == 40) + return 48; + } + return -1; +} + +static_assert (foo (false) == 11); +constexpr int a = foo (true); // { dg-message "in 'constexpr' expansion of" } +static_assert (bar () == 44); +static_assert (baz () == 42); +static_assert (qux () == 48); diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh11.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh11.C new file mode 100644 index 000000000000..287e06664402 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh11.C @@ -0,0 +1,69 @@ +// C++26 P3068R5 - Allowing exception throwing in constant-evaluation +// { dg-do compile { target c++26 } } +// { dg-require-effective-target exceptions_enabled } + +struct A { + explicit constexpr A (int x) noexcept : a (x) {} + constexpr virtual int foo () const noexcept { return a; } + constexpr virtual ~A () {} + int a; +}; +struct B : public A { + explicit constexpr B (int x) noexcept : A (x) {} + constexpr int foo () const noexcept override { return a | 0x10; } +}; +struct C : public A { + explicit constexpr C (int x) noexcept : A (x) {} +}; +struct D : public A { + explicit constexpr D (int x) noexcept : A (x) {} +}; +struct E { + constexpr E () noexcept : e (0) {} + explicit constexpr E (int x) noexcept : e (x) {} + int e; +}; +struct F : public E, public B { + explicit constexpr F (int x) noexcept : B (x) {} +}; +struct G : public E, public C { + explicit constexpr G (int x) noexcept : C (x) {} +}; +struct H : public E, public D { + explicit constexpr H (int x) noexcept : D (x) {} +}; + +consteval int +bar (void (*fn) ()) +{ + try + { + fn (); + } + catch (C &a) + { + return a.foo () | 0x20; + } + catch (const C &b) // { dg-warning "exception of type 'C' will be caught by earlier handler" } + { + return b.foo () | 0x60; + } + catch (A &c) + { + return c.foo (); + } + catch (const A &d) // { dg-warning "exception of type 'A' will be caught by earlier handler" } + { + return d.foo () | 0x40; + } + return -1; +} + +static_assert (bar ([] { throw A { 1 }; }) == 1); +static_assert (bar ([] { throw B { 2 }; }) == 0x12); +static_assert (bar ([] { throw C { 3 }; }) == 0x23); +static_assert (bar ([] { throw D { 4 }; }) == 4); +constexpr int a = bar ([] { throw E { 5 }; }); // { dg-error "uncaught exception 'E\\\{5\\\}'" } +static_assert (bar ([] { throw F { 6 }; }) == 0x16); +static_assert (bar ([] { throw G { 7 }; }) == 0x27); +static_assert (bar ([] { throw H { 8 }; }) == 8); diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh12.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh12.C new file mode 100644 index 000000000000..4a85f1521cc7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh12.C @@ -0,0 +1,74 @@ +// C++26 P3068R5 - Allowing exception throwing in constant-evaluation +// { dg-do compile { target c++26 } } +// { dg-require-effective-target exceptions_enabled } + +struct A { + explicit constexpr A (int x) noexcept : a (x) {} + constexpr virtual int foo () const noexcept { return a; } + constexpr virtual ~A () {} + int a; +}; +struct B : public A { + explicit constexpr B (int x) noexcept : A (x) {} + constexpr int foo () const noexcept override { return a | 0x10; } +}; +struct C : public A { + explicit constexpr C (int x) noexcept : A (x) {} +}; +struct D : public A { + explicit constexpr D (int x) noexcept : A (x) {} +}; +struct E { + constexpr E () noexcept : e (0) {} + explicit constexpr E (int x) noexcept : e (x) {} + int e; +}; +struct F : public E, public B { + explicit constexpr F (int x) noexcept : B (x) {} +}; +struct G : public E, public C { + explicit constexpr G (int x) noexcept : C (x) {} +}; +struct H : public E, public D { + explicit constexpr H (int x) noexcept : D (x) {} +}; + +consteval int +bar (void (*fn) ()) +{ + int r = 0; + try + { + fn (); + } + catch (C *a) + { + r = a->foo () | 0x20; + delete a; + } + catch (const C *b) + { + r = b->foo () | 0x60; + delete b; + } + catch (A *c) + { + r = c->foo (); + delete c; + } + catch (const A *d) + { + r = d->foo () | 0x40; + delete d; + } + return r; +} + +static_assert (bar ([] { throw new A { 1 }; }) == 1); +static_assert (bar ([] { throw new B { 2 }; }) == 0x12); +static_assert (bar ([] { throw new C { 3 }; }) == 0x23); +static_assert (bar ([] { throw new D { 4 }; }) == 4); +constexpr int a = bar ([] { throw new E { 5 }; }); // { dg-error "uncaught exception of type 'E\\\*'" } +static_assert (bar ([] { throw new F { 6 }; }) == 0x16); +static_assert (bar ([] { throw new G { 7 }; }) == 0x27); +static_assert (bar ([] { throw new H { 8 }; }) == 8); diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh13.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh13.C new file mode 100644 index 000000000000..d62771c687aa --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh13.C @@ -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 +consteval T +foo (T x) +{ + try + { + throw &x; + } + catch (void *ptr) // { dg-message "for type 'void\\\*'" } + { + return *static_cast (ptr) | 0x10; + } + catch (const void *ptr) // { dg-message "for type 'const void\\\*'" } + { + return *static_cast (ptr) | 0x20; + } + catch (T *ptr) // { dg-warning "exception of type 'T\\\*' will be caught by earlier handler" } + { // { dg-warning "exception of type 'int\\\*' will be caught by earlier handler" "" { target *-*-* } .-1 } + return *ptr | 0x30; // { dg-warning "exception of type 'long long unsigned int\\\*' will be caught by earlier handler" "" { target *-*-* } .-2 } + } + catch (const T *ptr) + { + return *ptr | 0x40; + } + catch (...) + { + return -1; + } +} + +static_assert (foo (1) == 0x11); +static_assert (foo (2ULL) == 0x12ULL); diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh14.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh14.C new file mode 100644 index 000000000000..3e52f2b15e3e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh14.C @@ -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 +constexpr T +foo (T x, auto... y) +{ + const T z[] = { x, y... }; + try + { + throw z; + } + catch (const T (&a)[4]) + { + return T (); + } + catch (const T *b) + { + return b[0]; + } + catch (...) + { + return T (); + } +} + +void +bar () +{ +} + +void +baz () +{ +} + +static_assert (foo (42, 43, 44, 45, 46) == 42); +static_assert (foo (43U, 44U, 45U, 46U) == 43U); +static_assert (foo (44LL, 45LL) == 44LL); +static_assert (foo (bar, baz, bar, baz) == bar); +static_assert (foo (baz, bar) == baz); diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh15.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh15.C new file mode 100644 index 000000000000..3dea461c7d0f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh15.C @@ -0,0 +1,39 @@ +// C++26 P3068R5 - Allowing exception throwing in constant-evaluation +// { dg-do compile { target c++26 } } +// { dg-require-effective-target exceptions_enabled } + +int +foo (int x, int y) +{ + return x + y; +} + +constexpr int +bar (int x) +{ + if (x < 0) + throw x; + return x; +} + +constexpr int +baz (int x, int y) +{ + return foo (bar (x), bar (y)); +} + +constexpr int +qux (int x, int y) +{ + try + { + return baz (x, y); + } + catch (int) + { + return 42; + } +} + +static_assert (qux (12, -1) == 42); +static_assert (qux (-7, 12) == 42); diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh2.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh2.C new file mode 100644 index 000000000000..8bd6b6f2eb78 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh2.C @@ -0,0 +1,112 @@ +// C++26 P3068R5 - Allowing exception throwing in constant-evaluation +// { dg-do compile { target c++26 } } +// { dg-require-effective-target exceptions_enabled } + +struct S { + constexpr S () : s (0) {} + constexpr S (int x) : s (x) { if (x == 42) throw 42; } + constexpr S (const S &x) : s (x.s) {} + constexpr ~S () noexcept (false) { if (s == 41) throw 41; } + constexpr const char *what () const noexcept { return "S"; } + int s; +}; +struct T : public S { + constexpr T () {} + constexpr T (int x) : S (x) {} + constexpr T (const T &x) : S (x.s) {} + constexpr ~T () {} + constexpr const char *what () const noexcept { return "T"; } +}; +struct U { + constexpr U () : u (0) {} + constexpr U (int x) : u (x) {} + constexpr U (const S &x) : u (0) {} + constexpr U (const U &x) : u (x.u) { if (u == 42) throw 43; } + constexpr ~U () {} + constexpr const char *what () const noexcept { return "U"; } + int u; +}; + +constexpr int +foo (int x) +{ + if (x == 1) + throw 43; + return x; +} + +constexpr int +bar (int x) noexcept // { dg-error "'std::terminate' called" } +{ // { dg-message "uncaught exception exited from 'noexcept' function 'constexpr int bar\\\(int\\\)'" "" { target *-*-* } .-1 } + return foo (x); +} + +constexpr int +baz (int x) +{ + switch (x) + { + case 0: throw 1; break; + case 1: try { x = bar (x); } catch (...) {} break; // { dg-message "in 'constexpr' expansion of" } + case 2: throw S (2); break; + case 3: try { throw S (42); } catch (int a) { if (a != 42) throw -1; } break; + case 4: try { S s (41); throw 2; } catch (...) {} break; // { dg-error "'std::terminate' called" } + case 5: return 5; // { dg-message "destructor exited with an exception" "" { target *-*-* } .-1 } + case 6: + try + { + throw S (5); + } + catch (const T &) {} + catch (int) {} + catch (const bool &) {} + catch (const T **const &) {} + break; + case 7: try { constexpr int y = foo (2); } catch (...) {} break; + case 8: + try + { + try + { + throw U (); + } + catch (U &u) + { + u.u = 42; + throw; + } + } + catch (U u) // { dg-error "'std::terminate' called" } + { // { dg-message "constructor exited with another exception while entering handler" "" { target *-*-* } .-1 } + } + break; + case 9: + try + { + throw U (S (41)); // { dg-error "'std::terminate' called" } + } // { dg-message "destructor exited with an exception" "" { target *-*-* } .-1 } + catch (...) + { + } + break; + } + return -1; +} + +constexpr int +qux (int x) +{ + try { constexpr int y = foo (1); } catch (...) {} // { dg-error "uncaught exception" } + return 0; +} + +constexpr int a = baz (0); // { dg-error "uncaught exception" } +constexpr int b = baz (1); // { dg-message "in 'constexpr' expansion of" } +constexpr int c = baz (2); // { dg-error "uncaught exception" } +constexpr int d = baz (3); +constexpr int e = baz (4); // { dg-message "in 'constexpr' expansion of" } +constexpr int f = baz (5); +constexpr int g = baz (6); // { dg-error "uncaught exception" } +constexpr int h = baz (7); +constexpr int i = baz (8); // { dg-message "in 'constexpr' expansion of" } +constexpr int j = baz (9); // { dg-message "in 'constexpr' expansion of" } diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh3.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh3.C new file mode 100644 index 000000000000..f844d118ce8b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh3.C @@ -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 +#include +#include + +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((B &) f); + return -1; + } + catch (std::bad_cast x) + { + const char *p = x.what (); + return 2; + } + catch (...) + { + return -1; + } + break; + case 2: + try + { + H h; + h.h = 1; + if (std::current_exception () != nullptr) + return -1; + throw G (); + } + catch (const G &g) + { + if (std::uncaught_exceptions () != 0) + return -1; + if (std::current_exception () == nullptr) + return -1; + return 3; + } + catch (...) + { + return -1; + } + break; + case 3: + try + { + decltype (sizeof 0) x = -64; + char (*a)[2] = new char[x][2]; + delete[] a; + } + catch (std::bad_array_new_length x) + { + return 4; + } + break; + case 4: + try + { + int y = -1; + int *a = new int[y]; + delete[] a; + } + catch (const std::bad_array_new_length &) + { + return 5; + } + break; + case 5: + try + { + int z = 1; + int *a = new int[z]{1, 2, 3}; + delete[] a; + } + catch (std::bad_array_new_length &) + { + return 6; + } + break; + case 6: + { + std::exception_ptr b, d; + if (b != nullptr || d != nullptr) + return -1; + try + { + throw 1; + } + catch (int a) + { + if (a != 1) + return -1; + b = std::current_exception (); + if (b == nullptr) + return -1; + try + { + throw 2L; + } + catch (long int c) + { + if (c != 2L) + return -1; + d = std::current_exception (); + if (d == nullptr || b == d) + return -1; + } + if (std::current_exception () != b) + return -1; + } + if (std::current_exception () != nullptr) + return -1; + try + { + std::rethrow_exception (d); + } + catch (long int &e) + { + if (e != 2L) + return -1; + try + { + std::rethrow_exception (b); + } + catch (const int &f) + { + if (f != 1) + return -1; + try + { + std::rethrow_exception (d); + } + catch (const long int g) + { + if (g != 2L) + return -1; + try + { + std::rethrow_exception (b); + } + catch (int h) + { + if (h != 1) + return -1; + std::exception_ptr i (b); + std::exception_ptr j; + if (j != nullptr || i == nullptr || i != b || bool (j)) + return -1; + j = i; + if (j != b || !bool (j)) + return -1; + j = nullptr; + std::swap (i, j); + if (j == nullptr || j != b || i != nullptr) + return -1; + } + } + } + } + return 7; + } + case 7: + { + std::exception_ptr a = std::make_exception_ptr (42); + std::exception_ptr b = std::make_exception_ptr (std::exception ()); + std::exception_ptr c + = std::make_exception_ptr (std::bad_array_new_length ()); + try + { + std::rethrow_exception (a); + } + catch (int d) + { + if (d != 42) + return -1; + try + { + std::rethrow_exception (b); + } + catch (const std::exception &e) + { + const char *f = e.what (); + try + { + std::rethrow_exception (c); + } + catch (const std::bad_alloc &g) + { + try + { + throw; + } + catch (const std::bad_array_new_length &h) + { + const char *i = h.what (); + const char *j = g.what (); + } + } + } + } + return 8; + } + case 8: + { + std::nested_exception a; + if (a.nested_ptr () != nullptr) + return -1; + try + { + std::nested_exception b; + if (b.nested_ptr () != nullptr) + return -1; + throw 42; + } + catch (...) + { + std::nested_exception c; + if (c.nested_ptr () != std::current_exception ()) + return -1; + std::nested_exception d = c; + if (d.nested_ptr () != c.nested_ptr ()) + return -1; + c = d; + try + { + c.rethrow_nested (); + } + catch (const int &e) + { + if (e != 42) + return -1; + } + } + return 9; + } + case 9: + try + { + std::throw_with_nested (I ()); + } + catch (const std::nested_exception &a) + { + if (a.nested_ptr () != nullptr) + return -1; + try + { + throw; + } + catch (const I &) + { + return 10; + } + } + return -1; + case 10: + try + { + std::throw_with_nested (J ()); + } + catch (const std::nested_exception &a) + { + if (a.nested_ptr () != nullptr) + return -1; + try + { + throw; + } + catch (const J &) + { + return 11; + } + } + return -1; + case 11: + try + { + std::throw_with_nested (K ()); + } + catch (const std::nested_exception &) + { + return -1; + } + catch (const K &) + { + return 12; + } + return -1; + case 12: + try + { + throw 42; + } + catch (...) + { + I a; + try + { + std::rethrow_if_nested (a); + } + catch (const int &b) + { + if (b == 42) + return 13; + } + } + return -1; + case 13: + try + { + throw J (); + } + catch (const J &a) + { + std::rethrow_if_nested (a); + return 14; + } + return -1; + case 14: + try + { + throw 42; + } + catch (...) + { + try + { + throw L (); + } + catch (const J &a) + { + try + { + std::rethrow_if_nested (a); + } + catch (const int &b) + { + if (b == 42) + return 15; + } + } + } + return -1; + case 15: + { + std::rethrow_if_nested (1); + M m; + std::rethrow_if_nested (m); + N n; + std::rethrow_if_nested (n); + O o; + std::rethrow_if_nested (o); + return 16; + } + default: + break; + } + return -1; +} + +static_assert (foo (0) == 1); +static_assert (foo (1) == 2); +static_assert (foo (2) == 3); +static_assert (foo (3) == 4); +static_assert (foo (4) == 5); +static_assert (foo (5) == 6); +static_assert (foo (6) == 7); +static_assert (foo (7) == 8); +static_assert (foo (8) == 9); +static_assert (foo (9) == 10); +static_assert (foo (10) == 11); +static_assert (foo (11) == 12); +static_assert (foo (12) == 13); +static_assert (foo (13) == 14); +static_assert (foo (14) == 15); +static_assert (foo (15) == 16); +static_assert (std::uncaught_exceptions () == 0); diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh4.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh4.C new file mode 100644 index 000000000000..24118ca2a8a4 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh4.C @@ -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((B &) f); // { dg-error "called without 'std::bad_cast' being defined" } + return -1; + } + catch (...) + { + return -1; + } + break; + case 3: + try + { + decltype (sizeof 0) x = -64; + char (*a)[2] = new char[x][2]; // { dg-error "called without 'std::bad_array_new_length' being defined" } + delete[] a; + } + catch (...) + { + return -1; + } + break; + case 4: + try + { + int y = -1; + int *a = new int[y]; // { dg-error "called without 'std::bad_array_new_length' being defined" } + delete[] a; + } + catch (...) + { + return -1; + } + break; + case 5: + try + { + int z = 1; + int *a = new int[z]{1, 2, 3}; // { dg-error "called without 'std::bad_array_new_length' being defined" } + delete[] a; + } + catch (...) + { + return -1; + } + break; + } + return -1; +} + +constexpr int a = foo (1); // { dg-message "in 'constexpr' expansion of" } +constexpr int b = foo (3); // { dg-message "in 'constexpr' expansion of" } +constexpr int c = foo (4); // { dg-message "in 'constexpr' expansion of" } +constexpr int d = foo (5); // { dg-message "in 'constexpr' expansion of" } diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh5.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh5.C new file mode 100644 index 000000000000..512aa34d14b7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh5.C @@ -0,0 +1,55 @@ +// C++26 P3068R5 - Allowing exception throwing in constant-evaluation +// { dg-do compile { target c++26 } } +// { dg-require-effective-target exceptions_enabled } + +constexpr void +foo () +{ + throw 1; +} + +void +bar () +{ +} + +constexpr void +baz () +{ + foo (); + bar (); +} + +constexpr void +qux () +{ + if consteval { + throw 2; + } + bar (); +} + +constexpr bool +corge () +{ + try + { + baz (); + } + catch (int a) + { + if (a != 1) + return false; + try + { + qux (); + } + catch (int b) + { + return b == 2; + } + } + return false; +} + +static_assert (corge ()); diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh6.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh6.C new file mode 100644 index 000000000000..6fd9462ca31c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh6.C @@ -0,0 +1,134 @@ +// C++26 P3068R5 - Allowing exception throwing in constant-evaluation +// { dg-do compile { target c++26 } } +// { dg-require-effective-target exceptions_enabled } + +struct S { + constexpr S () : s (0) {} + constexpr S (int x) : s (x) {} + constexpr S (const S &x) : s (x.s) {} + constexpr ~S () {} + int s; +}; +struct T { + constexpr T () : t (0) {} + constexpr T (int x) : t (x) {} + constexpr T (const T &x) : t (x.t) {} + constexpr ~T () {} + int t; +}; +struct U : public S, public T { + constexpr U () : S (0), T (0) {} + constexpr U (int x, int y) : S (x), T (y) {} + constexpr U (const U &x) : S (x.s), T (x.t) {} + constexpr ~U () {} +}; + +constexpr bool +foo () +{ + try + { + throw U (1, 2); + } + catch (const U &x) + { + if (x.s != 1 || x.t != 2) + return false; + try + { + throw; + } + catch (const S &y) + { + if (y.s != 1) + return false; + try + { + throw; + } + catch (const T &z) + { + if (z.t != 2) + return false; + return true; + } + } + } + return false; +} + +constexpr bool +bar () +{ + try + { + throw U (1, 2); + } + catch (U &x) + { + if (x.s != 1 || x.t != 2) + return false; + try + { + x.s = 3; + x.t = 4; + throw; + } + catch (S &y) + { + if (y.s != 3) + return false; + try + { + throw; + } + catch (T &z) + { + if (z.t != 4) + return false; + return true; + } + } + } + return false; +} + +constexpr bool +baz () +{ + try + { + throw U (1, 2); + } + catch (U x) + { + if (x.s != 1 || x.t != 2) + return false; + try + { + x.s = 3; + x.t = 4; + throw; + } + catch (S y) + { + if (y.s != 1) + return false; + try + { + throw; + } + catch (T z) + { + if (z.t != 2) + return false; + return true; + } + } + } + return false; +} + +static_assert (foo ()); +static_assert (bar ()); +static_assert (baz ()); diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh7.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh7.C new file mode 100644 index 000000000000..6bdf0c369767 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh7.C @@ -0,0 +1,151 @@ +// C++26 P3068R5 - Allowing exception throwing in constant-evaluation +// { dg-do compile { target c++26 } } +// { dg-require-effective-target exceptions_enabled } + +constexpr char p[] = "hello"; +constexpr const char *q[] = { &p[0], &p[3] }; +constexpr const char *const *r = &q[0]; +const char *s[] = { &p[0], &p[3] }; +constexpr const char **t = &s[0]; + +constexpr bool +foo () +{ + try + { + throw t; + } + catch (const char **const &x) + { + if (x != t) + return false; + try + { + throw; + } + catch (const char **&y) + { + if (y != t) + return false; + try + { + throw; + } + catch (const char **z) + { + if (z != t) + return false; + try + { + throw; + } + catch (const char *const *const &v) + { + if (v != (const char *const *) t) + return false; + try + { + throw; + } + catch (const char *const *w) + { + if (w != (const char *const *) t) + return false; + return true; + } + } + } + } + } + return false; +} + +constexpr bool +bar () +{ + try + { + throw nullptr; + } + catch (const char **const &x) + { + if (x != nullptr) + return false; + try + { + throw; + } + catch (const char **&y) + { + if (y != nullptr) + return false; + try + { + throw; + } + catch (const char **z) + { + if (z != nullptr) + return false; + try + { + throw; + } + catch (const char *const *const &v) + { + if (v != nullptr) + return false; + try + { + throw; + } + catch (const char *const *w) + { + if (w != nullptr) + return false; + return true; + } + } + } + } + } + return false; +} + +constexpr bool +baz () +{ + try + { + throw r; + } + catch (const char *const *const &x) + { + if (x != r || **x != 'h') + return false; + try + { + throw; + } + catch (const char *const *&y) + { + if (y != r || **y != 'h') + return false; + try + { + throw; + } + catch (const char *const *z) + { + if (z != r || **z != 'h') + return false; + return true; + } + } + } + return false; +} + +static_assert (foo ()); +static_assert (bar ()); +static_assert (baz ()); diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh8.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh8.C new file mode 100644 index 000000000000..2560364fe55d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh8.C @@ -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 + +constexpr std::exception_ptr +foo () +{ + try + { + throw 42; + } + catch (...) + { + return std::current_exception (); + } +} + +constexpr bool +bar () +{ + try + { + std::rethrow_exception (foo ()); + } + catch (const int &a) + { + return a == 42; + } + return false; +} + +static_assert (bar ()); +constexpr std::exception_ptr a = foo (); // { dg-error "is not a constant expression because it refers to exception object allocated with '__cxa_allocate_exception'" } +constexpr std::exception_ptr b = std::make_exception_ptr (42ULL); // { dg-error "is not a constant expression because it refers to exception object allocated with '__cxa_allocate_exception'" } diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-eh9.C b/gcc/testsuite/g++.dg/cpp26/constexpr-eh9.C new file mode 100644 index 000000000000..fd39fe035737 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/constexpr-eh9.C @@ -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\\\}'" } diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C index e4ffc357fcea..97bd36be0d35 100644 --- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C +++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C @@ -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 diff --git a/gcc/testsuite/g++.dg/cpp26/static_assert1.C b/gcc/testsuite/g++.dg/cpp26/static_assert1.C index 1d0e6f2840f1..a6eab3cd6912 100644 --- a/gcc/testsuite/g++.dg/cpp26/static_assert1.C +++ b/gcc/testsuite/g++.dg/cpp26/static_assert1.C @@ -51,7 +51,7 @@ static_assert (true, H {}); // { dg-warning "'static_assert' with non-string mes struct I { constexpr signed char size () const { return 0; } const char *data () const { return ""; } }; static_assert (true, I {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } -struct J { constexpr int size () const { return j ? throw 1 : 0; } // { dg-error "expression '' is not a constant expression" } +struct J { constexpr int size () const { return j ? throw 1 : 0; } // { dg-error "expression '' is not a constant expression" "" { target c++23_down } } constexpr const char *data () const { return ""; }; constexpr J (int x) : j (x) {} int j; }; @@ -60,6 +60,7 @@ static_assert (false, J (0)); // { dg-warning "'static_assert' with non-string m // { dg-error "static assertion failed" "" { target *-*-* } .-1 } static_assert (false, J (1)); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } // { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target *-*-* } .-1 } + // { dg-error "uncaught exception '1'" "" { target c++26 } .-2 } struct K { constexpr operator int () { return 4; } }; struct L { constexpr operator const char * () { return "test"; } }; struct M { constexpr K size () const { return {}; } @@ -261,10 +262,11 @@ namespace NN #if __cplusplus >= 201402L struct J { static constexpr int size () { return 0; } - static constexpr const char *data (int x = 0) { if (x) return nullptr; else throw 1; } }; // { dg-error "expression '' 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 '' 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 '' is not a constant expression" "" { target c++23 } } + static constexpr int size () { if consteval { throw 1; } else { return 4; } } // { dg-error "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 '' is not a constant expression" "" { target c++23 } } + static constexpr const char *data () { if consteval { throw 1; } else { return "test"; } } // { dg-error "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"; } }; diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C index ca923519f98a..17a391bd84c7 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C @@ -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 { } diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval34.C b/gcc/testsuite/g++.dg/cpp2a/consteval34.C index 7562f403f741..73bcf77297c5 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval34.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval34.C @@ -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 } } diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C index 9ee93c334e9d..12a071a85e59 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C @@ -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(*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(*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 } diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C index cc1cadae3bc0..f966db45e921 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C @@ -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 ((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 ((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 ((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 ((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 ((C&)d)); -constexpr bool b03 = (dynamic_cast ((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 ((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 ((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 ((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 ((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 ((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 ((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 ((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 ((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 ((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 ((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 ((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 ((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 ((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 ((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 ((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 ((C&)(D&)e)); static_assert (&e == &dynamic_cast ((B&)(CC&)e)); static_assert (&(CC&)e == &dynamic_cast ((B&)(CC&)e)); constexpr EE ee; -constexpr bool b11 = (dynamic_cast ((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 ((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 ((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 ((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 ((C&)(D&)ee)); static_assert (&ee == &dynamic_cast ((B&)(CC&)ee)); static_assert (&(CC&)ee == &dynamic_cast ((B&)(CC&)ee)); @@ -78,14 +106,17 @@ constexpr X x; static_assert (&x == &dynamic_cast((B&)(CC&)(E&)x)); constexpr XX xx; -constexpr bool b13 = (dynamic_cast((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((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((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((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((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((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 ((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 ((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 ((C&)(D&)e)); constexpr K k; -constexpr bool b17 = (dynamic_cast ((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 ((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 ((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 ((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 } diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic18.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic18.C index 25d98c2b661f..112ff8a249cc 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic18.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic18.C @@ -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(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(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; } diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C index da647bfca5e3..a23713461aa9 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C @@ -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(static_cast(&g)) == nullptr); static_assert(g.f == (void*)(F*)&g); static_assert(dynamic_cast(static_cast(&g)) == &g); -constexpr int d_a = (dynamic_cast(static_cast(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(static_cast(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((A2&)(B&)g) == &(A&)(B&)g); // ... and from B to its A ... static_assert(&dynamic_cast((B&)g) == &(A&)(B&)g); // ... but not from D. -static_assert(&dynamic_cast((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((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((A2&)(B&)g) == &(D&)g); // Cannot cast from private base E to derived class F. -constexpr int e_f = (dynamic_cast((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((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((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((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((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((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((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((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 } diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C index d67c3072b544..4434a1ae074b 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C @@ -1,6 +1,22 @@ // PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr. // { dg-do compile { target c++20 } } +#if __cpp_constexpr_exceptions >= 202411L +namespace std { + struct exception { + constexpr exception () noexcept {} + constexpr virtual ~exception () noexcept {} + constexpr exception (const exception &) = default; + constexpr exception &operator= (const exception &) = default; + constexpr virtual const char *what () const noexcept { return "std::exception"; } + }; + struct bad_cast : public exception { + constexpr virtual ~bad_cast () noexcept {} + constexpr virtual const char *what () const noexcept { return "std::bad_cast"; } + }; +} +#endif + // Private base. struct P1 { virtual void p1(); }; @@ -12,14 +28,16 @@ struct A : B, C, private P2 { virtual void a(); }; constexpr A a; // P1 is a non-public base of A. -constexpr bool b1 = (dynamic_cast((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((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((P1*)&a) == nullptr); -constexpr bool b2 = (dynamic_cast((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((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((P1*)&a) == nullptr); static_assert (dynamic_cast((P2*)&a) == nullptr); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C index bc3efd05bccb..d504efd102f8 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C @@ -1,6 +1,22 @@ // PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr. // { dg-do compile { target c++20 } } +#if __cpp_constexpr_exceptions >= 202411L +namespace std { + struct exception { + constexpr exception () noexcept {} + constexpr virtual ~exception () noexcept {} + constexpr exception (const exception &) = default; + constexpr exception &operator= (const exception &) = default; + constexpr virtual const char *what () const noexcept { return "std::exception"; } + }; + struct bad_cast : public exception { + constexpr virtual ~bad_cast () noexcept {} + constexpr virtual const char *what () const noexcept { return "std::bad_cast"; } + }; +} +#endif + // Protected base. struct P1 { virtual void p1(); }; @@ -12,14 +28,16 @@ struct A : B, C, protected P2 { virtual void a(); }; constexpr A a; // P1 is a non-public base of A. -constexpr bool b1 = (dynamic_cast((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((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((P1*)&a) == nullptr); -constexpr bool b2 = (dynamic_cast((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((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((P1*)&a) == nullptr); static_assert (dynamic_cast((P2*)&a) == nullptr); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C index 1958cae441db..2fc824258104 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C @@ -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((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((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((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((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((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((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((B*)&a) == nullptr); static_assert (dynamic_cast((P1*)&a) == nullptr); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C index 7d42ffa5a78f..3a50dde82724 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C @@ -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((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((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((D*)&e) == nullptr); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new27.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new27.C new file mode 100644 index 000000000000..a26fc7eb0748 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new27.C @@ -0,0 +1,41 @@ +// { dg-do compile { target c++20 } } + +#if __cpp_constexpr_exceptions >= 202411L +namespace std { + struct exception { + constexpr exception () noexcept {} + constexpr virtual ~exception () noexcept {} + constexpr exception (const exception &) = default; + constexpr exception &operator= (const exception &) = default; + constexpr virtual const char *what () const noexcept { return "std::exception"; } + }; + struct bad_alloc : public exception { + constexpr virtual ~bad_alloc () noexcept {} + constexpr virtual const char *what () const noexcept { return "std::bad_alloc"; } + }; + struct bad_array_new_length : public bad_alloc { + constexpr virtual ~bad_array_new_length () noexcept {} + constexpr virtual const char *what () const noexcept { return "std::bad_array_new_length"; } + }; +} +#endif + +constexpr int +foo (__SIZE_TYPE__ x, int y, int z) +{ + char (*a)[2] = new char[x][2]; // { dg-error "call to non-'constexpr' function 'void __cxa_throw_bad_array_new_length\\\(\\\)'" "" { target c++23_down } } + delete[] a; // { dg-message "declared here" "" { target c++23_down } .-1 } + int *b = new int[y]; // { dg-error "call to non-'constexpr' function 'void __cxa_throw_bad_array_new_length\\\(\\\)'" "" { target c++23_down } } + delete[] b; + int *c = new int[z]{1, 2, 3}; // { dg-error "call to non-'constexpr' function 'void __cxa_throw_bad_array_new_length\\\(\\\)'" "" { target c++23_down } } + delete[] c; + return 0; +} + +constexpr int a = foo (16, 2, 3); +constexpr int b = foo (-64, 2, 3); // { dg-message "in 'constexpr' expansion of" "" { target c++23_down } } + // { dg-error "uncaught exception" "" { target c++26 } .-1 } +constexpr int c = foo (16, -1, 3); // { dg-message "in 'constexpr' expansion of" "" { target c++23_down } } + // { dg-error "uncaught exception" "" { target c++26 } .-1 } +constexpr int d = foo (16, 2, 1); // { dg-message "in 'constexpr' expansion of" "" { target c++23_down } } + // { dg-error "uncaught exception" "" { target c++26 } .-1 } diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-typeid5.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-typeid5.C new file mode 100644 index 000000000000..567383d8ffce --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-typeid5.C @@ -0,0 +1,20 @@ +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr. +// { dg-do compile { target c++17 } } + +#include + +template +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 ()); +static_assert (!foo ()); +constexpr bool a = foo (); // { dg-message "in 'constexpr' expansion of" "" { target c++23_down } } +// { dg-error "uncaught exception" "" { target c++26 } .-1 } diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 31385b53107a..df58e7018d81 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -2050,6 +2050,15 @@ ftms = { }; }; +ftms = { + name = constexpr_exceptions; + values = { + v = 202502; + cxxmin = 26; + extra_cond = "__cpp_constexpr_exceptions >= 202411L"; + }; +}; + // Standard test specifications. stds[97] = ">= 199711L"; stds[03] = ">= 199711L"; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index aa53f299848f..1414dd78ddab 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2299,4 +2299,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 diff --git a/libstdc++-v3/libsupc++/exception b/libstdc++-v3/libsupc++/exception index 61d41315f9f1..25ce8d97e315 100644 --- a/libstdc++-v3/libsupc++/exception +++ b/libstdc++-v3/libsupc++/exception @@ -57,8 +57,16 @@ namespace std _GLIBCXX_VISIBILITY(default) class bad_exception : public exception { public: - bad_exception() _GLIBCXX_USE_NOEXCEPT { } + _GLIBCXX26_CONSTEXPR bad_exception() _GLIBCXX_USE_NOEXCEPT { } +#if __cplusplus >= 202400L + constexpr virtual ~bad_exception() _GLIBCXX_TXN_SAFE_DYN noexcept {} + + constexpr virtual const char* what() const _GLIBCXX_TXN_SAFE_DYN noexcept + { + return "std::bad_exception"; + } +#else // This declaration is not useless: // http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118 virtual ~bad_exception() _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT; @@ -66,6 +74,7 @@ namespace std _GLIBCXX_VISIBILITY(default) // See comment in eh_exception.cc. virtual const char* what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT; +#endif }; /// If you write a replacement %terminate handler, it must be of this type. diff --git a/libstdc++-v3/libsupc++/exception.h b/libstdc++-v3/libsupc++/exception.h index f8bca9127bce..efb5fea5d863 100644 --- a/libstdc++-v3/libsupc++/exception.h +++ b/libstdc++-v3/libsupc++/exception.h @@ -61,19 +61,28 @@ namespace std _GLIBCXX_VISIBILITY(default) class exception { public: - exception() _GLIBCXX_NOTHROW { } + _GLIBCXX26_CONSTEXPR exception() _GLIBCXX_NOTHROW { } +#if __cplusplus >= 202400L + constexpr virtual ~exception() _GLIBCXX_TXN_SAFE_DYN noexcept {} +#else virtual ~exception() _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW; +#endif #if __cplusplus >= 201103L - exception(const exception&) = default; - exception& operator=(const exception&) = default; - exception(exception&&) = default; - exception& operator=(exception&&) = default; + _GLIBCXX26_CONSTEXPR exception(const exception&) = default; + _GLIBCXX26_CONSTEXPR exception& operator=(const exception&) = default; + _GLIBCXX26_CONSTEXPR exception(exception&&) = default; + _GLIBCXX26_CONSTEXPR exception& operator=(exception&&) = default; #endif /** Returns a C-style character string describing the general cause * of the current error. */ +#if __cplusplus >= 202400L + constexpr virtual const char* + what() const _GLIBCXX_TXN_SAFE_DYN noexcept { return "std::exception"; } +#else virtual const char* what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW; +#endif }; /// @} diff --git a/libstdc++-v3/libsupc++/exception_ptr.h b/libstdc++-v3/libsupc++/exception_ptr.h index a6ff8c0d6982..be516d7aae17 100644 --- a/libstdc++-v3/libsupc++/exception_ptr.h +++ b/libstdc++-v3/libsupc++/exception_ptr.h @@ -75,7 +75,8 @@ namespace std _GLIBCXX_VISIBILITY(default) exception_ptr current_exception() _GLIBCXX_USE_NOEXCEPT; template - exception_ptr make_exception_ptr(_Ex) _GLIBCXX_USE_NOEXCEPT; + _GLIBCXX26_CONSTEXPR exception_ptr make_exception_ptr(_Ex) + _GLIBCXX_USE_NOEXCEPT; /// Throw the object pointed to by the exception_ptr. void rethrow_exception(exception_ptr) __attribute__ ((__noreturn__)); @@ -105,7 +106,25 @@ namespace std _GLIBCXX_VISIBILITY(default) { void* _M_exception_object; +#if __cplusplus >= 202400L + constexpr explicit exception_ptr(void* __e) noexcept + : _M_exception_object(__e) + { + if (_M_exception_object) + { +#if __cpp_if_consteval >= 202106L \ + && _GLIBCXX_HAS_BUILTIN(__builtin_eh_ptr_adjust_ref) + if consteval { + __builtin_eh_ptr_adjust_ref(_M_exception_object, 1); + return; + } +#endif + _M_addref(); + } + } +#else explicit exception_ptr(void* __e) _GLIBCXX_USE_NOEXCEPT; +#endif void _M_addref() _GLIBCXX_USE_NOEXCEPT; void _M_release() _GLIBCXX_USE_NOEXCEPT; @@ -115,7 +134,8 @@ namespace std _GLIBCXX_VISIBILITY(default) friend exception_ptr std::current_exception() _GLIBCXX_USE_NOEXCEPT; friend void std::rethrow_exception(exception_ptr); template - 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 friend const _Ex* std::exception_ptr_cast(const exception_ptr&) noexcept; @@ -125,16 +145,17 @@ namespace std _GLIBCXX_VISIBILITY(default) _GLIBCXX_USE_NOEXCEPT; public: - exception_ptr() _GLIBCXX_USE_NOEXCEPT; + _GLIBCXX26_CONSTEXPR exception_ptr() _GLIBCXX_USE_NOEXCEPT; - exception_ptr(const exception_ptr&) _GLIBCXX_USE_NOEXCEPT; + _GLIBCXX26_CONSTEXPR exception_ptr(const exception_ptr&) + _GLIBCXX_USE_NOEXCEPT; #if __cplusplus >= 201103L - exception_ptr(nullptr_t) noexcept + _GLIBCXX26_CONSTEXPR exception_ptr(nullptr_t) noexcept : _M_exception_object(nullptr) { } - exception_ptr(exception_ptr&& __o) noexcept + _GLIBCXX26_CONSTEXPR exception_ptr(exception_ptr&& __o) noexcept : _M_exception_object(__o._M_exception_object) { __o._M_exception_object = nullptr; } #endif @@ -146,11 +167,11 @@ namespace std _GLIBCXX_VISIBILITY(default) exception_ptr(__safe_bool) _GLIBCXX_USE_NOEXCEPT; #endif - exception_ptr& + _GLIBCXX26_CONSTEXPR exception_ptr& operator=(const exception_ptr&) _GLIBCXX_USE_NOEXCEPT; #if __cplusplus >= 201103L - exception_ptr& + _GLIBCXX26_CONSTEXPR exception_ptr& operator=(exception_ptr&& __o) noexcept { exception_ptr(static_cast(__o)).swap(*this); @@ -158,9 +179,9 @@ namespace std _GLIBCXX_VISIBILITY(default) } #endif - ~exception_ptr() _GLIBCXX_USE_NOEXCEPT; + _GLIBCXX26_CONSTEXPR ~exception_ptr() _GLIBCXX_USE_NOEXCEPT; - void + _GLIBCXX26_CONSTEXPR void swap(exception_ptr&) _GLIBCXX_USE_NOEXCEPT; #ifdef _GLIBCXX_EH_PTR_COMPAT @@ -172,13 +193,13 @@ namespace std _GLIBCXX_VISIBILITY(default) #endif #if __cplusplus >= 201103L - explicit operator bool() const noexcept + _GLIBCXX26_CONSTEXPR explicit operator bool() const noexcept { return _M_exception_object; } #endif #if __cpp_impl_three_way_comparison >= 201907L \ && ! defined _GLIBCXX_EH_PTR_RELOPS_COMPAT - friend bool + _GLIBCXX26_CONSTEXPR friend bool operator==(const exception_ptr&, const exception_ptr&) noexcept = default; #else friend _GLIBCXX_EH_PTR_USED bool @@ -198,31 +219,49 @@ namespace std _GLIBCXX_VISIBILITY(default) }; _GLIBCXX_EH_PTR_USED - inline + _GLIBCXX26_CONSTEXPR inline exception_ptr::exception_ptr() _GLIBCXX_USE_NOEXCEPT : _M_exception_object(0) { } _GLIBCXX_EH_PTR_USED - inline + _GLIBCXX26_CONSTEXPR inline exception_ptr::exception_ptr(const exception_ptr& __other) _GLIBCXX_USE_NOEXCEPT : _M_exception_object(__other._M_exception_object) { if (_M_exception_object) - _M_addref(); + { +#if __cpp_if_consteval >= 202106L \ + && _GLIBCXX_HAS_BUILTIN(__builtin_eh_ptr_adjust_ref) + if consteval { + __builtin_eh_ptr_adjust_ref(_M_exception_object, 1); + return; + } +#endif + _M_addref(); + } } _GLIBCXX_EH_PTR_USED - inline + _GLIBCXX26_CONSTEXPR inline exception_ptr::~exception_ptr() _GLIBCXX_USE_NOEXCEPT { if (_M_exception_object) - _M_release(); + { +#if __cpp_if_consteval >= 202106L \ + && _GLIBCXX_HAS_BUILTIN(__builtin_eh_ptr_adjust_ref) + if consteval { + __builtin_eh_ptr_adjust_ref(_M_exception_object, -1); + return; + } +#endif + _M_release(); + } } _GLIBCXX_EH_PTR_USED - inline exception_ptr& + _GLIBCXX26_CONSTEXPR inline exception_ptr& exception_ptr::operator=(const exception_ptr& __other) _GLIBCXX_USE_NOEXCEPT { exception_ptr(__other).swap(*this); @@ -230,7 +269,7 @@ namespace std _GLIBCXX_VISIBILITY(default) } _GLIBCXX_EH_PTR_USED - inline void + _GLIBCXX26_CONSTEXPR inline void exception_ptr::swap(exception_ptr &__other) _GLIBCXX_USE_NOEXCEPT { void *__tmp = _M_exception_object; @@ -239,7 +278,7 @@ namespace std _GLIBCXX_VISIBILITY(default) } /// @relates exception_ptr - inline void + _GLIBCXX26_CONSTEXPR inline void swap(exception_ptr& __lhs, exception_ptr& __rhs) { __lhs.swap(__rhs); } @@ -258,9 +297,21 @@ namespace std _GLIBCXX_VISIBILITY(default) /// Obtain an exception_ptr pointing to a copy of the supplied object. #if (__cplusplus >= 201103L && __cpp_rtti) || __cpp_exceptions template - exception_ptr + _GLIBCXX26_CONSTEXPR exception_ptr make_exception_ptr(_Ex __ex) _GLIBCXX_USE_NOEXCEPT { +#if __cplusplus >= 202400L + if consteval { + try + { + throw __ex; + } + catch(...) + { + return current_exception(); + } + } +#endif #if __cplusplus >= 201103L && __cpp_rtti using _Ex2 = typename decay<_Ex>::type; void* __e = __cxxabiv1::__cxa_allocate_exception(sizeof(_Ex)); @@ -293,7 +344,7 @@ namespace std _GLIBCXX_VISIBILITY(default) // instead of a working one compiled with RTTI and/or exceptions enabled. template __attribute__ ((__always_inline__)) - inline exception_ptr + _GLIBCXX26_CONSTEXPR inline exception_ptr make_exception_ptr(_Ex) _GLIBCXX_USE_NOEXCEPT { return exception_ptr(); } #endif diff --git a/libstdc++-v3/libsupc++/nested_exception.h b/libstdc++-v3/libsupc++/nested_exception.h index c9f63e5a56a2..aff47bc3d7f9 100644 --- a/libstdc++-v3/libsupc++/nested_exception.h +++ b/libstdc++-v3/libsupc++/nested_exception.h @@ -62,17 +62,24 @@ namespace std _GLIBCXX_VISIBILITY(default) public: /// The default constructor stores the current exception (if any). + _GLIBCXX26_CONSTEXPR nested_exception() noexcept : _M_ptr(current_exception()) { } + _GLIBCXX26_CONSTEXPR nested_exception(const nested_exception&) noexcept = default; + _GLIBCXX26_CONSTEXPR nested_exception& operator=(const nested_exception&) noexcept = default; +#if __cplusplus >= 202400L + constexpr virtual ~nested_exception() noexcept {} +#else virtual ~nested_exception() noexcept; +#endif /// Rethrow the stored exception, or terminate if none was stored. [[noreturn]] - void + _GLIBCXX26_CONSTEXPR void rethrow_nested() const { if (_M_ptr) @@ -81,7 +88,7 @@ namespace std _GLIBCXX_VISIBILITY(default) } /// Access the stored exception. - exception_ptr + _GLIBCXX26_CONSTEXPR exception_ptr nested_ptr() const noexcept { return _M_ptr; } }; @@ -91,11 +98,11 @@ namespace std _GLIBCXX_VISIBILITY(default) template struct _Nested_exception : public _Except, public nested_exception { - explicit _Nested_exception(const _Except& __ex) + _GLIBCXX26_CONSTEXPR explicit _Nested_exception(const _Except& __ex) : _Except(__ex) { } - explicit _Nested_exception(_Except&& __ex) + _GLIBCXX26_CONSTEXPR explicit _Nested_exception(_Except&& __ex) : _Except(static_cast<_Except&&>(__ex)) { } }; @@ -144,7 +151,7 @@ namespace std _GLIBCXX_VISIBILITY(default) */ template [[noreturn]] - inline void + _GLIBCXX26_CONSTEXPR inline void throw_with_nested(_Tp&& __t) { using _Up = typename decay<_Tp>::type; @@ -204,7 +211,7 @@ namespace std _GLIBCXX_VISIBILITY(default) # if ! __cpp_rtti [[__gnu__::__always_inline__]] #endif - inline void + _GLIBCXX26_CONSTEXPR inline void rethrow_if_nested(const _Ex& __ex) { const _Ex* __ptr = __builtin_addressof(__ex); diff --git a/libstdc++-v3/libsupc++/new b/libstdc++-v3/libsupc++/new index a5503733cdcd..fb36dae25a6d 100644 --- a/libstdc++-v3/libsupc++/new +++ b/libstdc++-v3/libsupc++/new @@ -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 diff --git a/libstdc++-v3/libsupc++/typeinfo b/libstdc++-v3/libsupc++/typeinfo index 0a94b86bd745..a31be7c39eaf 100644 --- a/libstdc++-v3/libsupc++/typeinfo +++ b/libstdc++-v3/libsupc++/typeinfo @@ -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