aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2014-11-17 14:08:07 -0500
committerJason Merrill <jason@gcc.gnu.org>2014-11-17 14:08:07 -0500
commit56632b2773bfc2ebe722fa21c796c7369acf9a9c (patch)
tree73dc149a26c6bd49aa72590cac53a322919a5295
parent27d93d2c8a36cea495599e4eb070d3f922aab279 (diff)
downloadgcc-56632b2773bfc2ebe722fa21c796c7369acf9a9c.zip
gcc-56632b2773bfc2ebe722fa21c796c7369acf9a9c.tar.gz
gcc-56632b2773bfc2ebe722fa21c796c7369acf9a9c.tar.bz2
Handle C++14 constexpr flow control.
* constexpr.c (cxx_eval_loop_expr, cxx_eval_switch_expr): New. (cxx_eval_statement_list): New. (cxx_eval_constant_expression): Handle LABEL_EXPR, CASE_LABEL_EXPR, GOTO_EXPR, LOOP_EXPR, SWITCH_EXPR. Handle jump semantics of RETURN_EXPR. (many functions): Add jump_target parameter. (returns, breaks, continues, switches, label_matches): New. * cp-tree.h (LABEL_DECL_BREAK, LABEL_DECL_CONTINUE): New. * cp-gimplify.c (begin_bc_block): Set them. From-SVN: r217670
-rw-r--r--gcc/cp/ChangeLog11
-rw-r--r--gcc/cp/constexpr.c315
-rw-r--r--gcc/cp/cp-gimplify.c4
-rw-r--r--gcc/cp/cp-tree.h10
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-loop1.C13
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-return1.C11
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-return2.C7
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-switch1.C16
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-switch2.C20
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-switch3.C20
10 files changed, 361 insertions, 66 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index e77e7a4..18e06d8 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,16 @@
2014-11-17 Jason Merrill <jason@redhat.com>
+ Handle C++14 constexpr flow control.
+ * constexpr.c (cxx_eval_loop_expr, cxx_eval_switch_expr): New.
+ (cxx_eval_statement_list): New.
+ (cxx_eval_constant_expression): Handle LABEL_EXPR,
+ CASE_LABEL_EXPR, GOTO_EXPR, LOOP_EXPR, SWITCH_EXPR. Handle jump
+ semantics of RETURN_EXPR.
+ (many functions): Add jump_target parameter.
+ (returns, breaks, continues, switches, label_matches): New.
+ * cp-tree.h (LABEL_DECL_BREAK, LABEL_DECL_CONTINUE): New.
+ * cp-gimplify.c (begin_bc_block): Set them.
+
* cp-gimplify.c (genericize_cp_loop): Use LOOP_EXPR.
(genericize_for_stmt): Handle null statement-list.
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 8881271..5b25654 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -871,7 +871,7 @@ struct constexpr_ctx {
static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;
static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
- bool, bool, bool *, bool *);
+ bool, bool, bool *, bool *, tree *);
/* Compute a hash value for a constexpr call representation. */
@@ -993,7 +993,8 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t,
{
args[i] = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, i),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
if (allow_non_constant && *non_constant_p)
return t;
}
@@ -1068,7 +1069,7 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
}
arg = cxx_eval_constant_expression (ctx, x, allow_non_constant,
TREE_CODE (type) == REFERENCE_TYPE,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
/* Don't VERIFY_CONSTANT here. */
if (*non_constant_p && allow_non_constant)
return;
@@ -1149,7 +1150,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
/* Might be a constexpr function pointer. */
fun = cxx_eval_constant_expression (ctx, fun, allow_non_constant,
/*addr*/false, non_constant_p,
- overflow_p);
+ overflow_p, NULL);
STRIP_NOPS (fun);
if (TREE_CODE (fun) == ADDR_EXPR)
fun = TREE_OPERAND (fun, 0);
@@ -1185,7 +1186,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
{
tree arg = convert_from_reference (get_nth_callarg (t, 1));
return cxx_eval_constant_expression (ctx, arg, allow_non_constant,
- addr, non_constant_p, overflow_p);
+ addr, non_constant_p,
+ overflow_p, NULL);
}
else if (TREE_CODE (t) == AGGR_INIT_EXPR
&& AGGR_INIT_ZERO_FIRST (t))
@@ -1281,7 +1283,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
result = (cxx_eval_constant_expression
(&new_ctx, new_call.fundef->body,
allow_non_constant, addr,
- non_constant_p, overflow_p));
+ non_constant_p, overflow_p, NULL));
}
else
{
@@ -1321,8 +1323,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
else
ctx->values->put (res, NULL_TREE);
+ tree jump_target = NULL_TREE;
cxx_eval_constant_expression (ctx, body, allow_non_constant,
- addr, non_constant_p, overflow_p);
+ addr, non_constant_p, overflow_p,
+ &jump_target);
if (VOID_TYPE_P (TREE_TYPE (res)))
/* This can be null for a subobject constructor call, in
@@ -1454,7 +1458,8 @@ cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t,
tree r;
tree orig_arg = TREE_OPERAND (t, 0);
tree arg = cxx_eval_constant_expression (ctx, orig_arg, allow_non_constant,
- addr, non_constant_p, overflow_p);
+ addr, non_constant_p, overflow_p,
+ NULL);
VERIFY_CONSTANT (arg);
if (arg == orig_arg)
return t;
@@ -1477,11 +1482,11 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
tree lhs, rhs;
lhs = cxx_eval_constant_expression (ctx, orig_lhs,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
VERIFY_CONSTANT (lhs);
rhs = cxx_eval_constant_expression (ctx, orig_rhs,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
VERIFY_CONSTANT (rhs);
if (lhs == orig_lhs && rhs == orig_rhs)
return t;
@@ -1497,20 +1502,24 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
static tree
cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
bool allow_non_constant, bool addr,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
tree val = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
VERIFY_CONSTANT (val);
/* Don't VERIFY_CONSTANT the other operands. */
if (integer_zerop (val))
return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
}
/* Subroutine of cxx_eval_constant_expression.
@@ -1524,7 +1533,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
tree oldary = TREE_OPERAND (t, 0);
tree ary = cxx_eval_constant_expression (ctx, oldary,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
tree index, oldidx;
HOST_WIDE_INT i;
tree elem_type;
@@ -1534,7 +1543,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
oldidx = TREE_OPERAND (t, 1);
index = cxx_eval_constant_expression (ctx, oldidx,
allow_non_constant, false,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
VERIFY_CONSTANT (index);
if (addr && ary == oldary && index == oldidx)
return t;
@@ -1565,7 +1574,8 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
tree val = build_value_init (elem_type, tf_warning_or_error);
return cxx_eval_constant_expression (ctx, val,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
}
if (!allow_non_constant)
@@ -1611,7 +1621,7 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
tree orig_whole = TREE_OPERAND (t, 0);
tree whole = cxx_eval_constant_expression (ctx, orig_whole,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
if (whole == orig_whole)
return t;
if (addr)
@@ -1670,7 +1680,7 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
value = build_value_init (TREE_TYPE (t), tf_warning_or_error);
return cxx_eval_constant_expression (ctx, value,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
}
/* Subroutine of cxx_eval_constant_expression.
@@ -1688,7 +1698,7 @@ cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t,
HOST_WIDE_INT istart, isize;
tree whole = cxx_eval_constant_expression (ctx, orig_whole,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
tree start, field, value;
unsigned HOST_WIDE_INT i;
@@ -1771,13 +1781,14 @@ cxx_eval_logical_expression (const constexpr_ctx *ctx, tree t,
tree r;
tree lhs = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
VERIFY_CONSTANT (lhs);
if (tree_int_cst_equal (lhs, bailout_value))
return lhs;
gcc_assert (tree_int_cst_equal (lhs, continue_value));
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
- allow_non_constant, addr, non_constant_p, overflow_p);
+ allow_non_constant, addr, non_constant_p,
+ overflow_p, NULL);
VERIFY_CONSTANT (r);
return r;
}
@@ -1920,7 +1931,8 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
CONSTRUCTOR_APPEND_ELT (*p, index, new_ctx.ctor);
tree elt = cxx_eval_constant_expression (&new_ctx, value,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
/* Don't VERIFY_CONSTANT here. */
if (allow_non_constant && *non_constant_p)
break;
@@ -2038,7 +2050,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
we just pre-built above. */
eltinit = (cxx_eval_constant_expression
(&new_ctx, init, allow_non_constant,
- addr, non_constant_p, overflow_p));
+ addr, non_constant_p, overflow_p, NULL));
}
else
{
@@ -2052,7 +2064,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
eltinit = force_rvalue (eltinit, tf_warning_or_error);
eltinit = (cxx_eval_constant_expression
(&new_ctx, eltinit, allow_non_constant, addr,
- non_constant_p, overflow_p));
+ non_constant_p, overflow_p, NULL));
}
if (*non_constant_p && !allow_non_constant)
break;
@@ -2288,7 +2300,8 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
{
tree orig_op0 = TREE_OPERAND (t, 0);
tree op0 = cxx_eval_constant_expression (ctx, orig_op0, allow_non_constant,
- /*addr*/false, non_constant_p, overflow_p);
+ /*addr*/false, non_constant_p,
+ overflow_p, NULL);
bool empty_base = false;
tree r;
@@ -2301,7 +2314,7 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
if (r)
r = cxx_eval_constant_expression (ctx, r, allow_non_constant,
- addr, non_constant_p, overflow_p);
+ addr, non_constant_p, overflow_p, NULL);
else
{
tree sub = op0;
@@ -2403,7 +2416,8 @@ cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t,
{
args[i] = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, i),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
VERIFY_CONSTANT (args[i]);
}
@@ -2436,7 +2450,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
tree target = TREE_OPERAND (t, 0);
target = cxx_eval_constant_expression (ctx, target,
allow_non_constant, true,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
if (*non_constant_p)
return t;
@@ -2512,7 +2526,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
tree init = cxx_eval_constant_expression (&new_ctx, TREE_OPERAND (t, 1),
allow_non_constant, false,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
if (target == object)
/* The hash table might have moved since the get earlier. */
ctx->values->put (object, init);
@@ -2542,12 +2556,12 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
/* The operand as an lvalue. */
op = cxx_eval_constant_expression (ctx, op, allow_non_constant, true,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
/* 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);
+ non_constant_p, overflow_p, NULL);
VERIFY_CONSTANT (val);
/* The modified value. */
@@ -2559,7 +2573,7 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
/* 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);
+ true, non_constant_p, overflow_p, NULL);
/* And the value of the expression. */
if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR)
@@ -2576,6 +2590,159 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
return val;
}
+/* Predicates for the meaning of *jump_target. */
+
+static bool
+returns (tree *jump_target)
+{
+ return *jump_target
+ && TREE_CODE (*jump_target) == RETURN_EXPR;
+}
+
+static bool
+breaks (tree *jump_target)
+{
+ return *jump_target
+ && TREE_CODE (*jump_target) == LABEL_DECL
+ && LABEL_DECL_BREAK (*jump_target);
+}
+
+static bool
+continues (tree *jump_target)
+{
+ return *jump_target
+ && TREE_CODE (*jump_target) == LABEL_DECL
+ && LABEL_DECL_CONTINUE (*jump_target);
+}
+
+static bool
+switches (tree *jump_target)
+{
+ return *jump_target
+ && TREE_CODE (*jump_target) == INTEGER_CST;
+}
+
+/* Subroutine of cxx_eval_statement_list. Determine whether the statement
+ at I matches *jump_target. If we're looking for a case label and we see
+ the default label, copy I into DEFAULT_LABEL. */
+
+static bool
+label_matches (tree *jump_target, tree_stmt_iterator i,
+ tree_stmt_iterator& default_label)
+{
+ tree stmt = tsi_stmt (i);
+ switch (TREE_CODE (*jump_target))
+ {
+ case LABEL_DECL:
+ if (TREE_CODE (stmt) == LABEL_EXPR
+ && LABEL_EXPR_LABEL (stmt) == *jump_target)
+ return true;
+ break;
+
+ case INTEGER_CST:
+ if (TREE_CODE (stmt) == CASE_LABEL_EXPR)
+ {
+ if (!CASE_LOW (stmt))
+ default_label = i;
+ else if (tree_int_cst_equal (*jump_target, CASE_LOW (stmt)))
+ return true;
+ }
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ return false;
+}
+
+/* Evaluate a STATEMENT_LIST for side-effects. Handles various jump
+ semantics, for switch, break, continue, and return. */
+
+static tree
+cxx_eval_statement_list (const constexpr_ctx *ctx, tree t,
+ bool allow_non_constant,
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
+{
+ tree_stmt_iterator i;
+ tree_stmt_iterator default_label = tree_stmt_iterator();
+ for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
+ {
+ reenter:
+ tree stmt = tsi_stmt (i);
+ if (*jump_target)
+ {
+ if (TREE_CODE (stmt) == STATEMENT_LIST)
+ /* The label we want might be inside. */;
+ else if (label_matches (jump_target, i, default_label))
+ /* Found it. */
+ *jump_target = NULL_TREE;
+ else
+ continue;
+ }
+ cxx_eval_constant_expression (ctx, stmt,
+ allow_non_constant, false,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*non_constant_p)
+ break;
+ if (returns (jump_target) || breaks (jump_target))
+ break;
+ }
+ if (switches (jump_target) && !tsi_end_p (default_label))
+ {
+ i = default_label;
+ *jump_target = NULL_TREE;
+ goto reenter;
+ }
+ return NULL_TREE;
+}
+
+/* Evaluate a LOOP_EXPR for side-effects. Handles break and return
+ semantics; continue semantics are covered by cxx_eval_statement_list. */
+
+static tree
+cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
+ bool allow_non_constant,
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
+{
+ tree body = TREE_OPERAND (t, 0);
+ while (true)
+ {
+ cxx_eval_statement_list (ctx, body, allow_non_constant,
+ non_constant_p, overflow_p, jump_target);
+ if (returns (jump_target) || breaks (jump_target))
+ break;
+ }
+ if (breaks (jump_target))
+ *jump_target = NULL_TREE;
+ return NULL_TREE;
+}
+
+/* Evaluate a SWITCH_EXPR for side-effects. Handles switch and break jump
+ semantics. */
+
+static tree
+cxx_eval_switch_expr (const constexpr_ctx *ctx, tree t,
+ bool allow_non_constant,
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
+{
+ tree cond = TREE_OPERAND (t, 0);
+ cond = cxx_eval_constant_expression (ctx, cond, allow_non_constant, false,
+ non_constant_p, overflow_p, NULL);
+ VERIFY_CONSTANT (cond);
+ *jump_target = cond;
+
+ tree body = TREE_OPERAND (t, 1);
+ cxx_eval_statement_list (ctx, body, allow_non_constant,
+ non_constant_p, overflow_p, jump_target);
+ if (breaks (jump_target) || switches (jump_target))
+ *jump_target = NULL_TREE;
+ return NULL_TREE;
+}
+
/* 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 */
@@ -2584,7 +2751,8 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
static tree
cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
bool allow_non_constant, bool addr,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
constexpr_ctx new_ctx;
tree r = t;
@@ -2639,6 +2807,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case FUNCTION_DECL:
case TEMPLATE_DECL:
case LABEL_DECL:
+ case LABEL_EXPR:
+ case CASE_LABEL_EXPR:
return t;
case PARM_DECL:
@@ -2683,7 +2853,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
{
init = cxx_eval_constant_expression (ctx, init,
allow_non_constant, false,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
ctx->values->put (r, init);
}
else if (ctx == &new_ctx)
@@ -2721,7 +2892,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
initialization of a temporary. */
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
allow_non_constant, false,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
if (!*non_constant_p)
/* Adjust the type of the result to the type of the temporary. */
r = adjust_temp_type (TREE_TYPE (t), r);
@@ -2734,7 +2905,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
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);
+ non_constant_p, overflow_p, NULL);
break;
}
/* else fall through */
@@ -2746,10 +2917,16 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case SCOPE_REF:
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
break;
case RETURN_EXPR:
+ r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
+ allow_non_constant, addr,
+ non_constant_p, overflow_p, NULL);
+ *jump_target = t;
+ break;
+
case NON_LVALUE_EXPR:
case TRY_CATCH_EXPR:
case CLEANUP_POINT_EXPR:
@@ -2759,7 +2936,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case EH_SPEC_BLOCK:
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
/* These differ from cxx_eval_unary_expression in that this doesn't
@@ -2776,7 +2954,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
tree op = cxx_eval_constant_expression (ctx, oldop,
allow_non_constant,
/*addr*/true,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
/* Don't VERIFY_CONSTANT here. */
if (*non_constant_p)
return t;
@@ -2827,15 +3006,18 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
if ((TREE_CODE (op0) == TARGET_EXPR && op1 == TARGET_EXPR_SLOT (op0))
|| TREE_CODE (op1) == EMPTY_CLASS_EXPR)
r = cxx_eval_constant_expression (ctx, op0, allow_non_constant,
- addr, non_constant_p, overflow_p);
+ addr, non_constant_p, overflow_p,
+ jump_target);
else
{
/* Check that the LHS is constant and then discard it. */
cxx_eval_constant_expression (ctx, op0, allow_non_constant,
- false, non_constant_p, overflow_p);
+ false, non_constant_p, overflow_p,
+ jump_target);
op1 = TREE_OPERAND (t, 1);
r = cxx_eval_constant_expression (ctx, op1, allow_non_constant,
- addr, non_constant_p, overflow_p);
+ addr, non_constant_p, overflow_p,
+ jump_target);
}
}
break;
@@ -2929,7 +3111,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case COND_EXPR:
case VEC_COND_EXPR:
r = cxx_eval_conditional_expression (ctx, t, allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
case CONSTRUCTOR:
@@ -2960,7 +3143,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
tree oldop = TREE_OPERAND (t, 0);
tree op = cxx_eval_constant_expression (ctx, oldop,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
if (*non_constant_p)
return t;
if (POINTER_TYPE_P (TREE_TYPE (t))
@@ -2992,25 +3176,16 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
return t;
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;
+ new_ctx = *ctx;
+ new_ctx.ctor = new_ctx.object = NULL_TREE;
+ return cxx_eval_statement_list (&new_ctx, t, allow_non_constant,
+ non_constant_p, overflow_p, jump_target);
case BIND_EXPR:
return cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (t),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
case PREINCREMENT_EXPR:
case POSTINCREMENT_EXPR:
@@ -3058,16 +3233,24 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
gcc_assert (same_type_ignoring_top_level_qualifiers_p
(TREE_TYPE (t), TREE_TYPE (ctor)));
return cxx_eval_constant_expression
- (ctx, ctor, allow_non_constant, addr, non_constant_p, overflow_p);
+ (ctx, ctor, allow_non_constant, addr,
+ non_constant_p, overflow_p, NULL);
}
break;
case GOTO_EXPR:
+ *jump_target = TREE_OPERAND (t, 0);
+ gcc_assert (breaks (jump_target) || continues (jump_target));
+ break;
+
case LOOP_EXPR:
+ cxx_eval_loop_expr (ctx, t, allow_non_constant,
+ non_constant_p, overflow_p, jump_target);
+ break;
+
case SWITCH_EXPR:
- if (!allow_non_constant)
- sorry ("%qs in constant expression", get_tree_code_name (TREE_CODE (t)));
- *non_constant_p = true;
+ cxx_eval_switch_expr (ctx, t, allow_non_constant,
+ non_constant_p, overflow_p, jump_target);
break;
default:
@@ -3128,7 +3311,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
}
r = cxx_eval_constant_expression (&ctx, r, allow_non_constant,
- false, &non_constant_p, &overflow_p);
+ false, &non_constant_p, &overflow_p, NULL);
verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
@@ -3205,7 +3388,7 @@ is_sub_constant_expr (tree t)
hash_map <tree, tree> map;
ctx.values = &map;
cxx_eval_constant_expression (&ctx, t, true, false, &non_constant_p,
- &overflow_p);
+ &overflow_p, NULL);
return !non_constant_p && !overflow_p;
}
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 81b26d2..82be90b 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -74,6 +74,10 @@ begin_bc_block (enum bc_t bc, location_t location)
tree label = create_artificial_label (location);
DECL_CHAIN (label) = bc_label[bc];
bc_label[bc] = label;
+ if (bc == bc_break)
+ LABEL_DECL_BREAK (label) = true;
+ else
+ LABEL_DECL_CONTINUE (label) = true;
return label;
}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index adc8aa7..d3722d7 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -148,12 +148,14 @@ c-common.h, not after.
DECL_LOCAL_FUNCTION_P (in FUNCTION_DECL)
DECL_MUTABLE_P (in FIELD_DECL)
DECL_DEPENDENT_P (in USING_DECL)
+ LABEL_DECL_BREAK (in LABEL_DECL)
1: C_TYPEDEF_EXPLICITLY_SIGNED (in TYPE_DECL).
DECL_TEMPLATE_INSTANTIATED (in a VAR_DECL or a FUNCTION_DECL)
DECL_MEMBER_TEMPLATE_P (in TEMPLATE_DECL)
USING_DECL_TYPENAME_P (in USING_DECL)
DECL_VLA_CAPTURE_P (in FIELD_DECL)
DECL_ARRAY_PARAMETER_P (in PARM_DECL)
+ LABEL_DECL_CONTINUE (in LABEL_DECL)
2: DECL_THIS_EXTERN (in VAR_DECL or FUNCTION_DECL).
DECL_IMPLICIT_TYPEDEF_P (in a TYPE_DECL)
3: DECL_IN_AGGR_P.
@@ -3243,6 +3245,14 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
#define DECL_LOCAL_FUNCTION_P(NODE) \
DECL_LANG_FLAG_0 (FUNCTION_DECL_CHECK (NODE))
+/* Nonzero if NODE is the target for genericization of 'break' stmts. */
+#define LABEL_DECL_BREAK(NODE) \
+ DECL_LANG_FLAG_0 (LABEL_DECL_CHECK (NODE))
+
+/* Nonzero if NODE is the target for genericization of 'continue' stmts. */
+#define LABEL_DECL_CONTINUE(NODE) \
+ DECL_LANG_FLAG_1 (LABEL_DECL_CHECK (NODE))
+
/* True if NODE was declared with auto in its return type, but it has
started compilation and so the return type might have been changed by
return type deduction; its declared return type should be found in
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-loop1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-loop1.C
new file mode 100644
index 0000000..fc5d4f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-loop1.C
@@ -0,0 +1,13 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+ int j = 0;
+ for (; i > 0; --i)
+ ++j;
+ return j;
+}
+
+constexpr int i = f(42);
+#define SA(X) static_assert((X),#X)
+SA(i==42);
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-return1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-return1.C
new file mode 100644
index 0000000..b114e21
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-return1.C
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+ return 24;
+ return 36;
+}
+
+constexpr int i = f(42);
+#define SA(X) static_assert((X),#X)
+SA(i==24);
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-return2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-return2.C
new file mode 100644
index 0000000..ae2628d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-return2.C
@@ -0,0 +1,7 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+}
+
+constexpr int i = f(42); // { dg-error "flows off the end" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-switch1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch1.C
new file mode 100644
index 0000000..add7323
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch1.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ return 42;
+ default:
+ return 0;
+ }
+}
+
+constexpr int i = f(1);
+#define SA(X) static_assert((X),#X)
+SA(i==42);
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-switch2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch2.C
new file mode 100644
index 0000000..a459a5e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch2.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+ int j = 0;
+ switch (i)
+ {
+ case 1:
+ j = 42;
+ break;
+ default:
+ j = 24;
+ break;
+ }
+ return j;
+}
+
+constexpr int i = f(1);
+#define SA(X) static_assert((X),#X)
+SA(i==42);
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-switch3.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch3.C
new file mode 100644
index 0000000..1aa1cf6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch3.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+ int j = 0;
+ switch (i)
+ {
+ case 1:
+ j = 42;
+ break;
+ default:
+ j = 24;
+ break;
+ }
+ return j;
+}
+
+constexpr int i = f(2);
+#define SA(X) static_assert((X),#X)
+SA(i==24);