aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2016-09-28 21:21:47 +0200
committerJakub Jelinek <jakub@gcc.gnu.org>2016-09-28 21:21:47 +0200
commit4b3906980ac60781cddae5a787bf2908dd0920b7 (patch)
tree87190ab17a07d003e50e369e06ccb1875e978e4f /gcc
parente0ab375340dc9a651e00e62e79e45152ff3f8bad (diff)
downloadgcc-4b3906980ac60781cddae5a787bf2908dd0920b7.zip
gcc-4b3906980ac60781cddae5a787bf2908dd0920b7.tar.gz
gcc-4b3906980ac60781cddae5a787bf2908dd0920b7.tar.bz2
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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/ChangeLog23
-rw-r--r--gcc/cp/constexpr.c116
-rw-r--r--gcc/testsuite/ChangeLog5
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-77467.C128
4 files changed, 243 insertions, 29 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 1a1f186..0e18eb7 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -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]
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index bd4068e..2db13d2 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -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);
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index ee788ee..22a001b 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-77467.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-77467.C
new file mode 100644
index 0000000..fd94e78
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-77467.C
@@ -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, "");