constexpr functions. These routines are used both during actual parsing
and during the instantiation of template functions.
- Copyright (C) 1998-2018 Free Software Foundation, Inc.
+ Copyright (C) 1998-2019 Free Software Foundation, Inc.
This file is part of GCC.
#include "ubsan.h"
#include "gimple-fold.h"
#include "timevar.h"
+#include "fold-const-call.h"
static bool verify_constant (tree, bool, bool *, bool *);
#define VERIFY_CONSTANT(X) \
return t; \
} while (0)
+static HOST_WIDE_INT find_array_ctor_elt (tree ary, tree dindex,
+ bool insert = false);
+
/* Returns true iff FUN is an instantiation of a constexpr function
template or a defaulted constexpr function. */
{
if (DECL_DECLARED_CONSTEXPR_P (decl))
{
- error ("the type %qT of %<constexpr%> variable %qD "
- "is not literal", type, decl);
+ auto_diagnostic_group d;
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "the type %qT of %<constexpr%> variable %qD "
+ "is not literal", type, decl);
explain_non_literal_class (type);
decl = error_mark_node;
}
{
if (!is_instantiation_of_constexpr (current_function_decl))
{
- error ("variable %qD of non-literal type %qT in %<constexpr%> "
- "function", decl, type);
+ auto_diagnostic_group d;
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "variable %qD of non-literal type %qT in "
+ "%<constexpr%> function", decl, type);
explain_non_literal_class (type);
decl = error_mark_node;
}
else if (DECL_DECLARED_CONSTEXPR_P (decl)
&& variably_modified_type_p (type, NULL_TREE))
{
- error ("%<constexpr%> variable %qD has variably-modified type %qT",
- decl, type);
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "%<constexpr%> variable %qD has variably-modified "
+ "type %qT", decl, type);
decl = error_mark_node;
}
}
struct GTY((for_user)) constexpr_fundef {
tree decl;
tree body;
+ tree parms;
+ tree result;
};
struct constexpr_fundef_hasher : ggc_ptr_hash<constexpr_fundef>
static constexpr_fundef *
retrieve_constexpr_fundef (tree fun)
{
- constexpr_fundef fundef = { NULL, NULL };
if (constexpr_fundef_table == NULL)
return NULL;
- fundef.decl = fun;
+ constexpr_fundef fundef = { fun, NULL, NULL, NULL };
return constexpr_fundef_table->find (&fundef);
}
ret = false;
if (complain)
{
+ auto_diagnostic_group d;
error ("invalid type for parameter %d of %<constexpr%> "
"function %q+#D", DECL_PARM_INDEX (parm), fun);
explain_non_literal_class (TREE_TYPE (parm));
ret = false;
if (complain)
{
+ auto_diagnostic_group d;
error ("invalid return type %qT of %<constexpr%> function %q+D",
rettype, fun);
explain_non_literal_class (rettype);
&& !CLASSTYPE_LITERAL_P (DECL_CONTEXT (fun)))
{
ret = false;
- if (complain
- && pedwarn (DECL_SOURCE_LOCATION (fun), OPT_Wpedantic,
- "enclosing class of %<constexpr%> non-static member "
- "function %q+#D is not a literal type", fun))
- explain_non_literal_class (DECL_CONTEXT (fun));
+ if (complain)
+ {
+ auto_diagnostic_group d;
+ if (pedwarn (DECL_SOURCE_LOCATION (fun), OPT_Wpedantic,
+ "enclosing class of %<constexpr%> non-static"
+ " member function %q+#D is not a literal type",
+ fun))
+ explain_non_literal_class (DECL_CONTEXT (fun));
+ }
}
}
else if (CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fun)))
gcc_unreachable ();
}
found:
+ if (TREE_CODE (body) == TRY_BLOCK)
+ {
+ body = TREE_OPERAND (body, 0);
+ if (TREE_CODE (body) == BIND_EXPR)
+ body = BIND_EXPR_BODY (body);
+ }
if (TREE_CODE (body) == CLEANUP_POINT_EXPR)
{
body = TREE_OPERAND (body, 0);
break;
}
}
- else if (TREE_CODE (body) == TRY_BLOCK)
- {
- error ("body of %<constexpr%> constructor cannot be "
- "a function-try-block");
- return error_mark_node;
- }
else if (EXPR_P (body))
ok = build_data_member_initialization (body, &vec);
else
{
tree fun = get_function_named_in_call (body);
if (fun != NULL_TREE
- && DECL_FUNCTION_CODE (fun) == BUILT_IN_UNREACHABLE)
+ && fndecl_built_in_p (fun, BUILT_IN_UNREACHABLE))
return NULL_TREE;
}
/* Fallthru. */
}
if (!complain)
return true;
+ auto_diagnostic_group d;
error ("member %qD must be initialized by mem-initializer "
"in %<constexpr%> constructor", field);
inform (DECL_SOURCE_LOCATION (field), "declared here");
= hash_table<constexpr_fundef_hasher>::create_ggc (101);
entry.decl = fun;
- entry.body = body;
+ tree saved_fn = current_function_decl;
+ bool clear_ctx = false;
+ current_function_decl = fun;
+ if (DECL_RESULT (fun) && DECL_CONTEXT (DECL_RESULT (fun)) == NULL_TREE)
+ {
+ clear_ctx = true;
+ DECL_CONTEXT (DECL_RESULT (fun)) = fun;
+ }
+ entry.body = copy_fn (fun, entry.parms, entry.result);
+ current_function_decl = saved_fn;
slot = constexpr_fundef_table->find_slot (&entry, INSERT);
+ if (clear_ctx)
+ DECL_CONTEXT (DECL_RESULT (fun)) = NULL_TREE;
gcc_assert (*slot == NULL);
*slot = ggc_alloc<constexpr_fundef> ();
struct GTY((for_user)) constexpr_call {
/* Description of the constexpr function definition. */
constexpr_fundef *fundef;
- /* Parameter bindings environment. A TREE_LIST where each TREE_PURPOSE
- is a parameter _DECL and the TREE_VALUE is the value of the parameter.
- Note: This arrangement is made to accommodate the use of
- iterative_hash_template_arg (see pt.c). If you change this
- representation, also change the hash calculation in
- cxx_eval_call_expression. */
+ /* Parameter bindings environment. A TREE_VEC of arguments. */
tree bindings;
/* Result of the call.
NULL means the call is being evaluated.
/* The hash of this call; we remember it here to avoid having to
recalculate it when expanding the hash table. */
hashval_t hash;
+ /* Whether __builtin_is_constant_evaluated() should evaluate to true. */
+ bool manifestly_const_eval;
};
struct constexpr_call_hasher : ggc_ptr_hash<constexpr_call>
hash_map<tree,tree> *values;
/* SAVE_EXPRs that we've seen within the current LOOP_EXPR. NULL if we
aren't inside a loop. */
- hash_set<tree> *save_exprs;
+ vec<tree> *save_exprs;
/* The CONSTRUCTOR we're currently building up for an aggregate
initializer. */
tree ctor;
tree object;
/* If inside SWITCH_EXPR. */
constexpr_switch_state *css_state;
+ /* Number of cxx_eval_constant_expression calls (except skipped ones,
+ on simple constants or location wrappers) encountered during current
+ cxx_eval_outermost_constant_expr call. */
+ HOST_WIDE_INT *constexpr_ops_count;
+
/* Whether we should error on a non-constant expression or fail quietly. */
bool quiet;
/* Whether we are strictly conforming to constant expression rules or
trying harder to get a constant value. */
bool strict;
+ /* Whether __builtin_is_constant_evaluated () should be true. */
+ bool manifestly_const_eval;
};
/* A table of all constexpr calls that have been evaluated by the
bool
constexpr_call_hasher::equal (constexpr_call *lhs, constexpr_call *rhs)
{
- tree lhs_bindings;
- tree rhs_bindings;
if (lhs == 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;
- lhs_bindings = lhs->bindings;
- rhs_bindings = rhs->bindings;
- while (lhs_bindings != NULL && rhs_bindings != NULL)
- {
- tree lhs_arg = TREE_VALUE (lhs_bindings);
- tree rhs_arg = TREE_VALUE (rhs_bindings);
- gcc_assert (TREE_TYPE (lhs_arg) == TREE_TYPE (rhs_arg));
- if (!cp_tree_equal (lhs_arg, rhs_arg))
- return false;
- lhs_bindings = TREE_CHAIN (lhs_bindings);
- rhs_bindings = TREE_CHAIN (rhs_bindings);
- }
- return lhs_bindings == rhs_bindings;
+ return cp_tree_equal (lhs->bindings, rhs->bindings);
}
/* Initialize the constexpr call table, if needed. */
is parms, TYPE is result. */
static tree
-get_fundef_copy (tree fun)
+get_fundef_copy (constexpr_fundef *fundef)
{
maybe_initialize_fundef_copies_table ();
tree copy;
bool existed;
- tree *slot = &fundef_copies_table->get_or_insert (fun, &existed);
+ tree *slot = &fundef_copies_table->get_or_insert (fundef->decl, &existed);
if (!existed)
{
/* There is no cached function available, or in use. We can use
the function directly. That the slot is now created records
that this function is now in use. */
- copy = build_tree_list (DECL_SAVED_TREE (fun), DECL_ARGUMENTS (fun));
- TREE_TYPE (copy) = DECL_RESULT (fun);
+ copy = build_tree_list (fundef->body, fundef->parms);
+ TREE_TYPE (copy) = fundef->result;
}
else if (*slot == NULL_TREE)
{
/* We've already used the function itself, so make a copy. */
copy = build_tree_list (NULL, NULL);
- TREE_PURPOSE (copy) = copy_fn (fun, TREE_VALUE (copy), TREE_TYPE (copy));
+ tree saved_body = DECL_SAVED_TREE (fundef->decl);
+ tree saved_parms = DECL_ARGUMENTS (fundef->decl);
+ tree saved_result = DECL_RESULT (fundef->decl);
+ tree saved_fn = current_function_decl;
+ DECL_SAVED_TREE (fundef->decl) = fundef->body;
+ DECL_ARGUMENTS (fundef->decl) = fundef->parms;
+ DECL_RESULT (fundef->decl) = fundef->result;
+ current_function_decl = fundef->decl;
+ TREE_PURPOSE (copy) = copy_fn (fundef->decl, TREE_VALUE (copy),
+ TREE_TYPE (copy));
+ current_function_decl = saved_fn;
+ DECL_RESULT (fundef->decl) = saved_result;
+ DECL_ARGUMENTS (fundef->decl) = saved_parms;
+ DECL_SAVED_TREE (fundef->decl) = saved_body;
}
else
{
int i;
/* Don't fold __builtin_constant_p within a constexpr function. */
- bool bi_const_p = (DECL_FUNCTION_CODE (fun) == BUILT_IN_CONSTANT_P);
+ bool bi_const_p = DECL_IS_BUILTIN_CONSTANT_P (fun);
/* If we aren't requiring a constant expression, defer __builtin_constant_p
in a constexpr function until we have values for the parameters. */
if (bi_const_p
- && ctx->quiet
+ && !ctx->manifestly_const_eval
&& current_function_decl
&& DECL_DECLARED_CONSTEXPR_P (current_function_decl))
{
return t;
}
+ /* For __builtin_is_constant_evaluated, defer it if not
+ ctx->manifestly_const_eval, otherwise fold it to true. */
+ if (fndecl_built_in_p (fun, CP_BUILT_IN_IS_CONSTANT_EVALUATED,
+ BUILT_IN_FRONTEND))
+ {
+ if (!ctx->manifestly_const_eval)
+ {
+ *non_constant_p = true;
+ return t;
+ }
+ return boolean_true_node;
+ }
+
/* Be permissive for arguments to built-ins; __builtin_constant_p should
return constant false for a non-constant argument. */
constexpr_ctx new_ctx = *ctx;
new_ctx.quiet = true;
- bool dummy1 = false, dummy2 = false;
for (i = 0; i < nargs; ++i)
{
args[i] = CALL_EXPR_ARG (t, i);
of the builtin, verify it here. */
if (!builtin_valid_in_constant_expr_p (fun)
|| potential_constant_expression (args[i]))
- args[i] = cxx_eval_constant_expression (&new_ctx, args[i], false,
- &dummy1, &dummy2);
+ {
+ bool dummy1 = false, dummy2 = false;
+ args[i] = cxx_eval_constant_expression (&new_ctx, args[i], false,
+ &dummy1, &dummy2);
+ }
+
if (bi_const_p)
- /* For __built_in_constant_p, fold all expressions with constant values
+ /* For __builtin_constant_p, fold all expressions with constant values
even if they aren't C++ constant-expressions. */
- args[i] = cp_fully_fold (args[i]);
+ args[i] = cp_fold_rvalue (args[i]);
}
bool save_ffbcp = force_folding_builtin_constant_p;
force_folding_builtin_constant_p = true;
+ tree save_cur_fn = current_function_decl;
+ /* Return name of ctx->call->fundef->decl for __builtin_FUNCTION (). */
+ if (fndecl_built_in_p (fun, BUILT_IN_FUNCTION)
+ && ctx->call
+ && ctx->call->fundef)
+ current_function_decl = ctx->call->fundef->decl;
new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t),
CALL_EXPR_FN (t), nargs, args);
+ current_function_decl = save_cur_fn;
force_folding_builtin_constant_p = save_ffbcp;
if (new_call == NULL)
{
/* Do not allow__builtin_unreachable in constexpr function.
The __builtin_unreachable call with BUILTINS_LOCATION
comes from cp_maybe_instrument_return. */
- if (DECL_FUNCTION_CODE (fun) == BUILT_IN_UNREACHABLE
+ if (fndecl_built_in_p (fun, BUILT_IN_UNREACHABLE)
&& EXPR_LOCATION (t) == BUILTINS_LOCATION)
error ("%<constexpr%> call flows off the end of the function");
else
return t;
}
- if (!is_constant_expression (new_call))
+ if (!potential_constant_expression (new_call))
{
if (!*non_constant_p && !ctx->quiet)
error ("%q+E is not a constant expression", new_call);
static tree
adjust_temp_type (tree type, tree temp)
{
- if (TREE_TYPE (temp) == type)
+ if (same_type_p (TREE_TYPE (temp), type))
return temp;
/* Avoid wrapping an aggregate value in a NOP_EXPR. */
if (TREE_CODE (temp) == CONSTRUCTOR)
- return build_constructor (type, CONSTRUCTOR_ELTS (temp));
+ {
+ /* build_constructor wouldn't retain various CONSTRUCTOR flags. */
+ tree t = copy_node (temp);
+ TREE_TYPE (t) = type;
+ return t;
+ }
+ if (TREE_CODE (temp) == EMPTY_CLASS_EXPR)
+ return build0 (EMPTY_CLASS_EXPR, type);
gcc_assert (scalarish_type_p (type));
- return cp_fold_convert (type, temp);
+ /* Now we know we're dealing with a scalar, and a prvalue of non-class
+ type is cv-unqualified. */
+ return cp_fold_convert (cv_unqualified (type), temp);
}
-/* Callback for walk_tree used by unshare_constructor. */
+/* If T is a CONSTRUCTOR, return an unshared copy of T and any
+ sub-CONSTRUCTORs. Otherwise return T.
-static tree
-find_constructor (tree *tp, int *walk_subtrees, void *)
+ We use this whenever we initialize an object as a whole, whether it's a
+ parameter, a local variable, or a subobject, so that subsequent
+ modifications don't affect other places where it was used. */
+
+tree
+unshare_constructor (tree t MEM_STAT_DECL)
{
- if (TYPE_P (*tp))
- *walk_subtrees = 0;
- if (TREE_CODE (*tp) == CONSTRUCTOR)
- return *tp;
- return NULL_TREE;
+ if (!t || TREE_CODE (t) != CONSTRUCTOR)
+ return t;
+ auto_vec <tree*, 4> ptrs;
+ ptrs.safe_push (&t);
+ while (!ptrs.is_empty ())
+ {
+ tree *p = ptrs.pop ();
+ tree n = copy_node (*p PASS_MEM_STAT);
+ CONSTRUCTOR_ELTS (n) = vec_safe_copy (CONSTRUCTOR_ELTS (*p) PASS_MEM_STAT);
+ *p = n;
+ vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (n);
+ constructor_elt *ce;
+ for (HOST_WIDE_INT i = 0; vec_safe_iterate (v, i, &ce); ++i)
+ if (TREE_CODE (ce->value) == CONSTRUCTOR)
+ ptrs.safe_push (&ce->value);
+ }
+ return t;
}
-/* If T is a CONSTRUCTOR or an expression that has a CONSTRUCTOR node as a
- subexpression, return an unshared copy of T. Otherwise return T. */
+/* If T is a CONSTRUCTOR, ggc_free T and any sub-CONSTRUCTORs. */
-static tree
-unshare_constructor (tree t)
+static void
+free_constructor (tree t)
{
- tree ctor = walk_tree (&t, find_constructor, NULL, NULL);
- if (ctor != NULL_TREE)
- return unshare_expr (t);
- return t;
+ if (!t || TREE_CODE (t) != CONSTRUCTOR)
+ return;
+ releasing_vec ctors;
+ vec_safe_push (ctors, t);
+ while (!ctors->is_empty ())
+ {
+ tree c = ctors->pop ();
+ if (vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (c))
+ {
+ constructor_elt *ce;
+ for (HOST_WIDE_INT i = 0; vec_safe_iterate (elts, i, &ce); ++i)
+ if (TREE_CODE (ce->value) == CONSTRUCTOR)
+ vec_safe_push (ctors, ce->value);
+ ggc_free (elts);
+ }
+ ggc_free (c);
+ }
}
/* Subroutine of cxx_eval_call_expression.
{
const int nargs = call_expr_nargs (t);
tree fun = new_call->fundef->decl;
- tree parms = DECL_ARGUMENTS (fun);
+ tree parms = new_call->fundef->parms;
int i;
- tree *p = &new_call->bindings;
+ /* We don't record ellipsis args below. */
+ int nparms = list_length (parms);
+ int nbinds = nargs < nparms ? nargs : nparms;
+ tree binds = new_call->bindings = make_tree_vec (nbinds);
for (i = 0; i < nargs; ++i)
{
tree x, arg;
x = ctx->object;
x = build_address (x);
}
+ if (TREE_ADDRESSABLE (type))
+ /* Undo convert_for_arg_passing work here. */
+ x = convert_from_reference (x);
arg = cxx_eval_constant_expression (ctx, x, /*lval=*/false,
non_constant_p, overflow_p);
/* Don't VERIFY_CONSTANT here. */
if (!*non_constant_p)
{
- /* Don't share a CONSTRUCTOR that might be changed. */
+ /* Unsharing here isn't necessary for correctness, but it
+ significantly improves memory performance for some reason. */
arg = unshare_constructor (arg);
/* Make sure the binding has the same type as the parm. But
only for constant args. */
arg = adjust_temp_type (type, arg);
if (!TREE_CONSTANT (arg))
*non_constant_args = true;
- *p = build_tree_list (parms, arg);
- p = &TREE_CHAIN (*p);
+ TREE_VEC_ELT (binds, i) = arg;
}
parms = TREE_CHAIN (parms);
}
static int call_stack_tick;
static int last_cx_error_tick;
-static bool
+static int
push_cx_call_context (tree call)
{
++call_stack_tick;
if (!EXPR_HAS_LOCATION (call))
SET_EXPR_LOCATION (call, input_location);
call_stack.safe_push (call);
- if (call_stack.length () > (unsigned) max_constexpr_depth)
+ int len = call_stack.length ();
+ if (len > max_constexpr_depth)
return false;
- return true;
+ return len;
}
static void
return cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
false, non_constant_p, overflow_p);
+ case IFN_VEC_CONVERT:
+ {
+ tree arg = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
+ false, non_constant_p,
+ overflow_p);
+ if (TREE_CODE (arg) == VECTOR_CST)
+ return fold_const_call (CFN_VEC_CONVERT, TREE_TYPE (t), arg);
+ else
+ {
+ *non_constant_p = true;
+ return t;
+ }
+ }
+
default:
if (!ctx->quiet)
error_at (cp_expr_loc_or_loc (t, input_location),
{
location_t loc = cp_expr_loc_or_loc (t, input_location);
tree fun = get_function_named_in_call (t);
- constexpr_call new_call = { NULL, NULL, NULL, 0 };
- bool depth_ok;
+ 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,
STRIP_NOPS (fun);
if (TREE_CODE (fun) == ADDR_EXPR)
fun = TREE_OPERAND (fun, 0);
+ /* For TARGET_VTABLE_USES_DESCRIPTORS targets, there is no
+ indirection, the called expression is a pointer into the
+ virtual table which should contain FDESC_EXPR. Extract the
+ FUNCTION_DECL from there. */
+ else if (TARGET_VTABLE_USES_DESCRIPTORS
+ && TREE_CODE (fun) == POINTER_PLUS_EXPR
+ && TREE_CODE (TREE_OPERAND (fun, 0)) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (fun, 1)) == INTEGER_CST)
+ {
+ tree d = TREE_OPERAND (TREE_OPERAND (fun, 0), 0);
+ if (VAR_P (d)
+ && DECL_VTABLE_OR_VTT_P (d)
+ && TREE_CODE (TREE_TYPE (d)) == ARRAY_TYPE
+ && TREE_TYPE (TREE_TYPE (d)) == vtable_entry_type
+ && DECL_INITIAL (d)
+ && TREE_CODE (DECL_INITIAL (d)) == CONSTRUCTOR)
+ {
+ tree i = int_const_binop (TRUNC_DIV_EXPR, TREE_OPERAND (fun, 1),
+ TYPE_SIZE_UNIT (vtable_entry_type));
+ HOST_WIDE_INT idx = find_array_ctor_elt (DECL_INITIAL (d), i);
+ if (idx >= 0)
+ {
+ tree fdesc
+ = (*CONSTRUCTOR_ELTS (DECL_INITIAL (d)))[idx].value;
+ if (TREE_CODE (fdesc) == FDESC_EXPR
+ && integer_zerop (TREE_OPERAND (fdesc, 1)))
+ fun = TREE_OPERAND (fdesc, 0);
+ }
+ }
+ }
}
if (TREE_CODE (fun) != FUNCTION_DECL)
{
if (is_ubsan_builtin_p (fun))
return void_node;
- if (is_builtin_fn (fun))
+ if (fndecl_built_in_p (fun))
return cxx_eval_builtin_function_call (ctx, t, fun,
lval, non_constant_p, overflow_p);
if (!DECL_DECLARED_CONSTEXPR_P (fun))
bool non_constant_args = false;
cxx_bind_parameters_in_call (ctx, t, &new_call,
non_constant_p, overflow_p, &non_constant_args);
+
+ /* 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
+ this function exits. */
+ struct free_bindings
+ {
+ tree &bindings;
+ bool do_free;
+ free_bindings (tree &b): bindings (b), do_free(true) { }
+ void preserve () { do_free = false; }
+ ~free_bindings () {
+ if (do_free)
+ {
+ for (int i = 0; i < TREE_VEC_LENGTH (bindings); ++i)
+ free_constructor (TREE_VEC_ELT (bindings, i));
+ ggc_free (bindings);
+ }
+ }
+ } fb (new_call.bindings);
+
if (*non_constant_p)
return t;
constexpr_call *entry = NULL;
if (depth_ok && !non_constant_args && ctx->strict)
{
- new_call.hash = iterative_hash_template_arg
- (new_call.bindings, constexpr_fundef_hasher::hash (new_call.fundef));
+ 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 ();
entry = *slot;
if (entry == NULL)
{
- /* We need to keep a pointer to the entry, not just the slot, as the
- slot can move in the call to cxx_eval_builtin_function_call. */
- *slot = entry = ggc_alloc<constexpr_call> ();
- *entry = new_call;
+ /* Only cache up to constexpr_cache_depth to limit memory use. */
+ if (depth_ok < constexpr_cache_depth)
+ {
+ /* We need to keep a pointer to the entry, not just the slot, as
+ the slot can move during evaluation of the body. */
+ *slot = entry = ggc_alloc<constexpr_call> ();
+ *entry = new_call;
+ fb.preserve ();
+ }
}
- /* Calls that are in progress have their result set to NULL,
- so that we can detect circular dependencies. */
+ /* 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
+ start deeper, but they'll hit the recursion or ops limit. */
else if (entry->result == NULL)
{
if (!ctx->quiet)
{
if (!ctx->quiet)
error ("%<constexpr%> evaluation depth exceeds maximum of %d (use "
- "-fconstexpr-depth= to increase the maximum)",
+ "%<-fconstexpr-depth=%> to increase the maximum)",
max_constexpr_depth);
*non_constant_p = true;
result = error_mark_node;
else
{
tree body, parms, res;
+ releasing_vec ctors;
/* Reuse or create a new unshared copy of this function's body. */
- tree copy = get_fundef_copy (fun);
+ tree copy = get_fundef_copy (new_call.fundef);
body = TREE_PURPOSE (copy);
parms = TREE_VALUE (copy);
res = TREE_TYPE (copy);
/* Associate the bindings with the remapped parms. */
tree bound = new_call.bindings;
tree remapped = parms;
- while (bound)
+ for (int i = 0; i < TREE_VEC_LENGTH (bound); ++i)
{
- tree oparm = TREE_PURPOSE (bound);
- tree arg = TREE_VALUE (bound);
- gcc_assert (DECL_NAME (remapped) == DECL_NAME (oparm));
+ tree arg = TREE_VEC_ELT (bound, i);
/* Don't share a CONSTRUCTOR that might be changed. */
arg = unshare_constructor (arg);
+ if (TREE_CODE (arg) == CONSTRUCTOR)
+ vec_safe_push (ctors, arg);
ctx->values->put (remapped, arg);
- bound = TREE_CHAIN (bound);
remapped = DECL_CHAIN (remapped);
}
/* Add the RESULT_DECL to the values map, too. */
/* Track the callee's evaluated SAVE_EXPRs so that we can forget
their values after the call. */
constexpr_ctx ctx_with_save_exprs = *ctx;
- hash_set<tree> save_exprs;
+ auto_vec<tree, 10> save_exprs;
ctx_with_save_exprs.save_exprs = &save_exprs;
ctx_with_save_exprs.call = &new_call;
}
/* Forget the saved values of the callee's SAVE_EXPRs. */
- for (hash_set<tree>::iterator iter = save_exprs.begin();
- iter != save_exprs.end(); ++iter)
- ctx_with_save_exprs.values->remove (*iter);
+ unsigned int i;
+ tree save_expr;
+ FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
+ ctx_with_save_exprs.values->remove (save_expr);
/* Remove the parms/result from the values map. Is it worth
bothering to do this when the map itself is only live for
for (tree parm = parms; parm; parm = TREE_CHAIN (parm))
ctx->values->remove (parm);
+ /* Free any parameter CONSTRUCTORs we aren't returning directly. */
+ while (!ctors->is_empty ())
+ {
+ tree c = ctors->pop ();
+ if (c != result)
+ free_constructor (c);
+ }
+
/* Make the unshared function copy we used available for re-use. */
save_fundef_copy (fun, copy);
}
clear_no_implicit_zero (result);
pop_cx_call_context ();
- return unshare_constructor (result);
+ return result;
}
/* FIXME speed this up, it's taking 16% of compile time on sieve testcase. */
if (compare_tree_int (rhs, uprec) >= 0)
{
if (!ctx->quiet)
- permerror (loc, "right operand of shift expression %q+E is >= than "
- "the precision of the left operand",
- build2_loc (loc, code, type, lhs, rhs));
+ permerror (loc, "right operand of shift expression %q+E is greater "
+ "than or equal to the precision %wu of the left operand",
+ build2_loc (loc, code, type, lhs, rhs), uprec);
return (!flag_permissive || ctx->quiet);
}
if E1 has a signed type and non-negative value, and E1x2^E2 is
representable in the corresponding unsigned type of the result type,
then that value, converted to the result type, is the resulting value;
- otherwise, the behavior is undefined. */
- if (code == LSHIFT_EXPR && !TYPE_UNSIGNED (lhstype)
- && (cxx_dialect >= cxx11))
+ otherwise, the behavior is undefined.
+ For C++2a:
+ The value of E1 << E2 is the unique value congruent to E1 x 2^E2 modulo
+ 2^N, where N is the range exponent of the type of the result. */
+ if (code == LSHIFT_EXPR
+ && !TYPE_UNSIGNED (lhstype)
+ && cxx_dialect >= cxx11
+ && cxx_dialect < cxx2a)
{
if (tree_int_cst_sgn (lhs) == -1)
{
{
if (!ctx->quiet)
error ("arithmetic involving a null pointer in %qE", lhs);
+ *non_constant_p = true;
return t;
}
else if (code == POINTER_PLUS_EXPR)
VERIFY_CONSTANT (val);
/* Don't VERIFY_CONSTANT the other operands. */
if (integer_zerop (val))
- return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
- lval,
- non_constant_p, overflow_p,
- jump_target);
- return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
- lval,
- non_constant_p, overflow_p,
- jump_target);
+ val = TREE_OPERAND (t, 2);
+ else
+ val = TREE_OPERAND (t, 1);
+ if (TREE_CODE (t) == IF_STMT && !val)
+ val = void_node;
+ return cxx_eval_constant_expression (ctx, val, lval, non_constant_p,
+ overflow_p, jump_target);
}
/* Subroutine of cxx_eval_constant_expression.
if none. If INSERT is true, insert a matching element rather than fail. */
static HOST_WIDE_INT
-find_array_ctor_elt (tree ary, tree dindex, bool insert = false)
+find_array_ctor_elt (tree ary, tree dindex, bool insert)
{
if (tree_int_cst_sgn (dindex) < 0)
return -1;
an out-of-bounds subscript INDEX into the expression ARRAY. */
static void
-diag_array_subscript (const constexpr_ctx *ctx, tree array, tree index)
+diag_array_subscript (location_t loc, const constexpr_ctx *ctx, tree array, tree index)
{
if (!ctx->quiet)
{
/* Convert the unsigned array subscript to a signed integer to avoid
printing huge numbers for small negative values. */
tree sidx = fold_convert (ssizetype, index);
+ STRIP_ANY_LOCATION_WRAPPER (array);
if (DECL_P (array))
{
if (TYPE_DOMAIN (arraytype))
- error ("array subscript value %qE is outside the bounds "
- "of array %qD of type %qT", sidx, array, arraytype);
+ error_at (loc, "array subscript value %qE is outside the bounds "
+ "of array %qD of type %qT", sidx, array, arraytype);
else
- error ("non-zero array subscript %qE is used with array %qD of "
- "type %qT with unknown bounds", sidx, array, arraytype);
+ error_at (loc, "nonzero array subscript %qE is used with array %qD of "
+ "type %qT with unknown bounds", sidx, array, arraytype);
inform (DECL_SOURCE_LOCATION (array), "declared here");
}
else if (TYPE_DOMAIN (arraytype))
- error ("array subscript value %qE is outside the bounds "
- "of array type %qT", sidx, arraytype);
+ error_at (loc, "array subscript value %qE is outside the bounds "
+ "of array type %qT", sidx, arraytype);
else
- error ("non-zero array subscript %qE is used with array of type %qT "
- "with unknown bounds", sidx, arraytype);
+ error_at (loc, "nonzero array subscript %qE is used with array of type %qT "
+ "with unknown bounds", sidx, arraytype);
}
}
return r;
}
-/* Subroutine of cxx_eval_constant_expression.
- Attempt to reduce a reference to an array slot. */
+/* Subroutine of cxx_eval_array_reference. T is an ARRAY_REF; evaluate the
+ subscript, diagnose any problems with it, and return the result. */
static tree
-cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
- bool lval,
- bool *non_constant_p, bool *overflow_p)
+eval_and_check_array_index (const constexpr_ctx *ctx,
+ tree t, bool allow_one_past,
+ bool *non_constant_p, bool *overflow_p)
{
- tree oldary = TREE_OPERAND (t, 0);
- tree ary = cxx_eval_constant_expression (ctx, oldary,
- lval,
- non_constant_p, overflow_p);
- tree index, oldidx;
- HOST_WIDE_INT i = 0;
- tree elem_type = NULL_TREE;
- unsigned len = 0, elem_nchars = 1;
- if (*non_constant_p)
- return t;
- oldidx = TREE_OPERAND (t, 1);
- index = cxx_eval_constant_expression (ctx, oldidx,
- false,
- non_constant_p, overflow_p);
+ location_t loc = cp_expr_loc_or_loc (t, input_location);
+ tree ary = TREE_OPERAND (t, 0);
+ t = TREE_OPERAND (t, 1);
+ tree index = cxx_eval_constant_expression (ctx, t, false,
+ non_constant_p, overflow_p);
VERIFY_CONSTANT (index);
- if (!lval)
- {
- elem_type = TREE_TYPE (TREE_TYPE (ary));
- if (TREE_CODE (ary) == VIEW_CONVERT_EXPR
- && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (ary, 0)))
- && TREE_TYPE (t) == TREE_TYPE (TREE_TYPE (TREE_OPERAND (ary, 0))))
- ary = TREE_OPERAND (ary, 0);
- if (TREE_CODE (ary) == CONSTRUCTOR)
- len = CONSTRUCTOR_NELTS (ary);
- else if (TREE_CODE (ary) == STRING_CST)
- {
- elem_nchars = (TYPE_PRECISION (elem_type)
- / TYPE_PRECISION (char_type_node));
- len = (unsigned) TREE_STRING_LENGTH (ary) / elem_nchars;
- }
- else if (TREE_CODE (ary) == VECTOR_CST)
- /* We don't create variable-length VECTOR_CSTs. */
- len = VECTOR_CST_NELTS (ary).to_constant ();
- else
- {
- /* We can't do anything with other tree codes, so use
- VERIFY_CONSTANT to complain and fail. */
- VERIFY_CONSTANT (ary);
- gcc_unreachable ();
- }
- if (!tree_fits_shwi_p (index)
- || (i = tree_to_shwi (index)) < 0)
- {
- diag_array_subscript (ctx, ary, index);
- *non_constant_p = true;
- return t;
- }
+ if (!tree_fits_shwi_p (index)
+ || tree_int_cst_sgn (index) < 0)
+ {
+ diag_array_subscript (loc, ctx, ary, index);
+ *non_constant_p = true;
+ return t;
}
tree nelts = get_array_or_vector_nelts (ctx, TREE_TYPE (ary), non_constant_p,
overflow_p);
VERIFY_CONSTANT (nelts);
- if ((lval
- ? !tree_int_cst_le (index, nelts)
- : !tree_int_cst_lt (index, nelts))
- || tree_int_cst_sgn (index) < 0)
+ if (allow_one_past
+ ? !tree_int_cst_le (index, nelts)
+ : !tree_int_cst_lt (index, nelts))
{
- diag_array_subscript (ctx, ary, index);
+ diag_array_subscript (loc, ctx, ary, index);
*non_constant_p = true;
return t;
}
+ return index;
+}
+
+/* Subroutine of cxx_eval_constant_expression.
+ Attempt to reduce a reference to an array slot. */
+
+static tree
+cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
+ bool lval,
+ bool *non_constant_p, bool *overflow_p)
+{
+ tree oldary = TREE_OPERAND (t, 0);
+ tree ary = cxx_eval_constant_expression (ctx, oldary,
+ lval,
+ non_constant_p, overflow_p);
+ if (*non_constant_p)
+ return t;
+ if (!lval
+ && TREE_CODE (ary) == VIEW_CONVERT_EXPR
+ && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (ary, 0)))
+ && TREE_TYPE (t) == TREE_TYPE (TREE_TYPE (TREE_OPERAND (ary, 0))))
+ ary = TREE_OPERAND (ary, 0);
+
+ tree oldidx = TREE_OPERAND (t, 1);
+ tree index = eval_and_check_array_index (ctx, t, lval,
+ non_constant_p, overflow_p);
+ if (*non_constant_p)
+ return t;
+
if (lval && ary == oldary && index == oldidx)
return t;
else if (lval)
return build4 (ARRAY_REF, TREE_TYPE (t), ary, index, NULL, NULL);
+ unsigned len = 0, elem_nchars = 1;
+ tree elem_type = TREE_TYPE (TREE_TYPE (ary));
+ if (TREE_CODE (ary) == CONSTRUCTOR)
+ len = CONSTRUCTOR_NELTS (ary);
+ else if (TREE_CODE (ary) == STRING_CST)
+ {
+ elem_nchars = (TYPE_PRECISION (elem_type)
+ / TYPE_PRECISION (char_type_node));
+ len = (unsigned) TREE_STRING_LENGTH (ary) / elem_nchars;
+ }
+ else if (TREE_CODE (ary) == VECTOR_CST)
+ /* We don't create variable-length VECTOR_CSTs. */
+ len = VECTOR_CST_NELTS (ary).to_constant ();
+ else
+ {
+ /* We can't do anything with other tree codes, so use
+ VERIFY_CONSTANT to complain and fail. */
+ VERIFY_CONSTANT (ary);
+ gcc_unreachable ();
+ }
+
bool found;
+ HOST_WIDE_INT i = 0;
if (TREE_CODE (ary) == CONSTRUCTOR)
{
HOST_WIDE_INT ix = find_array_ctor_elt (ary, index);
i = ix;
}
else
- found = (i < len);
+ {
+ i = tree_to_shwi (index);
+ found = (i < len);
+ }
if (found)
{
lval,
non_constant_p, overflow_p);
if (INDIRECT_REF_P (whole)
- && integer_zerop (TREE_OPERAND (whole, 0))
- && !ctx->quiet)
- error ("dereferencing a null pointer in %qE", orig_whole);
+ && integer_zerop (TREE_OPERAND (whole, 0)))
+ {
+ if (!ctx->quiet)
+ error ("dereferencing a null pointer in %qE", orig_whole);
+ *non_constant_p = true;
+ return t;
+ }
if (TREE_CODE (whole) == PTRMEM_CST)
whole = cplus_expand_constant (whole);
/* We only create a CONSTRUCTOR for a subobject when we modify it, so empty
classes never get represented; throw together a value now. */
- if (is_really_empty_class (TREE_TYPE (t)))
+ if (is_really_empty_class (TREE_TYPE (t), /*ignore_vptr*/false))
return build_constructor (TREE_TYPE (t), NULL);
gcc_assert (DECL_CONTEXT (part) == TYPE_MAIN_VARIANT (TREE_TYPE (whole)));
{
if (TYPE_P (t))
return t;
- tree type = cv_unqualified (TREE_TYPE (t));
- if (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == AGGR_INIT_EXPR)
+ tree type = TREE_TYPE (t);
+ if (TREE_CODE (t) == CALL_EXPR)
{
/* A constructor call has void type, so we need to look deeper. */
tree fn = get_function_named_in_call (t);
&& DECL_CXX_CONSTRUCTOR_P (fn))
type = DECL_CONTEXT (fn);
}
- return type;
+ else if (TREE_CODE (t) == COMPOUND_EXPR)
+ return initialized_type (TREE_OPERAND (t, 1));
+ else if (TREE_CODE (t) == AGGR_INIT_EXPR)
+ type = TREE_TYPE (AGGR_INIT_EXPR_SLOT (t));
+ return cv_unqualified (type);
}
/* We're about to initialize element INDEX of an array or class from VALUE.
}
else if (!init)
{
- vec<tree, va_gc> *argvec = make_tree_vector ();
+ releasing_vec argvec;
init = build_special_member_call (NULL_TREE, complete_ctor_identifier,
&argvec, elttype, LOOKUP_NORMAL,
complain);
- release_tree_vector (argvec);
- init = build_aggr_init_expr (TREE_TYPE (init), init);
+ init = build_aggr_init_expr (elttype, init);
pre_init = true;
}
{
/* Initializing an element using value or default initialization
we just pre-built above. */
+ if (init == void_node)
+ /* Trivial default-init, don't do anything to the CONSTRUCTOR. */
+ return ctx->ctor;
eltinit = cxx_eval_constant_expression (&new_ctx, init, lval,
non_constant_p, overflow_p);
reuse = i == 0;
maybe_simplify_trivial_copy (target, init);
tree type = TREE_TYPE (target);
- target = cxx_eval_constant_expression (ctx, target,
- true,
- non_constant_p, overflow_p);
- if (*non_constant_p)
- return t;
-
- /* cxx_eval_array_reference for lval = true allows references one past
- end of array, because it does not know if it is just taking address
- (which is valid), or actual dereference. Here we know it is
- a dereference, so diagnose it here. */
- for (tree probe = target; probe; )
- {
- switch (TREE_CODE (probe))
- {
- case ARRAY_REF:
- tree nelts, ary;
- ary = TREE_OPERAND (probe, 0);
- nelts = get_array_or_vector_nelts (ctx, TREE_TYPE (ary),
- non_constant_p, overflow_p);
- VERIFY_CONSTANT (nelts);
- gcc_assert (TREE_CODE (nelts) == INTEGER_CST
- && TREE_CODE (TREE_OPERAND (probe, 1)) == INTEGER_CST);
- if (wi::to_widest (TREE_OPERAND (probe, 1)) == wi::to_widest (nelts))
- {
- diag_array_subscript (ctx, ary, TREE_OPERAND (probe, 1));
- *non_constant_p = true;
- return t;
- }
- /* FALLTHRU */
-
- case BIT_FIELD_REF:
- case COMPONENT_REF:
- probe = TREE_OPERAND (probe, 0);
- continue;
-
- default:
- probe = NULL_TREE;
- continue;
- }
+ bool preeval = SCALAR_TYPE_P (type) || TREE_CODE (t) == MODIFY_EXPR;
+ if (preeval)
+ {
+ /* Evaluate the value to be stored without knowing what object it will be
+ stored in, so that any side-effects happen first. */
+ if (!SCALAR_TYPE_P (type))
+ new_ctx.ctor = new_ctx.object = NULL_TREE;
+ init = cxx_eval_constant_expression (&new_ctx, init, false,
+ non_constant_p, overflow_p);
+ if (*non_constant_p)
+ return t;
}
- if (!same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (target), type))
+ bool evaluated = false;
+ if (lval)
{
- /* For initialization of an empty base, the original target will be
- *(base*)this, which the above evaluation resolves to the object
- argument, which has the derived type rather than the base type. In
- this situation, just evaluate the initializer and return, since
- there's no actual data to store. */
- gcc_assert (is_empty_class (type));
- return cxx_eval_constant_expression (ctx, init, false,
- non_constant_p, overflow_p);
+ /* If we want to return a reference to the target, we need to evaluate it
+ as a whole; otherwise, only evaluate the innermost piece to avoid
+ building up unnecessary *_REFs. */
+ target = cxx_eval_constant_expression (ctx, target, true,
+ non_constant_p, overflow_p);
+ evaluated = true;
+ if (*non_constant_p)
+ return t;
}
- /* And then find the underlying variable. */
- vec<tree,va_gc> *refs = make_tree_vector();
+ /* Find the underlying variable. */
+ releasing_vec refs;
tree object = NULL_TREE;
for (tree probe = target; object == NULL_TREE; )
{
case BIT_FIELD_REF:
case COMPONENT_REF:
case ARRAY_REF:
- vec_safe_push (refs, TREE_OPERAND (probe, 1));
- vec_safe_push (refs, TREE_TYPE (probe));
- probe = TREE_OPERAND (probe, 0);
+ {
+ tree ob = TREE_OPERAND (probe, 0);
+ tree elt = TREE_OPERAND (probe, 1);
+ if (TREE_CODE (probe) == ARRAY_REF)
+ {
+ elt = eval_and_check_array_index (ctx, probe, false,
+ non_constant_p, overflow_p);
+ if (*non_constant_p)
+ return t;
+ }
+ vec_safe_push (refs, elt);
+ vec_safe_push (refs, TREE_TYPE (probe));
+ probe = ob;
+ }
break;
default:
- object = probe;
+ if (evaluated)
+ object = probe;
+ else
+ {
+ probe = cxx_eval_constant_expression (ctx, probe, true,
+ non_constant_p, overflow_p);
+ evaluated = true;
+ if (*non_constant_p)
+ return t;
+ }
+ break;
}
}
type = TREE_TYPE (object);
bool no_zero_init = true;
- vec<tree,va_gc> *ctors = make_tree_vector ();
+ releasing_vec ctors;
while (!refs->is_empty())
{
if (*valp == NULL_TREE)
if (code == UNION_TYPE && CONSTRUCTOR_NELTS (*valp)
&& CONSTRUCTOR_ELT (*valp, 0)->index != index)
- /* Changing active member. */
- vec_safe_truncate (CONSTRUCTOR_ELTS (*valp), 0);
+ {
+ if (cxx_dialect < cxx2a)
+ {
+ if (!ctx->quiet)
+ error_at (cp_expr_loc_or_loc (t, input_location),
+ "change of the active member of a union "
+ "from %qD to %qD",
+ CONSTRUCTOR_ELT (*valp, 0)->index,
+ index);
+ *non_constant_p = true;
+ }
+ /* Changing active member. */
+ vec_safe_truncate (CONSTRUCTOR_ELTS (*valp), 0);
+ no_zero_init = true;
+ }
for (idx = 0;
vec_safe_iterate (CONSTRUCTOR_ELTS (*valp), idx, &cep);
}
valp = &cep->value;
}
- release_tree_vector (refs);
- if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
+ if (!preeval)
{
/* Create a new CONSTRUCTOR in case evaluation of the initializer
wants to modify it. */
*valp = build_constructor (type, NULL);
CONSTRUCTOR_NO_CLEARING (*valp) = no_zero_init;
}
- else if (TREE_CODE (*valp) == PTRMEM_CST)
- *valp = cplus_expand_constant (*valp);
new_ctx.ctor = *valp;
new_ctx.object = target;
+ init = cxx_eval_constant_expression (&new_ctx, init, false,
+ non_constant_p, overflow_p);
+ if (ctors->is_empty())
+ /* The hash table might have moved since the get earlier. */
+ valp = ctx->values->get (object);
}
- init = cxx_eval_constant_expression (&new_ctx, init, false,
- non_constant_p, overflow_p);
/* Don't share a CONSTRUCTOR that might be changed later. */
init = unshare_constructor (init);
- if (target == object)
- /* The hash table might have moved since the get earlier. */
- valp = ctx->values->get (object);
- if (TREE_CODE (init) == CONSTRUCTOR)
+ if (*valp && TREE_CODE (*valp) == CONSTRUCTOR
+ && TREE_CODE (init) == CONSTRUCTOR)
{
/* An outer ctx->ctor might be pointing to *valp, so replace
its contents. */
+ if (!same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (init),
+ TREE_TYPE (*valp)))
+ {
+ /* For initialization of an empty base, the original target will be
+ *(base*)this, evaluation of which resolves to the object
+ argument, which has the derived type rather than the base type. In
+ this situation, just evaluate the initializer and return, since
+ there's no actual data to store. */
+ gcc_assert (is_empty_class (TREE_TYPE (init)) && !lval);
+ return init;
+ }
CONSTRUCTOR_ELTS (*valp) = CONSTRUCTOR_ELTS (init);
TREE_CONSTANT (*valp) = TREE_CONSTANT (init);
TREE_SIDE_EFFECTS (*valp) = TREE_SIDE_EFFECTS (init);
bool c = TREE_CONSTANT (init);
bool s = TREE_SIDE_EFFECTS (init);
if (!c || s)
- FOR_EACH_VEC_SAFE_ELT (ctors, i, elt)
+ FOR_EACH_VEC_ELT (*ctors, i, elt)
{
if (!c)
TREE_CONSTANT (elt) = false;
if (s)
TREE_SIDE_EFFECTS (elt) = true;
}
- release_tree_vector (ctors);
if (*non_constant_p)
return t;
tree store = build2 (MODIFY_EXPR, type, op, mod);
cxx_eval_constant_expression (ctx, store,
true, non_constant_p, overflow_p);
+ ggc_free (store);
/* And the value of the expression. */
if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR)
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);
}
continues (tree *jump_target)
{
return *jump_target
- && TREE_CODE (*jump_target) == LABEL_DECL
- && LABEL_DECL_CONTINUE (*jump_target);
+ && ((TREE_CODE (*jump_target) == LABEL_DECL
+ && LABEL_DECL_CONTINUE (*jump_target))
+ || TREE_CODE (*jump_target) == CONTINUE_STMT);
+
}
static bool
}
break;
+ case BREAK_STMT:
+ case CONTINUE_STMT:
+ /* These two are handled directly in cxx_eval_loop_expr by testing
+ breaks (jump_target) or continues (jump_target). */
+ break;
+
default:
gcc_unreachable ();
}
for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
{
tree stmt = tsi_stmt (i);
+ /* We've found a continue, so skip everything until we reach
+ the label its jumping to. */
+ if (continues (jump_target))
+ {
+ if (label_matches (ctx, jump_target, stmt))
+ /* Found it. */
+ *jump_target = NULL_TREE;
+ else
+ continue;
+ }
if (TREE_CODE (stmt) == DEBUG_BEGIN_STMT)
continue;
r = cxx_eval_constant_expression (ctx, stmt, false,
if (returns (jump_target) || breaks (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_loc (r, input_location),
+ "statement is not a constant expression");
+ *non_constant_p = true;
+ }
return r;
}
{
constexpr_ctx new_ctx = *ctx;
- tree body = TREE_OPERAND (t, 0);
+ tree body, cond = NULL_TREE, expr = NULL_TREE;
int count = 0;
+ switch (TREE_CODE (t))
+ {
+ case LOOP_EXPR:
+ body = LOOP_EXPR_BODY (t);
+ break;
+ case DO_STMT:
+ body = DO_BODY (t);
+ cond = DO_COND (t);
+ break;
+ case WHILE_STMT:
+ body = WHILE_BODY (t);
+ cond = WHILE_COND (t);
+ count = -1;
+ break;
+ case FOR_STMT:
+ if (FOR_INIT_STMT (t))
+ cxx_eval_constant_expression (ctx, FOR_INIT_STMT (t), /*lval*/false,
+ non_constant_p, overflow_p, jump_target);
+ if (*non_constant_p)
+ return NULL_TREE;
+ body = FOR_BODY (t);
+ cond = FOR_COND (t);
+ expr = FOR_EXPR (t);
+ count = -1;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ auto_vec<tree, 10> save_exprs;
+ new_ctx.save_exprs = &save_exprs;
do
{
- hash_set<tree> save_exprs;
- new_ctx.save_exprs = &save_exprs;
+ if (count != -1)
+ {
+ if (body)
+ cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (breaks (jump_target))
+ {
+ *jump_target = NULL_TREE;
+ break;
+ }
- cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
- non_constant_p, overflow_p, jump_target);
+ if (TREE_CODE (t) != LOOP_EXPR && continues (jump_target))
+ *jump_target = NULL_TREE;
+
+ if (expr)
+ cxx_eval_constant_expression (&new_ctx, expr, /*lval*/false,
+ non_constant_p, overflow_p,
+ jump_target);
+ }
+
+ if (cond)
+ {
+ tree res
+ = cxx_eval_constant_expression (&new_ctx, cond, /*lval*/false,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (res)
+ {
+ if (verify_constant (res, ctx->quiet, non_constant_p,
+ overflow_p))
+ break;
+ if (integer_zerop (res))
+ break;
+ }
+ else
+ gcc_assert (*jump_target);
+ }
/* Forget saved values of SAVE_EXPRs. */
- for (hash_set<tree>::iterator iter = save_exprs.begin();
- iter != save_exprs.end(); ++iter)
- new_ctx.values->remove (*iter);
+ unsigned int i;
+ tree save_expr;
+ FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
+ new_ctx.values->remove (save_expr);
+ save_exprs.truncate (0);
+
if (++count >= constexpr_loop_limit)
{
if (!ctx->quiet)
error_at (cp_expr_loc_or_loc (t, input_location),
"%<constexpr%> loop iteration count exceeds limit of %d "
- "(use -fconstexpr-loop-limit= to increase the limit)",
+ "(use %<-fconstexpr-loop-limit=%> to increase the limit)",
constexpr_loop_limit);
*non_constant_p = true;
break;
}
while (!returns (jump_target)
&& !breaks (jump_target)
- && !switches (jump_target)
+ && !continues (jump_target)
+ && (!switches (jump_target) || count == 0)
&& !*non_constant_p);
- if (breaks (jump_target))
- *jump_target = NULL_TREE;
+ /* Forget saved values of SAVE_EXPRs. */
+ unsigned int i;
+ tree save_expr;
+ FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
+ new_ctx.values->remove (save_expr);
return NULL_TREE;
}
bool *non_constant_p, bool *overflow_p,
tree *jump_target)
{
- tree cond = TREE_OPERAND (t, 0);
+ tree cond
+ = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_COND (t) : SWITCH_COND (t);
cond = cxx_eval_constant_expression (ctx, cond, false,
non_constant_p, overflow_p);
VERIFY_CONSTANT (cond);
*jump_target = cond;
- tree body = TREE_OPERAND (t, 1);
+ tree body
+ = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_BODY (t) : SWITCH_BODY (t);
constexpr_ctx new_ctx = *ctx;
constexpr_switch_state css = css_default_not_seen;
new_ctx.css_state = &css;
cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
bool lval,
bool *non_constant_p, bool *overflow_p,
- tree *jump_target)
+ tree *jump_target /* = NULL */)
{
- constexpr_ctx new_ctx;
- tree r = t;
-
if (jump_target && *jump_target)
{
/* If we are jumping, ignore all statements/expressions except those
case STATEMENT_LIST:
case LOOP_EXPR:
case COND_EXPR:
+ case IF_STMT:
+ case DO_STMT:
+ case WHILE_STMT:
+ case FOR_STMT:
break;
case LABEL_EXPR:
case CASE_LABEL_EXPR:
return NULL_TREE;
}
}
- if (t == error_mark_node)
+ if (error_operand_p (t))
{
*non_constant_p = true;
return t;
}
+
+ STRIP_ANY_LOCATION_WRAPPER (t);
+
if (CONSTANT_CLASS_P (t))
{
if (TREE_OVERFLOW (t))
return t;
}
+ /* Avoid excessively long constexpr evaluations. */
+ if (++*ctx->constexpr_ops_count >= constexpr_ops_limit)
+ {
+ if (!ctx->quiet)
+ error_at (cp_expr_loc_or_loc (t, input_location),
+ "%<constexpr%> evaluation operation count exceeds limit of "
+ "%wd (use %<-fconstexpr-ops-limit=%> to increase the limit)",
+ constexpr_ops_limit);
+ *ctx->constexpr_ops_count = INTTYPE_MINIMUM (HOST_WIDE_INT);
+ *non_constant_p = true;
+ return t;
+ }
+
+ constexpr_ctx new_ctx;
+ tree r = t;
+
tree_code tcode = TREE_CODE (t);
switch (tcode)
{
case VAR_DECL:
if (DECL_HAS_VALUE_EXPR_P (t))
- return cxx_eval_constant_expression (ctx, DECL_VALUE_EXPR (t),
- lval, non_constant_p, overflow_p);
+ {
+ if (is_normal_capture_proxy (t)
+ && current_function_decl == DECL_CONTEXT (t))
+ {
+ /* Function parms aren't constexpr within the function
+ definition, so don't try to look at the closure. But if the
+ captured variable is constant, try to evaluate it directly. */
+ r = DECL_CAPTURED_VARIABLE (t);
+ tree type = TREE_TYPE (t);
+ if (TYPE_REF_P (type) != TYPE_REF_P (TREE_TYPE (r)))
+ {
+ /* Adjust r to match the reference-ness of t. */
+ if (TYPE_REF_P (type))
+ r = build_address (r);
+ else
+ r = convert_from_reference (r);
+ }
+ }
+ else
+ r = DECL_VALUE_EXPR (t);
+ return cxx_eval_constant_expression (ctx, r, lval, non_constant_p,
+ overflow_p);
+ }
/* fall through */
case CONST_DECL:
/* We used to not check lval for CONST_DECL, but darwin.c uses
if (lval)
return t;
if (COMPLETE_TYPE_P (TREE_TYPE (t))
- && is_really_empty_class (TREE_TYPE (t)))
+ && is_really_empty_class (TREE_TYPE (t), /*ignore_vptr*/false))
{
/* If the class is empty, we aren't actually loading anything. */
r = build_constructor (TREE_TYPE (t), NULL);
else if (TYPE_REF_P (TREE_TYPE (t)))
/* Defer, there's no lvalue->rvalue conversion. */;
else if (COMPLETE_TYPE_P (TREE_TYPE (t))
- && is_really_empty_class (TREE_TYPE (t)))
+ && is_really_empty_class (TREE_TYPE (t), /*ignore_vptr*/false))
{
/* If the class is empty, we aren't actually loading anything. */
r = build_constructor (TREE_TYPE (t), NULL);
case DECL_EXPR:
{
r = DECL_EXPR_DECL (t);
+ if (TREE_CODE (r) == USING_DECL)
+ {
+ r = void_node;
+ break;
+ }
if (AGGREGATE_TYPE_P (TREE_TYPE (r))
|| VECTOR_TYPE_P (TREE_TYPE (r)))
{
{
if (!ctx->quiet)
{
+ auto_diagnostic_group d;
error ("temporary of non-literal type %qT in a "
"constant expression", TREE_TYPE (t));
explain_non_literal_class (TREE_TYPE (t));
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
lval,
non_constant_p, overflow_p);
+ /* FALLTHRU */
+ case BREAK_STMT:
+ case CONTINUE_STMT:
if (jump_target)
*jump_target = t;
else
non_constant_p, overflow_p);
ctx->values->put (t, r);
if (ctx->save_exprs)
- ctx->save_exprs->add (t);
+ ctx->save_exprs->safe_push (t);
}
break;
jump_target);
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)
+ /* Also evaluate the cleanup. */
+ cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
+ non_constant_p, overflow_p,
+ jump_target);
+ break;
+
/* These differ from cxx_eval_unary_expression in that this doesn't
check for a constant operand or result; an address can be
constant without its operand being, and vice versa. */
/* This function does more aggressive folding than fold itself. */
r = build_fold_addr_expr_with_type (op, TREE_TYPE (t));
if (TREE_CODE (r) == ADDR_EXPR && TREE_OPERAND (r, 0) == oldop)
- return t;
+ {
+ ggc_free (r);
+ return t;
+ }
break;
}
case SIZEOF_EXPR:
r = fold_sizeof_expr (t);
- VERIFY_CONSTANT (r);
+ /* In a template, fold_sizeof_expr may merely create a new SIZEOF_EXPR,
+ which could lead to an infinite recursion. */
+ if (TREE_CODE (r) != SIZEOF_EXPR)
+ r = cxx_eval_constant_expression (ctx, r, lval,
+ non_constant_p, overflow_p,
+ jump_target);
+ else
+ {
+ *non_constant_p = true;
+ gcc_assert (ctx->quiet);
+ }
+
break;
case COMPOUND_EXPR:
break;
case COND_EXPR:
+ case IF_STMT:
if (jump_target && *jump_target)
{
+ tree orig_jump = *jump_target;
+ tree arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 1))
+ ? TREE_OPERAND (t, 1) : void_node);
/* When jumping to a label, the label might be either in the
then or else blocks, so process then block first in skipping
mode first, and if we are still in the skipping mode at its end,
process the else block too. */
- r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
- lval, non_constant_p, overflow_p,
- jump_target);
- if (*jump_target)
- r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
- lval, non_constant_p, overflow_p,
- jump_target);
+ r = cxx_eval_constant_expression (ctx, arg, lval, non_constant_p,
+ overflow_p, jump_target);
+ /* It's possible that we found the label in the then block. But
+ it could have been followed by another jumping statement, e.g.
+ say we're looking for case 1:
+ if (cond)
+ {
+ // skipped statements
+ case 1:; // clears up *jump_target
+ return 1; // and sets it to a RETURN_EXPR
+ }
+ else { ... }
+ in which case we need not go looking to the else block.
+ (goto is not allowed in a constexpr function.) */
+ if (*jump_target == orig_jump)
+ {
+ arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 2))
+ ? TREE_OPERAND (t, 2) : void_node);
+ r = cxx_eval_constant_expression (ctx, arg, lval, non_constant_p,
+ overflow_p, jump_target);
+ }
break;
}
r = cxx_eval_conditional_expression (ctx, t, lval,
break;
case CONSTRUCTOR:
- if (TREE_CONSTANT (t))
+ if (TREE_CONSTANT (t) && reduced_constant_expression_p (t))
{
/* Don't re-process a constant CONSTRUCTOR, but do fold it to
VECTOR_CST if applicable. */
{
if (!ctx->quiet)
error_at (cp_expr_loc_or_loc (t, input_location),
- "a reinterpret_cast is not a constant expression");
+ "%<reinterpret_cast%> is not a constant expression");
*non_constant_p = true;
return t;
}
if (*non_constant_p)
return t;
tree type = TREE_TYPE (t);
+
+ if (VOID_TYPE_P (type))
+ return void_node;
+
if (TREE_CODE (op) == PTRMEM_CST
&& !TYPE_PTRMEM_P (type))
op = cplus_expand_constant (op);
conversion. */
return fold (t);
+ tree sop;
+
/* Handle an array's bounds having been deduced after we built
the wrapping expression. */
if (same_type_ignoring_tlq_and_bounds_p (type, TREE_TYPE (op)))
r = op;
+ else if (sop = tree_strip_nop_conversions (op),
+ sop != op && (same_type_ignoring_tlq_and_bounds_p
+ (type, TREE_TYPE (sop))))
+ r = sop;
else if (tcode == UNARY_PLUS_EXPR)
r = fold_convert (TREE_TYPE (t), op);
else
case MODOP_EXPR:
/* GCC internal stuff. */
case VA_ARG_EXPR:
- case OBJ_TYPE_REF:
case NON_DEPENDENT_EXPR:
case BASELINK:
case OFFSET_REF:
*non_constant_p = true;
break;
+ case OBJ_TYPE_REF:
+ {
+ /* Virtual function call. Let the constexpr machinery figure out
+ the dynamic type. */
+ int token = tree_to_shwi (OBJ_TYPE_REF_TOKEN (t));
+ tree obj = OBJ_TYPE_REF_OBJECT (t);
+ obj = cxx_eval_constant_expression (ctx, obj, lval, non_constant_p,
+ overflow_p);
+ /* We expect something in the form of &x.D.2103.D.2094; get x. */
+ if (TREE_CODE (obj) != ADDR_EXPR
+ || !DECL_P (get_base_address (TREE_OPERAND (obj, 0))))
+ {
+ if (!ctx->quiet)
+ error_at (cp_expr_loc_or_loc (t, input_location),
+ "expression %qE is not a constant expression", t);
+ *non_constant_p = true;
+ return t;
+ }
+ obj = TREE_OPERAND (obj, 0);
+ while (TREE_CODE (obj) == COMPONENT_REF
+ && DECL_FIELD_IS_BASE (TREE_OPERAND (obj, 1)))
+ obj = TREE_OPERAND (obj, 0);
+ tree objtype = TREE_TYPE (obj);
+ /* Find the function decl in the virtual functions list. TOKEN is
+ the DECL_VINDEX that says which function we're looking for. */
+ tree virtuals = BINFO_VIRTUALS (TYPE_BINFO (objtype));
+ if (TARGET_VTABLE_USES_DESCRIPTORS)
+ token /= MAX (TARGET_VTABLE_USES_DESCRIPTORS, 1);
+ r = TREE_VALUE (chain_index (token, virtuals));
+ break;
+ }
+
case PLACEHOLDER_EXPR:
/* Use of the value or address of the current object. */
if (tree ctor = lookup_placeholder (ctx, lval, TREE_TYPE (t)))
break;
case LOOP_EXPR:
+ case DO_STMT:
+ case WHILE_STMT:
+ case FOR_STMT:
cxx_eval_loop_expr (ctx, t,
non_constant_p, overflow_p, jump_target);
break;
case SWITCH_EXPR:
+ case SWITCH_STMT:
cxx_eval_switch_expr (ctx, t,
non_constant_p, overflow_p, jump_target);
break;
input_location = loc;
}
+/* ALLOW_NON_CONSTANT is false if T is required to be a constant expression.
+ STRICT has the same sense as for constant_value_1: true if we only allow
+ conforming C++ constant expressions, or false if we want a constant value
+ even if it doesn't conform.
+ MANIFESTLY_CONST_EVAL is true if T is manifestly const-evaluated as
+ per P0595 even when ALLOW_NON_CONSTANT is true. */
+
static tree
cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
- bool strict = true, tree object = NULL_TREE)
+ bool strict = true,
+ bool manifestly_const_eval = false,
+ tree object = NULL_TREE)
{
auto_timevar time (TV_CONSTEXPR);
bool non_constant_p = false;
bool overflow_p = false;
hash_map<tree,tree> map;
+ HOST_WIDE_INT constexpr_ctx_count = 0;
constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL,
- allow_non_constant, strict };
+ &constexpr_ctx_count, allow_non_constant, strict,
+ manifestly_const_eval || !allow_non_constant };
tree type = initialized_type (t);
tree r = t;
+ if (VOID_TYPE_P (type))
+ return t;
if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
{
/* In C++14 an NSDMI can participate in aggregate initialization,
return error_mark_node;
else if (non_constant_p && TREE_CONSTANT (r))
{
+ /* If __builtin_is_constant_evaluated () was evaluated to true
+ and the result is not a valid constant expression, we need to
+ punt. */
+ if (manifestly_const_eval)
+ return cxx_eval_outermost_constant_expr (t, true, strict,
+ false, object);
/* This isn't actually constant, so unset TREE_CONSTANT.
Don't clear TREE_CONSTANT on ADDR_EXPR, as the middle-end requires
it to be set if it is invariant address, even when it is not
r = build_nop (TREE_TYPE (r), r);
TREE_CONSTANT (r) = false;
}
- else if (non_constant_p || r == t)
+ else if (non_constant_p)
return t;
if (should_unshare)
if (TREE_CODE (r) == CONSTRUCTOR && CLASS_TYPE_P (TREE_TYPE (r)))
{
+ r = adjust_temp_type (type, r);
if (TREE_CODE (t) == TARGET_EXPR
&& TARGET_EXPR_INITIAL (t) == r)
return t;
- else
+ else if (TREE_CODE (t) != CONSTRUCTOR)
{
r = get_target_expr (r);
TREE_CONSTANT (r) = true;
- return r;
}
}
- else
- return r;
-}
-
-/* Returns true if T is a valid subexpression of a constant expression,
- even if it isn't itself a constant expression. */
-bool
-is_sub_constant_expr (tree t)
-{
- bool non_constant_p = false;
- bool overflow_p = false;
- hash_map <tree, tree> map;
-
- constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL, true, true };
-
- instantiate_constexpr_fns (t);
- cxx_eval_constant_expression (&ctx, t, false, &non_constant_p,
- &overflow_p);
- return !non_constant_p && !overflow_p;
+ return r;
}
/* If T represents a constant expression returns its reduced value.
tree
cxx_constant_value (tree t, tree decl)
{
- return cxx_eval_outermost_constant_expr (t, false, true, decl);
+ return cxx_eval_outermost_constant_expr (t, false, true, true, decl);
}
/* Helper routine for fold_simple function. Either return simplified
/* If T is a constant expression, returns its reduced value.
Otherwise, if T does not have TREE_CONSTANT set, returns T.
- Otherwise, returns a version of T without TREE_CONSTANT. */
+ Otherwise, returns a version of T without TREE_CONSTANT.
+ MANIFESTLY_CONST_EVAL is true if T is manifestly const-evaluated
+ as per P0595. */
static GTY((deletable)) hash_map<tree, tree> *cv_cache;
tree
-maybe_constant_value (tree t, tree decl)
+maybe_constant_value (tree t, tree decl, bool manifestly_const_eval)
{
tree r;
/* No caching or evaluation needed. */
return t;
+ if (manifestly_const_eval)
+ return cxx_eval_outermost_constant_expr (t, true, true, true, decl);
+
if (cv_cache == NULL)
cv_cache = hash_map<tree, tree>::create_ggc (101);
if (tree *cached = cv_cache->get (t))
return *cached;
- r = cxx_eval_outermost_constant_expr (t, true, true, decl);
+ r = cxx_eval_outermost_constant_expr (t, true, true, false, decl);
gcc_checking_assert (r == t
|| CONVERT_EXPR_P (t)
|| TREE_CODE (t) == VIEW_CONVERT_EXPR
clear_fold_cache ();
}
+/* Internal function handling expressions in templates for
+ fold_non_dependent_expr and fold_non_dependent_init.
+
+ If we're in a template, but T isn't value dependent, simplify
+ it. We're supposed to treat:
+
+ template <typename T> void f(T[1 + 1]);
+ template <typename T> void f(T[2]);
+
+ as two declarations of the same function, for example. */
+
+static tree
+fold_non_dependent_expr_template (tree t, tsubst_flags_t complain,
+ bool manifestly_const_eval)
+{
+ gcc_assert (processing_template_decl);
+
+ if (is_nondependent_constant_expression (t))
+ {
+ processing_template_decl_sentinel s;
+ t = instantiate_non_dependent_expr_internal (t, complain);
+
+ if (type_unknown_p (t) || BRACE_ENCLOSED_INITIALIZER_P (t))
+ {
+ if (TREE_OVERFLOW_P (t))
+ {
+ t = build_nop (TREE_TYPE (t), t);
+ TREE_CONSTANT (t) = false;
+ }
+ return t;
+ }
+
+ tree r = cxx_eval_outermost_constant_expr (t, true, true,
+ manifestly_const_eval,
+ NULL_TREE);
+ /* cp_tree_equal looks through NOPs, so allow them. */
+ gcc_checking_assert (r == t
+ || CONVERT_EXPR_P (t)
+ || TREE_CODE (t) == VIEW_CONVERT_EXPR
+ || (TREE_CONSTANT (t) && !TREE_CONSTANT (r))
+ || !cp_tree_equal (r, t));
+ return r;
+ }
+ else if (TREE_OVERFLOW_P (t))
+ {
+ t = build_nop (TREE_TYPE (t), t);
+ TREE_CONSTANT (t) = false;
+ }
+
+ return t;
+}
+
/* Like maybe_constant_value but first fully instantiate the argument.
Note: this is equivalent to instantiate_non_dependent_expr_sfinae
- (t, tf_none) followed by maybe_constant_value but is more efficient,
- because calls instantiation_dependent_expression_p and
- potential_constant_expression at most once. */
+ (t, complain) followed by maybe_constant_value but is more efficient,
+ because it calls instantiation_dependent_expression_p and
+ potential_constant_expression at most once.
+ The manifestly_const_eval argument is passed to maybe_constant_value.
+
+ Callers should generally pass their active complain, or if they are in a
+ non-template, diagnosing context, they can use the default of
+ tf_warning_or_error. Callers that might be within a template context, don't
+ have a complain parameter, and aren't going to remember the result for long
+ (e.g. null_ptr_cst_p), can pass tf_none and deal with error_mark_node
+ appropriately. */
tree
-fold_non_dependent_expr (tree t)
+fold_non_dependent_expr (tree t,
+ tsubst_flags_t complain /* = tf_warning_or_error */,
+ bool manifestly_const_eval /* = false */)
{
if (t == NULL_TREE)
return NULL_TREE;
- /* If we're in a template, but T isn't value dependent, simplify
- it. We're supposed to treat:
+ if (processing_template_decl)
+ return fold_non_dependent_expr_template (t, complain,
+ manifestly_const_eval);
- template <typename T> void f(T[1 + 1]);
- template <typename T> void f(T[2]);
+ return maybe_constant_value (t, NULL_TREE, manifestly_const_eval);
+}
- as two declarations of the same function, for example. */
- if (processing_template_decl)
- {
- if (is_nondependent_constant_expression (t))
- {
- processing_template_decl_sentinel s;
- t = instantiate_non_dependent_expr_internal (t, tf_none);
- if (type_unknown_p (t)
- || BRACE_ENCLOSED_INITIALIZER_P (t))
- {
- if (TREE_OVERFLOW_P (t))
- {
- t = build_nop (TREE_TYPE (t), t);
- TREE_CONSTANT (t) = false;
- }
- return t;
- }
+/* Like maybe_constant_init but first fully instantiate the argument. */
- tree r = cxx_eval_outermost_constant_expr (t, true, true, NULL_TREE);
- /* cp_tree_equal looks through NOPs, so allow them. */
- gcc_checking_assert (r == t
- || CONVERT_EXPR_P (t)
- || TREE_CODE (t) == VIEW_CONVERT_EXPR
- || (TREE_CONSTANT (t) && !TREE_CONSTANT (r))
- || !cp_tree_equal (r, t));
- return r;
- }
- else if (TREE_OVERFLOW_P (t))
+tree
+fold_non_dependent_init (tree t,
+ tsubst_flags_t complain /*=tf_warning_or_error*/,
+ bool manifestly_const_eval /*=false*/)
+{
+ if (t == NULL_TREE)
+ return NULL_TREE;
+
+ if (processing_template_decl)
+ {
+ t = fold_non_dependent_expr_template (t, complain,
+ manifestly_const_eval);
+ /* maybe_constant_init does this stripping, so do it here too. */
+ if (TREE_CODE (t) == TARGET_EXPR)
{
- t = build_nop (TREE_TYPE (t), t);
- TREE_CONSTANT (t) = false;
+ tree init = TARGET_EXPR_INITIAL (t);
+ if (TREE_CODE (init) == CONSTRUCTOR)
+ t = init;
}
return t;
}
- return maybe_constant_value (t);
+ return maybe_constant_init (t, NULL_TREE, manifestly_const_eval);
}
/* Like maybe_constant_value, but returns a CONSTRUCTOR directly, rather
- than wrapped in a TARGET_EXPR. */
+ than wrapped in a TARGET_EXPR.
+ ALLOW_NON_CONSTANT is false if T is required to be a constant expression.
+ MANIFESTLY_CONST_EVAL is true if T is manifestly const-evaluated as
+ per P0595 even when ALLOW_NON_CONSTANT is true. */
static tree
-maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant)
+maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant,
+ bool manifestly_const_eval)
{
if (!t)
return t;
else if (CONSTANT_CLASS_P (t) && allow_non_constant)
/* No evaluation needed. */;
else
- t = cxx_eval_outermost_constant_expr (t, allow_non_constant, false, decl);
+ t = cxx_eval_outermost_constant_expr (t, allow_non_constant,
+ /*strict*/false,
+ manifestly_const_eval, decl);
if (TREE_CODE (t) == TARGET_EXPR)
{
tree init = TARGET_EXPR_INITIAL (t);
/* Wrapper for maybe_constant_init_1 which permits non constants. */
tree
-maybe_constant_init (tree t, tree decl)
+maybe_constant_init (tree t, tree decl, bool manifestly_const_eval)
{
- return maybe_constant_init_1 (t, decl, true);
+ return maybe_constant_init_1 (t, decl, true, manifestly_const_eval);
}
/* Wrapper for maybe_constant_init_1 which does not permit non constants. */
tree
cxx_constant_init (tree t, tree decl)
{
- return maybe_constant_init_1 (t, decl, false);
+ return maybe_constant_init_1 (t, decl, false, true);
}
#if 0
}
#endif
+/* Data structure for passing data from potential_constant_expression_1
+ to check_for_return_continue via cp_walk_tree. */
+struct check_for_return_continue_data {
+ hash_set<tree> *pset;
+ tree continue_stmt;
+};
+
+/* 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 if RETURN_EXPR is not found. */
+static tree
+check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
+{
+ tree t = *tp, s;
+ check_for_return_continue_data *d = (check_for_return_continue_data *) data;
+ switch (TREE_CODE (t))
+ {
+ case RETURN_EXPR:
+ return t;
+
+ case CONTINUE_STMT:
+ if (d->continue_stmt == NULL_TREE)
+ d->continue_stmt = t;
+ break;
+
+#define RECUR(x) \
+ if (tree r = cp_walk_tree (&x, check_for_return_continue, data, \
+ d->pset)) \
+ return r
+
+ /* For loops, walk subtrees manually, so that continue stmts found
+ inside of the bodies of the loops are ignored. */
+ case DO_STMT:
+ *walk_subtrees = 0;
+ RECUR (DO_COND (t));
+ s = d->continue_stmt;
+ RECUR (DO_BODY (t));
+ d->continue_stmt = s;
+ break;
+
+ case WHILE_STMT:
+ *walk_subtrees = 0;
+ RECUR (WHILE_COND (t));
+ s = d->continue_stmt;
+ RECUR (WHILE_BODY (t));
+ d->continue_stmt = s;
+ break;
+
+ case FOR_STMT:
+ *walk_subtrees = 0;
+ RECUR (FOR_INIT_STMT (t));
+ RECUR (FOR_COND (t));
+ RECUR (FOR_EXPR (t));
+ s = d->continue_stmt;
+ RECUR (FOR_BODY (t));
+ d->continue_stmt = s;
+ break;
+
+ case RANGE_FOR_STMT:
+ *walk_subtrees = 0;
+ RECUR (RANGE_FOR_EXPR (t));
+ s = d->continue_stmt;
+ RECUR (RANGE_FOR_BODY (t));
+ d->continue_stmt = s;
+ break;
+#undef RECUR
+
+ case STATEMENT_LIST:
+ case CONSTRUCTOR:
+ break;
+
+ default:
+ if (!EXPR_P (t))
+ *walk_subtrees = 0;
+ break;
+ }
+
+ return NULL_TREE;
+}
+
/* Return true if T denotes a potentially constant expression. Issue
diagnostic as appropriate under control of FLAGS. If WANT_RVAL is true,
an lvalue-rvalue conversion is implied. If NOW is true, we want to
static bool
potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
- tsubst_flags_t flags)
+ tsubst_flags_t flags, tree *jump_target)
{
#define RECUR(T,RV) \
- potential_constant_expression_1 ((T), (RV), strict, now, flags)
+ potential_constant_expression_1 ((T), (RV), strict, now, flags, jump_target)
enum { any = false, rval = true };
int i;
if (t == NULL_TREE)
return true;
location_t loc = cp_expr_loc_or_loc (t, input_location);
- if (TREE_THIS_VOLATILE (t) && !DECL_P (t))
+
+ if (*jump_target)
+ /* If we are jumping, ignore everything. This is simpler than the
+ cxx_eval_constant_expression handling because we only need to be
+ conservatively correct, and we don't necessarily have a constant value
+ available, so we don't bother with switch tracking. */
+ return true;
+
+ if (TREE_THIS_VOLATILE (t) && want_rval)
{
if (flags & tf_error)
- error_at (loc, "expression %qE has side-effects", t);
+ error_at (loc, "lvalue-to-rvalue conversion of a volatile lvalue "
+ "%qE with type %qT", t, TREE_TYPE (t));
return false;
}
if (CONSTANT_CLASS_P (t))
case USING_DECL:
case USING_STMT:
case PLACEHOLDER_EXPR:
- case BREAK_STMT:
- case CONTINUE_STMT:
case REQUIRES_EXPR:
case STATIC_ASSERT:
case DEBUG_BEGIN_STMT:
return true;
+ case RETURN_EXPR:
+ if (!RECUR (TREE_OPERAND (t, 0), any))
+ return false;
+ /* FALLTHROUGH */
+
+ case BREAK_STMT:
+ case CONTINUE_STMT:
+ *jump_target = t;
+ return true;
+
case PARM_DECL:
if (now)
{
case IFN_SUB_OVERFLOW:
case IFN_MUL_OVERFLOW:
case IFN_LAUNDER:
+ case IFN_VEC_CONVERT:
bail = false;
+ break;
default:
break;
if (!DECL_DECLARED_CONSTEXPR_P (fun)
/* Allow any built-in function; if the expansion
isn't constant, we'll deal with that then. */
- && !is_builtin_fn (fun))
+ && !fndecl_built_in_p (fun))
{
if (flags & tf_error)
{
constexpr substitution might not use the value. */
bool sub_now = false;
if (!potential_constant_expression_1 (x, rval, strict,
- sub_now, flags))
+ sub_now, flags,
+ jump_target))
return false;
i = 1;
}
substitution might not use the value of the argument. */
bool sub_now = false;
if (!potential_constant_expression_1 (x, rv, strict,
- sub_now, flags))
+ sub_now, flags, jump_target))
return false;
}
return true;
|| (DECL_INITIAL (t)
&& !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (t)))
&& COMPLETE_TYPE_P (TREE_TYPE (t))
- && !is_really_empty_class (TREE_TYPE (t)))
+ && !is_really_empty_class (TREE_TYPE (t), /*ignore_vptr*/false))
{
if (flags & tf_error)
non_const_var_error (t);
return true;
case NOP_EXPR:
+ if (REINTERPRET_CAST_P (t))
+ {
+ if (flags & tf_error)
+ error_at (loc, "%<reinterpret_cast%> is not a constant expression");
+ return false;
+ }
+ /* FALLTHRU */
case CONVERT_EXPR:
case VIEW_CONVERT_EXPR:
/* -- a reinterpret_cast. FIXME not implemented, and this rule
may change to something more specific to type-punning (DR 1312). */
{
tree from = TREE_OPERAND (t, 0);
- if (INDIRECT_TYPE_P (TREE_TYPE (t))
- && TREE_CODE (from) == INTEGER_CST
- && !integer_zerop (from))
+ if (location_wrapper_p (t))
+ return (RECUR (from, want_rval));
+ if (INDIRECT_TYPE_P (TREE_TYPE (t)))
{
- if (flags & tf_error)
- error_at (loc, "reinterpret_cast from integer to pointer");
- return false;
+ STRIP_ANY_LOCATION_WRAPPER (from);
+ if (TREE_CODE (from) == INTEGER_CST
+ && !integer_zerop (from))
+ {
+ if (flags & tf_error)
+ error_at (loc,
+ "%<reinterpret_cast%> from integer to pointer");
+ return false;
+ }
}
return (RECUR (from, TREE_CODE (t) != VIEW_CONVERT_EXPR));
}
return false;
if (!RECUR (DO_BODY (t), any))
return false;
+ if (breaks (jump_target) || continues (jump_target))
+ *jump_target = NULL_TREE;
return true;
case FOR_STMT:
if (!RECUR (FOR_INIT_STMT (t), any))
return false;
- if (!RECUR (FOR_COND (t), rval))
+ tmp = FOR_COND (t);
+ if (!RECUR (tmp, rval))
return false;
+ if (tmp)
+ {
+ if (!processing_template_decl)
+ tmp = cxx_eval_outermost_constant_expr (tmp, true);
+ /* If we couldn't evaluate the condition, it might not ever be
+ true. */
+ if (!integer_onep (tmp))
+ return true;
+ }
if (!RECUR (FOR_EXPR (t), any))
return false;
if (!RECUR (FOR_BODY (t), any))
return false;
+ if (breaks (jump_target) || continues (jump_target))
+ *jump_target = NULL_TREE;
return true;
case RANGE_FOR_STMT:
+ if (!RECUR (RANGE_FOR_INIT_STMT (t), any))
+ return false;
if (!RECUR (RANGE_FOR_EXPR (t), any))
return false;
if (!RECUR (RANGE_FOR_BODY (t), any))
return false;
+ if (breaks (jump_target) || continues (jump_target))
+ *jump_target = NULL_TREE;
return true;
case WHILE_STMT:
- if (!RECUR (WHILE_COND (t), rval))
+ tmp = WHILE_COND (t);
+ if (!RECUR (tmp, rval))
return false;
+ if (!processing_template_decl)
+ tmp = cxx_eval_outermost_constant_expr (tmp, true);
+ /* If we couldn't evaluate the condition, it might not ever be true. */
+ if (!integer_onep (tmp))
+ return true;
if (!RECUR (WHILE_BODY (t), any))
return false;
+ if (breaks (jump_target) || continues (jump_target))
+ *jump_target = NULL_TREE;
return true;
case SWITCH_STMT:
if (!RECUR (SWITCH_STMT_COND (t), rval))
return false;
/* FIXME we don't check SWITCH_STMT_BODY currently, because even
- unreachable labels would be checked. */
+ unreachable labels would be checked and it is enough if there is
+ a single switch cond value for which it is a valid constant
+ expression. We need to check if there are any RETURN_EXPRs
+ or CONTINUE_STMTs inside of the body though, as in that case
+ we need to set *jump_target. */
+ else
+ {
+ hash_set<tree> pset;
+ check_for_return_continue_data data = { &pset, NULL_TREE };
+ if (tree ret_expr
+ = cp_walk_tree (&SWITCH_STMT_BODY (t), check_for_return_continue,
+ &data, &pset))
+ /* The switch might return. */
+ *jump_target = ret_expr;
+ else if (data.continue_stmt)
+ /* The switch can't return, but might continue. */
+ *jump_target = data.continue_stmt;
+ }
return true;
case STMT_EXPR:
case OMP_ATOMIC_READ:
case OMP_ATOMIC_CAPTURE_OLD:
case OMP_ATOMIC_CAPTURE_NEW:
+ case OMP_DEPOBJ:
case OACC_PARALLEL:
case OACC_KERNELS:
case OACC_DATA:
case OACC_UPDATE:
/* GCC internal stuff. */
case VA_ARG_EXPR:
- case OBJ_TYPE_REF:
case TRANSACTION_EXPR:
case ASM_EXPR:
case AT_ENCODE_EXPR:
error_at (loc, "expression %qE is not a constant expression", t);
return false;
+ case OBJ_TYPE_REF:
+ if (cxx_dialect >= cxx2a)
+ /* In C++2a virtual calls can be constexpr, don't give up yet. */
+ return true;
+ else if (flags & tf_error)
+ error_at (loc,
+ "virtual functions cannot be %<constexpr%> before C++2a");
+ return false;
+
case TYPEID_EXPR:
/* -- a typeid expression whose operand is of polymorphic
class type; */
&& TYPE_POLYMORPHIC_P (TREE_TYPE (e)))
{
if (flags & tf_error)
- error_at (loc, "typeid-expression is not a constant expression "
+ error_at (loc, "%<typeid%> is not a constant expression "
"because %qE is of polymorphic type", e);
return false;
}
case PAREN_EXPR:
case NON_DEPENDENT_EXPR:
/* For convenience. */
- case RETURN_EXPR:
case LOOP_EXPR:
case EXIT_EXPR:
return RECUR (TREE_OPERAND (t, 0), want_rval);
{
if (flags & tf_error)
{
+ auto_diagnostic_group d;
error_at (loc, "temporary of non-literal type %qT in a "
"constant expression", TREE_TYPE (t));
explain_non_literal_class (TREE_TYPE (t));
return RECUR (TREE_OPERAND (t, 1), want_rval);
for (i = 1; i < 3; ++i)
if (potential_constant_expression_1 (TREE_OPERAND (t, i),
- want_rval, strict, now, tf_none))
+ want_rval, strict, now,
+ tf_none, jump_target))
return true;
if (flags & tf_error)
error_at (loc, "expression %qE is not a constant expression", t);
tree *target = &TREE_OPERAND (t, 0);
/* Gotos representing break and continue are OK. */
if (breaks (target) || continues (target))
- return true;
+ {
+ *jump_target = *target;
+ return true;
+ }
if (flags & tf_error)
error_at (loc, "%<goto%> is not a constant expression");
return false;
#undef RECUR
}
+bool
+potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
+ tsubst_flags_t flags)
+{
+ tree target = NULL_TREE;
+ return potential_constant_expression_1 (t, want_rval, strict, now,
+ flags, &target);
+}
+
/* The main entry point to the above. */
bool