Implement C++17 constexpr if.

* cp-tree.h (IF_STMT_CONSTEXPR_P): New.
	* name-lookup.c (push_to_top_level, pop_from_top_level_1): Handle it.
	* parser.h (struct cp_parser): Add in_discarded_stmt field.
	* parser.c (cp_parser_selection_statement): Handle 'if constexpr'.
	(cp_parser_jump_statement): Avoid deducing from a discarded return.
	* pt.c (tsubst_expr): Only instantiate taken branch of constexpr if.
	* semantics.c (begin_if_stmt): Set the binding level this_entity.
	(finish_if_stmt_cond): Require the condition of a
	constexpr if to be constant.
	* decl.c (level_for_constexpr_if): New.
	(named_label_entry): Add in_constexpr_if field.
	(poplevel_named_label_1): Set it.
	(check_goto): Check it.
	(check_previous_goto_1): Check level_for_constexpr_if.

From-SVN: r239338
This commit is contained in:
Jason Merrill 2016-08-10 17:58:23 -04:00 committed by Jason Merrill
parent e9e6d4f6cc
commit 99f9d4b1b6
16 changed files with 253 additions and 11 deletions

View File

@ -1,3 +1,21 @@
2016-08-10 Jason Merrill <jason@redhat.com>
Implement C++17 constexpr if.
* cp-tree.h (IF_STMT_CONSTEXPR_P): New.
* name-lookup.c (push_to_top_level, pop_from_top_level_1): Handle it.
* parser.h (struct cp_parser): Add in_discarded_stmt field.
* parser.c (cp_parser_selection_statement): Handle 'if constexpr'.
(cp_parser_jump_statement): Avoid deducing from a discarded return.
* pt.c (tsubst_expr): Only instantiate taken branch of constexpr if.
* semantics.c (begin_if_stmt): Set the binding level this_entity.
(finish_if_stmt_cond): Require the condition of a
constexpr if to be constant.
* decl.c (level_for_constexpr_if): New.
(named_label_entry): Add in_constexpr_if field.
(poplevel_named_label_1): Set it.
(check_goto): Check it.
(check_previous_goto_1): Check level_for_constexpr_if.
2016-08-09 Jason Merrill <jason@redhat.com>
PR c++/68703

View File

@ -145,6 +145,7 @@ operator == (const cp_expr &lhs, tree rhs)
WILDCARD_PACK_P (in WILDCARD_DECL)
BLOCK_OUTER_CURLY_BRACE_P (in BLOCK)
FOLD_EXPR_MODOP_P (*_FOLD_EXPR)
IF_STMT_CONSTEXPR_P (IF_STMT)
1: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
TI_PENDING_TEMPLATE_FLAG.
TEMPLATE_PARMS_FOR_INLINE.
@ -4530,6 +4531,7 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
#define THEN_CLAUSE(NODE) TREE_OPERAND (IF_STMT_CHECK (NODE), 1)
#define ELSE_CLAUSE(NODE) TREE_OPERAND (IF_STMT_CHECK (NODE), 2)
#define IF_SCOPE(NODE) TREE_OPERAND (IF_STMT_CHECK (NODE), 3)
#define IF_STMT_CONSTEXPR_P(NODE) TREE_LANG_FLAG_0 (IF_STMT_CHECK (NODE))
/* WHILE_STMT accessors. These give access to the condition of the
while statement and the body of the while statement, respectively. */
@ -6303,7 +6305,7 @@ extern void add_decl_expr (tree);
extern tree maybe_cleanup_point_expr_void (tree);
extern tree finish_expr_stmt (tree);
extern tree begin_if_stmt (void);
extern void finish_if_stmt_cond (tree, tree);
extern tree finish_if_stmt_cond (tree, tree);
extern tree finish_then_clause (tree);
extern void begin_else_clause (tree);
extern void finish_else_clause (tree);

View File

