Patchwork Support noexcept-specifications for transaction statements and expressions.

login
register
mail settings
Submitter Torvald Riegel
Date Dec. 10, 2011, 8:28 p.m.
Message ID <1323548885.4159.6999.camel@triegel.csb>
Download mbox | patch
Permalink /patch/130563/
State New
Headers show

Comments

Torvald Riegel - Dec. 10, 2011, 8:28 p.m.
Here is the most recent version, which is now based on both Richard
Henderson's fix for voidify_wrapper_expr (patch2) and Jason Merrill's
conditional MUST_NOT_THROW_EXPR (patch3).

patch2: voidify_wrapper_expr wasn't handling MUST_NOT_THROW_EXPR.  The
patch adds a reasonable default handling that applies to this case (and
this way, we don't need to teach language-independent code about
MUST_NOT_THROW_EXPR).

patch3: As Jason pointed out, storing the noexcept specification in the
TRANSACTION_EXPR is less clean than just extending MUST_NOT_THROW_EXPR
with support for a boolean condition.  This patch does that, including
instantiation etc.

patch4: This is the noexcept patch again.  Nothing changed since last
time except it's now based on conditional MUST_NOT_THROW_EXPR, which
simiplifies things.

OK for trunk?

On Mon, 2011-11-21 at 16:39 -0500, Jason Merrill wrote:
> On 11/21/2011 04:27 PM, Torvald Riegel wrote:
> > On Mon, 2011-11-21 at 16:06 -0500, Jason Merrill wrote:
> >> At this point I think it'd be simpler to handle noexcept in a
> >> transaction-expression directly in cp_parser_transaction_expression.
> >
> > And keep transaction statements as is, or change them as well?
> 
> I don't have a strong opinion either way on statements, since they don't 
> have to deal with the ambiguity issue.  Whichever seems more convenient 
> to you.
> 
> >> Since TRANSACTION_EXPR_NOEX is only for the template representation, I'd
> >> rather not add it to the language-independent tree code.  Maybe
> >> introduce a C++ template-specific NOEXCEPT_REGION tree code?
> 
> > Hmm, I'm a bit confused because looking at the previous iterations of
> > this patch we said that it's best to keep the noexcept information
> > attached to the transactions.  What made you change your mind?  I don't
> > see any changes to language-independent code that didn't exist in the
> > previous iterations.
> 
> Right, this issue just didn't occur to me before, sorry.
> 
> Jason
commit 80fc3e71ee98e98fd0e75cd720fe9db25616f281
Author: Torvald Riegel <triegel@redhat.com>
Date:   Mon Nov 21 00:25:08 2011 +0100

    Add default handling for outermost wrappers in voidify_wrapper_expr.
    
    	gcc/
    	* gimplify.c (voidify_wrapper_expr): Add default handling for
    	outermost wrapper.
commit c97d12da67d3b2936cdb75c31cc10c02e4fa388d
Author: Torvald Riegel <triegel@redhat.com>
Date:   Thu Dec 8 12:23:57 2011 +0100

    Add condition to MUST_NOT_THROW_EXPR.
    
    	gcc/cp/
    	* cp-tree.def (MUST_NOT_THROW_EXPR): Add condition parameter.
    	* cp-tree.h (MUST_NOT_THROW_COND): New.
    	(build_must_not_throw_expr): Declare.
    	* dump.c (cp_dump_tree): Dump MUST_NOT_THROW_EXPR condition.
    	* except.c (build_must_not_throw_expr): New.
    	(initialize_handler_parm): Use it.
    	(begin_eh_spec_block, wrap_cleanups_r): Adapt to condition.
    	* pt.c (tsubst_expr): Handle MUST_NOT_THROW_EXPR.

diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 5fc5496..83e0b5b 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -281,8 +281,9 @@ DEFTREECODE (EH_SPEC_BLOCK, "eh_spec_block", tcc_statement, 2)
 DEFTREECODE (HANDLER, "handler", tcc_statement, 2)
 
 /* A MUST_NOT_THROW_EXPR wraps an expression that may not
-   throw, and must call terminate if it does.  */
-DEFTREECODE (MUST_NOT_THROW_EXPR, "must_not_throw_expr", tcc_expression, 1)
+   throw, and must call terminate if it does.  The second argument
+   is a condition, used in templates to express noexcept (condition).  */
+DEFTREECODE (MUST_NOT_THROW_EXPR, "must_not_throw_expr", tcc_expression, 2)
 
 /* A CLEANUP_STMT marks the point at which a declaration is fully
    constructed.  The CLEANUP_EXPR is run on behalf of CLEANUP_DECL
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 955d0eb..c28b229 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3016,6 +3016,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define VEC_INIT_EXPR_VALUE_INIT(NODE) \
   TREE_LANG_FLAG_1 (VEC_INIT_EXPR_CHECK (NODE))
 
+/* The condition under which this MUST_NOT_THROW_EXPR actually blocks
+   exceptions.  NULL_TREE means 'true'.  */
+#define MUST_NOT_THROW_COND(NODE) \
+  TREE_OPERAND (MUST_NOT_THROW_EXPR_CHECK (NODE), 1)
+
 /* The TYPE_MAIN_DECL for a class template type is a TYPE_DECL, not a
    TEMPLATE_DECL.  This macro determines whether or not a given class
    type is really a template type, as opposed to an instantiation or
@@ -5148,6 +5153,7 @@ extern bool type_noexcept_p			(const_tree);
 extern bool type_throw_all_p			(const_tree);
 extern tree build_noexcept_spec			(tree, int);
 extern void choose_personality_routine		(enum languages);
+extern tree build_must_not_throw_expr		(tree,tree);
 extern tree eh_type_info			(tree);
 extern tree begin_eh_spec_block			(void);
 extern void finish_eh_spec_block		(tree, tree);
diff --git a/gcc/cp/dump.c b/gcc/cp/dump.c
index d1631fc..a461094 100644
--- a/gcc/cp/dump.c
+++ b/gcc/cp/dump.c
@@ -413,6 +413,7 @@ cp_dump_tree (void* dump_info, tree t)
     case MUST_NOT_THROW_EXPR:
       dump_stmt (di, t);
       dump_child ("body", TREE_OPERAND (t, 0));
+      dump_child ("cond", MUST_NOT_THROW_COND (t));
       break;
 
     case USING_STMT:
diff --git a/gcc/cp/except.c b/gcc/cp/except.c
index 670a66f..c56dc2c 100644
--- a/gcc/cp/except.c
+++ b/gcc/cp/except.c
@@ -374,6 +374,28 @@ choose_personality_routine (enum languages lang)
   state = gave_error;
 }
 
+/* Wrap EXPR in a MUST_NOT_THROW_EXPR expressing that EXPR must
+   not throw any exceptions if COND is true.  A condition of
+   NULL_TREE is treated as 'true'.  */
+
+tree
+build_must_not_throw_expr (tree body, tree cond)
+{
+  tree type = body ? TREE_TYPE (body) : void_type_node;
+
+  if (cond && !value_dependent_expression_p (cond))
+    {
+      cond = cxx_constant_value (cond);
+      if (integer_zerop (cond))
+	return body;
+      else if (integer_onep (cond))
+	cond = NULL_TREE;
+    }
+
+  return build2 (MUST_NOT_THROW_EXPR, type, body, cond);
+}
+
+
 /* Initialize the catch parameter DECL.  */
 
 static void
@@ -418,7 +440,7 @@ initialize_handler_parm (tree decl, tree exp)
       /* Force cleanups now to avoid nesting problems with the
 	 MUST_NOT_THROW_EXPR.  */
       init = fold_build_cleanup_point_expr (TREE_TYPE (init), init);
-      init = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (init), init);
+      init = build_must_not_throw_expr (init, NULL_TREE);
     }
 
   decl = pushdecl (decl);
