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) \