re PR c++/77467 (Segmentation fault with switch statement in constexpr function)
PR c++/77467 * constexpr.c (enum constexpr_switch_state): New. (struct constexpr_ctx): Add css_state field. (label_matches): Add CTX and STMT arguments, remove I and DEFAULT_LABEL. For CASE_LABEL_EXPR assert ctx->css_state != NULL, handle default labels according to css_state. (cxx_eval_statement_list): Remove statement skipping, label_matches and default_label handling code. (cxx_eval_loop_expr): Exit after first iteration even if switches (jump_target). (cxx_eval_switch_expr): Set up css_state field in ctx, if default label has been seen in the body, but no cases matched, evaluate the body second time. (cxx_eval_constant_expression): Handle stmt skipping and label_matches here. Handle PREDICT_EXPR. For MODIFY_EXPR or INIT_EXPR, assert statement is not skipped. For COND_EXPR during skipping, don't evaluate condition, just the then block and if still skipping at the end also the else block. (cxx_eval_outermost_constant_expr): Adjust constexpr_ctx initializer. (is_sub_constant_expr): Likewise. * g++.dg/cpp1y/constexpr-77467.C: New test. From-SVN: r240591
This commit is contained in:
parent
e0ab375340
commit
4b3906980a
@ -1,3 +1,26 @@
|
||||
2016-09-28 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR c++/77467
|
||||
* constexpr.c (enum constexpr_switch_state): New.
|
||||
(struct constexpr_ctx): Add css_state field.
|
||||
(label_matches): Add CTX and STMT arguments, remove I and
|
||||
DEFAULT_LABEL. For CASE_LABEL_EXPR assert ctx->css_state != NULL,
|
||||
handle default labels according to css_state.
|
||||
(cxx_eval_statement_list): Remove statement skipping, label_matches
|
||||
and default_label handling code.
|
||||
(cxx_eval_loop_expr): Exit after first iteration even if
|
||||
switches (jump_target).
|
||||
(cxx_eval_switch_expr): Set up css_state field in ctx, if default
|
||||
label has been seen in the body, but no cases matched, evaluate
|
||||
the body second time.
|
||||
(cxx_eval_constant_expression): Handle stmt skipping and label_matches
|
||||
here. Handle PREDICT_EXPR. For MODIFY_EXPR or INIT_EXPR, assert
|
||||
statement is not skipped. For COND_EXPR during skipping, don't
|
||||
evaluate condition, just the then block and if still skipping at the
|
||||
end also the else block.
|
||||
(cxx_eval_outermost_constant_expr): Adjust constexpr_ctx initializer.
|
||||
(is_sub_constant_expr): Likewise.
|
||||
|
||||
2016-09-27 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
Implement P0018R3, C++17 lambda capture of *this by value as [=,*this]
|
||||
|
@ -900,6 +900,18 @@ struct constexpr_call_hasher : ggc_ptr_hash<constexpr_call>
|
||||
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
|
||||
@ -919,6 +931,8 @@ struct constexpr_ctx {
|
||||
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
|
||||
@ -3484,14 +3498,12 @@ switches (tree *jump_target)
|
||||
}
|
||||
|
||||
/* 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:
|
||||
@ -3503,8 +3515,18 @@ label_matches (tree *jump_target, tree_stmt_iterator i,
|
||||
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)
|
||||
@ -3531,7 +3553,6 @@ cxx_eval_statement_list (const constexpr_ctx *ctx, tree t,
|
||||
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;
|
||||
@ -3542,18 +3563,7 @@ cxx_eval_statement_list (const constexpr_ctx *ctx, tree t,
|
||||
}
|
||||
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);
|
||||
@ -3562,12 +3572,6 @@ cxx_eval_statement_list (const constexpr_ctx *ctx, tree t,
|
||||
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;
|
||||
}
|
||||
|
||||
@ -3606,7 +3610,10 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
|
||||
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;
|
||||
@ -3629,8 +3636,20 @@ cxx_eval_switch_expr (const constexpr_ctx *ctx, tree t,
|
||||
*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;
|
||||
@ -3650,6 +3669,27 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
|
||||
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;
|
||||
@ -3730,6 +3770,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
|
||||
case LABEL_DECL:
|
||||
case LABEL_EXPR:
|
||||
case CASE_LABEL_EXPR:
|
||||
case PREDICT_EXPR:
|
||||
return t;
|
||||
|
||||
case PARM_DECL:
|
||||
@ -3835,6 +3876,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
|
||||
|
||||
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;
|
||||
@ -4065,6 +4107,22 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
|
||||
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,
|
||||
@ -4340,7 +4398,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
|
||||
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);
|
||||
@ -4460,7 +4518,7 @@ is_sub_constant_expr (tree 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);
|
||||
|
@ -1,3 +1,8 @@
|
||||
2016-09-28 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR c++/77467
|
||||
* g++.dg/cpp1y/constexpr-77467.C: New test.
|
||||
|
||||
2016-09-28 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR c/77762
|
||||
|
128
gcc/testsuite/g++.dg/cpp1y/constexpr-77467.C
Normal file
128
gcc/testsuite/g++.dg/cpp1y/constexpr-77467.C
Normal file
@ -0,0 +1,128 @@
|
||||
// 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, "");
|
Loading…
Reference in New Issue
Block a user