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.
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);
/* 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. */
{
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))
/* 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
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);
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);
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;
}
/* 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
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);
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))
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;
}
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;
}
}
/* 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;
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>
--- /dev/null
+// { 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" } } */
--- /dev/null
+// { 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);
+}
--- /dev/null
+// { 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" } } */
--- /dev/null
+// { 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" } } */
--- /dev/null
+// { 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" } } */