This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [patch] trans-mem: Support noexcept specifications for transaction statements and expressions.
- From: Torvald Riegel <triegel at redhat dot com>
- To: Jason Merrill <jason at redhat dot com>
- Cc: GCC Patches <gcc-patches at gcc dot gnu dot org>, Richard Henderson <rth at redhat dot com>
- Date: Wed, 16 Nov 2011 20:06:36 +0100
- Subject: Re: [patch] trans-mem: Support noexcept specifications for transaction statements and expressions.
- References: <1320723774.32515.25.camel@triegel.csb> <4EB8AC1B.6000501@redhat.com>
Here is a revised version of the patch. It still fails when combined
with transaction expressions (noexcept-4.C and noexcept-1.C) because
gimplify_must_not_throw_expr() calls voidify_wrapper_expr() on a
MUST_NOT_THROW_EXPR which it doesn't know to be a wrapper. What's the
cleanest way to solve that? Adding handling of MUST_NOT_THROW_EXPR
inside voidify_... will include C++ stuff there, right? Or should there
be a C++ version of voidify_...? Or something else?
Jason, I also addressed your other previous comments regarding not
adding MUST_NOT_THROW_EXPR until instantiation time, except...
On Mon, 2011-11-07 at 23:12 -0500, Jason Merrill wrote:
> On 11/07/2011 10:42 PM, Torvald Riegel wrote:
> > + noex = tsubst_copy_and_build (noex, args, complain, in_decl,
> > + /*function_p=*/false,
> > + /*integral_const_expr_p=*/true);
> > + noex = build_noexcept_spec (TREE_PURPOSE (noex),
> > + tf_warning_or_error);
>
> If you're going to pull the TREE_PURPOSE out, you might as well do that
> before tsubsting rather than after.
... this one. I tried that, but this failed when I tried to use the
purpose obtained before tsubst for the call to build_noexcept_spec.
diff --git a/gcc/c-parser.c b/gcc/c-parser.c
index b88b11f..e1ce35f 100644
--- a/gcc/c-parser.c
+++ b/gcc/c-parser.c
@@ -10703,7 +10703,8 @@ c_parser_transaction_expression (c_parser *parser, enum rid keyword)
{
tree expr = c_parser_expression (parser).value;
ret.original_type = TREE_TYPE (expr);
- ret.value = build1 (TRANSACTION_EXPR, ret.original_type, expr);
+ ret.value = build2 (TRANSACTION_EXPR, ret.original_type, expr,
+ NULL_TREE);
if (this_in & TM_STMT_ATTR_RELAXED)
TRANSACTION_EXPR_RELAXED (ret.value) = 1;
SET_EXPR_LOCATION (ret.value, loc);
diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c
index 4a134b0..f015ff6 100644
--- a/gcc/c-typeck.c
+++ b/gcc/c-typeck.c
@@ -10930,7 +10930,7 @@ c_finish_omp_clauses (tree clauses)
tree
c_finish_transaction (location_t loc, tree block, int flags)
{
- tree stmt = build_stmt (loc, TRANSACTION_EXPR, block);
+ tree stmt = build_stmt (loc, TRANSACTION_EXPR, block, NULL_TREE);
if (flags & TM_STMT_ATTR_OUTER)
TRANSACTION_EXPR_OUTER (stmt) = 1;
if (flags & TM_STMT_ATTR_RELAXED)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index fe50e34..6c5042b 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5564,8 +5564,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 f839112..a9246e1 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -19482,19 +19482,17 @@ 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. */
+ Returns a noexcept specification, or NULL_TREE. */
static tree
-cp_parser_exception_specification_opt (cp_parser* parser)
+cp_parser_noexcept_specification_opt (cp_parser* parser)
{
cp_token *token;
- tree type_id_list;
const char *saved_message;
/* Peek at the next token. */
@@ -19527,6 +19525,32 @@ cp_parser_exception_specification_opt (cp_parser* parser)
return build_noexcept_spec (expr, tf_warning_or_error);
}
+ 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);
+ 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))
@@ -26751,11 +26775,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
@@ -26764,7 +26786,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);
@@ -26782,6 +26804,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);
+
/* Keep track if we're in the lexical scope of an outer transaction. */
new_in = this_in | (old_in & TM_STMT_ATTR_OUTER);
@@ -26791,7 +26816,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;
}
@@ -26799,10 +26824,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
@@ -26811,7 +26834,7 @@ 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;
gcc_assert (keyword == RID_TRANSACTION_ATOMIC
|| keyword == RID_TRANSACTION_RELAXED);
@@ -26831,12 +26854,15 @@ cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
if (keyword == RID_TRANSACTION_RELAXED)
this_in |= TM_STMT_ATTR_RELAXED;
+ /* Parse a noexcept specification. */
+ noex = cp_parser_noexcept_specification_opt(parser);
+
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);
+ expr = build_transaction_expr (token->location, expr, this_in, noex);
cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
parser->in_transaction = old_in;
@@ -26894,7 +26920,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 9738026..c3574e7 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -13140,22 +13140,41 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
case TRANSACTION_EXPR:
{
int flags = 0;
+ tree noex;
flags |= (TRANSACTION_EXPR_OUTER (t) ? TM_STMT_ATTR_OUTER : 0);
flags |= (TRANSACTION_EXPR_RELAXED (t) ? TM_STMT_ATTR_RELAXED : 0);
+ noex = TRANSACTION_EXPR_NOEX (t);
+ if (noex)
+ {
+ if (noex != noexcept_false_spec && noex != noexcept_true_spec
+ && noex != error_mark_node)
+ {
+ /* Check the expression again. */
+ noex = tsubst_copy_and_build (noex, args, complain, in_decl,
+ /*function_p=*/false,
+ /*integral_const_expr_p=*/true);
+ noex = build_noexcept_spec (TREE_PURPOSE (noex),
+ tf_warning_or_error);
+ }
+ gcc_assert (noex == noexcept_false_spec
+ || noex == noexcept_true_spec
+ || noex == error_mark_node);
+ TRANSACTION_EXPR_NOEX (t) = NULL_TREE;
+ }
- if (TRANSACTION_EXPR_IS_STMT (t))
- {
- stmt = begin_transaction_stmt (input_location, NULL, flags);
- RECUR (TRANSACTION_EXPR_BODY (t));
- finish_transaction_stmt (stmt, NULL, flags);
- }
- else
- {
- stmt = build_transaction_expr (EXPR_LOCATION (t),
+ if (TRANSACTION_EXPR_IS_STMT (t))
+ {
+ stmt = begin_transaction_stmt (input_location, NULL, flags);
+ RECUR (TRANSACTION_EXPR_BODY (t));
+ finish_transaction_stmt (stmt, NULL, flags, noex);
+ }
+ else
+ {
+ stmt = build_transaction_expr (EXPR_LOCATION (t),
RECUR (TRANSACTION_EXPR_BODY (t)),
- flags);
- return stmt;
- }
+ flags, noex);
+ return stmt;
+ }
}
break;
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index f70bdb3..fdda43c 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5000,7 +5000,7 @@ begin_transaction_stmt (location_t loc, tree *pcompound, int flags)
if (pcompound)
*pcompound = begin_compound_stmt (0);
- r = build_stmt (loc, TRANSACTION_EXPR, NULL_TREE);
+ r = build_stmt (loc, TRANSACTION_EXPR, NULL_TREE, NULL_TREE);
/* Only add the statement to the function if support enabled. */
if (flag_tm)
@@ -5018,28 +5018,53 @@ 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 NOEXCEPT_TRUE_SPEC, we wrap
+ the body in a MUST_NOT_THROW_EXPR. */
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 (processing_template_decl)
+ TRANSACTION_EXPR_NOEX (stmt) = noex;
+ else if (noex == noexcept_true_spec)
+ {
+ tree body = TRANSACTION_EXPR_BODY (stmt);
+ body = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (body), body);
+ 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 NOEXCEPT_TRUE_SPEC, we wrap the body in a MUST_NOT_THROW_EXPR. */
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;
- ret = build1 (TRANSACTION_EXPR, TREE_TYPE (expr), expr);
+ if (!processing_template_decl)
+ {
+ if (noex == noexcept_true_spec)
+ {
+ expr = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (expr), expr);
+ SET_EXPR_LOCATION (expr, loc);
+ TREE_SIDE_EFFECTS (expr) = 1;
+ }
+ noex = NULL_TREE;
+ }
+ ret = build2 (TRANSACTION_EXPR, TREE_TYPE (expr), expr, noex);
if (flags & TM_STMT_ATTR_RELAXED)
TRANSACTION_EXPR_RELAXED (ret) = 1;
SET_EXPR_LOCATION (ret, loc);
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..86940d2
--- /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_RU4\\s*\\(&global" 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..5a49ade
--- /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_RU4\\s*\\(&global" 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..a54f482
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tm/noexcept-4.C
@@ -0,0 +1,14 @@
+// { dg-do compile }
+// { dg-options "-fgnu-tm -O -std=c++0x -fdump-tree-tmmark -fdump-tree-tmlower" }
+
+int global;
+
+int f2()
+{
+ return __transaction_atomic noexcept(true) (global + 3);
+}
+
+/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 1 "tmlower" } } */
+/* { dg-final { scan-tree-dump-times "ITM_RU4\\s*\\(&global" 1 "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..c56fc0d
--- /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_RU4\\s*\\(&global" 1 "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmlower" } } */
diff --git a/gcc/tree.def b/gcc/tree.def
index 2a2363e..e87fc61 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -1077,8 +1077,9 @@ DEFTREECODE (OMP_ATOMIC_CAPTURE_NEW, "omp_atomic_capture_new", tcc_statement, 2)
DEFTREECODE (OMP_CLAUSE, "omp_clause", tcc_exceptional, 0)
/* TRANSACTION_EXPR tree code.
- Operand 0: BODY: contains body of the transaction. */
-DEFTREECODE (TRANSACTION_EXPR, "transaction_expr", tcc_expression, 1)
+ Operand 0: BODY: contains body of the transaction.
+ Operand 1: contains noexcept argument, if noexcept has been specified. */
+DEFTREECODE (TRANSACTION_EXPR, "transaction_expr", tcc_expression, 2)
/* Reduction operations.
Operations that take a vector of elements and "reduce" it to a scalar
diff --git a/gcc/tree.h b/gcc/tree.h
index a76b0bc..6307aca 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1826,6 +1826,8 @@ extern void protected_set_expr_location (tree, location_t);
/* TM directives and accessors. */
#define TRANSACTION_EXPR_BODY(NODE) \
TREE_OPERAND (TRANSACTION_EXPR_CHECK (NODE), 0)
+#define TRANSACTION_EXPR_NOEX(NODE) \
+ TREE_OPERAND (TRANSACTION_EXPR_CHECK (NODE), 1)
#define TRANSACTION_EXPR_OUTER(NODE) \
(TRANSACTION_EXPR_CHECK (NODE)->base.static_flag)
#define TRANSACTION_EXPR_RELAXED(NODE) \