@ -218,6 +218,7 @@ struct GTY((for_user)) named_label_entry {
bool in_catch_scope;
bool in_omp_scope;
bool in_transaction_scope;
bool in_constexpr_if;
};
#define named_labels cp_function_chain->x_named_labels
@ -476,6 +477,16 @@ objc_mark_locals_volatile (void *enclosing_blk)
}
}
/* True if B is the level for the condition of a constexpr if. */
static bool
level_for_constexpr_if (cp_binding_level *b)
{
return (b->kind == sk_cond && b->this_entity
&& TREE_CODE (b->this_entity) == IF_STMT
&& IF_STMT_CONSTEXPR_P (b->this_entity));
}
/* Update data for defined and undefined labels when leaving a scope. */
int
@ -512,6 +523,10 @@ poplevel_named_label_1 (named_label_entry **slot, cp_binding_level *bl)
case sk_transaction:
ent->in_transaction_scope = true;
break;
case sk_block:
if (level_for_constexpr_if (bl->level_chain))
ent->in_constexpr_if = true;
break;
default:
break;
}
@ -3047,7 +3062,7 @@ check_previous_goto_1 (tree decl, cp_binding_level* level, tree names,
cp_binding_level *b;
bool complained = false;
int identified = 0;
bool saw_eh = false, saw_omp = false, saw_tm = false;
bool saw_eh = false, saw_omp = false, saw_tm = false, saw_cxif = false;
if (exited_omp)
{
@ -3132,6 +3147,20 @@ check_previous_goto_1 (tree decl, cp_binding_level* level, tree names,
" enters synchronized or atomic statement");
saw_tm = true;
}
if (!saw_cxif && b->kind == sk_block
&& level_for_constexpr_if (b->level_chain))
{
if (identified < 2)
{
complained = identify_goto (decl, input_location, locus,
DK_ERROR);
identified = 2;
}
if (complained)
inform (EXPR_LOCATION (b->level_chain->this_entity),
" enters constexpr if statement");
saw_cxif = true;
}
}
return !identified;
@ -3200,10 +3229,11 @@ check_goto (tree decl)
}
if (ent->in_try_scope || ent->in_catch_scope || ent->in_transaction_scope
|| ent->in_constexpr_if
|| ent->in_omp_scope || !vec_safe_is_empty (ent->bad_decls))
{
diagnostic_t diag_kind = DK_PERMERROR;
if (ent->in_try_scope || ent->in_catch_scope
if (ent->in_try_scope || ent->in_catch_scope || ent->in_constexpr_if
|| ent->in_transaction_scope || ent->in_omp_scope)
diag_kind = DK_ERROR;
complained = identify_goto (decl, DECL_SOURCE_LOCATION (decl),
@ -3248,6 +3278,8 @@ check_goto (tree decl)
inform (input_location, " enters catch block");
else if (ent->in_transaction_scope)
inform (input_location, " enters synchronized or atomic statement");
else if (ent->in_constexpr_if)
inform (input_location, " enters constexpr if statement");
}
if (ent->in_omp_scope)

View File

@ -10899,6 +10899,18 @@ cp_parser_selection_statement (cp_parser* parser, bool *if_p,
tree statement;
tree condition;
bool cx = false;
if (keyword == RID_IF
&& cp_lexer_next_token_is_keyword (parser->lexer,
RID_CONSTEXPR))
{
cx = true;
cp_token *tok = cp_lexer_consume_token (parser->lexer);
if (cxx_dialect < cxx1z && !in_system_header_at (tok->location))
pedwarn (tok->location, 0, "%<if constexpr%> only available "
"with -std=c++1z or -std=gnu++1z");
}
/* Look for the `('. */
if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
{
@ -10908,7 +10920,10 @@ cp_parser_selection_statement (cp_parser* parser, bool *if_p,
/* Begin the selection-statement. */
if (keyword == RID_IF)
{
statement = begin_if_stmt ();
IF_STMT_CONSTEXPR_P (statement) = cx;
}
else
statement = begin_switch_stmt ();
@ -10925,7 +10940,7 @@ cp_parser_selection_statement (cp_parser* parser, bool *if_p,
unsigned char in_statement;
/* Add the condition. */
finish_if_stmt_cond (condition, statement);
condition = finish_if_stmt_cond (condition, statement);
if (warn_duplicated_cond)
warn_duplicated_cond_add_or_warn (token->location, condition,
@ -10934,16 +10949,44 @@ cp_parser_selection_statement (cp_parser* parser, bool *if_p,
/* Parse the then-clause. */
in_statement = parser->in_statement;
parser->in_statement |= IN_IF_STMT;
/* Outside a template, the non-selected branch of a constexpr
if is a 'discarded statement', i.e. unevaluated. */
bool was_discarded = parser->in_discarded_stmt;
bool discard_then = (cx && !processing_template_decl
&& integer_zerop (condition));
if (discard_then)
{
parser->in_discarded_stmt = true;
++c_inhibit_evaluation_warnings;
}
cp_parser_implicitly_scoped_statement (parser, &nested_if,
guard_tinfo);
parser->in_statement = in_statement;
finish_then_clause (statement);
if (discard_then)
{
THEN_CLAUSE (statement) = NULL_TREE;
parser->in_discarded_stmt = was_discarded;
--c_inhibit_evaluation_warnings;
}
/* If the next token is `else', parse the else-clause. */
if (cp_lexer_next_token_is_keyword (parser->lexer,
RID_ELSE))
{
bool discard_else = (cx && !processing_template_decl
&& integer_nonzerop (condition));
if (discard_else)
{
parser->in_discarded_stmt = true;
++c_inhibit_evaluation_warnings;
}
guard_tinfo
= get_token_indent_info (cp_lexer_peek_token (parser->lexer));
/* Consume the `else' keyword. */
@ -10993,6 +11036,13 @@ cp_parser_selection_statement (cp_parser* parser, bool *if_p,
when we get back up to the parent if statement. */
if (if_p != NULL)
*if_p = true;
if (discard_else)
{
ELSE_CLAUSE (statement) = NULL_TREE;
parser->in_discarded_stmt = was_discarded;
--c_inhibit_evaluation_warnings;
}
}
else
{
@ -11864,6 +11914,9 @@ cp_parser_jump_statement (cp_parser* parser)
expression. */
expr = NULL_TREE;
/* Build the return-statement. */
if (current_function_auto_return_pattern && parser->in_discarded_stmt)
/* Don't deduce from a discarded return statement. */;
else
statement = finish_return_stmt (expr);
/* Look for the final `;'. */
cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);

View File

@ -336,6 +336,10 @@ struct GTY(()) cp_parser {
a local class. */
bool in_function_body;
/* TRUE if we are parsing a C++17 discarded statement (the non-taken branch
of an if constexpr). */
bool in_discarded_stmt;
/* Nonzero if we're processing a __transaction_atomic or
__transaction_relaxed statement. */
unsigned char in_transaction;

View File

@ -15387,12 +15387,18 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
case IF_STMT:
stmt = begin_if_stmt ();
IF_STMT_CONSTEXPR_P (stmt) = IF_STMT_CONSTEXPR_P (t);
tmp = RECUR (IF_COND (t));
finish_if_stmt_cond (tmp, stmt);
tmp = finish_if_stmt_cond (tmp, stmt);
if (IF_STMT_CONSTEXPR_P (t) && integer_zerop (tmp))
/* Don't instantiate the THEN_CLAUSE. */;
else
RECUR (THEN_CLAUSE (t));
finish_then_clause (stmt);
if (ELSE_CLAUSE (t))
if (IF_STMT_CONSTEXPR_P (t) && integer_nonzerop (tmp))
/* Don't instantiate the ELSE_CLAUSE. */;
else if (ELSE_CLAUSE (t))
{
begin_else_clause (stmt);
RECUR (ELSE_CLAUSE (t));

View File

@ -715,6 +715,7 @@ begin_if_stmt (void)
scope = do_pushlevel (sk_cond);
r = build_stmt (input_location, IF_STMT, NULL_TREE,
NULL_TREE, NULL_TREE, scope);
current_binding_level->this_entity = r;
begin_cond (&IF_COND (r));
return r;
}
@ -722,12 +723,18 @@ begin_if_stmt (void)
/* Process the COND of an if-statement, which may be given by
IF_STMT. */
void
tree
finish_if_stmt_cond (tree cond, tree if_stmt)
{
finish_cond (&IF_COND (if_stmt), maybe_convert_cond (cond));
cond = maybe_convert_cond (cond);
if (IF_STMT_CONSTEXPR_P (if_stmt)
&& require_potential_rvalue_constant_expression (cond)
&& !value_dependent_expression_p (cond))
cond = cxx_constant_value (cond, NULL_TREE);
finish_cond (&IF_COND (if_stmt), cond);
add_stmt (if_stmt);
THEN_CLAUSE (if_stmt) = push_stmt_list ();
return cond;
}
/* Finish the then-clause of an if-statement, which may be given by

View File

@ -0,0 +1,14 @@
// Testcase from P0292R2
// { dg-do compile { target c++11 } }
// { dg-options "" }
template<typename T, typename ... Rest> void g(T&& p, Rest&& ...rs) {
// ... handle p
if constexpr (sizeof...(rs) > 0) // { dg-warning "constexpr" "" { target c++14_down } }
g(rs...); // never instantiated with an empty argument list.
}
int main()
{
g(1,2,3);
}

View File

@ -0,0 +1,15 @@
// { dg-do compile { target c++14 } }
// { dg-options "" }
template <class,class> struct Same;
template <class T> struct Same<T,T> {};
auto f()
{
if constexpr (sizeof(int)==3) // { dg-warning "constexpr" "" { target c++14_only } }
return 42;
else
return 42L;
}
Same<decltype(f()), long> s;

View File

@ -0,0 +1,13 @@
// Testcase from P0292R2
// { dg-do link { target c++11 } }
// { dg-options "" }
extern int x; // no definition of x required
int main() {
if constexpr (true) // { dg-warning "constexpr" "" { target c++14_down } }
return 0;
else if (x)
return x;
else
return -x;
}

View File

@ -0,0 +1,11 @@
// { dg-do compile { target c++11 } }
// { dg-options "-w" }
void f()
{
goto l; // { dg-message "from here" }
if constexpr (false) // { dg-message "enters constexpr if" }
{
l:; // { dg-error "jump to label" }
}
}

View File

@ -0,0 +1,14 @@
// { dg-do compile { target c++11 } }
// { dg-options "-w" }
void f()
{
if constexpr (false) // { dg-message "enters constexpr if" }
{
goto l; // { dg-message "from here" }
}
else
{
l:; // { dg-error "jump to label" }
}
}

View File

@ -0,0 +1,14 @@
// { dg-do compile { target c++11 } }
// { dg-options "-w" }
void f()
{
if constexpr (false)
{
goto l;
l:;
}
else
{
}
}

View File

@ -0,0 +1,14 @@
// { dg-do compile { target c++11 } }
// { dg-options "-w" }
void f()
{
if constexpr (false)
{
l:;
goto l;
}
else
{
}
}

View File

@ -0,0 +1,14 @@
// { dg-do compile { target c++11 } }
// { dg-options "-w" }
void f()
{
if constexpr (false)
{
l:; // { dg-error "jump to label" }
}
else
{
goto l; // { dg-message "from here" }
}
}

View File

@ -0,0 +1,11 @@
// { dg-do compile { target c++11 } }
// { dg-options "-w" }
void f(int i)
{
switch (i)
if constexpr (false) // { dg-message "enters constexpr if" }
{
case 42:; // { dg-error "jump to case label" }
}
}