* 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
+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
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.
#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. */
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);
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
}
}
+/* 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
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;
}
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)
{
" 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;
}
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),
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)
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))
{
/* 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 ();
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,
/* 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. */
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
{
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);
}
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;
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));
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;
}
/* 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
--- /dev/null
+// 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);
+}
--- /dev/null
+// { 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;
--- /dev/null
+// 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;
+}
--- /dev/null
+// { 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" }
+ }
+}
--- /dev/null
+// { 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" }
+ }
+}
--- /dev/null
+// { dg-do compile { target c++11 } }
+// { dg-options "-w" }
+
+void f()
+{
+ if constexpr (false)
+ {
+ goto l;
+ l:;
+ }
+ else
+ {
+ }
+}
--- /dev/null
+// { dg-do compile { target c++11 } }
+// { dg-options "-w" }
+
+void f()
+{
+ if constexpr (false)
+ {
+ l:;
+ goto l;
+ }
+ else
+ {
+ }
+}
--- /dev/null
+// { 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" }
+ }
+}
--- /dev/null
+// { 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" }
+ }
+}