static bool equal (constexpr_call *, constexpr_call *);
};
+enum constexpr_switch_state {
+ /* Used when processing a switch for the first time by cxx_eval_switch_expr
+ and default: label for that switch has not been seen yet. */
+ css_default_not_seen,
+ /* Used when processing a switch for the first time by cxx_eval_switch_expr
+ and default: label for that switch has been seen already. */
+ css_default_seen,
+ /* Used when processing a switch for the second time by
+ cxx_eval_switch_expr, where default: label should match. */
+ css_default_processing
+};
+
/* The constexpr expansion context. CALL is the current function
expansion, CTOR is the current aggregate initializer, OBJECT is the
object being initialized by CTOR, either a VAR_DECL or a _REF. VALUES
tree ctor;
/* The object we're building the CONSTRUCTOR for. */
tree object;
+ /* If inside SWITCH_EXPR. */
+ constexpr_switch_state *css_state;
/* Whether we should error on a non-constant expression or fail quietly. */
bool quiet;
/* Whether we are strictly conforming to constant expression rules or
}
/* 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. */
+ STMT matches *jump_target. If we're looking for a case label and we see
+ the default label, note it in ctx->css_state. */
static bool
-label_matches (tree *jump_target, tree_stmt_iterator i,
- tree_stmt_iterator& default_label)
+label_matches (const constexpr_ctx *ctx, tree *jump_target, tree stmt)
{
- tree stmt = tsi_stmt (i);
switch (TREE_CODE (*jump_target))
{
case LABEL_DECL:
case INTEGER_CST:
if (TREE_CODE (stmt) == CASE_LABEL_EXPR)
{
+ gcc_assert (ctx->css_state != NULL);
if (!CASE_LOW (stmt))
- default_label = i;
+ {
+ /* default: should appear just once in a SWITCH_EXPR
+ body (excluding nested SWITCH_EXPR). */
+ gcc_assert (*ctx->css_state != css_default_seen);
+ /* When evaluating SWITCH_EXPR body for the second time,
+ return true for the default: label. */
+ if (*ctx->css_state == css_default_processing)
+ return true;
+ *ctx->css_state = css_default_seen;
+ }
else if (CASE_HIGH (stmt))
{
if (tree_int_cst_le (CASE_LOW (stmt), *jump_target)
tree *jump_target)
{
tree_stmt_iterator i;
- tree_stmt_iterator default_label = tree_stmt_iterator();
tree local_target;
/* In a statement-expression we want to return the last value. */
tree r = NULL_TREE;
}
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;
- }
r = cxx_eval_constant_expression (ctx, stmt, false,
non_constant_p, overflow_p,
jump_target);
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 r;
}
break;
}
}
- while (!returns (jump_target) && !breaks (jump_target) && !*non_constant_p);
+ while (!returns (jump_target)
+ && !breaks (jump_target)
+ && !switches (jump_target)
+ && !*non_constant_p);
if (breaks (jump_target))
*jump_target = NULL_TREE;
*jump_target = cond;
tree body = TREE_OPERAND (t, 1);
- cxx_eval_statement_list (ctx, body,
- non_constant_p, overflow_p, jump_target);
+ constexpr_ctx new_ctx = *ctx;
+ constexpr_switch_state css = css_default_not_seen;
+ new_ctx.css_state = &css;
+ cxx_eval_constant_expression (&new_ctx, body, false,
+ non_constant_p, overflow_p, jump_target);
+ if (switches (jump_target) && css == css_default_seen)
+ {
+ /* If the SWITCH_EXPR body has default: label, process it once again,
+ this time instructing label_matches to return true for default:
+ label on switches (jump_target). */
+ css = css_default_processing;
+ cxx_eval_constant_expression (&new_ctx, body, false,
+ non_constant_p, overflow_p, jump_target);
+ }
if (breaks (jump_target) || switches (jump_target))
*jump_target = NULL_TREE;
return NULL_TREE;
constexpr_ctx new_ctx;
tree r = t;
+ if (jump_target && *jump_target)
+ {
+ /* If we are jumping, ignore all statements/expressions except those
+ that could have LABEL_EXPR or CASE_LABEL_EXPR in their bodies. */
+ switch (TREE_CODE (t))
+ {
+ case BIND_EXPR:
+ case STATEMENT_LIST:
+ case LOOP_EXPR:
+ case COND_EXPR:
+ break;
+ case LABEL_EXPR:
+ case CASE_LABEL_EXPR:
+ if (label_matches (ctx, jump_target, t))
+ /* Found it. */
+ *jump_target = NULL_TREE;
+ return NULL_TREE;
+ default:
+ return NULL_TREE;
+ }
+ }
if (t == error_mark_node)
{
*non_constant_p = true;
case LABEL_DECL:
case LABEL_EXPR:
case CASE_LABEL_EXPR:
+ case PREDICT_EXPR:
return t;
case PARM_DECL:
case INIT_EXPR:
case MODIFY_EXPR:
+ gcc_assert (jump_target == NULL || *jump_target == NULL_TREE);
r = cxx_eval_store_expression (ctx, t, lval,
non_constant_p, overflow_p);
break;
break;
case COND_EXPR:
+ if (jump_target && *jump_target)
+ {
+ /* 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);
+ break;
+ }
+ /* FALLTHRU */
case VEC_COND_EXPR:
r = cxx_eval_conditional_expression (ctx, t, lval,
non_constant_p, overflow_p,
bool overflow_p = false;
hash_map<tree,tree> map;
- constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL,
+ constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL,
allow_non_constant, strict };
tree type = initialized_type (t);
bool overflow_p = false;
hash_map <tree, tree> map;
- constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, true, true };
+ constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL, true, true };
cxx_eval_constant_expression (&ctx, t, false, &non_constant_p,
&overflow_p);
--- /dev/null
+// PR c++/77467
+// { dg-do compile { target c++14 } }
+
+constexpr int
+foo (const int x, const unsigned n) noexcept
+{
+ switch (n)
+ {
+ case 0:
+ return 1;
+ case 1:
+ return x;
+ default:
+ const auto m = (n >> 1);
+ const auto y = foo (x, m);
+ return ((m << 1) == n) ? y * y : x * y * y;
+ }
+}
+
+static_assert (foo (3, 2) == 9, "");
+static_assert (foo (2, 3) == 8, "");
+
+constexpr int
+bar (int x)
+{
+ int a = x;
+ switch (x)
+ a = x + 1;
+ return a;
+}
+
+static_assert (bar (0) == 0, "");
+static_assert (bar (1) == 1, "");
+
+constexpr int
+baz (const int x, int y) noexcept
+{
+ switch (x)
+ {
+ case 0:
+ return 1;
+ case 1:
+ return x;
+ case 2:
+ if ((y += 2) == 0)
+ {
+ case 3:
+ y += 4;
+ break;
+ }
+ else
+ {
+ case 4:
+ y += 8;
+ break;
+ }
+ break;
+ case 5:
+ for (y = 0; y < 3; y++)
+ {
+ case 7:
+ if (y == -4)
+ y += 3;
+ if (y == -3)
+ continue;
+ if (y == -2)
+ {
+ y += 18;
+ break;
+ }
+ if (y == 2)
+ {
+ case 6:
+ y += 12;
+ default:
+ y++;
+ break;
+ }
+ }
+ break;
+ case -1:
+ case -2:
+ switch (y)
+ {
+ case 19:
+ y += 2;
+ break;
+ case 20:
+ y += 3;
+ if (x == 2)
+ case 21:;
+ y += 2;
+ if (x == 3)
+ default:;
+ y += 4;
+ break;
+ }
+ return x + y + 1;
+ }
+ return x + y;
+}
+
+static_assert (baz (0, 7) == 1, "");
+static_assert (baz (1, 7) == 1, "");
+static_assert (baz (2, -2) == 6, "");
+static_assert (baz (2, 0) == 12, "");
+static_assert (baz (3, 1) == 8, "");
+static_assert (baz (4, 2) == 14, "");
+static_assert (baz (5, -20) == 20, "");
+static_assert (baz (6, 5) == 24, "");
+static_assert (baz (7, -5) == 22, "");
+static_assert (baz (7, -4) == 22, "");
+static_assert (baz (7, -3) == 23, "");
+static_assert (baz (7, -2) == 23, "");
+static_assert (baz (7, -1) == 22, "");
+static_assert (baz (7, 0) == 22, "");
+static_assert (baz (7, 2) == 22, "");
+static_assert (baz (7, 6) == 14, "");
+static_assert (baz (8, 9) == 18, "");
+static_assert (baz (8, -2) == 7, "");
+static_assert (baz (-1, 19) == 21, "");
+static_assert (baz (-1, 20) == 29, "");
+static_assert (baz (-1, 21) == 27, "");
+static_assert (baz (-1, 5) == 9, "");
+static_assert (baz (-2, 19) == 20, "");
+static_assert (baz (-2, 20) == 28, "");
+static_assert (baz (-2, 21) == 26, "");
+static_assert (baz (-2, 5) == 8, "");