Support noexcept-specifications for transaction statements and expressions.
authortorvald <torvald@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 12 Dec 2011 12:05:37 +0000 (12:05 +0000)
committertorvald <torvald@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 12 Dec 2011 12:05:37 +0000 (12:05 +0000)
gcc/cp/
* semantics.c (finish_transaction_stmt, build_transaction_expr):
Accept new noexcept parameter and handle it.
* cp-tree.h (finish_transaction_stmt, build_transaction_expr): Adapt
declarations.
* parser.c (cp_parser_exception_specification_opt): Extract
noexcept-specification parsing to ...
(cp_parser_noexcept_specification_opt): ...here.  Allow for parsing
non-constexpr noexcept arguments.
(cp_parser_transaction, cp_parser_transaction_expression): Parse
and handle noexcept-specifications.
(cp_parser_function_transaction): Adapt to finish_transaction_stmt
change.
* pt.c (tsubst_expr): Adapt to new noexcept parameters when
building transactions.

gcc/testsuite/
* g++.dg/tm/noexcept-1.C: New test.
* g++.dg/tm/noexcept-2.C: New test.
* g++.dg/tm/noexcept-3.C: New test.
* g++.dg/tm/noexcept-4.C: New test.
* g++.dg/tm/noexcept-5.C: New test.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@182234 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/parser.c
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/tm/noexcept-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tm/noexcept-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tm/noexcept-3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tm/noexcept-4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tm/noexcept-5.C [new file with mode: 0644]

index d553dce..7db2598 100644 (file)
@@ -1,5 +1,22 @@
 2011-12-12  Torvald Riegel  <triegel@redhat.com>
 
+       * semantics.c (finish_transaction_stmt, build_transaction_expr):
+       Accept new noexcept parameter and handle it.
+       * cp-tree.h (finish_transaction_stmt, build_transaction_expr): Adapt
+       declarations.
+       * parser.c (cp_parser_exception_specification_opt): Extract
+       noexcept-specification parsing to ...
+       (cp_parser_noexcept_specification_opt): ...here.  Allow for parsing
+       non-constexpr noexcept arguments.
+       (cp_parser_transaction, cp_parser_transaction_expression): Parse
+    and handle noexcept-specifications.
+       (cp_parser_function_transaction): Adapt to finish_transaction_stmt
+       change.
+       * pt.c (tsubst_expr): Adapt to new noexcept parameters when
+       building transactions.
+
+2011-12-12  Torvald Riegel  <triegel@redhat.com>
+
        * cp-tree.def (MUST_NOT_THROW_EXPR): Add condition parameter.
        * cp-tree.h (MUST_NOT_THROW_COND): New.
        (build_must_not_throw_expr): Declare.
index c28b229..f443816 100644 (file)
@@ -5587,8 +5587,8 @@ extern void finish_omp_barrier                    (void);
 extern void finish_omp_flush                   (void);
 extern void finish_omp_taskwait                        (void);
 extern tree begin_transaction_stmt             (location_t, tree *, int);
-extern void finish_transaction_stmt            (tree, tree, int);
-extern tree build_transaction_expr             (location_t, tree, int);
+extern void finish_transaction_stmt            (tree, tree, int, tree);
+extern tree build_transaction_expr             (location_t, tree, int, tree);
 extern void finish_omp_taskyield               (void);
 extern bool cxx_omp_create_clause_info         (tree, tree, bool, bool, bool);
 extern tree baselink_for_fns                    (tree);
index 2985d76..a0aa2a1 100644 (file)
@@ -19564,19 +19564,25 @@ cp_parser_base_specifier (cp_parser* parser)
 
 /* Exception handling [gram.exception] */
 
-/* Parse an (optional) exception-specification.
+/* Parse an (optional) noexcept-specification.
 
-   exception-specification:
-     throw ( type-id-list [opt] )
+   noexcept-specification:
+     noexcept ( constant-expression ) [opt]
 
-   Returns a TREE_LIST representing the exception-specification.  The
-   TREE_VALUE of each node is a type.  */
+   If no noexcept-specification is present, returns NULL_TREE.
+   Otherwise, if REQUIRE_CONSTEXPR is false, then either parse and return any
+   expression if parentheses follow noexcept, or return BOOLEAN_TRUE_NODE if
+   there are no parentheses.  CONSUMED_EXPR will be set accordingly.
+   Otherwise, returns a noexcept specification unless RETURN_COND is true,
+   in which case a boolean condition is returned instead.  */
 
 static tree
