Commit 3075affd by Jason Merrill Committed by Jason Merrill

PR c++/86678 - constexpr function with non-constant after return.

	In this testcase, the call to f() can never be a constant
	expression, but that's not a problem because it isn't always
	reached by calls to g.  We were wrongly rejecting this because
	potential_constant_expression_1 lacked the jump tracking that
	cxx_eval_constant_expression has.  So this patch adds a simpler
	version of that tracking.

	* constexpr.c (potential_constant_expression_1): Add jump_target.
	(breaks): Check for BREAK_STMT.
	(continues): Check for CONTINUE_STMT.

From-SVN: r264171
parent 61856507
2018-09-08 Jason Merrill <jason@redhat.com>
PR c++/86678 - constexpr function with non-constant after return.
* constexpr.c (potential_constant_expression_1): Add jump_target.
(breaks): Check for BREAK_STMT.
(continues): Check for CONTINUE_STMT.
2018-09-08 Marek Polacek <polacek@redhat.com> 2018-09-08 Marek Polacek <polacek@redhat.com>
* cxx-pretty-print.c (cxx_pretty_printer::statement) <case * cxx-pretty-print.c (cxx_pretty_printer::statement) <case
......
...@@ -3893,6 +3893,7 @@ breaks (tree *jump_target) ...@@ -3893,6 +3893,7 @@ breaks (tree *jump_target)
return *jump_target return *jump_target
&& ((TREE_CODE (*jump_target) == LABEL_DECL && ((TREE_CODE (*jump_target) == LABEL_DECL
&& LABEL_DECL_BREAK (*jump_target)) && LABEL_DECL_BREAK (*jump_target))
|| TREE_CODE (*jump_target) == BREAK_STMT
|| TREE_CODE (*jump_target) == EXIT_EXPR); || TREE_CODE (*jump_target) == EXIT_EXPR);
} }
...@@ -3900,8 +3901,10 @@ static bool ...@@ -3900,8 +3901,10 @@ static bool
continues (tree *jump_target) continues (tree *jump_target)
{ {
return *jump_target return *jump_target
&& TREE_CODE (*jump_target) == LABEL_DECL && ((TREE_CODE (*jump_target) == LABEL_DECL
&& LABEL_DECL_CONTINUE (*jump_target); && LABEL_DECL_CONTINUE (*jump_target))
|| TREE_CODE (*jump_target) == CONTINUE_STMT);
} }
static bool static bool
...@@ -4123,7 +4126,7 @@ static tree ...@@ -4123,7 +4126,7 @@ static tree
cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
bool lval, bool lval,
bool *non_constant_p, bool *overflow_p, bool *non_constant_p, bool *overflow_p,
tree *jump_target) tree *jump_target /* = NULL */)
{ {
constexpr_ctx new_ctx; constexpr_ctx new_ctx;
tree r = t; tree r = t;
...@@ -4149,7 +4152,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, ...@@ -4149,7 +4152,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
return NULL_TREE; return NULL_TREE;
} }
} }
if (t == error_mark_node) if (error_operand_p (t))
{ {
*non_constant_p = true; *non_constant_p = true;
return t; return t;
...@@ -5398,10 +5401,10 @@ check_automatic_or_tls (tree ref) ...@@ -5398,10 +5401,10 @@ check_automatic_or_tls (tree ref)
static bool static bool
potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
tsubst_flags_t flags) tsubst_flags_t flags, tree *jump_target)
{ {
#define RECUR(T,RV) \ #define RECUR(T,RV) \
potential_constant_expression_1 ((T), (RV), strict, now, flags) potential_constant_expression_1 ((T), (RV), strict, now, flags, jump_target)
enum { any = false, rval = true }; enum { any = false, rval = true };
int i; int i;
...@@ -5412,6 +5415,14 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, ...@@ -5412,6 +5415,14 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
if (t == NULL_TREE) if (t == NULL_TREE)
return true; return true;
location_t loc = cp_expr_loc_or_loc (t, input_location); location_t loc = cp_expr_loc_or_loc (t, input_location);
if (*jump_target)
/* If we are jumping, ignore everything. This is simpler than the
cxx_eval_constant_expression handling because we only need to be
conservatively correct, and we don't necessarily have a constant value
available, so we don't bother with switch tracking. */
return true;
if (TREE_THIS_VOLATILE (t) && !DECL_P (t)) if (TREE_THIS_VOLATILE (t) && !DECL_P (t))
{ {
if (flags & tf_error) if (flags & tf_error)
...@@ -5449,13 +5460,21 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, ...@@ -5449,13 +5460,21 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
case USING_DECL: case USING_DECL:
case USING_STMT: case USING_STMT:
case PLACEHOLDER_EXPR: case PLACEHOLDER_EXPR:
case BREAK_STMT:
case CONTINUE_STMT:
case REQUIRES_EXPR: case REQUIRES_EXPR:
case STATIC_ASSERT: case STATIC_ASSERT:
case DEBUG_BEGIN_STMT: case DEBUG_BEGIN_STMT:
return true; return true;
case RETURN_EXPR:
if (!RECUR (TREE_OPERAND (t, 0), any))
return false;
/* FALLTHROUGH */
case BREAK_STMT:
case CONTINUE_STMT:
*jump_target = t;
return true;
case PARM_DECL: case PARM_DECL:
if (now) if (now)
{ {
...@@ -5544,7 +5563,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, ...@@ -5544,7 +5563,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
constexpr substitution might not use the value. */ constexpr substitution might not use the value. */
bool sub_now = false; bool sub_now = false;
if (!potential_constant_expression_1 (x, rval, strict, if (!potential_constant_expression_1 (x, rval, strict,
sub_now, flags)) sub_now, flags,
jump_target))
return false; return false;
i = 1; i = 1;
} }
...@@ -5578,7 +5598,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, ...@@ -5578,7 +5598,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
substitution might not use the value of the argument. */ substitution might not use the value of the argument. */
bool sub_now = false; bool sub_now = false;
if (!potential_constant_expression_1 (x, rv, strict, if (!potential_constant_expression_1 (x, rv, strict,
sub_now, flags)) sub_now, flags, jump_target))
return false; return false;
} }
return true; return true;
...@@ -5753,6 +5773,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, ...@@ -5753,6 +5773,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
return false; return false;
if (!RECUR (DO_BODY (t), any)) if (!RECUR (DO_BODY (t), any))
return false; return false;
if (breaks (jump_target) || continues (jump_target))
*jump_target = NULL_TREE;
return true; return true;
case FOR_STMT: case FOR_STMT:
...@@ -5764,6 +5786,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, ...@@ -5764,6 +5786,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
return false; return false;
if (!RECUR (FOR_BODY (t), any)) if (!RECUR (FOR_BODY (t), any))
return false; return false;
if (breaks (jump_target) || continues (jump_target))
*jump_target = NULL_TREE;
return true; return true;
case RANGE_FOR_STMT: case RANGE_FOR_STMT:
...@@ -5773,6 +5797,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, ...@@ -5773,6 +5797,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
return false; return false;
if (!RECUR (RANGE_FOR_BODY (t), any)) if (!RECUR (RANGE_FOR_BODY (t), any))
return false; return false;
if (breaks (jump_target) || continues (jump_target))
*jump_target = NULL_TREE;
return true; return true;
case WHILE_STMT: case WHILE_STMT:
...@@ -5780,6 +5806,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, ...@@ -5780,6 +5806,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
return false; return false;
if (!RECUR (WHILE_BODY (t), any)) if (!RECUR (WHILE_BODY (t), any))
return false; return false;
if (breaks (jump_target) || continues (jump_target))
*jump_target = NULL_TREE;
return true; return true;
case SWITCH_STMT: case SWITCH_STMT:
...@@ -5963,7 +5991,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, ...@@ -5963,7 +5991,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
case PAREN_EXPR: case PAREN_EXPR:
case NON_DEPENDENT_EXPR: case NON_DEPENDENT_EXPR:
/* For convenience. */ /* For convenience. */
case RETURN_EXPR:
case LOOP_EXPR: case LOOP_EXPR:
case EXIT_EXPR: case EXIT_EXPR:
return RECUR (TREE_OPERAND (t, 0), want_rval); return RECUR (TREE_OPERAND (t, 0), want_rval);
...@@ -6173,7 +6200,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, ...@@ -6173,7 +6200,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
return RECUR (TREE_OPERAND (t, 1), want_rval); return RECUR (TREE_OPERAND (t, 1), want_rval);
for (i = 1; i < 3; ++i) for (i = 1; i < 3; ++i)
if (potential_constant_expression_1 (TREE_OPERAND (t, i), if (potential_constant_expression_1 (TREE_OPERAND (t, i),
want_rval, strict, now, tf_none)) want_rval, strict, now,
tf_none, jump_target))
return true; return true;
if (flags & tf_error) if (flags & tf_error)
error_at (loc, "expression %qE is not a constant expression", t); error_at (loc, "expression %qE is not a constant expression", t);
...@@ -6204,7 +6232,10 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, ...@@ -6204,7 +6232,10 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
tree *target = &TREE_OPERAND (t, 0); tree *target = &TREE_OPERAND (t, 0);
/* Gotos representing break and continue are OK. */ /* Gotos representing break and continue are OK. */
if (breaks (target) || continues (target)) if (breaks (target) || continues (target))
return true; {
*jump_target = *target;
return true;
}
if (flags & tf_error) if (flags & tf_error)
error_at (loc, "%<goto%> is not a constant expression"); error_at (loc, "%<goto%> is not a constant expression");
return false; return false;
...@@ -6224,6 +6255,15 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, ...@@ -6224,6 +6255,15 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
#undef RECUR #undef RECUR
} }
bool
potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
tsubst_flags_t flags)
{
tree target = NULL_TREE;
return potential_constant_expression_1 (t, want_rval, strict, now,
flags, &target);
}
/* The main entry point to the above. */ /* The main entry point to the above. */
bool bool
......
// PR c++/86678
// { dg-do compile { target c++14 } }
constexpr bool always_true() { return true; }
int f() { return 1; }
constexpr int g() {
if (always_true())
return 0;
return f();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment