Implement C++17 constexpr if.
authorJason Merrill <jason@redhat.com>
Wed, 10 Aug 2016 21:58:23 +0000 (17:58 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Wed, 10 Aug 2016 21:58:23 +0000 (17:58 -0400)
* 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

16 files changed:
gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/parser.c
gcc/cp/parser.h
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/testsuite/g++.dg/cpp1z/constexpr-if1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-if2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-if3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-if4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-if5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-if6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-if7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-if8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-if9.C [new file with mode: 0644]

index ca04d6438a486255fbe00504e6f91753ff570b15..647b3e65800a12f1163d888353dda5c08d1b6d69 100644 (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
index f98b1c4d50304897ddfb45248960b414506cbbda..8a32f179ecdeefcca05501bd66d5fcda805814c9 100644 (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);
index 45286d0bf67b71608e6394705c3f46b39ee53f53..43cf3df36bac33dc7f1d5529dc098aba7338c190 100644 (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)
index 6db5e84475bd778aebcbf416633d6de00fa617b3..1b9359ed050e8100f7da0013c81f2e404894401f 100644 (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 ();
+         {
+           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);
       }
index 2d3feb6915772c5db3e301745f52e2f1e0951342..6a52b12e81334942081675167a920a4f7b877f5f 100644 (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;
index 2638564612d73245730f2927419729b940c2eb7e..1ee5fd4b579ab9ed585a26d05140ff9f2f94151e 100644 (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);
-      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));
index bffdddbb965bf7361490d7ad85440087921b6935..a2e04f6b61d8c70617817923288827ccdd11f6c8 100644 (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
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if1.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if1.C
new file mode 100644 (file)
index 0000000..416d9ec
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if2.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if2.C
new file mode 100644 (file)
index 0000000..7f3a577
--- /dev/null
@@ -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;
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if3.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if3.C
new file mode 100644 (file)
index 0000000..1cc5780
--- /dev/null
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if4.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if4.C
new file mode 100644 (file)
index 0000000..612eff8
--- /dev/null
@@ -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" }
+    }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if5.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if5.C
new file mode 100644 (file)
index 0000000..69d03e3
--- /dev/null
@@ -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" }
+    }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if6.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if6.C
new file mode 100644 (file)
index 0000000..87aeabc
--- /dev/null
@@ -0,0 +1,14 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-w" }
+
+void f()
+{
+  if constexpr (false)
+    {
+      goto l;
+    l:;
+    }
+  else
+    {
+    }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if7.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if7.C
new file mode 100644 (file)
index 0000000..64829cc
--- /dev/null
@@ -0,0 +1,14 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-w" }
+
+void f()
+{
+  if constexpr (false)
+    {
+    l:;
+      goto l;
+    }
+  else
+    {
+    }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if8.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if8.C
new file mode 100644 (file)
index 0000000..7efdc47
--- /dev/null
@@ -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" }
+    }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if9.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if9.C
new file mode 100644 (file)
index 0000000..748278a
--- /dev/null
@@ -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" }
+      }
+}