-cp_parser_exception_specification_opt (cp_parser* parser)
+cp_parser_noexcept_specification_opt (cp_parser* parser,
+                                     bool require_constexpr,
+                                     bool* consumed_expr,
+                                     bool return_cond)
 {
   cp_token *token;
-  tree type_id_list;
   const char *saved_message;
 
   /* Peek at the next token.  */
@@ -19592,23 +19598,67 @@ cp_parser_exception_specification_opt (cp_parser* parser)
        {
          cp_lexer_consume_token (parser->lexer);
 
-         /* Types may not be defined in an exception-specification.  */
-         saved_message = parser->type_definition_forbidden_message;
-         parser->type_definition_forbidden_message
-           = G_("types may not be defined in an exception-specification");
+         if (require_constexpr)
+           {
+             /* Types may not be defined in an exception-specification.  */
+             saved_message = parser->type_definition_forbidden_message;
+             parser->type_definition_forbidden_message
+             = G_("types may not be defined in an exception-specification");
 
-         expr = cp_parser_constant_expression (parser, false, NULL);
+             expr = cp_parser_constant_expression (parser, false, NULL);
 
-         /* Restore the saved message.  */
-         parser->type_definition_forbidden_message = saved_message;
+             /* Restore the saved message.  */
+             parser->type_definition_forbidden_message = saved_message;
+           }
+         else
+           {
+             expr = cp_parser_expression (parser, false, NULL);
+             *consumed_expr = true;
+           }
 
          cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
        }
       else
-       expr = boolean_true_node;
+       {
+         expr = boolean_true_node;
+         if (!require_constexpr)
+           *consumed_expr = false;
+       }
 
-      return build_noexcept_spec (expr, tf_warning_or_error);
+      /* We cannot build a noexcept-spec right away because this will check
+        that expr is a constexpr.  */
+      if (!return_cond)
+       return build_noexcept_spec (expr, tf_warning_or_error);
+      else
+       return expr;
     }
+  else
+    return NULL_TREE;
+}
+
+/* Parse an (optional) exception-specification.
+
+   exception-specification:
+     throw ( type-id-list [opt] )
+
+   Returns a TREE_LIST representing the exception-specification.  The
+   TREE_VALUE of each node is a type.  */
+
+static tree
+cp_parser_exception_specification_opt (cp_parser* parser)
+{
+  cp_token *token;
+  tree type_id_list;
+  const char *saved_message;
+
+  /* Peek at the next token.  */
+  token = cp_lexer_peek_token (parser->lexer);
+
+  /* Is it a noexcept-specification?  */
+  type_id_list = cp_parser_noexcept_specification_opt(parser, true, NULL,
+                                                     false);
+  if (type_id_list != NULL_TREE)
+    return type_id_list;
 
   /* If it's not `throw', then there's no exception-specification.  */
   if (!cp_parser_is_keyword (token, RID_THROW))
@@ -26837,11 +26887,9 @@ cp_parser_txn_attribute_opt (cp_parser *parser)
 /* Parse a __transaction_atomic or __transaction_relaxed statement.
 
    transaction-statement:
-     __transaction_atomic txn-attribute[opt] txn-exception-spec[opt]
+     __transaction_atomic txn-attribute[opt] txn-noexcept-spec[opt]
        compound-statement
-     __transaction_relaxed txn-exception-spec[opt] compound-statement
-
-   ??? The exception specification is not yet implemented.
+     __transaction_relaxed txn-noexcept-spec[opt] compound-statement
 */
 
 static tree
@@ -26850,7 +26898,7 @@ cp_parser_transaction (cp_parser *parser, enum rid keyword)
   unsigned char old_in = parser->in_transaction;
   unsigned char this_in = 1, new_in;
   cp_token *token;
-  tree stmt, attrs;
+  tree stmt, attrs, noex;
 
   gcc_assert (keyword == RID_TRANSACTION_ATOMIC
       || keyword == RID_TRANSACTION_RELAXED);
@@ -26868,6 +26916,9 @@ cp_parser_transaction (cp_parser *parser, enum rid keyword)
        this_in |= parse_tm_stmt_attr (attrs, TM_STMT_ATTR_OUTER);
     }
 
+  /* Parse a noexcept specification.  */
+  noex = cp_parser_noexcept_specification_opt (parser, true, NULL, true);
+
   /* Keep track if we're in the lexical scope of an outer transaction.  */
   new_in = this_in | (old_in & TM_STMT_ATTR_OUTER);
 
@@ -26877,7 +26928,7 @@ cp_parser_transaction (cp_parser *parser, enum rid keyword)
   cp_parser_compound_statement (parser, NULL, false, false);
   parser->in_transaction = old_in;
 
-  finish_transaction_stmt (stmt, NULL, this_in);
+  finish_transaction_stmt (stmt, NULL, this_in, noex);
 
   return stmt;
 }
@@ -26885,10 +26936,8 @@ cp_parser_transaction (cp_parser *parser, enum rid keyword)
 /* Parse a __transaction_atomic or __transaction_relaxed expression.
 
    transaction-expression:
-     __transaction_atomic txn-exception-spec[opt] ( expression )
-     __transaction_relaxed txn-exception-spec[opt] ( expression )
-
-   ??? The exception specification is not yet implemented.
+     __transaction_atomic txn-noexcept-spec[opt] ( expression )
+     __transaction_relaxed txn-noexcept-spec[opt] ( expression )
 */
 
 static tree
@@ -26897,7 +26946,8 @@ cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
   unsigned char old_in = parser->in_transaction;
   unsigned char this_in = 1;
   cp_token *token;
-  tree expr;
+  tree expr, noex;
+  bool noex_expr;
 
   gcc_assert (keyword == RID_TRANSACTION_ATOMIC
       || keyword == RID_TRANSACTION_RELAXED);
@@ -26917,14 +26967,36 @@ cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
   if (keyword == RID_TRANSACTION_RELAXED)
     this_in |= TM_STMT_ATTR_RELAXED;
 
+  /* Set this early.  This might mean that we allow transaction_cancel in
+     an expression that we find out later actually has to be a constexpr.
+     However, we expect that cxx_constant_value will be able to deal with
+     this; also, if the noexcept has no constexpr, then what we parse next
+     really is a transaction's body.  */
   parser->in_transaction = this_in;
-  cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
 
-  expr = cp_parser_expression (parser, /*cast_p=*/false, NULL);
-  finish_parenthesized_expr (expr);
-  expr = build_transaction_expr (token->location, expr, this_in);
+  /* Parse a noexcept specification.  */
+  noex = cp_parser_noexcept_specification_opt (parser, false, &noex_expr,
+                                              true);
 
-  cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+  if (!noex || !noex_expr
+      || cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
+    {
+      cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+
+      expr = cp_parser_expression (parser, /*cast_p=*/false, NULL);
+      finish_parenthesized_expr (expr);
+
+      cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+    }
+  else
+    {
+      /* The only expression that is available got parsed for the noexcept
+         already.  noexcept is true then.  */
+      expr = noex;
+      noex = boolean_true_node;
+    }
+
+  expr = build_transaction_expr (token->location, expr, this_in, noex);
   parser->in_transaction = old_in;
 
   if (cp_parser_non_integral_constant_expression (parser, NIC_TRANSACTION))
@@ -26980,7 +27052,7 @@ cp_parser_function_transaction (cp_parser *parser, enum rid keyword)
 
   parser->in_transaction = old_in;
 
-  finish_transaction_stmt (stmt, compound_stmt, new_in);
+  finish_transaction_stmt (stmt, compound_stmt, new_in, NULL_TREE);
 
   return ctor_initializer_p;
 }
index f5a3175..3b0cb23 100644 (file)
@@ -13246,15 +13246,24 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
 
         if (TRANSACTION_EXPR_IS_STMT (t))
           {
+           tree body = TRANSACTION_EXPR_BODY (t);
+           tree noex = NULL_TREE;
+           if (TREE_CODE (body) == MUST_NOT_THROW_EXPR)
+             {
+               noex = MUST_NOT_THROW_COND (body);
+               if (noex == NULL_TREE)
+                 noex = boolean_true_node;
+               body = TREE_OPERAND (body, 0);
+             }
             stmt = begin_transaction_stmt (input_location, NULL, flags);
-            RECUR (TRANSACTION_EXPR_BODY (t));
-            finish_transaction_stmt (stmt, NULL, flags);
+            RECUR (body);
+            finish_transaction_stmt (stmt, NULL, flags, RECUR (noex));
           }
         else
           {
             stmt = build_transaction_expr (EXPR_LOCATION (t),
                                           RECUR (TRANSACTION_EXPR_BODY (t)),
-                                          flags);
+                                          flags, NULL_TREE);
             return stmt;
           }
       }
index 2dab6a7..4b1ec46 100644 (file)
@@ -5020,27 +5020,47 @@ begin_transaction_stmt (location_t loc, tree *pcompound, int flags)
 
 /* End a __transaction_atomic or __transaction_relaxed statement.
    If COMPOUND_STMT is non-null, this is for a function-transaction-block,
-   and we should end the compound.  */
+   and we should end the compound.  If NOEX is non-NULL, we wrap the body in
+   a MUST_NOT_THROW_EXPR with NOEX as condition.  */
 
 void
-finish_transaction_stmt (tree stmt, tree compound_stmt, int flags)
+finish_transaction_stmt (tree stmt, tree compound_stmt, int flags, tree noex)
 {
   TRANSACTION_EXPR_BODY (stmt) = pop_stmt_list (TRANSACTION_EXPR_BODY (stmt));
   TRANSACTION_EXPR_OUTER (stmt) = (flags & TM_STMT_ATTR_OUTER) != 0;
   TRANSACTION_EXPR_RELAXED (stmt) = (flags & TM_STMT_ATTR_RELAXED) != 0;
   TRANSACTION_EXPR_IS_STMT (stmt) = 1;
 
+  /* noexcept specifications are not allowed for function transactions.  */
+  gcc_assert (!(noex && compound_stmt));
+  if (noex)
+    {
+      tree body = build_must_not_throw_expr (TRANSACTION_EXPR_BODY (stmt),
+                                            noex);
+      SET_EXPR_LOCATION (body, EXPR_LOCATION (TRANSACTION_EXPR_BODY (stmt)));
+      TREE_SIDE_EFFECTS (body) = 1;
+      TRANSACTION_EXPR_BODY (stmt) = body;
+    }
+
   if (compound_stmt)
     finish_compound_stmt (compound_stmt);
   finish_stmt ();
 }
 
-/* Build a __transaction_atomic or __transaction_relaxed expression.  */
+/* Build a __transaction_atomic or __transaction_relaxed expression.  If
+   NOEX is non-NULL, we wrap the body in a MUST_NOT_THROW_EXPR with NOEX as
+   condition.  */
 
 tree
-build_transaction_expr (location_t loc, tree expr, int flags)
+build_transaction_expr (location_t loc, tree expr, int flags, tree noex)
 {
   tree ret;
+  if (noex)
+    {
+      expr = build_must_not_throw_expr (expr, noex);
+      SET_EXPR_LOCATION (expr, loc);
+      TREE_SIDE_EFFECTS (expr) = 1;
+    }
   ret = build1 (TRANSACTION_EXPR, TREE_TYPE (expr), expr);
   if (flags & TM_STMT_ATTR_RELAXED)
        TRANSACTION_EXPR_RELAXED (ret) = 1;
index e6f64a7..e09e96d 100644 (file)
@@ -1,5 +1,13 @@
 2011-12-12  Torvald Riegel  <triegel@redhat.com>
 
+       * g++.dg/tm/noexcept-1.C: New test.
+       * g++.dg/tm/noexcept-2.C: New test.
+       * g++.dg/tm/noexcept-3.C: New test.
+       * g++.dg/tm/noexcept-4.C: New test.
+       * g++.dg/tm/noexcept-5.C: New test.
+
+2011-12-12  Torvald Riegel  <triegel@redhat.com>
+
        * c-c++-common/tm/20111206.c: New test.
 
 2011-12-12  Richard Guenther  <rguenther@suse.de>
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-1.C b/gcc/testsuite/g++.dg/tm/noexcept-1.C
new file mode 100644 (file)
index 0000000..9b1a8c6
--- /dev/null
@@ -0,0 +1,38 @@
+// { dg-do compile }
+// { dg-options "-fgnu-tm -O -std=c++0x -fdump-tree-tmmark -fdump-tree-tmlower" }
+
+struct TrueFalse
+{
+  static constexpr bool v() { return true; }
+};
+
+int global;
+
+template<typename T> int foo()
+{
+  __transaction_atomic noexcept(T::v()) { global += 1; }
+  return __transaction_atomic noexcept(T::v()) (global + 2);
+}
+
+int f1()
+{
+  return foo<TrueFalse>();
+}
+
+int f2()
+{
+  return __transaction_atomic noexcept(true) (global + 3)
+         + __transaction_atomic noexcept(TrueFalse::v()) (global + 4);
+}
+
+int f3()
+{
+  __transaction_atomic noexcept(true) { global += 5; }
+  __transaction_atomic noexcept(TrueFalse::v()) { global += 6; }
+  return global;
+}
+
+/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 6 "tmlower" } } */
+/* { dg-final { scan-tree-dump-times "ITM_RU" 6 "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmlower" } } */
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-2.C b/gcc/testsuite/g++.dg/tm/noexcept-2.C
new file mode 100644 (file)
index 0000000..f9cbbb6
--- /dev/null
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-fgnu-tm -std=c++0x" }
+
+// All of these must fail, because they are not constant expressions.
+template<typename T> int foo(int x, T t)
+{
+  __transaction_atomic noexcept(t) { x++; }      /* { dg-error "not a constant" } */
+  return __transaction_atomic noexcept(t) (x+1); /* { dg-error "not a constant" } */
+}
+
+int bar(int x)
+{
+  __transaction_atomic noexcept(x == 23) { x++; }      /* { dg-error "not a constant" } */
+  return __transaction_atomic noexcept(x == 42) (x+1); /* { dg-error "not a constant" } */
+}
+
+int f(int x)
+{
+  return foo<bool>(x, true);
+}
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-3.C b/gcc/testsuite/g++.dg/tm/noexcept-3.C
new file mode 100644 (file)
index 0000000..958290e
--- /dev/null
@@ -0,0 +1,40 @@
+// { dg-do compile }
+// { dg-options "-fgnu-tm -O -std=c++0x -fdump-tree-tmmark -fdump-tree-tmlower" }
+
+// Same as noexcept-1.C but all noexcepts are false.
+
+struct TrueFalse
+{
+  static constexpr bool v() { return false; }
+};
+
+int global;
+
+template<typename T> int foo()
+{
+  __transaction_atomic noexcept(T::v()) { global += 1; }
+  return __transaction_atomic noexcept(T::v()) (global + 2);
+}
+
+int f1()
+{
+  return foo<TrueFalse>();
+}
+
+int f2()
+{
+  return __transaction_atomic noexcept(false) (global + 3)
+         + __transaction_atomic noexcept(TrueFalse::v()) (global + 4);
+}
+
+int f3()
+{
+  __transaction_atomic noexcept(false) { global += 5; }
+  __transaction_atomic noexcept(TrueFalse::v()) { global += 6; }
+  return global;
+}
+
+/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 0 "tmlower" } } */
+/* { dg-final { scan-tree-dump-times "ITM_RU" 6 "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmlower" } } */
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-4.C b/gcc/testsuite/g++.dg/tm/noexcept-4.C
new file mode 100644 (file)
index 0000000..1166a15
--- /dev/null
@@ -0,0 +1,35 @@
+// { dg-do compile }
+// { dg-options "-fgnu-tm -O -std=c++0x -fdump-tree-tmmark -fdump-tree-tmlower" }
+
+// Similar to noexcept-1.C but without an explicit (true) for noexcept.
+
+struct TrueFalse
+{
+  static constexpr bool v() { return true; }
+};
+
+int global;
+
+template<typename T> int foo()
+{
+  __transaction_atomic noexcept { global += 1; }
+  return __transaction_atomic noexcept (global + 2)
+         + __transaction_atomic noexcept (global + 3);
+}
+
+int f1()
+{
+  return foo<TrueFalse>();
+}
+
+int f3()
+{
+  __transaction_atomic noexcept { global += 4; }
+  return __transaction_atomic noexcept (global + 5)
+         + __transaction_atomic noexcept (global + 6);
+}
+
+/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 6 "tmlower" } } */
+/* { dg-final { scan-tree-dump-times "ITM_RU" 6 "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmlower" } } */
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-5.C b/gcc/testsuite/g++.dg/tm/noexcept-5.C
new file mode 100644 (file)
index 0000000..44ef617
--- /dev/null
@@ -0,0 +1,21 @@
+// { dg-do compile }
+// { dg-options "-fgnu-tm -O -std=c++0x -fdump-tree-tmmark -fdump-tree-tmlower" }
+
+int global;
+
+void f2(int x)
+{
+  __transaction_atomic
+    {
+      __transaction_atomic noexcept(true)
+        {
+         global += 1;
+         if (x)
+             throw 23;
+        }
+    }
+}
+/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 1 "tmlower" } } */
+/* { dg-final { scan-tree-dump-times "ITM_RU" 1 "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmlower" } } */