Commit 12d9ce19 by Jason Merrill Committed by Jason Merrill

re PR c++/64333 (C++14 constexpr gives wrong results when a looping constexpr…

re PR c++/64333 (C++14 constexpr gives wrong results when a looping constexpr function is evaluated twice)

	PR c++/64333
	* constexpr.c (cxx_bind_parameters_in_call): non_constant_args parm.
	(cxx_eval_call_expression): Don't cache calls with non-constant args.
	(cxx_eval_constant_expression) [COMPOUND_EXPR]: Pass true for lval.
	(cxx_eval_unary_expression, cxx_eval_binary_expression)
	(cxx_eval_conditional_expression): Pass	false for lval.

From-SVN: r218832
parent 92a596e8
2014-12-17 Jason Merrill <jason@redhat.com> 2014-12-17 Jason Merrill <jason@redhat.com>
PR c++/64333
* constexpr.c (cxx_bind_parameters_in_call): non_constant_args parm.
(cxx_eval_call_expression): Don't cache calls with non-constant args.
(cxx_eval_constant_expression) [COMPOUND_EXPR]: Pass true for lval.
(cxx_eval_unary_expression, cxx_eval_binary_expression)
(cxx_eval_conditional_expression): Pass false for lval.
* constexpr.c: Change "addr" parm names to "lval". * constexpr.c: Change "addr" parm names to "lval".
* constexpr.c: Tweak comments and formatting. * constexpr.c: Tweak comments and formatting.
......
...@@ -1050,7 +1050,8 @@ adjust_temp_type (tree type, tree temp) ...@@ -1050,7 +1050,8 @@ adjust_temp_type (tree type, tree temp)
static void static void
cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
constexpr_call *new_call, constexpr_call *new_call,
bool *non_constant_p, bool *overflow_p) bool *non_constant_p, bool *overflow_p,
bool *non_constant_args)
{ {
const int nargs = call_expr_nargs (t); const int nargs = call_expr_nargs (t);
tree fun = new_call->fundef->decl; tree fun = new_call->fundef->decl;
...@@ -1099,6 +1100,8 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, ...@@ -1099,6 +1100,8 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
/* Make sure the binding has the same type as the parm. */ /* Make sure the binding has the same type as the parm. */
if (TREE_CODE (type) != REFERENCE_TYPE) if (TREE_CODE (type) != REFERENCE_TYPE)
arg = adjust_temp_type (type, arg); arg = adjust_temp_type (type, arg);
if (!TREE_CONSTANT (arg))
*non_constant_args = true;
*p = build_tree_list (parms, arg); *p = build_tree_list (parms, arg);
p = &TREE_CHAIN (*p); p = &TREE_CHAIN (*p);
next: next:
...@@ -1155,10 +1158,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, ...@@ -1155,10 +1158,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
{ {
location_t loc = EXPR_LOC_OR_LOC (t, input_location); location_t loc = EXPR_LOC_OR_LOC (t, input_location);
tree fun = get_function_named_in_call (t); tree fun = get_function_named_in_call (t);
tree result;
constexpr_call new_call = { NULL, NULL, NULL, 0 }; constexpr_call new_call = { NULL, NULL, NULL, 0 };
constexpr_call **slot;
constexpr_call *entry;
bool depth_ok; bool depth_ok;
if (fun == NULL_TREE) if (fun == NULL_TREE)
...@@ -1264,36 +1264,45 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, ...@@ -1264,36 +1264,45 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
ctx = &new_ctx; ctx = &new_ctx;
} }
bool non_constant_args = false;
cxx_bind_parameters_in_call (ctx, t, &new_call, cxx_bind_parameters_in_call (ctx, t, &new_call,
non_constant_p, overflow_p); non_constant_p, overflow_p, &non_constant_args);
if (*non_constant_p) if (*non_constant_p)
return t; return t;
depth_ok = push_cx_call_context (t); depth_ok = push_cx_call_context (t);
new_call.hash tree result = NULL_TREE;
= iterative_hash_template_arg (new_call.bindings,
constexpr_fundef_hasher::hash (new_call.fundef));
/* If we have seen this call before, we are done. */ constexpr_call *entry = NULL;
maybe_initialize_constexpr_call_table (); if (!non_constant_args)
slot = constexpr_call_table->find_slot (&new_call, INSERT);
entry = *slot;
if (entry == NULL)
{ {
/* We need to keep a pointer to the entry, not just the slot, as the new_call.hash = iterative_hash_template_arg
slot can move in the call to cxx_eval_builtin_function_call. */ (new_call.bindings, constexpr_fundef_hasher::hash (new_call.fundef));
*slot = entry = ggc_alloc<constexpr_call> ();
*entry = new_call; /* If we have seen this call before, we are done. */
} maybe_initialize_constexpr_call_table ();
/* Calls which are in progress have their result set to NULL constexpr_call **slot
so that we can detect circular dependencies. */ = constexpr_call_table->find_slot (&new_call, INSERT);
else if (entry->result == NULL) entry = *slot;
{ if (entry == NULL)
if (!ctx->quiet) {
error ("call has circular dependency"); /* We need to keep a pointer to the entry, not just the slot, as the
*non_constant_p = true; slot can move in the call to cxx_eval_builtin_function_call. */
entry->result = result = error_mark_node; *slot = entry = ggc_alloc<constexpr_call> ();
*entry = new_call;
}
/* Calls which are in progress have their result set to NULL
so that we can detect circular dependencies. */
else if (entry->result == NULL)
{
if (!ctx->quiet)
error ("call has circular dependency");
*non_constant_p = true;
entry->result = result = error_mark_node;
}
else
result = entry->result;
} }
if (!depth_ok) if (!depth_ok)
...@@ -1303,11 +1312,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, ...@@ -1303,11 +1312,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
"-fconstexpr-depth= to increase the maximum)", "-fconstexpr-depth= to increase the maximum)",
max_constexpr_depth); max_constexpr_depth);
*non_constant_p = true; *non_constant_p = true;
entry->result = result = error_mark_node; result = error_mark_node;
} }
else else
{ {
result = entry->result;
if (!result || result == error_mark_node) if (!result || result == error_mark_node)
{ {
if (!use_new_call) if (!use_new_call)
...@@ -1395,7 +1403,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, ...@@ -1395,7 +1403,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
if (result == error_mark_node) if (result == error_mark_node)
*non_constant_p = true; *non_constant_p = true;
if (*non_constant_p) if (*non_constant_p)
entry->result = result = error_mark_node; result = error_mark_node;
else if (result) else if (result)
{ {
/* If this was a call to initialize an object, set the type of /* If this was a call to initialize an object, set the type of
...@@ -1409,10 +1417,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, ...@@ -1409,10 +1417,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
result = adjust_temp_type (TREE_TYPE (TREE_TYPE (ob_arg)), result = adjust_temp_type (TREE_TYPE (TREE_TYPE (ob_arg)),
result); result);
} }
entry->result = result;
} }
else else
result = void_node; result = void_node;
if (entry)
entry->result = result;
} }
pop_cx_call_context (); pop_cx_call_context ();
...@@ -1558,13 +1567,13 @@ cxx_eval_check_shift_p (location_t loc, const constexpr_ctx *ctx, ...@@ -1558,13 +1567,13 @@ cxx_eval_check_shift_p (location_t loc, const constexpr_ctx *ctx,
static tree static tree
cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t, cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t,
bool lval, bool /*lval*/,
bool *non_constant_p, bool *overflow_p) bool *non_constant_p, bool *overflow_p)
{ {
tree r; tree r;
tree orig_arg = TREE_OPERAND (t, 0); tree orig_arg = TREE_OPERAND (t, 0);
tree arg = cxx_eval_constant_expression (ctx, orig_arg, tree arg = cxx_eval_constant_expression (ctx, orig_arg, /*lval*/false,
lval, non_constant_p, overflow_p); non_constant_p, overflow_p);
VERIFY_CONSTANT (arg); VERIFY_CONSTANT (arg);
location_t loc = EXPR_LOCATION (t); location_t loc = EXPR_LOCATION (t);
enum tree_code code = TREE_CODE (t); enum tree_code code = TREE_CODE (t);
...@@ -1586,19 +1595,17 @@ cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t, ...@@ -1586,19 +1595,17 @@ cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t,
static tree static tree
cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
bool lval, bool /*lval*/,
bool *non_constant_p, bool *overflow_p) bool *non_constant_p, bool *overflow_p)
{ {
tree r; tree r;
tree orig_lhs = TREE_OPERAND (t, 0); tree orig_lhs = TREE_OPERAND (t, 0);
tree orig_rhs = TREE_OPERAND (t, 1); tree orig_rhs = TREE_OPERAND (t, 1);
tree lhs, rhs; tree lhs, rhs;
lhs = cxx_eval_constant_expression (ctx, orig_lhs, lhs = cxx_eval_constant_expression (ctx, orig_lhs, /*lval*/false,
lval,
non_constant_p, overflow_p); non_constant_p, overflow_p);
VERIFY_CONSTANT (lhs); VERIFY_CONSTANT (lhs);
rhs = cxx_eval_constant_expression (ctx, orig_rhs, rhs = cxx_eval_constant_expression (ctx, orig_rhs, /*lval*/false,
lval,
non_constant_p, overflow_p); non_constant_p, overflow_p);
VERIFY_CONSTANT (rhs); VERIFY_CONSTANT (rhs);
...@@ -1630,7 +1637,7 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, ...@@ -1630,7 +1637,7 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
tree *jump_target) tree *jump_target)
{ {
tree val = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), tree val = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
lval, /*lval*/false,
non_constant_p, overflow_p); non_constant_p, overflow_p);
VERIFY_CONSTANT (val); VERIFY_CONSTANT (val);
/* Don't VERIFY_CONSTANT the other operands. */ /* Don't VERIFY_CONSTANT the other operands. */
...@@ -3085,7 +3092,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, ...@@ -3085,7 +3092,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
r = *p; r = *p;
else else
{ {
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), addr, r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), false,
non_constant_p, overflow_p); non_constant_p, overflow_p);
ctx->values->put (t, r); ctx->values->put (t, r);
} }
...@@ -3174,7 +3181,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, ...@@ -3174,7 +3181,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
{ {
/* Check that the LHS is constant and then discard it. */ /* Check that the LHS is constant and then discard it. */
cxx_eval_constant_expression (ctx, op0, cxx_eval_constant_expression (ctx, op0,
false, non_constant_p, overflow_p, true, non_constant_p, overflow_p,
jump_target); jump_target);
op1 = TREE_OPERAND (t, 1); op1 = TREE_OPERAND (t, 1);
r = cxx_eval_constant_expression (ctx, op1, r = cxx_eval_constant_expression (ctx, op1,
......
// PR c++/64333
// { dg-do compile { target c++14 } }
#include <initializer_list>
constexpr int max(std::initializer_list<int> ints)
{
int ret = *(ints.begin());
for (int i = 0; i < ints.size(); ++i) {
if (*(ints.begin()+i) > ret) {
ret = *(ints.begin()+i);
}
}
return ret;
}
int main()
{
constexpr int z = max({7,6,5,4,3,2,1});
constexpr int z2 = max({5,4,3,2,1});
static_assert(z == 7, "");
static_assert(z2 == 5, "");
}
// { dg-do compile { target c++14 } }
constexpr int f(int* p) { return *p; }
constexpr int g(int n)
{
int sum = 0;
for (int i = 1; i <= n; ++i)
sum += f(&i);
return sum;
}
static_assert(g(3) == 3+2+1,"");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment