aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp/constexpr.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/cp/constexpr.cc')
-rw-r--r--gcc/cp/constexpr.cc2272
1 files changed, 1841 insertions, 431 deletions
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 4820bcc..eb19784 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -303,11 +303,20 @@ is_valid_constexpr_fn (tree fun, bool complain)
}
}
}
- else if (CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fun)))
+ else if (CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fun)) && cxx_dialect < cxx26)
{
ret = false;
if (complain)
- error ("%q#T has virtual base classes", DECL_CONTEXT (fun));
+ {
+ if (DECL_CONSTRUCTOR_P (fun))
+ error ("%<constexpr%> constructor in %q#T that has "
+ "virtual base classes only available with "
+ "%<-std=c++2c%> or %<-std=gnu++2c%>", DECL_CONTEXT (fun));
+ else
+ error ("%<constexpr%> destructor in %q#T that has "
+ "virtual base classes only available with "
+ "%<-std=c++2c%> or %<-std=gnu++2c%>", DECL_CONTEXT (fun));
+ }
}
return ret;
@@ -1048,6 +1057,12 @@ explain_invalid_constexpr_fn (tree fun)
{
static hash_set<tree> *diagnosed;
tree body;
+
+ /* Don't try to explain a function we already complained about. */
+ if (function *f = DECL_STRUCT_FUNCTION (fun))
+ if (f->language->erroneous)
+ return;
+
/* In C++23, a function marked 'constexpr' may not actually be a constant
expression. We haven't diagnosed the problem yet: -Winvalid-constexpr
wasn't enabled. The function was called, so diagnose why it cannot be
@@ -1097,17 +1112,14 @@ explain_invalid_constexpr_fn (tree fun)
body = fd->body;
else
body = DECL_SAVED_TREE (fun);
- body = massage_constexpr_body (fun, body);
- require_potential_rvalue_constant_expression (body);
+ tree massaged = massage_constexpr_body (fun, body);
+ require_potential_rvalue_constant_expression (massaged);
if (DECL_CONSTRUCTOR_P (fun))
{
- cx_check_missing_mem_inits (DECL_CONTEXT (fun), body, true);
+ cx_check_missing_mem_inits (DECL_CONTEXT (fun), massaged, true);
if (cxx_dialect > cxx11)
- {
- /* Also check the body, not just the ctor-initializer. */
- body = DECL_SAVED_TREE (fun);
- require_potential_rvalue_constant_expression (body);
- }
+ /* Also check the body, not just the ctor-initializer. */
+ require_potential_rvalue_constant_expression (body);
}
}
}
@@ -1119,20 +1131,22 @@ explain_invalid_constexpr_fn (tree fun)
struct GTY((for_user)) constexpr_call {
/* Description of the constexpr function definition. */
- constexpr_fundef *fundef;
+ constexpr_fundef *fundef = nullptr;
/* Parameter bindings environment. A TREE_VEC of arguments. */
- tree bindings;
- /* Result of the call.
- NULL means the call is being evaluated.
+ tree bindings = NULL_TREE;
+ /* Result of the call, indexed by the value of
+ constexpr_ctx::manifestly_const_eval.
+ unknown_type_node means the call is being evaluated.
error_mark_node means that the evaluation was erroneous or otherwise
uncacheable (e.g. because it depends on the caller).
Otherwise, the actual value of the call. */
- tree result;
+ tree results[3] = { NULL_TREE, NULL_TREE, NULL_TREE };
/* The hash of this call; we remember it here to avoid having to
recalculate it when expanding the hash table. */
- hashval_t hash;
- /* The value of constexpr_ctx::manifestly_const_eval. */
- enum mce_value manifestly_const_eval;
+ hashval_t hash = 0;
+
+ /* The result slot corresponding to the given mce_value. */
+ tree& result (mce_value mce) { return results[1 + int(mce)]; }
};
struct constexpr_call_hasher : ggc_ptr_hash<constexpr_call>
@@ -1170,6 +1184,10 @@ public:
/* Heap VAR_DECLs created during the evaluation of the outermost constant
expression. */
auto_vec<tree, 16> heap_vars;
+ /* Vector of caught exceptions, including exceptions still not active at
+ the start of a handler (those are immediately followed up by HANDLER_TYPE
+ until __cxa_begin_catch finishes). */
+ auto_vec<tree, 2> caught_exceptions;
/* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR. */
vec<tree> *cleanups;
/* If non-null, only allow modification of existing values of the variables
@@ -1177,10 +1195,13 @@ public:
hash_set<tree> *modifiable;
/* Number of heap VAR_DECL deallocations. */
unsigned heap_dealloc_count;
+ /* Number of uncaught exceptions. */
+ unsigned uncaught_exceptions;
+
/* Constructor. */
constexpr_global_ctx ()
: constexpr_ops_count (0), cleanups (NULL), modifiable (nullptr),
- heap_dealloc_count (0) {}
+ heap_dealloc_count (0), uncaught_exceptions (0) {}
bool is_outside_lifetime (tree t)
{
@@ -1294,6 +1315,64 @@ 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. */
+
+static bool
+p2280_active_p (const constexpr_ctx *ctx)
+{
+ if (ctx->manifestly_const_eval != mce_true)
+ /* Disable these relaxations during speculative constexpr folding,
+ as it can significantly increase compile time/memory use
+ (PR119387). */
+ return false;
+
+ /* P2280R4 was accepted as a DR against C++11. */
+ return cxx_dialect >= cxx11;
+}
+
/* Remove T from the global values map, checking for attempts to destroy
a value that has already finished its lifetime. */
@@ -1411,8 +1490,6 @@ constexpr_call_hasher::equal (constexpr_call *lhs, constexpr_call *rhs)
return true;
if (lhs->hash != rhs->hash)
return false;
- if (lhs->manifestly_const_eval != rhs->manifestly_const_eval)
- return false;
if (!constexpr_fundef_hasher::equal (lhs->fundef, rhs->fundef))
return false;
return cp_tree_equal (lhs->bindings, rhs->bindings);
@@ -1515,13 +1592,671 @@ 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 *);
-static tree find_deleted_heap_var (tree *, int *, void *);
+
+/* For exception object EXC if it has class type and usable what () method
+ which returns cv char * return the xmalloced string literal which it returns
+ if possible, otherwise return NULL. */
+
+static char *
+exception_what_str (const constexpr_ctx *ctx, tree exc)
+{
+ tree type = strip_array_types (TREE_TYPE (exc));
+ if (!CLASS_TYPE_P (type))
+ return NULL;
+ tree std_exception = lookup_qualified_name (std_node, "exception",
+ LOOK_want::NORMAL, false);
+ if (TREE_CODE (std_exception) != TYPE_DECL)
+ return NULL;
+ if (!CLASS_TYPE_P (TREE_TYPE (std_exception)))
+ return NULL;
+ base_kind b_kind;
+ tree binfo = lookup_base (type, TREE_TYPE (std_exception), ba_check, &b_kind,
+ tf_none);
+ if (binfo == NULL_TREE || binfo == error_mark_node)
+ return NULL;
+ if (type != TREE_TYPE (exc))
+ exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL);
+ tree call
+ = finish_class_member_access_expr (exc, get_identifier ("what"), false,
+ tf_none);
+ if (call == error_mark_node)
+ return NULL;
+ releasing_vec what_args;
+ call = finish_call_expr (call, &what_args, false, false, tf_none);
+ if (call == error_mark_node)
+ return NULL;
+ if (TREE_CODE (TREE_TYPE (call)) != POINTER_TYPE
+ || !INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (call)))
+ || !COMPLETE_TYPE_P (TREE_TYPE (TREE_TYPE (call)))
+ || !tree_int_cst_equal (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (call))),
+ TYPE_SIZE_UNIT (char_type_node))
+ || TYPE_PRECISION (TREE_TYPE (TREE_TYPE (call))) != BITS_PER_UNIT)
+ return NULL;
+ if (!potential_constant_expression (call))
+ return NULL;
+ bool non_constant_p = false, overflow_p = false;
+ tree jmp_target = NULL;
+ tree ptr = cxx_eval_constant_expression (ctx, call, vc_prvalue,
+ &non_constant_p, &overflow_p,
+ &jmp_target);
+ if (throws (&jmp_target) || non_constant_p)
+ return NULL;
+ if (reduced_constant_expression_p (ptr))
+ if (const char *msg = c_getstr (ptr))
+ return xstrdup (msg);
+ auto_vec <char, 32> v;
+ for (unsigned i = 0; i < INT_MAX; ++i)
+ {
+ tree t = call;
+ if (i)
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr, size_int (i));
+ t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t);
+ non_constant_p = false;
+ overflow_p = false;
+ jmp_target = NULL;
+ tree t2 = cxx_eval_constant_expression (ctx, t, vc_prvalue,
+ &non_constant_p, &overflow_p,
+ &jmp_target);
+ if (throws (&jmp_target)
+ || non_constant_p
+ || !tree_fits_shwi_p (t2))
+ return NULL;
+ char c = tree_to_shwi (t2);
+ v.safe_push (c);
+ if (c == '\0')
+ break;
+ }
+ return xstrdup (v.address ());
+}
+
+/* Diagnose constant expression evaluation encountering call to
+ std::terminate due to exception EXC. */
+
+static void
+diagnose_std_terminate (location_t loc, const constexpr_ctx *ctx, tree exc)
+{
+ tree type = strip_array_types (TREE_TYPE (exc));
+ if (char *str = exception_what_str (ctx, exc))
+ {
+ error_at (loc, "%qs called after throwing an exception of type %qT; "
+ "%<what()%>: %qs", "std::terminate", type, str);
+ free (str);
+ }
+ else
+ {
+ if (type != TREE_TYPE (exc))
+ exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL);
+ bool non_constant_p = false, overflow_p = false;
+ tree jmp_target = NULL;
+ tree val = cxx_eval_constant_expression (ctx, exc, vc_prvalue,
+ &non_constant_p, &overflow_p,
+ &jmp_target);
+ gcc_assert (!throws (&jmp_target) && !non_constant_p);
+ if (reduced_constant_expression_p (val))
+ error_at (loc, "%qs called after throwing an exception %qE",
+ "std::terminate", val);
+ else
+ error_at (loc, "%qs called after throwing an exception of type %qT",
+ "std::terminate", type);
+ }
+}
+
+/* Diagnose constant expression evaluation encountering call to
+ uncaught exception EXC. */
+
+static void
+diagnose_uncaught_exception (location_t loc, const constexpr_ctx *ctx, tree exc)
+{
+ tree type = strip_array_types (TREE_TYPE (exc));
+ if (char *str = exception_what_str (ctx, exc))
+ {
+ error_at (loc, "uncaught exception of type %qT; %<what()%>: %qs", type, str);
+ free (str);
+ }
+ else
+ {
+ if (type != TREE_TYPE (exc))
+ exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL);
+ bool non_constant_p = false, overflow_p = false;
+ tree jmp_target = NULL;
+ tree val = cxx_eval_constant_expression (ctx, exc, vc_prvalue,
+ &non_constant_p, &overflow_p,
+ &jmp_target);
+ gcc_assert (!throws (&jmp_target) && !non_constant_p);
+ if (reduced_constant_expression_p (val))
+ error_at (loc, "uncaught exception %qE", val);
+ else
+ error_at (loc, "uncaught exception of type %qT", type);
+ }
+}
+
+/* Kinds of __cxa_* functions (and a few other EH related ones) we handle as
+ magic constexpr functions for C++26. */
+
+enum cxa_builtin {
+ CXA_NONE = 0,
+ CXA_ALLOCATE_EXCEPTION = 1,
+ CXA_FREE_EXCEPTION = 2,
+ CXA_THROW = 3,
+ CXA_BEGIN_CATCH = 4,
+ CXA_END_CATCH = 5,
+ CXA_RETHROW = 6,
+ CXA_GET_EXCEPTION_PTR = 7,
+ CXA_BAD_CAST = 8,
+ CXA_BAD_TYPEID = 9,
+ CXA_THROW_BAD_ARRAY_NEW_LENGTH = 10,
+ STD_UNCAUGHT_EXCEPTIONS = 11,
+ STD_CURRENT_EXCEPTION = 12,
+ STD_RETHROW_EXCEPTION = 13,
+ BUILTIN_EH_PTR_ADJUST_REF = 14
+};
+
+/* Return cxa_builtin if FNDECL is a __cxa_* function handled as
+ magic constexpr function for C++26. Return CXA_NONE otherwise. */
+
+static enum cxa_builtin
+cxx_cxa_builtin_fn_p (tree fndecl)
+{
+ if (cxx_dialect < cxx26)
+ return CXA_NONE;
+ if (DECL_LANGUAGE (fndecl) != lang_c)
+ {
+ if (!decl_in_std_namespace_p (fndecl))
+ return CXA_NONE;
+ if (id_equal (DECL_NAME (fndecl), "uncaught_exceptions"))
+ return STD_UNCAUGHT_EXCEPTIONS;
+ if (id_equal (DECL_NAME (fndecl), "current_exception"))
+ return STD_CURRENT_EXCEPTION;
+ if (id_equal (DECL_NAME (fndecl), "rethrow_exception"))
+ return STD_RETHROW_EXCEPTION;
+ return CXA_NONE;
+ }
+ if (!startswith (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "__cxa_"))
+ return CXA_NONE;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_allocate_exception"))
+ return CXA_ALLOCATE_EXCEPTION;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_free_exception"))
+ return CXA_FREE_EXCEPTION;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_throw"))
+ return CXA_THROW;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_begin_catch"))
+ return CXA_BEGIN_CATCH;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_end_catch"))
+ return CXA_END_CATCH;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_rethrow"))
+ return CXA_RETHROW;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_get_exception_ptr"))
+ return CXA_GET_EXCEPTION_PTR;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_bad_cast"))
+ return CXA_BAD_CAST;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_bad_typeid"))
+ return CXA_BAD_TYPEID;
+ if (id_equal (DECL_NAME (fndecl), "__cxa_throw_bad_array_new_length"))
+ return CXA_THROW_BAD_ARRAY_NEW_LENGTH;
+ return CXA_NONE;
+}
+
+/* Helper function for cxx_eval_cxa_builtin_fn.
+ Check if ARG is a valid first argument of __cxa_throw or
+ __cxa_free_exception or __builtin_eh_ptr_adjust_ref. Return NULL_TREE if
+ not, otherwise return the artificial __cxa_allocate_exception allocated
+ VAR_DECL. FREE_EXC is true for __cxa_free_exception, false otherwise. */
+
+static tree
+cxa_check_throw_arg (tree arg, bool free_exc)
+{
+ STRIP_NOPS (arg);
+ if (TREE_CODE (arg) != ADDR_EXPR)
+ return NULL_TREE;
+ arg = TREE_OPERAND (arg, 0);
+ if (!VAR_P (arg)
+ || !DECL_ARTIFICIAL (arg)
+ || ((!free_exc || DECL_NAME (arg) != heap_uninit_identifier)
+ && DECL_NAME (arg) != heap_identifier)
+ || !DECL_LANG_SPECIFIC (arg))
+ return NULL_TREE;
+ return arg;
+}
+
+/* Helper function for cxx_eval_cxa_builtin_fn.
+ "Allocate" on the constexpr heap an exception object of TYPE
+ with REFCOUNT. */
+
+static tree
+cxa_allocate_exception (location_t loc, const constexpr_ctx *ctx, tree type,
+ tree refcount)
+{
+ tree var = build_decl (loc, VAR_DECL, heap_uninit_identifier, type);
+ DECL_ARTIFICIAL (var) = 1;
+ retrofit_lang_decl (var);
+ DECL_EXCEPTION_REFCOUNT (var) = refcount;
+ ctx->global->heap_vars.safe_push (var);
+ return var;
+}
+
+/* Evaluate various __cxa_* calls as magic constexpr builtins for
+ C++26 constexpr exception support (P3068R5). */
+
+static tree
+cxx_eval_cxa_builtin_fn (const constexpr_ctx *ctx, tree call,
+ enum cxa_builtin kind, tree fndecl,
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
+{
+ int nargs = call_expr_nargs (call);
+ location_t loc = cp_expr_loc_or_input_loc (call);
+ tree args[4], arg;
+ if (nargs > 4)
+ {
+ invalid_nargs:
+ if (!ctx->quiet)
+ error_at (loc, "call to %qD function with incorrect"
+ "number of arguments", fndecl);
+ *non_constant_p = true;
+ return call;
+ }
+ if ((kind == CXA_BEGIN_CATCH || kind == CXA_GET_EXCEPTION_PTR)
+ && nargs == 1
+ && (arg = CALL_EXPR_ARG (call, 0))
+ && TREE_CODE (arg) == CALL_EXPR
+ && call_expr_nargs (arg) == 1
+ && integer_zerop (CALL_EXPR_ARG (arg, 0)))
+ if (tree fun = get_function_named_in_call (arg))
+ if (fndecl_built_in_p (fun, BUILT_IN_EH_POINTER))
+ {
+ if (ctx->global->caught_exceptions.length () < 2)
+ {
+ no_caught_exceptions:
+ if (!ctx->quiet)
+ error_at (loc, "%qD called with no caught exceptions pending",
+ fndecl);
+ *non_constant_p = true;
+ return call;
+ }
+ /* Both __cxa_get_exception_ptr (__builtin_eh_pointer (0))
+ and __cxa_begin_catch (__builtin_eh_pointer (0)) calls expect
+ ctx->global->caught_exceptions vector to end with
+ __cxa_allocate_exception created artificial VAR_DECL (the
+ exception object) followed by handler type, pushed by TRY_BLOCK
+ evaluation. The only difference between the functions is that
+ __cxa_begin_catch pops the handler type from the vector and keeps
+ the VAR_DECL last and decreases uncaught_exceptions. The
+ VAR_DECL after __cxa_begin_catch serves as the current exception
+ and is then popped in __cxa_end_catch evaluation. */
+ tree handler_type = ctx->global->caught_exceptions.last ();
+ if (handler_type && VAR_P (handler_type))
+ goto no_caught_exceptions;
+ unsigned idx = ctx->global->caught_exceptions.length () - 2;
+ arg = ctx->global->caught_exceptions[idx];
+ gcc_assert (VAR_P (arg));
+ if (kind == CXA_BEGIN_CATCH)
+ {
+ ctx->global->caught_exceptions.pop ();
+ --ctx->global->uncaught_exceptions;
+ }
+ if (handler_type == NULL_TREE)
+ /* Used for catch (...). Just return void. */
+ return void_node;
+ else if (POINTER_TYPE_P (handler_type))
+ {
+ /* Used for catch of a pointer. */
+ if (TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE)
+ arg = build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (arg)), arg,
+ size_zero_node, NULL_TREE, NULL_TREE);
+ arg = cp_convert (handler_type, arg,
+ ctx->quiet ? tf_none : tf_warning_or_error);
+ if (arg == error_mark_node)
+ {
+ *non_constant_p = true;
+ return call;
+ }
+ }
+ else
+ {
+ /* Used for catch of a non-pointer type. */
+ tree exc_type = strip_array_types (TREE_TYPE (arg));
+ tree exc_ptr_type = build_pointer_type (exc_type);
+ arg = build_fold_addr_expr_with_type (arg, exc_ptr_type);
+ if (CLASS_TYPE_P (handler_type))
+ {
+ tree ptr_type = build_pointer_type (handler_type);
+ arg = cp_convert (ptr_type, arg,
+ ctx->quiet ? tf_none
+ : tf_warning_or_error);
+ if (arg == error_mark_node)
+ {
+ *non_constant_p = true;
+ return call;
+ }
+ }
+ }
+ return cxx_eval_constant_expression (ctx, arg, vc_prvalue,
+ non_constant_p, overflow_p,
+ jump_target);
+ }
+ for (int i = 0; i < nargs; ++i)
+ {
+ args[i] = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (call, i),
+ vc_prvalue, non_constant_p,
+ overflow_p, jump_target);
+ if (*non_constant_p)
+ return call;
+ if (*jump_target)
+ return NULL_TREE;
+ }
+ switch (kind)
+ {
+ case CXA_ALLOCATE_EXCEPTION:
+ if (nargs != 1)
+ goto invalid_nargs;
+ if (!tree_fits_uhwi_p (args[0]))
+ {
+ if (!ctx->quiet)
+ error_at (loc, "cannot allocate exception: size not constant");
+ *non_constant_p = true;
+ return call;
+ }
+ else
+ {
+ tree type = build_array_type_nelts (char_type_node,
+ tree_to_uhwi (args[0]));
+ tree var = cxa_allocate_exception (loc, ctx, type, size_zero_node);
+ ctx->global->put_value (var, NULL_TREE);
+ return fold_convert (ptr_type_node, build_address (var));
+ }
+ case CXA_FREE_EXCEPTION:
+ if (nargs != 1)
+ goto invalid_nargs;
+ arg = cxa_check_throw_arg (args[0], true);
+ if (arg == NULL_TREE)
+ {
+ invalid_ptr:
+ if (!ctx->quiet)
+ error_at (loc, "first argument to %qD function not result of "
+ "%<__cxa_allocate_exception%>", fndecl);
+ *non_constant_p = true;
+ return call;
+ }
+ DECL_NAME (arg) = heap_deleted_identifier;
+ ctx->global->destroy_value (arg);
+ ctx->global->heap_dealloc_count++;
+ return void_node;
+ case CXA_THROW:
+ if (nargs != 3)
+ goto invalid_nargs;
+ arg = cxa_check_throw_arg (args[0], false);
+ if (arg == NULL_TREE)
+ goto invalid_ptr;
+ DECL_EXCEPTION_REFCOUNT (arg)
+ = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg),
+ size_one_node);
+ ++ctx->global->uncaught_exceptions;
+ *jump_target = arg;
+ return void_node;
+ case CXA_BEGIN_CATCH:
+ case CXA_GET_EXCEPTION_PTR:
+ goto invalid_nargs;
+ case CXA_END_CATCH:
+ if (nargs != 0)
+ goto invalid_nargs;
+ if (ctx->global->caught_exceptions.is_empty ())
+ {
+ no_active_exc:
+ if (!ctx->quiet)
+ error_at (loc, "%qD called with no caught exceptions active",
+ fndecl);
+ *non_constant_p = true;
+ return call;
+ }
+ else
+ {
+ arg = ctx->global->caught_exceptions.pop ();
+ if (arg == NULL_TREE || !VAR_P (arg))
+ goto no_active_exc;
+ free_except:
+ DECL_EXCEPTION_REFCOUNT (arg)
+ = size_binop (MINUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg),
+ size_one_node);
+ if (integer_zerop (DECL_EXCEPTION_REFCOUNT (arg)))
+ {
+ if (type_build_dtor_call (TREE_TYPE (arg)))
+ {
+ tree cleanup
+ = cxx_maybe_build_cleanup (arg, (ctx->quiet ? tf_none
+ : tf_warning_or_error));
+ if (cleanup == error_mark_node)
+ *non_constant_p = true;
+ tree jmp_target = NULL_TREE;
+ cxx_eval_constant_expression (ctx, cleanup, vc_discard,
+ non_constant_p, overflow_p,
+ &jmp_target);
+ if (throws (&jmp_target))
+ *jump_target = jmp_target;
+ }
+ DECL_NAME (arg) = heap_deleted_identifier;
+ ctx->global->destroy_value (arg);
+ ctx->global->heap_dealloc_count++;
+ }
+ }
+ return void_node;
+ case CXA_RETHROW:
+ if (nargs != 0)
+ goto invalid_nargs;
+ unsigned idx;
+ FOR_EACH_VEC_ELT_REVERSE (ctx->global->caught_exceptions, idx, arg)
+ if (arg == NULL_TREE || !VAR_P (arg))
+ --idx;
+ else
+ break;
+ if (arg == NULL_TREE)
+ {
+ if (!ctx->quiet)
+ error_at (loc, "%qD called with no caught exceptions active",
+ fndecl);
+ *non_constant_p = true;
+ return call;
+ }
+ DECL_EXCEPTION_REFCOUNT (arg)
+ = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), size_one_node);
+ ++ctx->global->uncaught_exceptions;
+ *jump_target = arg;
+ return void_node;
+ case CXA_BAD_CAST:
+ case CXA_BAD_TYPEID:
+ case CXA_THROW_BAD_ARRAY_NEW_LENGTH:
+ if (nargs != 0)
+ goto invalid_nargs;
+ else
+ {
+ tree name;
+ switch (kind)
+ {
+ case CXA_BAD_CAST:
+ name = get_identifier ("bad_cast");
+ break;
+ case CXA_BAD_TYPEID:
+ name = get_identifier ("bad_typeid");
+ break;
+ case CXA_THROW_BAD_ARRAY_NEW_LENGTH:
+ name = get_identifier ("bad_array_new_length");
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ tree decl = lookup_qualified_name (std_node, name);
+ if (TREE_CODE (decl) != TYPE_DECL
+ || !CLASS_TYPE_P (TREE_TYPE (decl))
+ || !type_build_ctor_call (TREE_TYPE (decl)))
+ {
+ if (!ctx->quiet)
+ error_at (loc, "%qD called without %<std::%D%> being defined",
+ fndecl, name);
+ *non_constant_p = true;
+ return call;
+ }
+ tree type = TREE_TYPE (decl);
+ tree var = cxa_allocate_exception (loc, ctx, type, size_one_node);
+ tree ctor
+ = build_special_member_call (var, complete_ctor_identifier,
+ NULL, type, LOOKUP_NORMAL,
+ ctx->quiet ? tf_none
+ : tf_warning_or_error);
+ if (ctor == error_mark_node)
+ {
+ *non_constant_p = true;
+ return call;
+ }
+ if (TREE_CONSTANT (ctor))
+ ctx->global->put_value (var, ctor);
+ else
+ {
+ ctx->global->put_value (var, NULL_TREE);
+ cxx_eval_constant_expression (ctx, ctor, vc_discard,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*non_constant_p)
+ return call;
+ if (throws (jump_target))
+ return NULL_TREE;
+ }
+ ++ctx->global->uncaught_exceptions;
+ *jump_target = var;
+ }
+ return void_node;
+ case STD_UNCAUGHT_EXCEPTIONS:
+ if (nargs != 0)
+ goto invalid_nargs;
+ /* Similarly to __builtin_is_constant_evaluated (), we don't
+ want to give a definite answer during mce_unknown evaluation,
+ because that might prevent evaluation later on when some
+ exceptions might be uncaught. But unlike that, we don't
+ want to constant fold it even during cp_fold, because at runtime
+ std::uncaught_exceptions () might still be non-zero. */
+ if (ctx->manifestly_const_eval != mce_true)
+ {
+ *non_constant_p = true;
+ return call;
+ }
+ return build_int_cst (integer_type_node,
+ ctx->global->uncaught_exceptions);
+ case STD_CURRENT_EXCEPTION:
+ if (nargs != 0)
+ goto invalid_nargs;
+ else
+ {
+ tree name = get_identifier ("exception_ptr");
+ tree decl = lookup_qualified_name (std_node, name);
+ tree fld;
+ if (TREE_CODE (decl) != TYPE_DECL
+ || !CLASS_TYPE_P (TREE_TYPE (decl))
+ || !COMPLETE_TYPE_P (TREE_TYPE (decl))
+ || !(fld = next_aggregate_field (TYPE_FIELDS (TREE_TYPE (decl))))
+ || DECL_ARTIFICIAL (fld)
+ || TREE_CODE (TREE_TYPE (fld)) != POINTER_TYPE
+ || next_aggregate_field (DECL_CHAIN (fld))
+ || !tree_int_cst_equal (TYPE_SIZE (TREE_TYPE (decl)),
+ TYPE_SIZE (TREE_TYPE (fld))))
+ {
+ if (!ctx->quiet)
+ error_at (loc, "%qD called without supportable %qs",
+ fndecl, "std::exception_ptr");
+ *non_constant_p = true;
+ return call;
+ }
+ FOR_EACH_VEC_ELT_REVERSE (ctx->global->caught_exceptions, idx, arg)
+ if (arg == NULL_TREE || !VAR_P (arg))
+ --idx;
+ else
+ break;
+ /* Similarly to __builtin_is_constant_evaluated (), we don't
+ want to give a definite answer during mce_unknown evaluation,
+ because that might prevent evaluation later on when some
+ exceptions might be current. But unlike that, we don't
+ want to constant fold it to null even during cp_fold, because
+ at runtime std::current_exception () might still be non-null. */
+ if (ctx->manifestly_const_eval != mce_true && arg == NULL_TREE)
+ {
+ *non_constant_p = true;
+ return call;
+ }
+ if (arg == NULL_TREE)
+ arg = build_zero_cst (TREE_TYPE (fld));
+ else
+ {
+ DECL_EXCEPTION_REFCOUNT (arg)
+ = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg),
+ size_one_node);
+ arg = fold_convert (ptr_type_node, build_address (arg));
+ }
+ return build_constructor_single (TREE_TYPE (decl), fld, arg);
+ }
+ case STD_RETHROW_EXCEPTION:
+ if (nargs != 1)
+ goto invalid_nargs;
+ if (TYPE_REF_P (TREE_TYPE (args[0])))
+ {
+ arg = args[0];
+ STRIP_NOPS (arg);
+ if (TREE_CODE (arg) == ADDR_EXPR)
+ {
+ args[0]
+ = cxx_eval_constant_expression (ctx, TREE_OPERAND (arg, 0),
+ vc_prvalue, non_constant_p,
+ overflow_p, jump_target);
+ if (*non_constant_p)
+ return call;
+ if (*jump_target)
+ return NULL_TREE;
+ }
+ }
+ if (TREE_CODE (args[0]) != CONSTRUCTOR
+ || CONSTRUCTOR_NELTS (args[0]) != 1)
+ {
+ invalid_std_rethrow:
+ if (!ctx->quiet)
+ error_at (loc, "%qD called with unexpected %qs argument",
+ fndecl, "std::exception_ptr");
+ *non_constant_p = true;
+ return void_node;
+ }
+ arg = cxa_check_throw_arg (CONSTRUCTOR_ELT (args[0], 0)->value, false);
+ if (arg == NULL_TREE)
+ goto invalid_std_rethrow;
+ DECL_EXCEPTION_REFCOUNT (arg)
+ = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), size_one_node);
+ ++ctx->global->uncaught_exceptions;
+ *jump_target = arg;
+ return void_node;
+ case BUILTIN_EH_PTR_ADJUST_REF:
+ if (nargs != 2)
+ goto invalid_nargs;
+ arg = cxa_check_throw_arg (args[0], false);
+ if (arg == NULL_TREE)
+ goto invalid_ptr;
+ if (integer_onep (args[1]))
+ DECL_EXCEPTION_REFCOUNT (arg)
+ = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg),
+ size_one_node);
+ else if (integer_minus_onep (args[1]))
+ goto free_except;
+ else
+ {
+ if (!ctx->quiet)
+ error_at (loc, "%qD called with second argument "
+ "other than 1 or -1", fndecl);
+ *non_constant_p = true;
+ }
+ return void_node;
+ default:
+ gcc_unreachable ();
+ }
+}
/* Attempt to evaluate T which represents a call to a builtin function.
We assume here that all builtin functions evaluate to scalar types
@@ -1530,7 +2265,8 @@ static tree find_deleted_heap_var (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));
@@ -1576,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))
@@ -1650,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)
@@ -1740,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
@@ -1854,20 +2603,29 @@ 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,
- bool *non_constant_p, bool *overflow_p,
- bool *non_constant_args)
+ tree orig_fun, bool *non_constant_p,
+ bool *overflow_p, bool *non_constant_args,
+ tree *jump_target)
{
- const int nargs = call_expr_nargs (t);
+ int nargs = call_expr_nargs (t);
tree parms = DECL_ARGUMENTS (fun);
- int i;
+ int i, j = 0;
+ if (DECL_HAS_IN_CHARGE_PARM_P (fun) && fun != orig_fun)
+ ++nargs;
+ if (DECL_HAS_VTT_PARM_P (fun)
+ && fun != orig_fun
+ && (DECL_COMPLETE_CONSTRUCTOR_P (orig_fun)
+ || DECL_COMPLETE_DESTRUCTOR_P (orig_fun)))
+ ++nargs;
/* We don't record ellipsis args below. */
int nparms = list_length (parms);
int nbinds = nargs < nparms ? nargs : nparms;
tree binds = make_tree_vec (nbinds);
/* The call is not a constant expression if it involves the cdtor for a type
- with virtual bases. */
- if (DECL_HAS_IN_CHARGE_PARM_P (fun) || DECL_HAS_VTT_PARM_P (fun))
+ with virtual bases before C++26. */
+ if (cxx_dialect < cxx26
+ && (DECL_HAS_IN_CHARGE_PARM_P (fun) || DECL_HAS_VTT_PARM_P (fun)))
{
if (!ctx->quiet)
{
@@ -1885,7 +2643,30 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun,
tree type = parms ? TREE_TYPE (parms) : void_type_node;
if (parms && DECL_BY_REFERENCE (parms))
type = TREE_TYPE (type);
- x = get_nth_callarg (t, i);
+ if (i == 1
+ && j == 0
+ && DECL_HAS_IN_CHARGE_PARM_P (fun)
+ && orig_fun != fun)
+ {
+ if (DECL_COMPLETE_CONSTRUCTOR_P (orig_fun)
+ || DECL_COMPLETE_DESTRUCTOR_P (orig_fun))
+ x = boolean_true_node;
+ else
+ x = boolean_false_node;
+ j = -1;
+ }
+ else if (i == 2
+ && j == -1
+ && DECL_HAS_VTT_PARM_P (fun)
+ && orig_fun != fun
+ && (DECL_COMPLETE_CONSTRUCTOR_P (orig_fun)
+ || DECL_COMPLETE_DESTRUCTOR_P (orig_fun)))
+ {
+ x = build_zero_cst (type);
+ j = -2;
+ }
+ else
+ x = get_nth_callarg (t, i + j);
/* For member function, the first argument is a pointer to the implied
object. For a constructor, it might still be a dummy object, in
which case we get the real argument from ctx. */
@@ -1900,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)
@@ -1925,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;
@@ -2036,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);
@@ -2125,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))
@@ -2138,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)
@@ -2175,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;
@@ -2208,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;
@@ -2232,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);
@@ -2504,14 +3299,12 @@ get_component_with_type (tree path, tree type, tree stop)
dst_ptr + src2dst == src_ptr
-1: unspecified relationship
-2: src_type is not a public base of dst_type
- -3: src_type is a multiple public non-virtual base of dst_type
-
- Since literal types can't have virtual bases, we only expect hint >=0,
- -2, or -3. */
+ -3: src_type is a multiple public non-virtual base of dst_type */
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)
@@ -2530,19 +3323,42 @@ 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
+ d.D.1234 using cxx_fold_indirect_ref. */
+ if (cxx_dialect >= cxx26 && CONVERT_EXPR_P (obj))
+ {
+ tree objo = obj;
+ STRIP_NOPS (objo);
+ if (TREE_CODE (objo) == POINTER_PLUS_EXPR)
+ {
+ objo = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (TREE_TYPE (obj)),
+ obj, NULL, jump_target);
+ if (objo)
+ obj = build_fold_addr_expr (objo);
+ }
+ }
/* We expect OBJ to be in form of &d.D.2102 when HINT == 0,
but when HINT is > 0, it can also be something like
@@ -2554,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)
{
@@ -2576,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))
@@ -2610,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)
{
@@ -2643,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)
{
@@ -2665,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)
{
@@ -2761,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);
@@ -2804,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
@@ -2814,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)))
@@ -2822,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;
}
}
@@ -2835,23 +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);
- constexpr_call new_call
- = { NULL, NULL, NULL, 0, ctx->manifestly_const_eval };
- int depth_ok;
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);
@@ -2894,6 +3719,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
*non_constant_p = true;
return t;
}
+ tree orig_fun = fun;
if (DECL_CLONED_FUNCTION_P (fun) && !DECL_DELETING_DESTRUCTOR_P (fun))
fun = DECL_CLONED_FUNCTION (fun);
@@ -2902,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
@@ -2919,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)
@@ -2930,12 +3762,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
gcc_assert (arg0);
if (new_op_p)
{
- /* FIXME: We should not get here; the VERIFY_CONSTANT above
- should have already caught it. But currently a conversion
- from pointer type to arithmetic type is only considered
- non-constant for CONVERT_EXPRs, not NOP_EXPRs. */
if (!tree_fits_uhwi_p (arg0))
{
+ /* We should not get here; the VERIFY_CONSTANT above
+ should have already caught it. */
+ gcc_checking_assert (false);
if (!ctx->quiet)
error_at (loc, "cannot allocate array: size not constant");
*non_constant_p = true;
@@ -2950,14 +3781,6 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
: heap_uninit_identifier,
type);
DECL_ARTIFICIAL (var) = 1;
- TREE_STATIC (var) = 1;
- // Temporarily register the artificial var in varpool,
- // so that comparisons of its address against NULL are folded
- // through nonzero_address even with
- // -fno-delete-null-pointer-checks or that comparison of
- // addresses of different heap artificial vars is folded too.
- // See PR98988 and PR99031.
- varpool_node::finalize_decl (var);
ctx->global->heap_vars.safe_push (var);
ctx->global->put_value (var, NULL_TREE);
return fold_convert (ptr_type_node, build_address (var));
@@ -3043,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
@@ -3053,19 +3879,35 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
return arg1;
}
else if (cxx_dynamic_cast_fn_p (fun))
- return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p);
+ return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p,
+ jump_target);
+ else if (enum cxa_builtin kind = cxx_cxa_builtin_fn_p (fun))
+ return cxx_eval_cxa_builtin_fn (ctx, t, kind, fun,
+ non_constant_p, overflow_p,
+ jump_target);
- if (!ctx->quiet)
+ /* Calls to non-constexpr functions can be diagnosed right away
+ before C++26, though in C++26 evaluation of the arguments might
+ throw and if caught it could be still constant expression.
+ So for C++26 this is diagnosed only after
+ cxx_bind_parameters_in_call. */
+ if (cxx_dialect >= cxx26)
+ non_constexpr_call = true;
+ else
{
- if (!lambda_static_thunk_p (fun))
- error_at (loc, "call to non-%<constexpr%> function %qD", fun);
- explain_invalid_constexpr_fn (fun);
+ if (!ctx->quiet)
+ {
+ if (!lambda_static_thunk_p (fun))
+ error_at (loc, "call to non-%<constexpr%> function %qD", fun);
+ explain_invalid_constexpr_fn (fun);
+ }
+ *non_constant_p = true;
+ return t;
}
- *non_constant_p = true;
- return t;
}
constexpr_ctx new_ctx = *ctx;
+ ctx = &new_ctx;
if (DECL_CONSTRUCTOR_P (fun) && !ctx->object
&& TREE_CODE (t) == AGGR_INIT_EXPR)
{
@@ -3075,30 +3917,30 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
tree ctor = new_ctx.ctor = build_constructor (DECL_CONTEXT (fun), NULL);
CONSTRUCTOR_NO_CLEARING (ctor) = true;
ctx->global->put_value (new_ctx.object, ctor);
- ctx = &new_ctx;
}
/* An immediate invocation is manifestly constant evaluated including the
arguments of the call, so use mce_true even for the argument
evaluation. */
if (DECL_IMMEDIATE_FUNCTION_P (fun))
- {
- new_ctx.manifestly_const_eval = mce_true;
- new_call.manifestly_const_eval = mce_true;
- ctx = &new_ctx;
- }
+ new_ctx.manifestly_const_eval = mce_true;
/* We used to shortcut trivial constructor/op= here, but nowadays
we can only get a trivial function here with -fno-elide-constructors. */
gcc_checking_assert (!trivial_fn_p (fun)
|| !flag_elide_constructors
+ /* Or it's a call from maybe_thunk_body (111075). */
+ || (TREE_CODE (t) == CALL_EXPR ? CALL_FROM_THUNK_P (t)
+ : AGGR_INIT_FROM_THUNK_P (t))
/* We don't elide constructors when processing
a noexcept-expression. */
|| cp_noexcept_operand);
bool non_constant_args = false;
+ constexpr_call new_call;
new_call.bindings
- = cxx_bind_parameters_in_call (ctx, t, fun, non_constant_p,
- overflow_p, &non_constant_args);
+ = cxx_bind_parameters_in_call (ctx, t, fun, orig_fun, non_constant_p,
+ 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
@@ -3112,8 +3954,21 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
void preserve () { bindings = NULL; }
} fb (new_call.bindings);
+ if (*jump_target)
+ return NULL_TREE;
if (*non_constant_p)
return t;
+ if (non_constexpr_call)
+ {
+ if (!ctx->quiet)
+ {
+ if (!lambda_static_thunk_p (fun))
+ error_at (loc, "call to non-%<constexpr%> function %qD", fun);
+ explain_invalid_constexpr_fn (fun);
+ }
+ *non_constant_p = true;
+ return t;
+ }
/* We can't defer instantiating the function any longer. */
if (!DECL_INITIAL (fun)
@@ -3169,7 +4024,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
}
}
- depth_ok = push_cx_call_context (t);
+ /* Don't complain about problems evaluating an ill-formed function. */
+ if (function *f = DECL_STRUCT_FUNCTION (fun))
+ if (f->language->erroneous)
+ new_ctx.quiet = true;
+
+ int depth_ok = push_cx_call_context (t);
/* Remember the object we are constructing or destructing. */
tree new_obj = NULL_TREE;
@@ -3181,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. */
@@ -3211,8 +4073,6 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
new_call.hash = constexpr_fundef_hasher::hash (new_call.fundef);
new_call.hash
= iterative_hash_template_arg (new_call.bindings, new_call.hash);
- new_call.hash
- = iterative_hash_object (ctx->manifestly_const_eval, new_call.hash);
/* If we have seen this call before, we are done. */
maybe_initialize_constexpr_call_table ();
@@ -3230,22 +4090,23 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
the slot can move during evaluation of the body. */
*slot = entry = ggc_alloc<constexpr_call> ();
*entry = new_call;
+ entry->result (ctx->manifestly_const_eval) = unknown_type_node;
fb.preserve ();
}
}
- /* Calls that are in progress have their result set to NULL, so that we
- can detect circular dependencies. Now that we only cache up to
- constexpr_cache_depth this won't catch circular dependencies that
+ /* Calls that are in progress have their result set to unknown_type_node,
+ so that we can detect circular dependencies. Now that we only cache
+ up to constexpr_cache_depth this won't catch circular dependencies that
start deeper, but they'll hit the recursion or ops limit. */
- else if (entry->result == NULL)
+ else if (entry->result (ctx->manifestly_const_eval) == unknown_type_node)
{
if (!ctx->quiet)
error ("call has circular dependency");
*non_constant_p = true;
- entry->result = result = error_mark_node;
+ entry->result (ctx->manifestly_const_eval) = result = error_mark_node;
}
else
- result = entry->result;
+ result = entry->result (ctx->manifestly_const_eval);
}
if (!depth_ok)
@@ -3331,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. */
@@ -3345,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
@@ -3382,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);
@@ -3425,11 +4295,6 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
cacheable = false;
break;
}
- /* And don't cache a ref to a deleted heap variable (119162). */
- if (cacheable
- && (cp_walk_tree_without_duplicates
- (&result, find_deleted_heap_var, NULL)))
- cacheable = false;
}
/* Rewrite all occurrences of the function's RESULT_DECL with the
@@ -3447,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;
+ }
}
}
@@ -3466,7 +4337,22 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
else if (!result)
result = void_node;
if (entry)
- entry->result = cacheable ? result : error_mark_node;
+ {
+ entry->result (ctx->manifestly_const_eval)
+ = cacheable ? result : error_mark_node;
+
+ if (result != error_mark_node
+ && ctx->manifestly_const_eval == mce_unknown)
+ {
+ /* Evaluation succeeded and was independent of whether we're in a
+ manifestly constant-evaluated context, so we can also reuse
+ this result when evaluating this call with a fixed context. */
+ if (!entry->result (mce_true))
+ entry->result (mce_true) = entry->result (mce_unknown);
+ if (!entry->result (mce_false))
+ entry->result (mce_false) = entry->result (mce_unknown);
+ }
+ }
}
/* The result of a constexpr function must be completely initialized.
@@ -3487,11 +4373,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
/* Return true if T is a valid constant initializer. If a CONSTRUCTOR
initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be
- cleared.
+ cleared. If called recursively on a FIELD_DECL's CONSTRUCTOR, SZ
+ is DECL_SIZE of the FIELD_DECL, otherwise NULL.
FIXME speed this up, it's taking 16% of compile time on sieve testcase. */
bool
-reduced_constant_expression_p (tree t)
+reduced_constant_expression_p (tree t, tree sz /* = NULL_TREE */)
{
if (t == NULL_TREE)
return false;
@@ -3502,6 +4389,9 @@ reduced_constant_expression_p (tree t)
/* Even if we can't lower this yet, it's constant. */
return true;
+ case OMP_DECLARE_MAPPER:
+ return true;
+
case CONSTRUCTOR:
/* And we need to handle PTRMEM_CST wrapped in a CONSTRUCTOR. */
tree field;
@@ -3556,7 +4446,12 @@ reduced_constant_expression_p (tree t)
{
/* If VAL is null, we're in the middle of initializing this
element. */
- if (!reduced_constant_expression_p (e.value))
+ if (!reduced_constant_expression_p (e.value,
+ (e.index
+ && (TREE_CODE (e.index)
+ == FIELD_DECL))
+ ? DECL_SIZE (e.index)
+ : NULL_TREE))
return false;
/* We want to remove initializers for empty fields in a struct to
avoid confusing output_constructor. */
@@ -3576,7 +4471,16 @@ reduced_constant_expression_p (tree t)
/* There could be a non-empty field at the end. */
for (; field; field = next_subobject_field (DECL_CHAIN (field)))
if (!is_really_empty_class (TREE_TYPE (field), /*ignore_vptr*/false))
- return false;
+ {
+ /* Ignore FIELD_DECLs with bit positions beyond DECL_SIZE of
+ the parent FIELD_DECL (if any) for classes with virtual
+ bases. */
+ if (cxx_dialect >= cxx26
+ && sz
+ && tree_int_cst_le (sz, bit_position (field)))
+ break;
+ return false;
+ }
ok:
if (CONSTRUCTOR_NO_CLEARING (t))
/* All the fields are initialized. */
@@ -3772,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);
@@ -3801,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)
@@ -3823,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;
@@ -3845,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;
@@ -3889,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);
@@ -3960,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)
@@ -4025,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))
{
@@ -4086,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);
@@ -4486,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)
@@ -4503,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;
}
@@ -4534,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)
@@ -4552,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)
@@ -4572,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)))
@@ -4589,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;
@@ -4709,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;
@@ -4721,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;
@@ -4730,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)))
{
@@ -4841,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.
@@ -4851,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;
@@ -4859,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
@@ -5143,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))
@@ -5157,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)
@@ -5237,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;
}
}
@@ -5279,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;
}
@@ -5448,7 +6417,8 @@ verify_ctor_sanity (const constexpr_ctx *ctx, tree type)
static tree
cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
value_cat lval,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (t);
bool changed = false;
@@ -5501,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;
@@ -5591,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);
@@ -5602,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
@@ -5639,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)
{
@@ -5665,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)
{
@@ -5681,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
@@ -5697,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)
@@ -5748,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);
@@ -5780,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
@@ -5812,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
@@ -5832,12 +6814,28 @@ 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;
if (off == 0 && similar_type_p (optype, type))
return op;
+ else if (cxx_dialect >= cxx26
+ && VAR_P (op)
+ && DECL_VTABLE_OR_VTT_P (op)
+ && same_type_ignoring_top_level_qualifiers_p (type,
+ ptrdiff_type_node)
+ && POINTER_TYPE_P (strip_array_types (optype)))
+ {
+ /* We often read some virtual table elements using ptrdiff_t rather
+ than pointer type. */
+ if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc,
+ strip_array_types (optype),
+ op, off, empty_base,
+ jump_target))
+ return fold_convert (type, ret);
+ }
else if (TREE_CODE (optype) == COMPLEX_TYPE
&& similar_type_p (type, TREE_TYPE (optype)))
{
@@ -5881,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 */
@@ -5890,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)));
@@ -5899,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;
}
}
@@ -5931,15 +6930,21 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type,
if (!tree_fits_uhwi_p (pos))
continue;
unsigned HOST_WIDE_INT upos = tree_to_uhwi (pos);
- unsigned HOST_WIDE_INT el_sz
- = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
+ unsigned HOST_WIDE_INT el_sz;
+ if (DECL_FIELD_IS_BASE (field)
+ && CLASS_TYPE_P (optype)
+ && CLASSTYPE_VBASECLASSES (optype))
+ el_sz = tree_to_uhwi (DECL_SIZE_UNIT (field));
+ else
+ el_sz = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
if (upos <= off && off < upos + el_sz)
{
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 - upos,
- empty_base))
+ empty_base,
+ jump_target))
return ret;
}
}
@@ -5959,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;
@@ -5983,6 +6988,25 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type,
offset positive, so that cxx_fold_indirect_ref_1 can identify
more folding opportunities. */
auto canonicalize_obj_off = [] (tree& obj, tree& off) {
+ if (cxx_dialect >= cxx26)
+ {
+ /* For C++26, we need to fold *(B *)(&x.D.1234 + 32) used
+ to access virtual base members. */
+ tree nobj = obj;
+ while (TREE_CODE (nobj) == COMPONENT_REF
+ && DECL_FIELD_IS_BASE (TREE_OPERAND (nobj, 1)))
+ nobj = TREE_OPERAND (nobj, 0);
+ if (nobj != obj
+ && CLASS_TYPE_P (TREE_TYPE (nobj))
+ && CLASSTYPE_VBASECLASSES (TREE_TYPE (nobj)))
+ while (obj != nobj)
+ {
+ tree field = TREE_OPERAND (obj, 1);
+ tree pos = byte_position (field);
+ off = int_const_binop (PLUS_EXPR, off, pos);
+ obj = TREE_OPERAND (obj, 0);
+ }
+ }
while (TREE_CODE (obj) == COMPONENT_REF
/* We need to preserve union member accesses so that we can
later properly diagnose accessing the wrong member. */
@@ -6021,8 +7045,9 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type,
{
tree off = integer_zero_node;
canonicalize_obj_off (op, off);
- gcc_assert (integer_zerop (off));
- return cxx_fold_indirect_ref_1 (ctx, loc, type, op, 0, empty_base);
+ return cxx_fold_indirect_ref_1 (ctx, loc, type, op,
+ tree_to_uhwi (off), empty_base,
+ jump_target);
}
}
else if (TREE_CODE (sub) == POINTER_PLUS_EXPR
@@ -6037,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] */
@@ -6047,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
@@ -6065,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;
@@ -6083,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;
@@ -6103,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
@@ -6133,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;
@@ -6243,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];
@@ -6253,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]);
}
@@ -6375,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;
@@ -6383,7 +7427,8 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
if (TREE_CLOBBER_P (init)
&& CLOBBER_KIND (init) < CLOBBER_OBJECT_END)
- /* Only handle clobbers ending the lifetime of objects. */
+ /* Only handle clobbers ending the lifetime of objects.
+ ??? We should probably set CONSTRUCTOR_NO_CLEARING. */
return void_node;
/* First we figure out where we're storing to. */
@@ -6400,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;
}
@@ -6412,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;
}
@@ -6439,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;
}
@@ -6496,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;
}
@@ -6841,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. */
@@ -6967,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);
@@ -6981,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));
@@ -7025,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;
@@ -7040,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. */
@@ -7123,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 ();
}
@@ -7137,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;
@@ -7173,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;
}
@@ -7196,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;
@@ -7258,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);
@@ -7353,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;
@@ -7400,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 ();
@@ -7418,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)
{
@@ -7551,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));
@@ -7587,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;
@@ -7614,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 */
@@ -7623,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. */
@@ -7749,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:
@@ -7792,7 +8853,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
r = TARGET_EXPR_INITIAL (r);
if (DECL_P (r)
/* P2280 allows references to unknown. */
- && !(VAR_P (t) && TYPE_REF_P (TREE_TYPE (t))))
+ && !(p2280_active_p (ctx) && VAR_P (t) && TYPE_REF_P (TREE_TYPE (t))))
{
if (!ctx->quiet)
non_const_var_error (loc, r, /*fundef_p*/false);
@@ -7811,6 +8872,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case LABEL_EXPR:
case CASE_LABEL_EXPR:
case PREDICT_EXPR:
+ case OMP_DECLARE_MAPPER:
return t;
case PARM_DECL:
@@ -7826,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. */;
@@ -7844,9 +8909,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
r = build_constructor (TREE_TYPE (t), NULL);
TREE_CONSTANT (r) = true;
}
- else if (TYPE_REF_P (TREE_TYPE (t)))
+ else if (p2280_active_p (ctx) && TYPE_REF_P (TREE_TYPE (t)))
/* P2280 allows references to unknown... */;
- else if (is_this_parameter (t))
+ else if (p2280_active_p (ctx) && is_this_parameter (t))
/* ...as well as the this pointer. */;
else
{
@@ -7859,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:
@@ -7923,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
@@ -7982,14 +9050,23 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
ctx->global->put_value (new_ctx.object, new_ctx.ctor);
ctx = &new_ctx;
}
+
+ /* If the initializer is complex, evaluate it to initialize slot. */
+ bool is_complex = target_expr_needs_replace (t);
+ if (is_complex)
+ /* In case no initialization actually happens, clear out any
+ void_node from a previous evaluation. */
+ ctx->global->put_value (slot, NULL_TREE);
+
/* 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 the initializer is complex, evaluate it to initialize slot. */
- bool is_complex = target_expr_needs_replace (t);
+ if (*jump_target)
+ return NULL_TREE;
if (!is_complex)
{
r = unshare_constructor (r);
@@ -7997,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)
@@ -8012,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:
@@ -8047,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)
@@ -8057,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),
@@ -8075,6 +9146,42 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
jump_target);
break;
+ case TRY_BLOCK:
+ r = cxx_eval_constant_expression (ctx, TRY_STMTS (t), lval,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (!*non_constant_p && throws (jump_target))
+ if (tree h = TRY_HANDLERS (t))
+ {
+ tree type = strip_array_types (TREE_TYPE (*jump_target));
+ if (TREE_CODE (h) == STATEMENT_LIST)
+ {
+ for (tree stmt : tsi_range (h))
+ if (TREE_CODE (stmt) == HANDLER
+ && handler_match_for_exception_type (stmt, type))
+ {
+ h = stmt;
+ break;
+ }
+ if (TREE_CODE (h) == STATEMENT_LIST)
+ h = NULL_TREE;
+ }
+ else if (TREE_CODE (h) != HANDLER
+ || !handler_match_for_exception_type (h, type))
+ h = NULL_TREE;
+ if (h)
+ {
+ gcc_assert (VAR_P (*jump_target));
+ ctx->global->caught_exceptions.safe_push (*jump_target);
+ ctx->global->caught_exceptions.safe_push (HANDLER_TYPE (h));
+ *jump_target = NULL_TREE;
+ r = cxx_eval_constant_expression (ctx, HANDLER_BODY (h),
+ vc_discard, non_constant_p,
+ overflow_p, jump_target);
+ }
+ }
+ break;
+
case CLEANUP_POINT_EXPR:
{
auto_vec<tree, 2> cleanups;
@@ -8092,47 +9199,132 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
ctx->global->cleanups = prev_cleanups;
unsigned int i;
- tree cleanup;
+ tree cleanup, jmp_target = NULL_TREE;
+ bool eh = throws (jump_target);
/* Evaluate the cleanups. */
FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup)
- cxx_eval_constant_expression (&new_ctx, cleanup, vc_discard,
- non_constant_p, overflow_p);
+ if (cleanup == NULL_TREE)
+ {
+ /* NULL_TREE cleanup is a marker that before it is
+ CLEANUP_EH_ONLY cleanup. Skip the cleanup before it
+ if the body didn't throw. */
+ if (!eh)
+ --i;
+ }
+ else
+ cxx_eval_constant_expression (&new_ctx, cleanup, vc_discard,
+ non_constant_p, overflow_p,
+ &jmp_target);
/* Forget SAVE_EXPRs and TARGET_EXPRs created by this
full-expression. */
for (tree save_expr : save_exprs)
destroy_value_checked (ctx, save_expr, non_constant_p);
+ if (throws (&jmp_target))
+ *jump_target = jmp_target;
}
break;
+ case MUST_NOT_THROW_EXPR:
+ r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
+ lval,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (throws (jump_target))
+ {
+ /* [except.handle]/7 - If the search for a handler exits the
+ function body of a function with a non-throwing exception
+ specification, the function std::terminate is invoked. */
+ if (!ctx->quiet)
+ {
+ auto_diagnostic_group d;
+ diagnose_std_terminate (loc, ctx, *jump_target);
+ if (MUST_NOT_THROW_NOEXCEPT_P (t)
+ && ctx->call
+ && ctx->call->fundef)
+ inform (loc, "uncaught exception exited from %<noexcept%> "
+ "function %qD",
+ ctx->call->fundef->decl);
+ else if (MUST_NOT_THROW_THROW_P (t))
+ inform (loc, "destructor exited with an exception after "
+ "initializing the exception object");
+ else if (MUST_NOT_THROW_CATCH_P (t))
+ inform (loc, "constructor exited with another exception while "
+ "entering handler");
+ }
+ *non_constant_p = true;
+ *jump_target = NULL_TREE;
+ r = NULL_TREE;
+ }
+ break;
+
+ case TRY_CATCH_EXPR:
+ if (TREE_OPERAND (t, 0) == NULL_TREE)
+ {
+ r = void_node;
+ break;
+ }
+ r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (!*non_constant_p && throws (jump_target))
+ {
+ tree jmp_target = NULL_TREE;
+ cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard,
+ non_constant_p, overflow_p,
+ &jmp_target);
+ r = merge_jump_target (loc, ctx, r, non_constant_p, jump_target,
+ jmp_target);
+ }
+ break;
+
case TRY_FINALLY_EXPR:
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
non_constant_p, overflow_p,
jump_target);
if (!*non_constant_p)
- /* Also evaluate the cleanup. */
- cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard,
- non_constant_p, overflow_p);
+ {
+ tree jmp_target = NULL_TREE;
+ /* Also evaluate the cleanup. */
+ if (TREE_CODE (TREE_OPERAND (t, 1)) == EH_ELSE_EXPR
+ && throws (jump_target))
+ cxx_eval_constant_expression (ctx,
+ TREE_OPERAND (TREE_OPERAND (t, 1),
+ 1), vc_discard,
+ non_constant_p, overflow_p,
+ &jmp_target);
+ else
+ cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard,
+ non_constant_p, overflow_p,
+ &jmp_target);
+ r = merge_jump_target (loc, ctx, r, non_constant_p, jump_target,
+ jmp_target);
+ }
break;
case EH_ELSE_EXPR:
/* Evaluate any cleanup that applies to non-EH exits. */
cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_discard,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
- /* We do not have constexpr exceptions yet, so skip the EH path. */
+ /* The EH path is handled in TRY_FINALLY_EXPR handling above. */
break;
case CLEANUP_STMT:
r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
non_constant_p, overflow_p,
jump_target);
- if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
+ if ((!CLEANUP_EH_ONLY (t) || throws (jump_target)) && !*non_constant_p)
{
iloc_sentinel ils (loc);
+ tree jmp_target = NULL_TREE;
/* Also evaluate the cleanup. */
cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), vc_discard,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ &jmp_target);
+ r = merge_jump_target (loc, ctx, r, non_constant_p, jump_target,
+ jmp_target);
}
break;
@@ -8142,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;
@@ -8169,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)
@@ -8190,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:
@@ -8228,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);
@@ -8280,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
@@ -8289,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:
@@ -8316,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))
@@ -8364,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:
@@ -8376,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:
@@ -8386,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:
@@ -8399,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:
@@ -8423,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);
@@ -8434,7 +9647,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
if (TREE_CODE (t) == CONVERT_EXPR
&& ARITHMETIC_TYPE_P (type)
&& INDIRECT_TYPE_P (TREE_TYPE (op))
- && ctx->manifestly_const_eval == mce_true)
+ && ctx->strict)
{
if (!ctx->quiet)
error_at (loc,
@@ -8480,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);
@@ -8586,7 +9802,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
tree cookie_size = NULL_TREE;
tree arg_size = NULL_TREE;
if (TREE_CODE (elt_type) == RECORD_TYPE
- && TYPE_NAME (elt_type) == heap_identifier)
+ && TYPE_IDENTIFIER (elt_type) == heap_identifier)
{
tree fld1 = TYPE_FIELDS (elt_type);
tree fld2 = DECL_CHAIN (fld1);
@@ -8607,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));
}
@@ -8649,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);
@@ -8686,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:
@@ -8707,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. */
@@ -8717,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;
@@ -8730,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;
@@ -8842,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:
@@ -8981,20 +10215,6 @@ find_heap_var_refs (tree *tp, int *walk_subtrees, void */*data*/)
return NULL_TREE;
}
-/* Look for deleted heap variables in the expression *TP. */
-
-static tree
-find_deleted_heap_var (tree *tp, int *walk_subtrees, void */*data*/)
-{
- if (VAR_P (*tp)
- && DECL_NAME (*tp) == heap_deleted_identifier)
- return *tp;
-
- if (TYPE_P (*tp))
- *walk_subtrees = 0;
- return NULL_TREE;
-}
-
/* Find immediate function decls in *TP if any. */
static tree
@@ -9100,6 +10320,15 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
tree fndecl = cp_get_callee_fndecl_nofold (x);
if (fndecl && DECL_IMMEDIATE_FUNCTION_P (fndecl))
is_consteval = true;
+ /* Don't try to evaluate a std::vector constructor taking an integer, it
+ will fail in the 'if (heap_var)' block below after doing all the work
+ (c++/113835). This will need adjustment if P3554 is accepted. Note
+ that evaluation of e.g. the vector default constructor can succeed, so
+ we don't shortcut all vector constructors. */
+ if (fndecl && DECL_CONSTRUCTOR_P (fndecl) && allow_non_constant
+ && is_std_class (type, "vector") && call_expr_nargs (x) > 1
+ && TREE_CODE (TREE_TYPE (get_nth_callarg (x, 1))) == INTEGER_TYPE)
+ return t;
}
if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
{
@@ -9166,30 +10395,67 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
if (manifestly_const_eval == mce_true)
instantiate_constexpr_fns (r);
+ tree jmp_target = NULL_TREE;
r = cxx_eval_constant_expression (&ctx, r, vc_prvalue,
- &non_constant_p, &overflow_p);
+ &non_constant_p, &overflow_p,
+ &jmp_target);
+ if (throws (&jmp_target) && !non_constant_p)
+ {
+ if (!ctx.quiet)
+ diagnose_uncaught_exception (input_location, &ctx, jmp_target);
+ non_constant_p = true;
+ jmp_target = NULL_TREE;
+ r = t;
+ }
+ else if (!non_constant_p && jmp_target)
+ {
+ non_constant_p = true;
+ if (!ctx.quiet)
+ {
+ if (breaks (&jmp_target))
+ error ("%<break%> outside of a loop or %<switch%>");
+ else if (continues (&jmp_target))
+ error ("%<continue%> outside of a loop");
+ else if (returns (&jmp_target))
+ error ("%<return%> in a statement expression");
+ else
+ gcc_unreachable ();
+ }
+ r = t;
+ }
/* If we got a non-simple TARGET_EXPR, the initializer was a sequence
of statements, and the result ought to be stored in ctx.ctor. */
if (r == void_node && !constexpr_dtor && ctx.ctor)
r = ctx.ctor;
- if (!constexpr_dtor)
- verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
- else
- DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true;
-
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 "
@@ -9197,15 +10463,6 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
non_constant_p = true;
}
- if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_NO_CLEARING (r))
- {
- if (!allow_non_constant)
- error ("%qE is not a constant expression because it refers to "
- "an incompletely initialized variable", t);
- TREE_CONSTANT (r) = false;
- non_constant_p = true;
- }
-
if (!non_constant_p && cxx_dialect >= cxx20
&& !global_ctx.heap_vars.is_empty ())
{
@@ -9215,9 +10472,16 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
if (heap_var)
{
if (!allow_non_constant && !non_constant_p)
- error_at (DECL_SOURCE_LOCATION (heap_var),
- "%qE is not a constant expression because it refers to "
- "a result of %<operator new%>", t);
+ {
+ if (DECL_LANG_SPECIFIC (heap_var))
+ error ("%qE is not a constant expression because it refers to "
+ "exception object allocated with "
+ "%<__cxa_allocate_exception%>", t);
+ else
+ error ("%qE is not a constant expression because it refers to "
+ "a result of %<operator new%>", t);
+ inform (DECL_SOURCE_LOCATION (heap_var), "allocated here");
+ }
r = t;
non_constant_p = true;
}
@@ -9226,13 +10490,14 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
if (DECL_NAME (heap_var) != heap_deleted_identifier)
{
if (!allow_non_constant && !non_constant_p)
- error_at (DECL_SOURCE_LOCATION (heap_var),
- "%qE is not a constant expression because allocated "
- "storage has not been deallocated", t);
+ {
+ error ("%qE is not a constant expression because allocated "
+ "storage has not been deallocated", t);
+ inform (DECL_SOURCE_LOCATION (heap_var), "allocated here");
+ }
r = t;
non_constant_p = true;
}
- varpool_node::get (heap_var)->remove ();
}
}
@@ -9258,6 +10523,20 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
non_constant_p = true;
}
+ if (!non_constant_p && !constexpr_dtor)
+ verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
+
+ /* After verify_constant because reduced_constant_expression_p can unset
+ CONSTRUCTOR_NO_CLEARING. */
+ if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_NO_CLEARING (r))
+ {
+ if (!allow_non_constant)
+ error ("%qE is not a constant expression because it refers to "
+ "an incompletely initialized variable", t);
+ TREE_CONSTANT (r) = false;
+ non_constant_p = true;
+ }
+
if (non_constant_p)
/* If we saw something bad, go back to our argument. The wrapping below is
only for the cases of TREE_CONSTANT argument or overflow. */
@@ -9274,13 +10553,17 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
if (non_constant_p && !allow_non_constant)
return error_mark_node;
- else if (constexpr_dtor)
- return r;
else if (non_constant_p && TREE_CONSTANT (r))
r = mark_non_constant (r);
else if (non_constant_p)
return t;
+ if (constexpr_dtor)
+ {
+ DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true;
+ return r;
+ }
+
/* Check we are not trying to return the wrong type. */
if (!same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (r)))
{
@@ -9300,7 +10583,8 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
if (TREE_CODE (t) == TARGET_EXPR
&& TARGET_EXPR_INITIAL (t) == r)
return t;
- else if (TREE_CODE (t) == CONSTRUCTOR || TREE_CODE (t) == CALL_EXPR)
+ else if (TREE_CODE (t) == CONSTRUCTOR || TREE_CODE (t) == CALL_EXPR
+ || TREE_CODE (t) == AGGR_INIT_EXPR)
/* Don't add a TARGET_EXPR if our argument didn't have one. */;
else if (TREE_CODE (t) == TARGET_EXPR && TARGET_EXPR_CLEANUP (t))
r = get_target_expr (r);
@@ -9431,6 +10715,9 @@ fold_simple (tree t)
tree
fold_to_constant (tree t)
{
+ if (processing_template_decl)
+ return t;
+
tree r = fold (t);
if (CONSTANT_CLASS_P (r) && !TREE_OVERFLOW (r))
return r;
@@ -9450,8 +10737,35 @@ tree
maybe_constant_value (tree t, tree decl /* = NULL_TREE */,
mce_value manifestly_const_eval /* = mce_unknown */)
{
+ tree orig_t = t;
tree r;
+ if (EXPR_P (t) && manifestly_const_eval == mce_unknown)
+ {
+ /* Look up each operand in the cv_cache first to see if we've already
+ reduced it, and reuse that result to avoid quadratic behavior if
+ we're called when building up a large expression. */
+ int n = cp_tree_operand_length (t);
+ tree *ops = XALLOCAVEC (tree, n);
+ bool rebuild = false;
+ for (int i = 0; i < n; ++i)
+ {
+ ops[i] = TREE_OPERAND (t, i);
+ if (tree *cached = hash_map_safe_get (cv_cache, ops[i]))
+ if (*cached != ops[i])
+ {
+ ops[i] = *cached;
+ rebuild = true;
+ }
+ }
+ if (rebuild)
+ {
+ t = copy_node (t);
+ for (int i = 0; i < n; ++i)
+ TREE_OPERAND (t, i) = ops[i];
+ }
+ }
+
if (!is_nondependent_constant_expression (t))
{
if (TREE_OVERFLOW_P (t)
@@ -9469,6 +10783,10 @@ maybe_constant_value (tree t, tree decl /* = NULL_TREE */,
return fold_to_constant (t);
if (manifestly_const_eval != mce_unknown)
+ /* TODO: Extend the cache to be mce_value aware. And if we have a
+ previously cached mce_unknown result that's TREE_CONSTANT, it means
+ the reduced value is independent of mce_value and so we should
+ be able to reuse it in the mce_true/false case. */
return cxx_eval_outermost_constant_expr (t, true, true,
manifestly_const_eval, false, decl);
@@ -9498,7 +10816,7 @@ maybe_constant_value (tree t, tree decl /* = NULL_TREE */,
|| (TREE_CONSTANT (t) && !TREE_CONSTANT (r))
|| !cp_tree_equal (r, t));
if (!c.evaluation_restricted_p ())
- cv_cache->put (t, r);
+ cv_cache->put (orig_t, r);
return r;
}
@@ -9662,7 +10980,7 @@ fold_non_dependent_init (tree t,
static tree
maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant,
- bool manifestly_const_eval)
+ mce_value manifestly_const_eval)
{
if (!t)
return t;
@@ -9688,17 +11006,27 @@ maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant,
{
/* [basic.start.static] allows constant-initialization of variables with
static or thread storage duration even if it isn't required, but we
- shouldn't bend the rules the same way for automatic variables. */
+ shouldn't bend the rules the same way for automatic variables.
+
+ But still enforce the requirements of constexpr/constinit.
+ [dcl.constinit] "If a variable declared with the constinit specifier
+ has dynamic initialization, the program is ill-formed, even if the
+ implementation would perform that initialization as a static
+ initialization." */
bool is_static = (decl && DECL_P (decl)
&& (TREE_STATIC (decl) || DECL_EXTERNAL (decl)));
+ bool strict = (!is_static
+ || (decl && DECL_P (decl)
+ && (DECL_DECLARED_CONSTEXPR_P (decl)
+ || DECL_DECLARED_CONSTINIT_P (decl))));
if (is_static)
- manifestly_const_eval = true;
+ manifestly_const_eval = mce_true;
- if (cp_unevaluated_operand && !manifestly_const_eval)
+ if (cp_unevaluated_operand && manifestly_const_eval != mce_true)
return fold_to_constant (t);
- t = cxx_eval_outermost_constant_expr (t, allow_non_constant, !is_static,
- mce_value (manifestly_const_eval),
+ t = cxx_eval_outermost_constant_expr (t, allow_non_constant, strict,
+ manifestly_const_eval,
false, decl);
}
if (TREE_CODE (t) == TARGET_EXPR)
@@ -9715,6 +11043,12 @@ maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant,
tree
maybe_constant_init (tree t, tree decl, bool manifestly_const_eval)
{
+ return maybe_constant_init_1 (t, decl, true, mce_value (manifestly_const_eval));
+}
+
+tree
+maybe_constant_init (tree t, tree decl, mce_value manifestly_const_eval)
+{
return maybe_constant_init_1 (t, decl, true, manifestly_const_eval);
}
@@ -9723,7 +11057,25 @@ maybe_constant_init (tree t, tree decl, bool manifestly_const_eval)
tree
cxx_constant_init (tree t, tree decl)
{
- return maybe_constant_init_1 (t, decl, false, true);
+ 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
@@ -9758,11 +11110,13 @@ struct check_for_return_continue_data {
hash_set<tree> *pset;
tree continue_stmt;
tree break_stmt;
+ bool could_throw;
};
/* Helper function for potential_constant_expression_1 SWITCH_STMT handling,
called through cp_walk_tree. Return the first RETURN_EXPR found, or note
- the first CONTINUE_STMT and/or BREAK_STMT if RETURN_EXPR is not found. */
+ the first CONTINUE_STMT and/or BREAK_STMT if RETURN_EXPR is not found.
+ For C++26 also note presence of possibly throwing calls. */
static tree
check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
{
@@ -9847,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;
@@ -10052,8 +11413,27 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
|| TREE_CODE (t) != CALL_EXPR
|| current_function_decl == NULL_TREE
|| !is_std_construct_at (current_function_decl))
- && !cxx_dynamic_cast_fn_p (fun))
+ && !cxx_dynamic_cast_fn_p (fun)
+ && !cxx_cxa_builtin_fn_p (fun))
{
+ /* In C++26 evaluation of the function arguments might
+ throw and in that case it is irrelevant whether
+ fun is constexpr or not. */
+ if (cxx_dialect >= cxx26)
+ for (; i < nargs; ++i)
+ {
+ tree x = get_nth_callarg (t, i);
+ bool rv = processing_template_decl ? any : rval;
+ bool sub_now = false;
+ if (!potential_constant_expression_1 (x, rv, strict,
+ sub_now,
+ fundef_p,
+ flags,
+ jump_target))
+ return false;
+ if (throws (jump_target))
+ return true;
+ }
if ((flags & tf_error)
&& constexpr_error (loc, fundef_p,
"call to non-%<constexpr%> "
@@ -10098,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;
}
@@ -10263,22 +11648,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
return true;
case INDIRECT_REF:
- {
- tree x = TREE_OPERAND (t, 0);
- STRIP_NOPS (x);
- if (is_this_parameter (x) && !is_capture_proxy (x))
- {
- if (now || !var_in_maybe_constexpr_fn (x))
- {
- if (flags & tf_error)
- constexpr_error (loc, fundef_p, "use of %<this%> in a "
- "constant expression");
- return false;
- }
- return true;
- }
- return RECUR (x, rval);
- }
+ return RECUR (TREE_OPERAND (t, 0), rval);
case STATEMENT_LIST:
for (tree stmt : tsi_range (t))
@@ -10336,11 +11706,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
a return. */
hash_set<tree> pset;
check_for_return_continue_data data = { &pset, NULL_TREE,
- NULL_TREE };
+ NULL_TREE, false };
if (tree ret_expr
= cp_walk_tree (&FOR_BODY (t), check_for_return_continue,
&data, &pset))
*jump_target = ret_expr;
+ if (data.could_throw)
+ *jump_target = void_node;
return true;
}
}
@@ -10380,11 +11752,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
a return. */
hash_set<tree> pset;
check_for_return_continue_data data = { &pset, NULL_TREE,
- NULL_TREE };
+ NULL_TREE, false };
if (tree ret_expr
= cp_walk_tree (&WHILE_BODY (t), check_for_return_continue,
&data, &pset))
*jump_target = ret_expr;
+ if (data.could_throw)
+ *jump_target = void_node;
return true;
}
if (!RECUR (WHILE_BODY (t), any))
@@ -10408,7 +11782,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
{
hash_set<tree> pset;
check_for_return_continue_data data = { &pset, NULL_TREE,
- NULL_TREE };
+ NULL_TREE, false };
if (tree ret_expr
= cp_walk_tree (&SWITCH_STMT_BODY (t), check_for_return_continue,
&data, &pset))
@@ -10417,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;
@@ -10446,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:
@@ -10497,6 +11872,16 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
"expression", t);
return false;
+ case OMP_DECLARE_MAPPER:
+ /* This can be used to initialize VAR_DECLs: it's treated as a magic
+ 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);
@@ -10625,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:
@@ -10866,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))
@@ -10880,7 +12281,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
couldn't return, break or continue. */
hash_set<tree> pset;
check_for_return_continue_data data = { &pset, NULL_TREE,
- NULL_TREE };
+ NULL_TREE,
+ false };
if (tree ret_expr
= cp_walk_tree (&TREE_OPERAND (t, 2),
check_for_return_continue, &data,
@@ -10893,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;
@@ -10944,6 +12348,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
*jump_target = *target;
return true;
}
+ if (DECL_ARTIFICIAL (*target))
+ /* The user didn't write this goto, this isn't the problem. */
+ return true;
if (flags & tf_error)
constexpr_error (loc, fundef_p, "%<goto%> is not a constant "
"expression");
@@ -10977,6 +12384,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
case CO_AWAIT_EXPR:
case CO_YIELD_EXPR:
case CO_RETURN_EXPR:
+ if (flags & tf_error)
+ constexpr_error (cp_expr_loc_or_loc (t, input_location), fundef_p,
+ "%qE is not a constant expression", t);
return false;
/* Assume a TU-local entity is not constant, we'll error later when