@@ -560,7 +582,8 @@ begin_eh_spec_block (void)
      MUST_NOT_THROW_EXPR.  */
   if (TYPE_NOEXCEPT_P (TREE_TYPE (current_function_decl)))
     {
-      r = build_stmt (spec_location, MUST_NOT_THROW_EXPR, NULL_TREE);
+      r = build_stmt (spec_location, MUST_NOT_THROW_EXPR,
+		      NULL_TREE, NULL_TREE);
       TREE_SIDE_EFFECTS (r) = 1;
     }
   else
@@ -664,7 +687,8 @@ wrap_cleanups_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
   cleanup = TARGET_EXPR_CLEANUP (exp);
   if (cleanup)
     {
-      cleanup = build1 (MUST_NOT_THROW_EXPR, void_type_node, cleanup);
+      cleanup = build2 (MUST_NOT_THROW_EXPR, void_type_node, cleanup,
+			NULL_TREE);
       TARGET_EXPR_CLEANUP (exp) = cleanup;
     }
 
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index bb5aa0c..f5a3175 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -13260,6 +13260,10 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
       }
       break;
 
+    case MUST_NOT_THROW_EXPR:
+      return build_must_not_throw_expr (RECUR (TREE_OPERAND (t, 0)),
+					RECUR (MUST_NOT_THROW_COND (t)));
+
     case EXPR_PACK_EXPANSION:
       error ("invalid use of pack expansion expression");
       return error_mark_node;
commit 09c6c04763b505525bdde3151a59219346a84de2
Author: Torvald Riegel <triegel@redhat.com>
Date:   Thu Dec 8 17:30:21 2011 +0100

    Support noexcept-specifications for transaction statements and expressions.
    
    	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.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index c28b229..f443816 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 5952a0f..bb685a6 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -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;
 }
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index f5a3175..3b0cb23 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -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;
           }
       }
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 2dab6a7..4b1ec46 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -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;
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-1.C b/gcc/testsuite/g++.dg/tm/noexcept-1.C
new file mode 100644
index 0000000..9b1a8c6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tm/noexcept-1.C
@@ -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
index 0000000..f9cbbb6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tm/noexcept-2.C
@@ -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
index 0000000..958290e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tm/noexcept-3.C
@@ -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
index 0000000..1166a15
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tm/noexcept-4.C
@@ -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
index 0000000..44ef617
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tm/noexcept-5.C
@@ -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" } } */
Richard Henderson - Dec. 10, 2011, 8:52 p.m.
On 12/10/2011 12:28 PM, Torvald Riegel wrote:
> patch2: voidify_wrapper_expr wasn't handling MUST_NOT_THROW_EXPR.  The
> patch adds a reasonable default handling that applies to this case (and
> this way, we don't need to teach language-independent code about
> MUST_NOT_THROW_EXPR).
> 
> patch3: As Jason pointed out, storing the noexcept specification in the
> TRANSACTION_EXPR is less clean than just extending MUST_NOT_THROW_EXPR
> with support for a boolean condition.  This patch does that, including
> instantiation etc.
> 
> patch4: This is the noexcept patch again.  Nothing changed since last
> time except it's now based on conditional MUST_NOT_THROW_EXPR, which
> simiplifies things.
> 
> OK for trunk?

Ok.


r~

Patch

diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index f548e70..fe8d2f8 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1081,6 +1081,15 @@  voidify_wrapper_expr (tree wrapper, tree temp)
 	      break;
 
 	    default:
+	      /* Assume that any tree upon which voidify_wrapper_expr is
+		 directly called is a wrapper, and that its body is op0.  */
+	      if (p == &wrapper)
+		{
+		  TREE_SIDE_EFFECTS (*p) = 1;
+		  TREE_TYPE (*p) = void_type_node;
+		  p = &TREE_OPERAND (*p, 0);
+		  break;
+		}
 	      goto out;
 	    }
 	}