From c7a53bdbb164f5a216f13801c1abbaa8c08c81f1 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Thu, 28 Mar 2019 15:47:47 +0100 Subject: [PATCH] re PR c++/89785 (Incorrect "not a constant expression" error with switch statement that returns) PR c++/89785 * constexpr.c (struct check_for_return_continue_data): New type. (check_for_return_continue): New function. (potential_constant_expression_1) : Walk SWITCH_STMT_BODY to find RETURN_EXPRs or CONTINUE_STMTs not nested in loop bodies and set *jump_target to that if found. * g++.dg/cpp1y/constexpr-89785-1.C: New test. * g++.dg/cpp1y/constexpr-89785-2.C: New test. From-SVN: r269995 --- gcc/cp/ChangeLog | 9 ++ gcc/cp/constexpr.c | 99 ++++++++++++++++++- gcc/testsuite/ChangeLog | 6 ++ .../g++.dg/cpp1y/constexpr-89785-1.C | 36 +++++++ .../g++.dg/cpp1y/constexpr-89785-2.C | 36 +++++++ 5 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-89785-1.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 9803ef947f8..b7c6c35a160 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,12 @@ +2019-03-28 Jakub Jelinek + + PR c++/89785 + * constexpr.c (struct check_for_return_continue_data): New type. + (check_for_return_continue): New function. + (potential_constant_expression_1) : Walk + SWITCH_STMT_BODY to find RETURN_EXPRs or CONTINUE_STMTs not nested + in loop bodies and set *jump_target to that if found. + 2019-03-27 Jason Merrill PR c++/89831 - error with qualified-id in const member function. diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index c00d642fcfe..daf34e10784 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -5731,6 +5731,86 @@ check_automatic_or_tls (tree ref) } #endif +/* Data structure for passing data from potential_constant_expression_1 + to check_for_return_continue via cp_walk_tree. */ +struct check_for_return_continue_data { + hash_set *pset; + tree continue_stmt; +}; + +/* Helper function for potential_constant_expression_1 SWITCH_STMT handling, + called through cp_walk_tree. Return the first RETURN_EXPR found, or note + the first CONTINUE_STMT if RETURN_EXPR is not found. */ +static tree +check_for_return_continue (tree *tp, int *walk_subtrees, void *data) +{ + tree t = *tp, s; + check_for_return_continue_data *d = (check_for_return_continue_data *) data; + switch (TREE_CODE (t)) + { + case RETURN_EXPR: + return t; + + case CONTINUE_STMT: + if (d->continue_stmt == NULL_TREE) + d->continue_stmt = t; + break; + +#define RECUR(x) \ + if (tree r = cp_walk_tree (&x, check_for_return_continue, data, \ + d->pset)) \ + return r + + /* For loops, walk subtrees manually, so that continue stmts found + inside of the bodies of the loops are ignored. */ + case DO_STMT: + *walk_subtrees = 0; + RECUR (DO_COND (t)); + s = d->continue_stmt; + RECUR (DO_BODY (t)); + d->continue_stmt = s; + break; + + case WHILE_STMT: + *walk_subtrees = 0; + RECUR (WHILE_COND (t)); + s = d->continue_stmt; + RECUR (WHILE_BODY (t)); + d->continue_stmt = s; + break; + + case FOR_STMT: + *walk_subtrees = 0; + RECUR (FOR_INIT_STMT (t)); + RECUR (FOR_COND (t)); + RECUR (FOR_EXPR (t)); + s = d->continue_stmt; + RECUR (FOR_BODY (t)); + d->continue_stmt = s; + break; + + case RANGE_FOR_STMT: + *walk_subtrees = 0; + RECUR (RANGE_FOR_EXPR (t)); + s = d->continue_stmt; + RECUR (RANGE_FOR_BODY (t)); + d->continue_stmt = s; + break; +#undef RECUR + + case STATEMENT_LIST: + case CONSTRUCTOR: + break; + + default: + if (!EXPR_P (t)) + *walk_subtrees = 0; + break; + } + + return NULL_TREE; +} + /* Return true if T denotes a potentially constant expression. Issue diagnostic as appropriate under control of FLAGS. If WANT_RVAL is true, an lvalue-rvalue conversion is implied. If NOW is true, we want to @@ -6196,7 +6276,24 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, if (!RECUR (SWITCH_STMT_COND (t), rval)) return false; /* FIXME we don't check SWITCH_STMT_BODY currently, because even - unreachable labels would be checked. */ + unreachable labels would be checked and it is enough if there is + a single switch cond value for which it is a valid constant + expression. We need to check if there are any RETURN_EXPRs + or CONTINUE_STMTs inside of the body though, as in that case + we need to set *jump_target. */ + else + { + hash_set pset; + check_for_return_continue_data data = { &pset, NULL_TREE }; + if (tree ret_expr + = cp_walk_tree (&SWITCH_STMT_BODY (t), check_for_return_continue, + &data, &pset)) + /* The switch might return. */ + *jump_target = ret_expr; + else if (data.continue_stmt) + /* The switch can't return, but might continue. */ + *jump_target = data.continue_stmt; + } return true; case STMT_EXPR: diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index f29455331f5..64457695b20 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2019-03-28 Jakub Jelinek + + PR c++/89785 + * g++.dg/cpp1y/constexpr-89785-1.C: New test. + * g++.dg/cpp1y/constexpr-89785-2.C: New test. + 2019-03-27 Janus Weil PR fortran/85537 diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-1.C new file mode 100644 index 00000000000..10f16b9e308 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-1.C @@ -0,0 +1,36 @@ +// PR c++/89785 +// { dg-do compile { target c++14 } } + +constexpr int +foo (int x) +{ + switch (x) + { + case 0: + throw -42; + case 2: + return 42; + } + throw 42; +} + +constexpr int +bar (int x) +{ + do + { + switch (x) + { + case 0: + throw 42; + case 1: + continue; + } + throw -42; + } + while (0); + return x; +} + +static_assert (foo (2) == 42, ""); +static_assert (bar (1) == 1, ""); diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C new file mode 100644 index 00000000000..5cd46c791af --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C @@ -0,0 +1,36 @@ +// PR c++/89785 +// { dg-do compile { target c++14 } } + +constexpr int +foo (int x) +{ + switch (x) + { + case 0: + break; + case 2: + break; + } + throw 42; // { dg-error "is not a constant expression" } + return 0; +} + +constexpr int +bar (int x) +{ + do + { + switch (x) + { + case 0: + throw 42; + case 1: + for (int i = 0; i < 10; i++) + continue; + break; + } + throw -42; // { dg-error "is not a constant expression" } + } + while (0); + return x; +} -- 2.30.2