#include "tree-iterator.h"
#include "gimplify.h"
#include "builtins.h"
+#include "tree-inline.h"
static bool verify_constant (tree, bool, bool *, bool *);
#define VERIFY_CONSTANT(X) \
error ("the type %qT of constexpr variable %qD is not literal",
type, decl);
else
- error ("variable %qD of non-literal type %qT in %<constexpr%> "
- "function", decl, type);
+ {
+ error ("variable %qD of non-literal type %qT in %<constexpr%> "
+ "function", decl, type);
+ cp_function_chain->invalid_constexpr = true;
+ }
explain_non_literal_class (type);
return NULL;
}
if (TREE_CODE (t) == CONVERT_EXPR)
t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == INIT_EXPR
- || TREE_CODE (t) == MODIFY_EXPR)
+ /* vptr initialization shows up as a MODIFY_EXPR. In C++14 we only
+ use what this function builds for cx_check_missing_mem_inits, and
+ assignment in the ctor body doesn't count. */
+ || (cxx_dialect < cxx14 && TREE_CODE (t) == MODIFY_EXPR))
{
member = TREE_OPERAND (t, 0);
init = break_out_target_exprs (TREE_OPERAND (t, 1));
}
else if (TREE_CODE (t) == CALL_EXPR)
{
+ tree fn = get_callee_fndecl (t);
+ if (!fn || !DECL_CONSTRUCTOR_P (fn))
+ /* We're only interested in calls to subobject constructors. */
+ return true;
member = CALL_EXPR_ARG (t, 0);
/* We don't use build_cplus_new here because it complains about
abstract bases. Leaving the call unwrapped means that it has the
}
else if (TREE_CODE (t) == BIND_EXPR)
return build_data_member_initialization (BIND_EXPR_BODY (t), vec);
- else if (TREE_CODE (t) == DECL_EXPR
- || TREE_CODE (t) == USING_STMT)
- /* Declaring a temporary, don't add it to the CONSTRUCTOR.
- Likewise for using directives. */
- return true;
else
- gcc_unreachable ();
+ /* Don't add anything else to the CONSTRUCTOR. */
+ return true;
if (INDIRECT_REF_P (member))
member = TREE_OPERAND (member, 0);
if (TREE_CODE (member) == NOP_EXPR)
{
gcc_assert (TREE_CODE (t) == BIND_EXPR);
- if (cxx_dialect >= cxx14)
- return true;
-
for (tree var = BIND_EXPR_VARS (t); var; var = DECL_CHAIN (var))
if (TREE_CODE (var) == TYPE_DECL
&& DECL_IMPLICIT_TYPEDEF_P (var))
case DECL_EXPR:
if (TREE_CODE (DECL_EXPR_DECL (list)) == USING_DECL)
return true;
- if (cxx_dialect >= cxx14)
- return true;
return false;
case CLEANUP_POINT_EXPR:
bool
check_constexpr_ctor_body (tree last, tree list, bool complain)
{
+ /* C++14 doesn't require a constexpr ctor to have an empty body. */
+ if (cxx_dialect >= cxx14)
+ return true;
+
bool ok = true;
if (TREE_CODE (list) == STATEMENT_LIST)
{
case DECL_EXPR:
if (TREE_CODE (DECL_EXPR_DECL (body)) == USING_DECL)
return NULL_TREE;
- if (cxx_dialect >= cxx14)
- return NULL_TREE;
return error_mark_node;
case CLEANUP_POINT_EXPR:
if (DECL_CONSTRUCTOR_P (fun))
body = build_constexpr_constructor_member_initializers
(DECL_CONTEXT (fun), body);
- else
+ else if (cxx_dialect < cxx14)
{
if (TREE_CODE (body) == EH_SPEC_BLOCK)
body = EH_SPEC_STMTS (body);
gcc_unreachable();
break;
}
- if (TREE_CODE (fun) == ADDR_EXPR
+ if (fun && TREE_CODE (fun) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (fun, 0)) == FUNCTION_DECL)
fun = TREE_OPERAND (fun, 0);
return fun;
return cp_fold_convert (type, temp);
}
+/* True if we want to use the new handling of constexpr calls based on
+ DECL_SAVED_TREE. Currently only active for C++14 mode. */
+#define use_new_call (cxx_dialect >= cxx14)
+
/* Subroutine of cxx_eval_call_expression.
We are processing a call expression (either CALL_EXPR or
AGGR_INIT_EXPR) in the context of CTX. Evaluate
tree fun = new_call->fundef->decl;
tree parms = DECL_ARGUMENTS (fun);
int i;
+ tree *p = &new_call->bindings;
for (i = 0; i < nargs; ++i)
{
tree x, arg;
x = get_nth_callarg (t, i);
/* 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 or the AGGR_INIT_EXPR. */
+ which case we get the real argument from ctx. */
if (i == 0 && DECL_CONSTRUCTOR_P (fun)
&& is_dummy_object (x))
{
x = ctx->object;
- if (!x)
- x = AGGR_INIT_EXPR_SLOT (t);
x = cp_build_addr_expr (x, tf_warning_or_error);
}
- if (parms && DECL_BY_REFERENCE (parms))
+ if (parms && DECL_BY_REFERENCE (parms) && !use_new_call)
{
/* cp_genericize made this a reference for argument passing, but
- we don't want to treat it like one for constexpr evaluation. */
+ we don't want to treat it like one for C++11 constexpr
+ evaluation. C++14 constexpr evaluation uses the genericized
+ DECL_SAVED_TREE. */
gcc_assert (TREE_CODE (type) == REFERENCE_TYPE);
gcc_assert (TREE_CODE (TREE_TYPE (x)) == REFERENCE_TYPE);
type = TREE_TYPE (type);
/* Make sure the binding has the same type as the parm. */
if (TREE_CODE (type) != REFERENCE_TYPE)
arg = adjust_temp_type (type, arg);
- new_call->bindings = tree_cons (parms, arg, new_call->bindings);
+ *p = build_tree_list (parms, arg);
+ p = &TREE_CHAIN (*p);
next:
parms = TREE_CHAIN (parms);
}
return t;
}
}
+
+ constexpr_ctx new_ctx = *ctx;
+ if (DECL_CONSTRUCTOR_P (fun) && !ctx->object
+ && TREE_CODE (t) == AGGR_INIT_EXPR)
+ {
+ /* We want to have an initialization target for an AGGR_INIT_EXPR.
+ If we don't already have one in CTX, use the AGGR_INIT_EXPR_SLOT. */
+ new_ctx.object = AGGR_INIT_EXPR_SLOT (t);
+ tree ctor = new_ctx.ctor = build_constructor (DECL_CONTEXT (fun), NULL);
+ CONSTRUCTOR_NO_IMPLICIT_ZERO (ctor) = true;
+ ctx->values->put (new_ctx.object, ctor);
+ ctx = &new_ctx;
+ }
+
cxx_bind_parameters_in_call (ctx, t, &new_call,
allow_non_constant, non_constant_p, overflow_p);
if (*non_constant_p)
result = entry->result;
if (!result || result == error_mark_node)
{
- constexpr_ctx new_ctx = *ctx;
- new_ctx.call = &new_call;
- result = (cxx_eval_constant_expression
- (&new_ctx, new_call.fundef->body,
- allow_non_constant, addr,
- non_constant_p, overflow_p));
+ if (!use_new_call)
+ {
+ new_ctx.call = &new_call;
+ result = (cxx_eval_constant_expression
+ (&new_ctx, new_call.fundef->body,
+ allow_non_constant, addr,
+ non_constant_p, overflow_p));
+ }
+ else
+ {
+ if (DECL_SAVED_TREE (fun) == NULL_TREE
+ && (DECL_CONSTRUCTOR_P (fun) || DECL_DESTRUCTOR_P (fun)))
+ /* The maybe-in-charge 'tor had its DECL_SAVED_TREE
+ cleared, try the first clone. */
+ fun = DECL_CHAIN (fun);
+ gcc_assert (DECL_SAVED_TREE (fun));
+ tree parms, res;
+
+ /* Unshare the whole function body. */
+ tree body = copy_fn (fun, parms, res);
+
+ /* Associate the bindings with the remapped parms. */
+ tree bound = new_call.bindings;
+ tree remapped = parms;
+ while (bound)
+ {
+ tree oparm = TREE_PURPOSE (bound);
+ tree arg = TREE_VALUE (bound);
+ gcc_assert (DECL_NAME (remapped) == DECL_NAME (oparm));
+ ctx->values->put (remapped, arg);
+ bound = TREE_CHAIN (bound);
+ remapped = DECL_CHAIN (remapped);
+ }
+ /* Add the RESULT_DECL to the values map, too. */
+ tree slot = NULL_TREE;
+ if (DECL_BY_REFERENCE (res))
+ {
+ slot = AGGR_INIT_EXPR_SLOT (t);
+ tree addr = build_address (slot);
+ addr = build_nop (TREE_TYPE (res), addr);
+ ctx->values->put (res, addr);
+ ctx->values->put (slot, NULL_TREE);
+ }
+ else
+ ctx->values->put (res, NULL_TREE);
+
+ cxx_eval_constant_expression (ctx, body, allow_non_constant,
+ addr, non_constant_p, overflow_p);
+
+ if (VOID_TYPE_P (TREE_TYPE (res)))
+ /* 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
+ value by evaluating *this, but we don't bother; there's
+ no need to put such a call in the hash table. */
+ result = addr ? ctx->object : ctx->ctor;
+ else
+ {
+ result = *ctx->values->get (slot ? slot : res);
+ if (result == NULL_TREE)
+ {
+ if (!allow_non_constant)
+ error ("constexpr call flows off the end "
+ "of the function");
+ *non_constant_p = true;
+ }
+ }
+
+ /* Remove the parms/result from the values map. Is it worth
+ bothering to do this when the map itself is only live for
+ one constexpr evaluation? If so, maybe also clear out
+ other vars from call, maybe in BIND_EXPR handling? */
+ ctx->values->remove (res);
+ if (slot)
+ ctx->values->remove (slot);
+ for (tree parm = parms; parm; parm = TREE_CHAIN (parm))
+ ctx->values->remove (parm);
+ }
}
+
if (result == error_mark_node)
*non_constant_p = true;
if (*non_constant_p)
entry->result = result = error_mark_node;
- else
+ else if (result)
{
/* If this was a call to initialize an object, set the type of
the CONSTRUCTOR to the type of that object. */
}
entry->result = result;
}
+ else
+ result = void_node;
}
pop_cx_call_context ();
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (whole), i, field, value)
{
if (field == part)
- return value;
+ {
+ if (value)
+ return value;
+ else
+ /* We're in the middle of initializing it. */
+ break;
+ }
}
if (TREE_CODE (TREE_TYPE (whole)) == UNION_TYPE
&& CONSTRUCTOR_NELTS (whole) > 0)
&argvec, elttype, LOOKUP_NORMAL,
tf_warning_or_error);
release_tree_vector (argvec);
+ init = build_aggr_init_expr (TREE_TYPE (init), init);
pre_init = true;
}
/* If we're pulling out the value of an empty base, make sure
that the whole object is constant and then return an empty
CONSTRUCTOR. */
- if (empty_base)
+ if (empty_base && !addr)
{
VERIFY_CONSTANT (r);
r = build_constructor (TREE_TYPE (t), NULL);
&& DECL_DECLARED_CONSTEXPR_P (ctx));
}
+/* Evaluate an INIT_EXPR or MODIFY_EXPR. */
+
+static tree
+cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
+ bool allow_non_constant, bool addr,
+ bool *non_constant_p, bool *overflow_p)
+{
+ constexpr_ctx new_ctx = *ctx;
+
+ /* First we figure out where we're storing to. */
+ tree target = TREE_OPERAND (t, 0);
+ target = cxx_eval_constant_expression (ctx, target,
+ allow_non_constant, true,
+ non_constant_p, overflow_p);
+ if (*non_constant_p)
+ return t;
+
+ /* And then find the underlying variable. */
+ vec<tree,va_gc> *refs = make_tree_vector();
+ tree object = NULL_TREE;
+ for (tree probe = target; object == NULL_TREE; )
+ {
+ switch (TREE_CODE (probe))
+ {
+ 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);
+ break;
+
+ default:
+ object = probe;
+ gcc_assert (DECL_P (object));
+ }
+ }
+
+ /* And then find/build up our initializer for the path to the subobject
+ we're initializing. */
+ tree *valp = ctx->values->get (object);
+ if (!valp)
+ {
+ /* A constant-expression cannot modify objects from outside the
+ constant-expression. */
+ if (!allow_non_constant)
+ error ("modification of %qD is not a constant-expression", object);
+ *non_constant_p = true;
+ return t;
+ }
+ tree type = TREE_TYPE (object);
+ while (!refs->is_empty())
+ {
+ if (*valp == NULL_TREE)
+ {
+ *valp = build_constructor (type, NULL);
+ CONSTRUCTOR_NO_IMPLICIT_ZERO (*valp) = true;
+ }
+
+ constructor_elt ce;
+ type = refs->pop();
+ ce.index = refs->pop();
+ ce.value = NULL_TREE;
+
+ unsigned HOST_WIDE_INT idx = 0;
+ constructor_elt *cep = NULL;
+ for (idx = 0;
+ vec_safe_iterate (CONSTRUCTOR_ELTS (*valp), idx, &cep);
+ idx++)
+ /* ??? slow */
+ if (cp_tree_equal (ce.index, cep->index))
+ break;
+ if (!cep)
+ cep = vec_safe_push (CONSTRUCTOR_ELTS (*valp), ce);
+ valp = &cep->value;
+ }
+ release_tree_vector (refs);
+
+ if ((AGGREGATE_TYPE_P (TREE_TYPE (t)) || VECTOR_TYPE_P (TREE_TYPE (t))))
+ {
+ /* Create a new CONSTRUCTOR in case evaluation of the initializer
+ wants to modify it. */
+ *valp = new_ctx.ctor = build_constructor (TREE_TYPE (t), NULL);
+ CONSTRUCTOR_NO_IMPLICIT_ZERO (new_ctx.ctor) = true;
+ new_ctx.object = target;
+ }
+
+ tree init = cxx_eval_constant_expression (&new_ctx, TREE_OPERAND (t, 1),
+ allow_non_constant, false,
+ non_constant_p, overflow_p);
+ if (target == object)
+ /* The hash table might have moved since the get earlier. */
+ ctx->values->put (object, init);
+ else
+ *valp = init;
+
+ if (*non_constant_p)
+ return t;
+ else if (addr)
+ return target;
+ else
+ return *valp;
+}
+
+/* Evaluate a ++ or -- expression. */
+
+static tree
+cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
+ bool allow_non_constant, bool addr,
+ bool *non_constant_p, bool *overflow_p)
+{
+ enum tree_code code = TREE_CODE (t);
+ tree type = TREE_TYPE (t);
+ tree op = TREE_OPERAND (t, 0);
+ tree offset = TREE_OPERAND (t, 1);
+ gcc_assert (TREE_CONSTANT (offset));
+
+ /* The operand as an lvalue. */
+ op = cxx_eval_constant_expression (ctx, op, allow_non_constant, true,
+ non_constant_p, overflow_p);
+
+ /* The operand as an rvalue. */
+ tree val = rvalue (op);
+ val = cxx_eval_constant_expression (ctx, val, allow_non_constant, false,
+ non_constant_p, overflow_p);
+ VERIFY_CONSTANT (val);
+
+ /* The modified value. */
+ bool inc = (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR);
+ tree mod = fold_build2 (inc ? PLUS_EXPR : MINUS_EXPR,
+ type, val, offset);
+ VERIFY_CONSTANT (mod);
+
+ /* Storing the modified value. */
+ tree store = build2 (MODIFY_EXPR, type, op, mod);
+ cxx_eval_constant_expression (ctx, store, allow_non_constant,
+ true, non_constant_p, overflow_p);
+
+ /* And the value of the expression. */
+ if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR)
+ {
+ /* Prefix ops are lvalues. */
+ if (addr)
+ return op;
+ else
+ /* But we optimize when the caller wants an rvalue. */
+ return mod;
+ }
+ else
+ /* Postfix ops are rvalues. */
+ return val;
+}
+
/* 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 */
+/* FIXME overflow_p is too global */
static tree
cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
switch (TREE_CODE (t))
{
+ case RESULT_DECL:
+ if (addr)
+ return t;
+ /* We ask for an rvalue for the RESULT_DECL when indirecting
+ through an invisible reference. */
+ gcc_assert (DECL_BY_REFERENCE (t));
+ return (*ctx->values->get (t));
+
case VAR_DECL:
if (addr)
return t;
if (TREE_CODE (r) == TARGET_EXPR
&& TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR)
r = TARGET_EXPR_INITIAL (r);
- if (TREE_CODE (r) == VAR_DECL && var_in_constexpr_fn (r)
- && DECL_INITIAL (r))
- r = cxx_eval_constant_expression (ctx, DECL_INITIAL (r),
- allow_non_constant, false,
- non_constant_p, overflow_p);
if (TREE_CODE (r) == VAR_DECL)
if (tree *p = ctx->values->get (r))
r = *p;
return t;
case PARM_DECL:
- if (ctx && ctx->call && DECL_CONTEXT (t) == ctx->call->fundef->decl)
+ if (!use_new_call && ctx
+ && ctx->call && DECL_CONTEXT (t) == ctx->call->fundef->decl)
r = lookup_parameter_binding (ctx->call, t);
+ else if (addr && TREE_CODE (TREE_TYPE (t)) != REFERENCE_TYPE)
+ /* glvalue use. */;
+ else if (tree *p = ctx->values->get (r))
+ r = *p;
else if (addr)
/* Defer in case this is only used for its type. */;
else
non_constant_p, overflow_p);
break;
+ case DECL_EXPR:
+ {
+ r = DECL_EXPR_DECL (t);
+ if (AGGREGATE_TYPE_P (TREE_TYPE (r))
+ || VECTOR_TYPE_P (TREE_TYPE (r)))
+ {
+ new_ctx = *ctx;
+ new_ctx.object = r;
+ new_ctx.ctor = build_constructor (TREE_TYPE (r), NULL);
+ CONSTRUCTOR_NO_IMPLICIT_ZERO (new_ctx.ctor) = true;
+ new_ctx.values->put (r, new_ctx.ctor);
+ ctx = &new_ctx;
+ }
+
+ if (tree init = DECL_INITIAL (r))
+ {
+ init = cxx_eval_constant_expression (ctx, init,
+ allow_non_constant, false,
+ non_constant_p, overflow_p);
+ ctx->values->put (r, init);
+ }
+ else if (ctx == &new_ctx)
+ /* We gave it a CONSTRUCTOR above. */;
+ else
+ ctx->values->put (r, NULL_TREE);
+ }
+ break;
+
case TARGET_EXPR:
if (!literal_type_p (TREE_TYPE (t)))
{
ctx->values->put (new_ctx.object, new_ctx.ctor);
ctx = &new_ctx;
}
- /* else fall through. */
- case INIT_EXPR:
- /* Pass false for 'addr' because these codes indicate
+ /* Pass false for 'addr' because this indicates
initialization of a temporary. */
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
allow_non_constant, false,
r = adjust_temp_type (TREE_TYPE (t), r);
break;
+ case INIT_EXPR:
+ if (!use_new_call)
+ {
+ /* In C++11 constexpr evaluation we are looking for the value,
+ not the side-effect of the initialization. */
+ r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
+ allow_non_constant, false,
+ non_constant_p, overflow_p);
+ break;
+ }
+ /* else fall through */
+ case MODIFY_EXPR:
+ r = cxx_eval_store_expression (ctx, t, allow_non_constant, addr,
+ non_constant_p, overflow_p);
+ break;
+
case SCOPE_REF:
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
allow_non_constant, addr,
case CLEANUP_POINT_EXPR:
case MUST_NOT_THROW_EXPR:
case SAVE_EXPR:
+ case EXPR_STMT:
+ case EH_SPEC_BLOCK:
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
allow_non_constant, addr,
non_constant_p, overflow_p);
used, and they can't do anything with it, so just return it. */
return t;
- case LAMBDA_EXPR:
+ case STATEMENT_LIST:
+ {
+ new_ctx = *ctx;
+ new_ctx.ctor = new_ctx.object = NULL_TREE;
+ tree_stmt_iterator i;
+ for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
+ {
+ cxx_eval_constant_expression (&new_ctx, tsi_stmt (i),
+ allow_non_constant, false,
+ non_constant_p, overflow_p);
+ if (*non_constant_p)
+ break;
+ }
+ }
+ break;
+
+ case BIND_EXPR:
+ return cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (t),
+ allow_non_constant, addr,
+ non_constant_p, overflow_p);
+
case PREINCREMENT_EXPR:
case POSTINCREMENT_EXPR:
case PREDECREMENT_EXPR:
case POSTDECREMENT_EXPR:
+ return cxx_eval_increment_expression (ctx, t, allow_non_constant,
+ addr, non_constant_p, overflow_p);
+
+ case LAMBDA_EXPR:
case NEW_EXPR:
case VEC_NEW_EXPR:
case DELETE_EXPR:
case VEC_DELETE_EXPR:
case THROW_EXPR:
- case MODIFY_EXPR:
case MODOP_EXPR:
/* GCC internal stuff. */
case VA_ARG_EXPR:
case OBJ_TYPE_REF:
case WITH_CLEANUP_EXPR:
- case STATEMENT_LIST:
- case BIND_EXPR:
case NON_DEPENDENT_EXPR:
case BASELINK:
- case EXPR_STMT:
case OFFSET_REF:
if (!allow_non_constant)
error_at (EXPR_LOC_OR_LOC (t, input_location),
}
break;
+ case GOTO_EXPR:
+ case LOOP_EXPR:
+ case SWITCH_EXPR:
+ if (!allow_non_constant)
+ sorry ("%qs in constant expression", get_tree_code_name (TREE_CODE (t)));
+ *non_constant_p = true;
+ break;
+
default:
internal_error ("unexpected expression %qE of kind %s", t,
get_tree_code_name (TREE_CODE (t)));
initialized. */
ctx.ctor = build_constructor (type, NULL);
CONSTRUCTOR_NO_IMPLICIT_ZERO (ctx.ctor) = true;
- if (!object && TREE_CODE (t) == TARGET_EXPR)
- object = TARGET_EXPR_SLOT (t);
+ if (!object)
+ {
+ if (TREE_CODE (t) == TARGET_EXPR)
+ object = TARGET_EXPR_SLOT (t);
+ else if (TREE_CODE (t) == AGGR_INIT_EXPR)
+ object = AGGR_INIT_EXPR_SLOT (t);
+ }
ctx.object = object;
if (object)
gcc_assert (same_type_ignoring_top_level_qualifiers_p
constexpr_ctx ctx = { NULL, NULL, NULL, NULL };
hash_map <tree, tree> map;
ctx.values = ↦
- tree type = initialized_type (t);
- if ((AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
- && TREE_CODE (t) != TARGET_EXPR)
- {
- ctx.ctor = build_constructor (type, NULL);
- CONSTRUCTOR_NO_IMPLICIT_ZERO (ctx.ctor) = true;
- }
cxx_eval_constant_expression (&ctx, t, true, false, &non_constant_p,
&overflow_p);
return !non_constant_p && !overflow_p;
if (TREE_CODE (t) == CONVERT_EXPR
&& VOID_TYPE_P (TREE_TYPE (t)))
t = TREE_OPERAND (t, 0);
+ if (TREE_CODE (t) == INIT_EXPR)
+ t = TREE_OPERAND (t, 1);
t = maybe_constant_value (t, decl);
if (TREE_CODE (t) == TARGET_EXPR)
{
case TEMPLATE_ID_EXPR:
case LABEL_DECL:
case LABEL_EXPR:
+ case CASE_LABEL_EXPR:
case CONST_DECL:
case SIZEOF_EXPR:
case ALIGNOF_EXPR:
case FIELD_DECL:
case PARM_DECL:
case USING_DECL:
+ case USING_STMT:
case PLACEHOLDER_EXPR:
+ case BREAK_STMT:
+ case CONTINUE_STMT:
return true;
case AGGR_INIT_EXPR:
const int nargs = call_expr_nargs (t);
i = 0;
+ if (fun == NULL_TREE)
+ {
+ /* fold_call_expr can't do anything with IFN calls. */
+ if (flags & tf_error)
+ error_at (EXPR_LOC_OR_LOC (t, input_location),
+ "call to internal function");
+ return false;
+ }
if (is_overloaded_fn (fun))
{
if (TREE_CODE (fun) == FUNCTION_DECL)
return potential_constant_expression_1 (x, rval, flags);
}
+ case STATEMENT_LIST:
+ {
+ tree_stmt_iterator i;
+ for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
+ {
+ if (!potential_constant_expression_1 (tsi_stmt (i), any, flags))
+ return false;
+ }
+ return true;
+ }
+ break;
+
+ case MODIFY_EXPR:
+ if (cxx_dialect < cxx14)
+ goto fail;
+ if (!potential_constant_expression_1 (TREE_OPERAND (t, 0), any, flags))
+ return false;
+ if (!potential_constant_expression_1 (TREE_OPERAND (t, 1), rval, flags))
+ return false;
+ return true;
+
+ case MODOP_EXPR:
+ if (cxx_dialect < cxx14)
+ goto fail;
+ if (!potential_constant_expression_1 (TREE_OPERAND (t, 0), rval, flags))
+ return false;
+ if (!potential_constant_expression_1 (TREE_OPERAND (t, 2), rval, flags))
+ return false;
+ return true;
+
+ case IF_STMT:
+ if (!potential_constant_expression_1 (IF_COND (t), rval, flags))
+ return false;
+ if (!potential_constant_expression_1 (THEN_CLAUSE (t), any, flags))
+ return false;
+ if (!potential_constant_expression_1 (ELSE_CLAUSE (t), any, flags))
+ return false;
+ return true;
+
+ case DO_STMT:
+ if (!potential_constant_expression_1 (DO_COND (t), rval, flags))
+ return false;
+ if (!potential_constant_expression_1 (DO_BODY (t), any, flags))
+ return false;
+ return true;
+
+ case FOR_STMT:
+ if (!potential_constant_expression_1 (FOR_INIT_STMT (t), any, flags))
+ return false;
+ if (!potential_constant_expression_1 (FOR_COND (t), rval, flags))
+ return false;
+ if (!potential_constant_expression_1 (FOR_EXPR (t), any, flags))
+ return false;
+ if (!potential_constant_expression_1 (FOR_BODY (t), any, flags))
+ return false;
+ return true;
+
+ case WHILE_STMT:
+ if (!potential_constant_expression_1 (WHILE_COND (t), rval, flags))
+ return false;
+ if (!potential_constant_expression_1 (WHILE_BODY (t), any, flags))
+ return false;
+ return true;
+
+ case SWITCH_STMT:
+ if (!potential_constant_expression_1 (SWITCH_STMT_COND (t), rval, flags))
+ return false;
+ if (!potential_constant_expression_1 (SWITCH_STMT_BODY (t), any, flags))
+ return false;
+ return true;
+
case LAMBDA_EXPR:
case DYNAMIC_CAST_EXPR:
case PSEUDO_DTOR_EXPR:
- case PREINCREMENT_EXPR:
- case POSTINCREMENT_EXPR:
- case PREDECREMENT_EXPR:
- case POSTDECREMENT_EXPR:
case NEW_EXPR:
case VEC_NEW_EXPR:
case DELETE_EXPR:
case VEC_DELETE_EXPR:
case THROW_EXPR:
- case MODIFY_EXPR:
- case MODOP_EXPR:
case OMP_ATOMIC:
case OMP_ATOMIC_READ:
case OMP_ATOMIC_CAPTURE_OLD:
/* GCC internal stuff. */
case VA_ARG_EXPR:
case OBJ_TYPE_REF:
- case WITH_CLEANUP_EXPR:
- case CLEANUP_POINT_EXPR:
- case MUST_NOT_THROW_EXPR:
- case TRY_CATCH_EXPR:
- case STATEMENT_LIST:
- /* Don't bother trying to define a subset of statement-expressions to
- be constant-expressions, at least for now. */
case STMT_EXPR:
- case EXPR_STMT:
- case BIND_EXPR:
case TRANSACTION_EXPR:
- case IF_STMT:
- case DO_STMT:
- case FOR_STMT:
- case WHILE_STMT:
- case DECL_EXPR:
+ case ASM_EXPR:
+ fail:
if (flags & tf_error)
error ("expression %qE is not a constant-expression", t);
return false;
want_rval = true;
goto binary;
+ case PREINCREMENT_EXPR:
+ case POSTINCREMENT_EXPR:
+ case PREDECREMENT_EXPR:
+ case POSTDECREMENT_EXPR:
+ if (cxx_dialect < cxx14)
+ goto fail;
+ goto unary;
+
case BIT_NOT_EXPR:
/* A destructor. */
if (TYPE_P (TREE_OPERAND (t, 0)))
case TRUTH_NOT_EXPR:
case FIXED_CONVERT_EXPR:
case UNARY_PLUS_EXPR:
+ unary:
return potential_constant_expression_1 (TREE_OPERAND (t, 0), rval,
flags);
(TREE_OPERAND (t, 0),
TREE_CODE (TREE_TYPE (t)) != REFERENCE_TYPE, flags));
+ case BIND_EXPR:
+ return potential_constant_expression_1 (BIND_EXPR_BODY (t),
+ want_rval, flags);
+
+ case WITH_CLEANUP_EXPR:
+ case CLEANUP_POINT_EXPR:
+ case MUST_NOT_THROW_EXPR:
+ case TRY_CATCH_EXPR:
+ case EH_SPEC_BLOCK:
+ case EXPR_STMT:
case PAREN_EXPR:
+ case DECL_EXPR:
case NON_DEPENDENT_EXPR:
/* For convenience. */
case RETURN_EXPR: