mirror of git://gcc.gnu.org/git/gcc.git
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:
parent
e9e6d4f6cc
commit
99f9d4b1b6
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
{
|
||||
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,7 +11914,10 @@ cp_parser_jump_statement (cp_parser* parser)
|
|||
expression. */
|
||||
expr = NULL_TREE;
|
||||
/* Build the return-statement. */
|
||||
statement = finish_return_stmt (expr);
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
12
gcc/cp/pt.c
12
gcc/cp/pt.c
|
|
@ -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);
|
||||
RECUR (THEN_CLAUSE (t));
|
||||
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));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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" }
|
||||
}
|
||||
}
|
||||
|
|
@ -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" }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// { dg-do compile { target c++11 } }
|
||||
// { dg-options "-w" }
|
||||
|
||||
void f()
|
||||
{
|
||||
if constexpr (false)
|
||||
{
|
||||
goto l;
|
||||
l:;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// { dg-do compile { target c++11 } }
|
||||
// { dg-options "-w" }
|
||||
|
||||
void f()
|
||||
{
|
||||
if constexpr (false)
|
||||
{
|
||||
l:;
|
||||
goto l;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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" }
|
||||
}
|
||||
}
|
||||
|
|
@ -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" }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue