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:
Jakub Jelinek 2016-09-28 21:21:47 +02:00 committed by Jakub Jelinek
parent e0ab375340
commit 4b3906980a
4 changed files with 243 additions and 29 deletions

View File

@ -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]

View File

@ -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);

View File

@ -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

View 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, "");