+2015-10-04 Jason Merrill <jason@redhat.com>
+
+ * builtins.def (BUILT_IN_ABORT): Add transaction_pure attribute.
+
2015-10-04 Uros Bizjak <ubizjak@gmail.com>
* config/i386/i386.c (ix86_nsaved_regs): Use GENERAL_REGNO_P to
DEF_GCC_BUILTIN (BUILT_IN_UMULLL_OVERFLOW, "umulll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, ATTR_NOTHROW_LEAF_LIST)
/* Category: miscellaneous builtins. */
-DEF_LIB_BUILTIN (BUILT_IN_ABORT, "abort", BT_FN_VOID, ATTR_NORETURN_NOTHROW_LEAF_LIST)
+DEF_LIB_BUILTIN (BUILT_IN_ABORT, "abort", BT_FN_VOID, ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST)
DEF_LIB_BUILTIN (BUILT_IN_ABS, "abs", BT_FN_INT_INT, ATTR_CONST_NOTHROW_LEAF_LIST)
DEF_GCC_BUILTIN (BUILT_IN_AGGREGATE_INCOMING_ADDRESS, "aggregate_incoming_address", BT_FN_PTR_VAR, ATTR_LEAF_LIST)
DEF_EXT_LIB_BUILTIN (BUILT_IN_ALLOCA, "alloca", BT_FN_PTR_SIZE, ATTR_MALLOC_NOTHROW_LEAF_LIST)
+2015-10-04 Jason Merrill <jason@redhat.com>
+
+ Implement N4514, C++ Extensions for Transactional Memory.
+ * c-common.c (c_common_reswords): Add C++ TM TS keywords.
+ (c_common_attribute_table): Add transaction_safe_dynamic.
+ transaction_safe now affects type identity.
+ (handle_tm_attribute): Handle transaction_safe_dynamic.
+ * c-common.h (enum rid): Add RID_ATOMIC_NOEXCEPT,
+ RID_ATOMIC_CANCEL, RID_SYNCHRONIZED.
+ (OBJC_IS_CXX_KEYWORD): Add RID_SYNCHRONIZED.
+ (D_TRANSMEM): New.
+ * c-cppbuiltin.c (c_cpp_builtins): Define __cpp_transactional_memory.
+ * c-pretty-print.c (pp_c_attributes_display): Don't print
+ transaction_safe in C++.
+
2015-10-02 Marek Polacek <polacek@redhat.com>
* c.opt (Wduplicated-cond): Don't enable by -Wall anymore.
{ "wchar_t", RID_WCHAR, D_CXXONLY },
{ "while", RID_WHILE, 0 },
+ /* C++ transactional memory. */
+ { "synchronized", RID_SYNCHRONIZED, D_CXX_OBJC | D_TRANSMEM },
+ { "atomic_noexcept", RID_ATOMIC_NOEXCEPT, D_CXXONLY | D_TRANSMEM },
+ { "atomic_cancel", RID_ATOMIC_CANCEL, D_CXXONLY | D_TRANSMEM },
+ { "atomic_commit", RID_TRANSACTION_ATOMIC, D_CXXONLY | D_TRANSMEM },
+
/* Concepts-related keywords */
{ "concept", RID_CONCEPT, D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
{ "requires", RID_REQUIRES, D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
{ "protocol", RID_AT_PROTOCOL, D_OBJC },
{ "selector", RID_AT_SELECTOR, D_OBJC },
{ "finally", RID_AT_FINALLY, D_OBJC },
- { "synchronized", RID_AT_SYNCHRONIZED, D_OBJC },
{ "optional", RID_AT_OPTIONAL, D_OBJC },
{ "required", RID_AT_REQUIRED, D_OBJC },
{ "property", RID_AT_PROPERTY, D_OBJC },
{ "transaction_callable", 0, 0, false, true, false,
handle_tm_attribute, false },
{ "transaction_unsafe", 0, 0, false, true, false,
- handle_tm_attribute, false },
+ handle_tm_attribute, true },
{ "transaction_safe", 0, 0, false, true, false,
+ handle_tm_attribute, true },
+ { "transaction_safe_dynamic", 0, 0, true, false, false,
handle_tm_attribute, false },
{ "transaction_may_cancel_outer", 0, 0, false, true, false,
handle_tm_attribute, false },
}
break;
+ case FUNCTION_DECL:
+ {
+ /* transaction_safe_dynamic goes on the FUNCTION_DECL, but we also
+ want to set transaction_safe on the type. */
+ gcc_assert (is_attribute_p ("transaction_safe_dynamic", name));
+ if (!TYPE_P (DECL_CONTEXT (*node)))
+ error_at (DECL_SOURCE_LOCATION (*node),
+ "%<transaction_safe_dynamic%> may only be specified for "
+ "a virtual function");
+ *no_add_attrs = false;
+ decl_attributes (&TREE_TYPE (*node),
+ build_tree_list (get_identifier ("transaction_safe"),
+ NULL_TREE),
+ 0);
+ break;
+ }
+
case POINTER_TYPE:
{
enum tree_code subcode = TREE_CODE (TREE_TYPE (*node));
/* C++ concepts */
RID_CONCEPT, RID_REQUIRES,
+ /* C++ transactional memory. */
+ RID_ATOMIC_NOEXCEPT, RID_ATOMIC_CANCEL, RID_SYNCHRONIZED,
+
/* Cilk Plus keywords. */
RID_CILK_SPAWN, RID_CILK_SYNC, RID_CILK_FOR,
is found elsewhere, it follows the rules of the C/C++ language.
*/
#define OBJC_IS_CXX_KEYWORD(rid) \
- (rid == RID_CLASS \
+ (rid == RID_CLASS || rid == RID_SYNCHRONIZED \
|| rid == RID_PUBLIC || rid == RID_PROTECTED || rid == RID_PRIVATE \
|| rid == RID_TRY || rid == RID_THROW || rid == RID_CATCH)
#define D_CXX_OBJC 0x100 /* In Objective C, and C++, but not C. */
#define D_CXXWARN 0x200 /* In C warn with -Wcxx-compat. */
#define D_CXX_CONCEPTS 0x400 /* In C++, only with concepts. */
+#define D_TRANSMEM 0X800 /* C++ transactional memory TS. */
#define D_CXX_CONCEPTS_FLAGS D_CXXONLY | D_CXX_CONCEPTS
/* Use a value smaller than the 201507 specified in
the TS, since we don't yet support extended auto. */
cpp_define (pfile, "__cpp_concepts=201500");
+ if (flag_tm)
+ /* Use a value smaller than the 201505 specified in
+ the TS, since we don't yet support atomic_cancel. */
+ cpp_define (pfile, "__cpp_transactional_memory=210500");
if (flag_sized_deallocation)
cpp_define (pfile, "__cpp_sized_deallocation=201309");
}
as = lookup_attribute_spec (TREE_PURPOSE (a));
if (!as || as->affects_type_identity == false)
continue;
+ if (c_dialect_cxx ()
+ && !strcmp ("transaction_safe", as->name))
+ /* In C++ transaction_safe is printed at the end of the declarator. */
+ continue;
if (is_first)
{
pp_c_ws_string (pp, "__attribute__");
+2015-10-04 Jason Merrill <jason@redhat.com>
+
+ * c-parser.c (c_lex_one_token): Handle @synchronized.
+ * c-decl.c (match_builtin_function_types): A declaration of a built-in
+ can change whether the function is transaction_safe.
+
2015-10-02 Marek Polacek <polacek@redhat.com>
PR c/67730
}
trytype = build_function_type (newrettype, tryargs);
- return build_type_attribute_variant (trytype, TYPE_ATTRIBUTES (oldtype));
+
+ /* Allow declaration to change transaction_safe attribute. */
+ tree oldattrs = TYPE_ATTRIBUTES (oldtype);
+ tree oldtsafe = lookup_attribute ("transaction_safe", oldattrs);
+ tree newattrs = TYPE_ATTRIBUTES (newtype);
+ tree newtsafe = lookup_attribute ("transaction_safe", newattrs);
+ if (oldtsafe && !newtsafe)
+ oldattrs = remove_attribute ("transaction_safe", oldattrs);
+ else if (newtsafe && !oldtsafe)
+ oldattrs = tree_cons (get_identifier ("transaction_safe"),
+ NULL_TREE, oldattrs);
+
+ return build_type_attribute_variant (trytype, oldattrs);
}
/* Subroutine of diagnose_mismatched_decls. Check for function type
case RID_THROW: token->keyword = RID_AT_THROW; break;
case RID_TRY: token->keyword = RID_AT_TRY; break;
case RID_CATCH: token->keyword = RID_AT_CATCH; break;
+ case RID_SYNCHRONIZED: token->keyword = RID_AT_SYNCHRONIZED; break;
default: token->keyword = C_RID_CODE (token->value);
}
break;
+2015-10-04 Jason Merrill <jason@redhat.com>
+
+ Implement N4514, C++ Extensions for Transactional Memory.
+ * cp-tree.h (struct cp_declarator): Add tx_qualifier field.
+ (BCS_NORMAL, BCS_TRANSACTION): New enumerators.
+ * lex.c (init_reswords): Limit TM kewords to -fgnu-tm.
+ * parser.c (cp_lexer_get_preprocessor_token): Fix @synchronized.
+ (make_call_declarator): Take tx_qualifier.
+ (cp_parser_tx_qualifier_opt): New.
+ (cp_parser_lambda_declarator_opt): Use it.
+ (cp_parser_direct_declarator): Likewise.
+ (cp_parser_statement): Handle atomic_noexcept, atomic_cancel.
+ (cp_parser_compound_statement): Change in_try parameter to bcs_flags.
+ (cp_parser_std_attribute): Map optimize_for_synchronized to
+ transaction_callable.
+ (cp_parser_transaction): Take the token. Handle atomic_noexcept.
+ * lambda.c (maybe_add_lambda_conv_op): Handle transaction-safety.
+ * call.c (enum conversion_kind): Add ck_tsafe.
+ (standard_conversion): Handle transaction-safety conversion.
+ (convert_like_real, resolve_address_of_overloaded_function): Likewise.
+ (check_methods): Diagnose transaction_safe_dynamic on non-virtual
+ function.
+ (look_for_tm_attr_overrides): Don't inherit transaction_safe_dynamic.
+ * cvt.c (tx_safe_fn_type_p, tx_unsafe_fn_variant)
+ (can_convert_tx_safety): New.
+ * typeck.c (composite_pointer_type): Handle transaction-safety.
+ * name-lookup.h (enum scope_kind): Add sk_transaction.
+ * name-lookup.c (begin_scope): Handle it.
+ * semantics.c (begin_compound_stmt): Pass it.
+ * decl.c (check_previous_goto_1): Check it.
+ (struct named_label_entry): Add in_transaction_scope.
+ (poplevel_named_label_1): Set it.
+ (check_goto): Check it.
+ (duplicate_decls): A specialization can be transaction_safe
+ independently of its template.
+ (grokdeclarator): Handle tx-qualifier.
+ * rtti.c (ptr_initializer): Handle transaction-safe.
+ * search.c (check_final_overrider): Check transaction_safe_dynamic.
+ Don't check transaction_safe.
+ * mangle.c (write_function_type): Mangle transaction_safe here.
+ (write_CV_qualifiers_for_type): Not here.
+ (write_type): Preserve transaction_safe when stripping attributes.
+ * error.c (dump_type_suffix): Print transaction_safe.
+
2015-10-02 Marek Polacek <polacek@redhat.com>
PR c/64249
enum conversion_kind {
ck_identity,
ck_lvalue,
+ ck_tsafe,
ck_qual,
ck_std,
ck_ptr,
conv = build_conv (ck_ptr, from, conv);
conv->base_p = true;
}
+ else if (tx_safe_fn_type_p (TREE_TYPE (from)))
+ {
+ /* A prvalue of type "pointer to transaction_safe function" can be
+ converted to a prvalue of type "pointer to function". */
+ tree unsafe = tx_unsafe_fn_variant (TREE_TYPE (from));
+ if (same_type_p (unsafe, TREE_TYPE (to)))
+ {
+ from = build_pointer_type (unsafe);
+ conv = build_conv (ck_tsafe, from, conv);
+ }
+ }
if (tcode == POINTER_TYPE)
{
case ck_lvalue:
return decay_conversion (expr, complain);
+ case ck_tsafe:
+ /* ??? Should the address of a transaction-safe pointer point to the TM
+ clone, and this conversion look up the primary function? */
+ return build_nop (totype, expr);
+
case ck_qual:
/* Warn about deprecated conversion if appropriate. */
string_conv_p (totype, expr, 1);
grok_special_member_properties. */
if (DECL_DESTRUCTOR_P (x) && user_provided_p (x))
TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) = 1;
+ if (!DECL_VIRTUAL_P (x)
+ && lookup_attribute ("transaction_safe_dynamic", DECL_ATTRIBUTES (x)))
+ error_at (DECL_SOURCE_LOCATION (x),
+ "%<transaction_safe_dynamic%> may only be specified for "
+ "a virtual function");
}
}
o = look_for_overrides_here (basetype, fndecl);
if (o)
- found |= tm_attr_to_mask (find_tm_attribute
- (TYPE_ATTRIBUTES (TREE_TYPE (o))));
+ {
+ if (lookup_attribute ("transaction_safe_dynamic",
+ DECL_ATTRIBUTES (o)))
+ /* transaction_safe_dynamic is not inherited. */;
+ else
+ found |= tm_attr_to_mask (find_tm_attribute
+ (TYPE_ATTRIBUTES (TREE_TYPE (o))));
+ }
else
found |= look_for_tm_attr_overrides (basetype, fndecl);
}
continue;
/* See if there's a match. */
- if (same_type_p (target_fn_type, static_fn_type (fn)))
+ tree fntype = static_fn_type (fn);
+ if (same_type_p (target_fn_type, fntype)
+ || can_convert_tx_safety (target_fn_type, fntype))
matches = tree_cons (fn, NULL_TREE, matches);
}
}
}
/* See if there's a match. */
- if (same_type_p (target_fn_type, static_fn_type (instantiation)))
+ tree fntype = static_fn_type (instantiation);
+ if (same_type_p (target_fn_type, fntype)
+ || can_convert_tx_safety (target_fn_type, fntype))
matches = tree_cons (instantiation, fn, matches);
}
cp_virt_specifiers virt_specifiers;
/* The ref-qualifier for the function. */
cp_ref_qualifier ref_qualifier;
+ /* The transaction-safety qualifier for the function. */
+ tree tx_qualifier;
/* The exception-specification for the function. */
tree exception_specification;
/* The late-specified return type, if any. */
extern tree build_expr_type_conversion (int, tree, bool);
extern tree type_promotes_to (tree);
extern tree perform_qualification_conversions (tree, tree);
+extern bool tx_safe_fn_type_p (tree);
+extern tree tx_unsafe_fn_variant (tree);
+extern bool can_convert_tx_safety (tree, tree);
/* in name-lookup.c */
extern tree pushdecl (tree);
extern bool is_this_parameter (tree);
enum {
+ BCS_NORMAL = 0,
BCS_NO_SCOPE = 1,
BCS_TRY_BLOCK = 2,
- BCS_FN_BODY = 4
+ BCS_FN_BODY = 4,
+ BCS_TRANSACTION = 8
};
extern tree begin_compound_stmt (unsigned int);
else
return error_mark_node;
}
+
+/* True iff T is a transaction-safe function type. */
+
+bool
+tx_safe_fn_type_p (tree t)
+{
+ if (TREE_CODE (t) != FUNCTION_TYPE
+ && TREE_CODE (t) != METHOD_TYPE)
+ return false;
+ return !!lookup_attribute ("transaction_safe", TYPE_ATTRIBUTES (t));
+}
+
+/* Return the transaction-unsafe variant of transaction-safe function type
+ T. */
+
+tree
+tx_unsafe_fn_variant (tree t)
+{
+ gcc_assert (tx_safe_fn_type_p (t));
+ tree attrs = remove_attribute ("transaction_safe",
+ TYPE_ATTRIBUTES (t));
+ return cp_build_type_attribute_variant (t, attrs);
+}
+
+/* Return true iff FROM can convert to TO by a transaction-safety
+ conversion. */
+
+bool
+can_convert_tx_safety (tree to, tree from)
+{
+ return (flag_tm && tx_safe_fn_type_p (from)
+ && same_type_p (to, tx_unsafe_fn_variant (from)));
+}
bool in_try_scope;
bool in_catch_scope;
bool in_omp_scope;
+ bool in_transaction_scope;
};
#define named_labels cp_function_chain->x_named_labels
case sk_omp:
ent->in_omp_scope = true;
break;
+ case sk_transaction:
+ ent->in_transaction_scope = true;
+ break;
default:
break;
}
}
}
+ /* An explicit specialization of a function template or of a member
+ function of a class template can be declared transaction_safe
+ independently of whether the corresponding template entity is declared
+ transaction_safe. */
+ if (flag_tm && TREE_CODE (newdecl) == FUNCTION_DECL
+ && DECL_TEMPLATE_INSTANTIATION (olddecl)
+ && DECL_TEMPLATE_SPECIALIZATION (newdecl)
+ && tx_safe_fn_type_p (newtype)
+ && !tx_safe_fn_type_p (TREE_TYPE (newdecl)))
+ newtype = tx_unsafe_fn_variant (newtype);
+
TREE_TYPE (newdecl) = TREE_TYPE (olddecl) = newtype;
if (TREE_CODE (newdecl) == FUNCTION_DECL)
{
cp_binding_level *b;
bool identified = false, complained = false;
- bool saw_eh = false, saw_omp = false;
+ bool saw_eh = false, saw_omp = false, saw_tm = false;
if (exited_omp)
{
inform (input_location, " enters OpenMP structured block");
saw_omp = true;
}
+ if (b->kind == sk_transaction && !saw_tm)
+ {
+ if (!identified)
+ {
+ complained = identify_goto (decl, locus);
+ identified = true;
+ }
+ if (complained)
+ inform (input_location,
+ " enters synchronized or atomic statement");
+ saw_tm = true;
+ }
}
return !identified;
return;
}
- if (ent->in_try_scope || ent->in_catch_scope
+ if (ent->in_try_scope || ent->in_catch_scope || ent->in_transaction_scope
|| ent->in_omp_scope || !vec_safe_is_empty (ent->bad_decls))
{
complained = permerror (DECL_SOURCE_LOCATION (decl),
inform (input_location, " enters try block");
else if (ent->in_catch_scope && !saw_catch)
inform (input_location, " enters catch block");
+ else if (ent->in_transaction_scope)
+ inform (input_location, " enters synchronized or atomic statement");
}
if (ent->in_omp_scope)
virt_specifiers = declarator->u.function.virt_specifiers;
/* And ref-qualifier, too */
rqual = declarator->u.function.ref_qualifier;
+ /* And tx-qualifier. */
+ tree tx_qual = declarator->u.function.tx_qualifier;
/* Pick up the exception specifications. */
raises = declarator->u.function.exception_specification;
/* If the exception-specification is ill-formed, let's pretend
}
type = build_function_type (type, arg_types);
- if (declarator->std_attributes)
+
+ tree attrs = declarator->std_attributes;
+ if (tx_qual)
+ {
+ tree att = build_tree_list (tx_qual, NULL_TREE);
+ /* transaction_safe applies to the type, but
+ transaction_safe_dynamic applies to the function. */
+ if (is_attribute_p ("transaction_safe", tx_qual))
+ attrs = chainon (attrs, att);
+ else
+ returned_attrs = chainon (returned_attrs, att);
+ }
+ if (attrs)
/* [dcl.fct]/2:
The optional attribute-specifier-seq appertains to
the function type. */
- decl_attributes (&type, declarator->std_attributes,
- 0);
+ decl_attributes (&type, attrs, 0);
}
break;
TREE_CODE (t) == FUNCTION_TYPE
&& (flags & TFF_POINTER));
dump_ref_qualifier (pp, t, flags);
+ if (tx_safe_fn_type_p (t))
+ pp_cxx_ws_string (pp, "transaction_safe");
dump_exception_spec (pp, TYPE_RAISES_EXCEPTIONS (t), flags);
dump_type_suffix (pp, TREE_TYPE (t), flags);
break;
dump_ref_qualifier (pp, fntype, flags);
}
+ if (tx_safe_fn_type_p (fntype))
+ {
+ pp->padding = pp_before;
+ pp_cxx_ws_string (pp, "transaction_safe");
+ }
+
if (flags & TFF_EXCEPTION_SPECIFICATION)
{
pp->padding = pp_before;
vec<tree, va_gc> *direct_argvec = 0;
tree decltype_call = 0, call = 0;
- tree fn_result = TREE_TYPE (TREE_TYPE (callop));
+ tree optype = TREE_TYPE (callop);
+ tree fn_result = TREE_TYPE (optype);
if (generic_lambda_p)
{
CALL_FROM_THUNK_P (call) = 1;
tree stattype = build_function_type (fn_result, FUNCTION_ARG_CHAIN (callop));
+ stattype = (cp_build_type_attribute_variant
+ (stattype, TYPE_ATTRIBUTES (optype)));
/* First build up the conversion op. */
mask |= D_CXX11;
if (!flag_concepts)
mask |= D_CXX_CONCEPTS;
+ if (!flag_tm)
+ mask |= D_TRANSMEM;
if (flag_no_asm)
mask |= D_ASM | D_EXT;
if (flag_no_gnu_keywords)
{
tree t = TYPE_MAIN_VARIANT (type);
if (TYPE_ATTRIBUTES (t) && !OVERLOAD_TYPE_P (t))
- t = cp_build_type_attribute_variant (t, NULL_TREE);
+ {
+ tree attrs = NULL_TREE;
+ if (tx_safe_fn_type_p (type))
+ attrs = tree_cons (get_identifier ("transaction_safe"),
+ NULL_TREE, attrs);
+ t = cp_build_type_attribute_variant (t, attrs);
+ }
gcc_assert (t != type);
if (TREE_CODE (t) == FUNCTION_TYPE
|| TREE_CODE (t) == METHOD_TYPE)
tree name = get_attribute_name (a);
const attribute_spec *as = lookup_attribute_spec (name);
if (as && as->affects_type_identity
+ && !is_attribute_p ("transaction_safe", name)
&& !is_attribute_p ("abi_tag", name))
vec.safe_push (a);
}
write_CV_qualifiers_for_type (this_type);
}
+ if (tx_safe_fn_type_p (type))
+ write_string ("Dx");
+
write_char ('F');
/* We don't track whether or not a type is `extern "C"'. Note that
you can have an `extern "C"' function that does not have
case sk_class:
case sk_scoped_enum:
case sk_function_parms:
+ case sk_transaction:
case sk_omp:
scope->keep = keep_next_level_flag;
break;
specialization. Since, by definition, an
explicit specialization is introduced by
"template <>", this scope is always empty. */
+ sk_transaction, /* A synchronized or atomic statement. */
sk_omp /* An OpenMP structured block. */
};
case RID_THROW: token->keyword = RID_AT_THROW; break;
case RID_TRY: token->keyword = RID_AT_TRY; break;
case RID_CATCH: token->keyword = RID_AT_CATCH; break;
+ case RID_SYNCHRONIZED: token->keyword = RID_AT_SYNCHRONIZED; break;
default: token->keyword = C_RID_CODE (token->u.value);
}
}
VAR_DECLs or FUNCTION_DECLs) should do that directly. */
static cp_declarator *make_call_declarator
- (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier, tree, tree, tree);
+ (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier, tree, tree, tree, tree);
static cp_declarator *make_array_declarator
(cp_declarator *, tree);
static cp_declarator *make_pointer_declarator
cp_cv_quals cv_qualifiers,
cp_virt_specifiers virt_specifiers,
cp_ref_qualifier ref_qualifier,
+ tree tx_qualifier,
tree exception_specification,
tree late_return_type,
tree requires_clause)
declarator->u.function.qualifiers = cv_qualifiers;
declarator->u.function.virt_specifiers = virt_specifiers;
declarator->u.function.ref_qualifier = ref_qualifier;
+ declarator->u.function.tx_qualifier = tx_qualifier;
declarator->u.function.exception_specification = exception_specification;
declarator->u.function.late_return_type = late_return_type;
declarator->u.function.requires_clause = requires_clause;
static tree cp_parser_expression_statement
(cp_parser *, tree);
static tree cp_parser_compound_statement
- (cp_parser *, tree, bool, bool);
+ (cp_parser *, tree, int, bool);
static void cp_parser_statement_seq_opt
(cp_parser *, tree);
static tree cp_parser_selection_statement
(cp_parser *);
static cp_ref_qualifier cp_parser_ref_qualifier_opt
(cp_parser *);
+static tree cp_parser_tx_qualifier_opt
+ (cp_parser *);
static tree cp_parser_late_return_type_opt
(cp_parser *, cp_declarator *, tree &, cp_cv_quals);
static tree cp_parser_declarator_id
/* Transactional Memory Extensions */
static tree cp_parser_transaction
- (cp_parser *, enum rid);
+ (cp_parser *, cp_token *);
static tree cp_parser_transaction_expression
(cp_parser *, enum rid);
static bool cp_parser_function_transaction
/* Start the statement-expression. */
tree expr = begin_stmt_expr ();
/* Parse the compound-statement. */
- cp_parser_compound_statement (parser, expr, false, false);
+ cp_parser_compound_statement (parser, expr, BCS_NORMAL, false);
/* Finish up. */
expr = finish_stmt_expr (expr, false);
/* Consume the ')'. */
tree attributes = NULL_TREE;
tree exception_spec = NULL_TREE;
tree template_param_list = NULL_TREE;
+ tree tx_qual = NULL_TREE;
/* The template-parameter-list is optional, but must begin with
an opening angle if present. */
LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
}
+ tx_qual = cp_parser_tx_qualifier_opt (parser);
+
/* Parse optional exception specification. */
exception_spec = cp_parser_exception_specification_opt (parser);
declarator = make_call_declarator (declarator, param_list, quals,
VIRT_SPEC_UNSPECIFIED,
REF_QUAL_NONE,
+ tx_qual,
exception_spec,
/*late_return_type=*/NULL_TREE,
/*requires_clause*/NULL_TREE);
case RID_TRANSACTION_ATOMIC:
case RID_TRANSACTION_RELAXED:
- statement = cp_parser_transaction (parser, keyword);
+ case RID_SYNCHRONIZED:
+ case RID_ATOMIC_NOEXCEPT:
+ case RID_ATOMIC_CANCEL:
+ statement = cp_parser_transaction (parser, token);
break;
case RID_TRANSACTION_CANCEL:
statement = cp_parser_transaction_cancel (parser);
}
/* Anything that starts with a `{' must be a compound-statement. */
else if (token->type == CPP_OPEN_BRACE)
- statement = cp_parser_compound_statement (parser, NULL, false, false);
+ statement = cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false);
/* CPP_PRAGMA is a #pragma inside a function body, which constitutes
a statement all its own. */
else if (token->type == CPP_PRAGMA)
static tree
cp_parser_compound_statement (cp_parser *parser, tree in_statement_expr,
- bool in_try, bool function_body)
+ int bcs_flags, bool function_body)
{
tree compound_stmt;
pedwarn (input_location, OPT_Wpedantic,
"compound-statement in constexpr function");
/* Begin the compound-statement. */
- compound_stmt = begin_compound_stmt (in_try ? BCS_TRY_BLOCK : 0);
+ compound_stmt = begin_compound_stmt (bcs_flags);
/* If the next keyword is `__label__' we have a label declaration. */
while (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL))
cp_parser_label_declaration (parser);
}
/* if a compound is opened, we simply parse the statement directly. */
else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
- statement = cp_parser_compound_statement (parser, NULL, false, false);
+ statement = cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false);
/* If the token is not a `{', then we must take special action. */
else
{
cv_quals = cp_parser_cv_qualifier_seq_opt (parser);
/* Parse the ref-qualifier. */
ref_qual = cp_parser_ref_qualifier_opt (parser);
+ /* Parse the tx-qualifier. */
+ tree tx_qual = cp_parser_tx_qualifier_opt (parser);
/* And the exception-specification. */
exception_specification
= cp_parser_exception_specification_opt (parser);
cv_quals,
virt_specifiers,
ref_qual,
+ tx_qual,
exception_specification,
late_return,
requires_clause);
return ref_qual;
}
+/* Parse an optional tx-qualifier.
+
+ tx-qualifier:
+ transaction_safe
+ transaction_safe_dynamic */
+
+static tree
+cp_parser_tx_qualifier_opt (cp_parser *parser)
+{
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
+ if (token->type == CPP_NAME)
+ {
+ tree name = token->u.value;
+ const char *p = IDENTIFIER_POINTER (name);
+ const int len = strlen ("transaction_safe");
+ if (!strncmp (p, "transaction_safe", len))
+ {
+ p += len;
+ if (*p == '\0'
+ || !strcmp (p, "_dynamic"))
+ {
+ cp_lexer_consume_token (parser->lexer);
+ if (!flag_tm)
+ {
+ error ("%E requires %<-fgnu-tm%>", name);
+ return NULL_TREE;
+ }
+ else
+ return name;
+ }
+ }
+ }
+ return NULL_TREE;
+}
+
/* Parse an (optional) virt-specifier-seq.
virt-specifier-seq:
static void
cp_parser_function_body (cp_parser *parser, bool in_function_try_block)
{
- cp_parser_compound_statement (parser, NULL, in_function_try_block, true);
+ cp_parser_compound_statement (parser, NULL, (in_function_try_block
+ ? BCS_TRY_BLOCK : BCS_NORMAL),
+ true);
}
/* Parse a ctor-initializer-opt followed by a function-body. Return
error ("%<try%> in %<constexpr%> function");
try_block = begin_try_block ();
- cp_parser_compound_statement (parser, NULL, true, false);
+ cp_parser_compound_statement (parser, NULL, BCS_TRY_BLOCK, false);
finish_try_block (try_block);
cp_parser_handler_seq (parser);
finish_handler_sequence (try_block);
declaration = cp_parser_exception_declaration (parser);
finish_handler_parms (declaration, handler);
cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
- cp_parser_compound_statement (parser, NULL, false, false);
+ cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false);
finish_handler (handler);
}
" use %<gnu::deprecated%>");
TREE_PURPOSE (TREE_PURPOSE (attribute)) = get_identifier ("gnu");
}
+ /* Transactional Memory TS optimize_for_synchronized attribute is
+ equivalent to GNU transaction_callable. */
+ else if (is_attribute_p ("optimize_for_synchronized", attr_id))
+ TREE_PURPOSE (attribute)
+ = get_identifier ("transaction_callable");
+ /* Transactional Memory attributes are GNU attributes. */
+ else if (tm_attr_to_mask (attr_id))
+ TREE_PURPOSE (attribute) = attr_id;
}
/* Now parse the optional argument clause of the attribute. */
/* NB: The @try block needs to be wrapped in its own STATEMENT_LIST
node, lest it get absorbed into the surrounding block. */
stmt = push_stmt_list ();
- cp_parser_compound_statement (parser, NULL, false, false);
+ cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false);
objc_begin_try_stmt (location, pop_stmt_list (stmt));
while (cp_lexer_next_token_is_keyword (parser->lexer, RID_AT_CATCH))
forget about the closing parenthesis and keep going. */
}
objc_begin_catch_clause (parameter_declaration);
- cp_parser_compound_statement (parser, NULL, false, false);
+ cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false);
objc_finish_catch_clause ();
}
if (cp_lexer_next_token_is_keyword (parser->lexer, RID_AT_FINALLY))
/* NB: The @finally block needs to be wrapped in its own STATEMENT_LIST
node, lest it get absorbed into the surrounding block. */
stmt = push_stmt_list ();
- cp_parser_compound_statement (parser, NULL, false, false);
+ cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false);
objc_build_finally_clause (location, pop_stmt_list (stmt));
}
/* NB: The @synchronized block needs to be wrapped in its own STATEMENT_LIST
node, lest it get absorbed into the surrounding block. */
stmt = push_stmt_list ();
- cp_parser_compound_statement (parser, NULL, false, false);
+ cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false);
return objc_build_synchronized (location, lock, pop_stmt_list (stmt));
}
attribute
[ [ identifier ] ]
- ??? Simplify this when C++0x bracket attributes are
- implemented properly. */
+ We use this instead of cp_parser_attributes_opt for transactions to avoid
+ the pedwarn in C++98 mode. */
static tree
cp_parser_txn_attribute_opt (cp_parser *parser)
*/
static tree
-cp_parser_transaction (cp_parser *parser, enum rid keyword)
+cp_parser_transaction (cp_parser *parser, cp_token *token)
{
unsigned char old_in = parser->in_transaction;
unsigned char this_in = 1, new_in;
- cp_token *token;
+ enum rid keyword = token->keyword;
tree stmt, attrs, noex;
- gcc_assert (keyword == RID_TRANSACTION_ATOMIC
- || keyword == RID_TRANSACTION_RELAXED);
- token = cp_parser_require_keyword (parser, keyword,
- (keyword == RID_TRANSACTION_ATOMIC ? RT_TRANSACTION_ATOMIC
- : RT_TRANSACTION_RELAXED));
- gcc_assert (token != NULL);
+ cp_lexer_consume_token (parser->lexer);
- if (keyword == RID_TRANSACTION_RELAXED)
+ if (keyword == RID_TRANSACTION_RELAXED
+ || keyword == RID_SYNCHRONIZED)
this_in |= TM_STMT_ATTR_RELAXED;
else
{
}
/* Parse a noexcept specification. */
- noex = cp_parser_noexcept_specification_opt (parser, true, NULL, true);
+ if (keyword == RID_ATOMIC_NOEXCEPT)
+ noex = boolean_true_node;
+ else if (keyword == RID_ATOMIC_CANCEL)
+ {
+ /* cancel-and-throw is unimplemented. */
+ sorry ("atomic_cancel");
+ noex = NULL_TREE;
+ }
+ else
+ 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);
stmt = begin_transaction_stmt (token->location, NULL, this_in);
parser->in_transaction = new_in;
- cp_parser_compound_statement (parser, NULL, false, false);
+ cp_parser_compound_statement (parser, NULL, BCS_TRANSACTION, false);
parser->in_transaction = old_in;
finish_transaction_stmt (stmt, NULL, this_in, noex);
if (incomplete)
flags |= 8;
+ if (tx_safe_fn_type_p (to))
+ {
+ flags |= 0x20;
+ to = tx_unsafe_fn_variant (to);
+ }
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, init);
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, build_int_cst (NULL_TREE, flags));
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE,
return 0;
}
- /* Check for conflicting type attributes. */
- if (!comp_type_attributes (over_type, base_type))
+ /* Check for conflicting type attributes. But leave transaction_safe for
+ set_one_vmethod_tm_attributes. */
+ if (!comp_type_attributes (over_type, base_type)
+ && !tx_safe_fn_type_p (base_type)
+ && !tx_safe_fn_type_p (over_type))
{
error ("conflicting type attributes specified for %q+#D", overrider);
error (" overriding %q+#D", basefn);
return 0;
}
+ /* A function declared transaction_safe_dynamic that overrides a function
+ declared transaction_safe (but not transaction_safe_dynamic) is
+ ill-formed. */
+ if (tx_safe_fn_type_p (base_type)
+ && lookup_attribute ("transaction_safe_dynamic",
+ DECL_ATTRIBUTES (overrider))
+ && !lookup_attribute ("transaction_safe_dynamic",
+ DECL_ATTRIBUTES (basefn)))
+ {
+ error_at (DECL_SOURCE_LOCATION (overrider),
+ "%qD declared %<transaction_safe_dynamic%>", overrider);
+ inform (DECL_SOURCE_LOCATION (basefn),
+ "overriding %qD declared %<transaction_safe%>", basefn);
+ }
+
if (DECL_DELETED_FN (basefn) != DECL_DELETED_FN (overrider))
{
if (DECL_DELETED_FN (overrider))
keep_next_level (false);
}
else
- r = do_pushlevel (flags & BCS_TRY_BLOCK ? sk_try : sk_block);
+ {
+ scope_kind sk = sk_block;
+ if (flags & BCS_TRY_BLOCK)
+ sk = sk_try;
+ else if (flags & BCS_TRANSACTION)
+ sk = sk_transaction;
+ r = do_pushlevel (sk);
+ }
/* When processing a template, we need to remember where the braces were,
so that we can set up identical scopes when instantiating the template
return error_mark_node;
}
}
+ else if (TYPE_PTR_P (t1) && TYPE_PTR_P (t2)
+ && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (t1))
+ && TREE_CODE (TREE_TYPE (t2)) == TREE_CODE (TREE_TYPE (t1)))
+ {
+ /* ...if T1 is "pointer to transaction_safe function" and T2 is "pointer
+ to function", where the function types are otherwise the same, T2, and
+ vice versa.... */
+ tree f1 = TREE_TYPE (t1);
+ tree f2 = TREE_TYPE (t2);
+ bool safe1 = tx_safe_fn_type_p (f1);
+ bool safe2 = tx_safe_fn_type_p (f2);
+ if (safe1 && !safe2)
+ t1 = build_pointer_type (tx_unsafe_fn_variant (f1));
+ else if (safe2 && !safe1)
+ t2 = build_pointer_type (tx_unsafe_fn_variant (f2));
+ }
return composite_pointer_type_r (t1, t2, operation, complain);
}
--- /dev/null
+// Test for composite pointer type.
+// { dg-options -fgnu-tm }
+
+void f(bool b)
+{
+ void (*p)() transaction_safe = 0;
+ void (*g)() = 0;
+
+ g = b ? p : g; // OK
+ p = b ? p : g; // { dg-error "" }
+
+ p == g;
+ p != g;
+}
--- /dev/null
+// Test that transaction_safe_dynamic can only be used on virtual functions.
+// { dg-options "-fgnu-tm -std=c++14" }
+
+void f() transaction_safe_dynamic; // { dg-error "virtual" }
+auto a = []() transaction_safe_dynamic {}; // { dg-error "virtual" }
+struct A {
+ void f() transaction_safe_dynamic; // { dg-error "virtual" }
+ virtual void g();
+};
+
+struct B: A {
+ void g() transaction_safe_dynamic;
+};
--- /dev/null
+// { dg-options "-fgnu-tm -std=c++14 -O2" }
+
+void unsafe();
+struct A {
+ virtual void f() transaction_safe_dynamic;
+};
+struct B:A {
+ void f() { unsafe(); }
+};
+
+void f() transaction_safe {
+ B b;
+ A& ar = b;
+ // This is undefined behavior, we want to give an error with
+ // devirtualization.
+ ar.f(); // { dg-error "unsafe" }
+}
--- /dev/null
+// A handler can involve a transaction-safety conversion.
+// { dg-do run }
+// { dg-options "-fgnu-tm" }
+
+void g() transaction_safe {}
+int main()
+{
+ try { throw g; }
+ catch (void (*p)()) { }
+}
--- /dev/null
+// A handler cannot do the reverse of a transaction-safety conversion.
+// { dg-do run }
+// { dg-options "-fgnu-tm" }
+
+extern "C" void abort();
+
+void g() {}
+
+int main()
+{
+ try { throw g; }
+ catch (void (*p)() transaction_safe) { abort(); }
+ catch (...) { }
+}
--- /dev/null
+// Test that throwing out of an atomic_commit block commits the transaction.
+
+// { dg-do run }
+// { dg-options "-fgnu-tm" }
+
+int main()
+{
+ static int i;
+ bool caught = false;
+ try {
+ atomic_commit {
+ i = 12;
+ throw 42;
+ i = 24;
+ }
+ } catch (int x) {
+ caught = (x == 42);
+ }
+ if (!caught || i != 12)
+ __builtin_abort();
+}
--- /dev/null
+// Testcase from TM TS
+// { dg-options "-std=c++14 -fgnu-tm" }
+
+struct B {
+ virtual void f() transaction_safe;
+};
+
+struct D3 : B
+{
+ void f() transaction_safe_dynamic override; // { dg-error "" "B::f() is transaction_safe" }
+};
--- /dev/null
+// Testcase from TM TS
+// { dg-options "-std=c++14 -fgnu-tm" }
+
+#include <iostream>
+
+struct B {
+ virtual void f() transaction_safe;
+ virtual ~B() transaction_safe_dynamic;
+};
+// pre-existing code
+struct D1 : B
+{
+ void f() override { } // ok
+ ~D1() override { } // ok
+};
+struct D2 : B
+{
+ void f() override { std::cout << "D2::f" << std::endl; } // { dg-error "" "transaction-safe f has transaction-unsafe definition" }
+ ~D2() override { std::cout << "~D2" << std::endl; } // ok
+};
+int main()
+{
+ D2 * d2 = new D2;
+ B * b2 = d2;
+ atomic_commit {
+ B b; // ok
+ D1 d1; // ok
+ B& b1 = d1;
+ D2 x; // { dg-error "" "destructor of D2 is not transaction-safe" }
+ b1.f(); // ok, calls D1::f()
+ delete b2; // undefined behavior: calls unsafe destructor of D2
+ }
+}
--- /dev/null
+// A goto or switch statement shall not be used to transfer control into a
+// synchronized or atomic block.
+// { dg-options "-fgnu-tm" }
+
+void f()
+{
+ static int i;
+ synchronized {
+ ++i;
+ inside: // { dg-message "" }
+ ++i;
+ }
+ goto inside; // { dg-message "" }
+
+ switch (i)
+ {
+ synchronized {
+ ++i;
+ case 42: // { dg-error "" }
+ ++i;
+ }
+ }
+}
--- /dev/null
+// Test that these aren't keywords without -fgnu-tm.
+
+int main()
+{
+ synchronized { } // { dg-error "not declared" }
+ atomic_noexcept { } // { dg-error "not declared" }
+ atomic_cancel { } // { dg-error "not declared" }
+ atomic_commit { } // { dg-error "not declared" }
+}
--- /dev/null
+// Test for lambda conversion.
+// { dg-options "-fgnu-tm -std=c++14" }
+
+void f(bool b)
+{
+ void (*p)() transaction_safe;
+
+ p = []() transaction_safe {};
+ p = []{}; // { dg-error "transaction_safe" }
+}
--- /dev/null
+// Test for lambda call.
+// { dg-options "-fgnu-tm -std=c++14" }
+
+void unsafe ();
+void f() transaction_safe
+{
+ []{}(); // OK, implicitly transaction-safe.
+ []{unsafe();}(); // { dg-error "unsafe" }
+}
--- /dev/null
+// { dg-options -fgnu-tm }
+
+#ifndef __cpp_transactional_memory
+#error __cpp_transactional_memory not defined
+#endif
--- /dev/null
+// Test for transaction_safe mangling.
+// { dg-options -fgnu-tm }
+
+// { dg-final { scan-assembler "_Z1fPDxFvvE" } }
+void f(void (*)() transaction_safe) {}
+
+// { dg-final { scan-assembler "_Z1fPDxFvvEPFvvE" } }
+void f(void (*)() transaction_safe, void (*)()) {}
+
+// { dg-final { scan-assembler "_Z1fPDxFvvES0_" } }
+void f(void (*)() transaction_safe, void (*)() transaction_safe) {}
+
+// { dg-final { scan-assembler "_Z1f1AIKDxFvvEE" } }
+template <class T> struct A { };
+void f(A<void () const transaction_safe>) { }
+
+// { dg-final { scan-assembler "_Z1fM1AIiEKDxFvvE" } }
+void f(void (A<int>::*)() const transaction_safe) { }
--- /dev/null
+// FIXME the TS says atomic_noexcept calls abort, not terminate.
+// { dg-options "-fgnu-tm" }
+
+void f()
+{
+ atomic_noexcept { throw; } // { dg-warning "terminate" }
+}
--- /dev/null
+// Function declarations that differ only in the presence or absence of a
+// tx-qualifier cannot be overloaded.
+// { dg-options "-fgnu-tm" }
+
+void f(); // { dg-message "" }
+void f() transaction_safe; // { dg-error "" }
--- /dev/null
+// 13.4p1: A function with type F is selected for the function type FT of the
+// target type required in the context if F (after possibly applying the
+// transaction-safety conversion (4.14 [conv.tx])) is identical to FT.
+// { dg-options "-fgnu-tm" }
+
+void f() transaction_safe;
+void f(int);
+
+void (*p)() = f;
--- /dev/null
+// Test for pretty-printing in diagnostics.
+// { dg-options "-fgnu-tm" }
+
+void f();
+void (*p)() transaction_safe = f; // { dg-error "void \\(\\*\\)\\(\\) transaction_safe" }
+
--- /dev/null
+// The inverse of a transaction-safety conversion cannot be performed with
+// static_cast.
+// { dg-options "-fgnu-tm" }
+
+typedef void (*TS)() transaction_safe;
+void f()
+{
+ static_cast<TS>(f); // { dg-error "static_cast" }
+}
--- /dev/null
+// Testcase from TM TS.
+// { dg-options -fgnu-tm }
+
+extern "C" int printf (const char *, ...);
+
+int f()
+{
+ static int i = 0;
+ synchronized {
+ printf("before %d\n", i);
+ ++i;
+ printf("after %d\n", i);
+ return i;
+ }
+}
--- /dev/null
+// { dg-do compile { target c++11 } }
+// { dg-options "-fgnu-tm -fdump-tree-optimized-asmname" }
+
+struct Tsafe
+{
+ void f() transaction_safe;
+};
+
+void Tsafe::f() { }
+
+struct Tcall
+{
+ [[optimize_for_synchronized]] void f();
+};
+
+void Tcall::f() { }
+
+// { dg-final { scan-tree-dump-times "_ZN5Tsafe1fEv" 1 "optimized" } }
+// { dg-final { scan-tree-dump-times "_ZN5Tcall1fEv" 1 "optimized" } }
+// { dg-final { scan-tree-dump-times "_ZGTtN5Tsafe1fEv" 1 "optimized" } }
+// { dg-final { scan-tree-dump-times "_ZGTtN5Tcall1fEv" 1 "optimized" } }
--- /dev/null
+// { dg-options "-fgnu-tm" }
+
+void fn(int) transaction_safe;
+void fn(double);
+
+template <class T> void f(T t) transaction_safe
+{
+ fn(t); // { dg-error "double" }
+}
+
+void g()
+{
+ f(42); // OK
+ f(3.14);
+}
--- /dev/null
+// Test for transaction-safety conversion in deduction.
+// { dg-options "-fgnu-tm" }
+
+void fn(int) transaction_safe;
+void fn();
+
+template <class T> void f(void(*)(T));
+template <class T> void f2(void(*)(T) transaction_safe);
+
+void g()
+{
+ f(fn);
+}
--- /dev/null
+// Test for deduction based on transaction_safe.
+// { dg-options "-fgnu-tm -std=c++11" }
+
+void f() transaction_safe;
+void g();
+
+template <class T> struct A;
+template <class R, class...Ps>
+struct A<R (Ps...) transaction_safe> { };
+
+A<decltype(f)> a;
+A<decltype(g)> b; // { dg-error "incomplete" }
--- /dev/null
+// Transaction-unsafe testcase from TM TS.
+// { dg-options -fgnu-tm }
+
+struct S {
+ virtual ~S();
+};
+int f() transaction_safe {
+ S s; // { dg-error "unsafe" "invocation of unsafe destructor" }
+}
+
+int g(int x) { // is transaction-safe
+ if (x <= 0)
+ return 0;
+ return x + g(x-1);
+}
--- /dev/null
+// Transaction-unsafe testcase from TM TS.
+// { dg-options -fgnu-tm }
+
+template<class T>
+void f(T) transaction_safe;
+template<>
+void f(bool); // not transaction-safe
+
+int g() transaction_safe
+{
+ f(42); // OK
+ f(true); // { dg-error "unsafe" }
+}
if [file exists "${gccpath}/librx/librx.a"] {
append flags "-L${gccpath}/librx "
}
+ if [file exists "${gccpath}/libitm/libitm.spec"] {
+ append flags "-B${gccpath}/libitm/ -L${gccpath}/libitm/.libs"
+ append ld_library_path ":${gccpath}/libitm/.libs"
+ }
append ld_library_path [gcc-set-multilib-library-path $GXX_UNDER_TEST]
} else {
global tool_root_dir
if (!a)
return 1;
}
+ if (lookup_attribute ("transaction_safe", CONST_CAST_TREE (a)))
+ return 0;
/* As some type combinations - like default calling-convention - might
be compatible, we have to call the target hook to get the final result. */
return targetm.comp_type_attributes (type1, type2);
#define COMPLETE_OR_UNBOUND_ARRAY_TYPE_P(NODE) \
(COMPLETE_TYPE_P (TREE_CODE (NODE) == ARRAY_TYPE ? TREE_TYPE (NODE) : (NODE)))
+#define FUNC_OR_METHOD_TYPE_P(NODE) \
+ (TREE_CODE (NODE) == FUNCTION_TYPE || TREE_CODE (NODE) == METHOD_TYPE)
+
/* Define many boolean fields that all tree nodes have. */
/* In VAR_DECL, PARM_DECL and RESULT_DECL nodes, nonzero means address
DEMANGLE_COMPONENT_PACK_EXPANSION,
/* A name with an ABI tag. */
DEMANGLE_COMPONENT_TAGGED_NAME,
+ /* A transaction-safe function type. */
+ DEMANGLE_COMPONENT_TRANSACTION_SAFE,
/* A cloned function. */
DEMANGLE_COMPONENT_CLONE
};
+2015-09-30 Jason Merrill <jason@redhat.com>
+
+ * cp-demangle.c (d_cv_qualifiers): Dx means transaction_safe.
+ (cplus_demangle_type): Let d_cv_qualifiers handle it.
+ (d_dump, d_make_comp, has_return_type, d_encoding)
+ (d_count_templates_scopes, d_print_comp_inner)
+ (d_print_mod_list, d_print_mod, d_print_function_type)
+ (is_ctor_or_dtor): Handle DEMANGLE_COMPONENT_TRANSACTION_SAFE.
+
2015-08-15 Ian Lance Taylor <iant@google.com>
* cp-demangle.c (d_abi_tags): Preserve di->last_name across any
case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
printf ("rvalue reference this\n");
break;
+ case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
+ printf ("transaction_safe this\n");
+ break;
case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
printf ("vendor type qualifier\n");
break;
case DEMANGLE_COMPONENT_RESTRICT_THIS:
case DEMANGLE_COMPONENT_VOLATILE_THIS:
case DEMANGLE_COMPONENT_CONST_THIS:
+ case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
case DEMANGLE_COMPONENT_REFERENCE_THIS:
case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
case DEMANGLE_COMPONENT_ARGLIST:
case DEMANGLE_COMPONENT_CONST_THIS:
case DEMANGLE_COMPONENT_REFERENCE_THIS:
case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
+ case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
return has_return_type (d_left (dc));
}
}
while (dc->type == DEMANGLE_COMPONENT_RESTRICT_THIS
|| dc->type == DEMANGLE_COMPONENT_VOLATILE_THIS
|| dc->type == DEMANGLE_COMPONENT_CONST_THIS
+ || dc->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE
|| dc->type == DEMANGLE_COMPONENT_REFERENCE_THIS
|| dc->type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS)
dc = d_left (dc);
while (dcr->type == DEMANGLE_COMPONENT_RESTRICT_THIS
|| dcr->type == DEMANGLE_COMPONENT_VOLATILE_THIS
|| dcr->type == DEMANGLE_COMPONENT_CONST_THIS
+ || dcr->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE
|| dcr->type == DEMANGLE_COMPONENT_REFERENCE_THIS
|| dcr->type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS)
dcr = d_left (dcr);
names. */
peek = d_peek_char (di);
- if (peek == 'r' || peek == 'V' || peek == 'K')
+ if (peek == 'r' || peek == 'V' || peek == 'K'
+ || (peek == 'D' && d_peek_next_char (di) == 'x'))
{
struct demangle_component **pret;
return ret;
}
-/* <CV-qualifiers> ::= [r] [V] [K] */
+/* <CV-qualifiers> ::= [r] [V] [K] [Dx] */
static struct demangle_component **
d_cv_qualifiers (struct d_info *di,
pstart = pret;
peek = d_peek_char (di);
- while (peek == 'r' || peek == 'V' || peek == 'K')
+ while (peek == 'r' || peek == 'V' || peek == 'K'
+ || (peek == 'D' && d_peek_next_char (di) == 'x'))
{
enum demangle_component_type t;
: DEMANGLE_COMPONENT_VOLATILE);
di->expansion += sizeof "volatile";
}
- else
+ else if (peek == 'K')
{
t = (member_fn
? DEMANGLE_COMPONENT_CONST_THIS
: DEMANGLE_COMPONENT_CONST);
di->expansion += sizeof "const";
}
+ else
+ {
+ t = DEMANGLE_COMPONENT_TRANSACTION_SAFE;
+ di->expansion += sizeof "transaction_safe";
+ d_advance (di, 1);
+ }
*pret = d_make_comp (di, t, NULL, NULL);
if (*pret == NULL)
return ret;
}
-/* <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] E */
+/* <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] [T] E */
static struct demangle_component *
d_function_type (struct d_info *di)
case DEMANGLE_COMPONENT_CONST_THIS:
case DEMANGLE_COMPONENT_REFERENCE_THIS:
case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
+ case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
case DEMANGLE_COMPONENT_POINTER:
case DEMANGLE_COMPONENT_COMPLEX:
&& typed_name->type != DEMANGLE_COMPONENT_VOLATILE_THIS
&& typed_name->type != DEMANGLE_COMPONENT_CONST_THIS
&& typed_name->type != DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS
+ && typed_name->type != DEMANGLE_COMPONENT_TRANSACTION_SAFE
&& typed_name->type != DEMANGLE_COMPONENT_REFERENCE_THIS)
break;
|| local_name->type == DEMANGLE_COMPONENT_VOLATILE_THIS
|| local_name->type == DEMANGLE_COMPONENT_CONST_THIS
|| local_name->type == DEMANGLE_COMPONENT_REFERENCE_THIS
+ || local_name->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE
|| (local_name->type
== DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS))
{
case DEMANGLE_COMPONENT_POINTER:
case DEMANGLE_COMPONENT_COMPLEX:
case DEMANGLE_COMPONENT_IMAGINARY:
+ case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
modifier:
{
/* We keep a list of modifiers on the stack. */
|| mods->mod->type == DEMANGLE_COMPONENT_VOLATILE_THIS
|| mods->mod->type == DEMANGLE_COMPONENT_CONST_THIS
|| mods->mod->type == DEMANGLE_COMPONENT_REFERENCE_THIS
+ || mods->mod->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE
|| (mods->mod->type
== DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS))))
{
|| dc->type == DEMANGLE_COMPONENT_VOLATILE_THIS
|| dc->type == DEMANGLE_COMPONENT_CONST_THIS
|| dc->type == DEMANGLE_COMPONENT_REFERENCE_THIS
+ || dc->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE
|| dc->type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS)
dc = d_left (dc);
case DEMANGLE_COMPONENT_CONST_THIS:
d_append_string (dpi, " const");
return;
+ case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
+ d_append_string (dpi, " transaction_safe");
+ return;
case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
d_append_char (dpi, ' ');
d_print_comp (dpi, options, d_right (mod));
case DEMANGLE_COMPONENT_CONST_THIS:
case DEMANGLE_COMPONENT_REFERENCE_THIS:
case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
+ case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
break;
default:
break;
case DEMANGLE_COMPONENT_CONST_THIS:
case DEMANGLE_COMPONENT_REFERENCE_THIS:
case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
+ case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
default:
dc = NULL;
break;
_ZNSt8ios_base7failureB5cxx11C1EPKcRKSt10error_code
std::ios_base::failure[abi:cxx11]::failure(char const*, std::error_code const&)
std::ios_base::failure[abi:cxx11]::failure
+--format=gnu-v3
+_Z1fPDxFvvES0_
+f(void (*)() transaction_safe, void (*)() transaction_safe)
__volatile_mask = 0x2,
__restrict_mask = 0x4,
__incomplete_mask = 0x8,
- __incomplete_class_mask = 0x10
+ __incomplete_class_mask = 0x10,
+ __transaction_safe_mask = 0x20
};
protected:
const __pbase_type_info *thrown_type =
static_cast <const __pbase_type_info *> (thr_type);
+
+ unsigned tflags = thrown_type->__flags;
+
+ bool throw_tx = (tflags & __transaction_safe_mask);
+ bool catch_tx = (__flags & __transaction_safe_mask);
+ if (throw_tx && !catch_tx)
+ /* Catch can perform a transaction-safety conversion. */
+ tflags &= ~__transaction_safe_mask;
+ if (catch_tx && !throw_tx)
+ /* But not the reverse. */
+ return false;
- if (thrown_type->__flags & ~__flags)
+ if (tflags & ~__flags)
// We're less qualified.
return false;