From 56632b2773bfc2ebe722fa21c796c7369acf9a9c Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 17 Nov 2014 14:08:07 -0500 Subject: [PATCH] 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 --- gcc/cp/ChangeLog | 11 + gcc/cp/constexpr.c | 315 ++++++++++++++---- gcc/cp/cp-gimplify.c | 4 + gcc/cp/cp-tree.h | 10 + gcc/testsuite/g++.dg/cpp1y/constexpr-loop1.C | 13 + .../g++.dg/cpp1y/constexpr-return1.C | 11 + .../g++.dg/cpp1y/constexpr-return2.C | 7 + .../g++.dg/cpp1y/constexpr-switch1.C | 16 + .../g++.dg/cpp1y/constexpr-switch2.C | 20 ++ .../g++.dg/cpp1y/constexpr-switch3.C | 20 ++ 10 files changed, 361 insertions(+), 66 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-loop1.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-return1.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-return2.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-switch1.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-switch2.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-switch3.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index e77e7a4169f..18e06d82cd1 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,16 @@ 2014-11-17 Jason Merrill + 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 8881271de24..5b2565413b0 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -871,7 +871,7 @@ struct constexpr_ctx { static GTY (()) hash_table *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 map; ctx.values = ↦ 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 81b26d2203d..82be90be437 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 adc8aa72d97..d3722d73f61 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 00000000000..fc5d4f81af1 --- /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 00000000000..b114e214888 --- /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 00000000000..ae2628d800c --- /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 00000000000..add732320f4 --- /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 00000000000..a459a5efce9 --- /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 00000000000..1aa1cf6bbae --- /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); -- 2.30.2