From: Yuxuan Chen Date: Tue, 21 Apr 2026 00:08:49 +0000 (-0400) Subject: c++: Add support for [[gnu::trivial_abi]] attribute [PR107187] X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1e89650aff94c20007f74949018f5d5150ea9ba7;p=thirdparty%2Fgcc.git c++: Add support for [[gnu::trivial_abi]] attribute [PR107187] Implement the trivial_abi attribute for GCC to fix ABI compatibility issues with Clang. Currently, GCC silently ignores this attribute, causing call convention mismatches when linking GCC-compiled code with Clang-compiled object files that use trivial_abi types. This attribute allows types to be treated as trivial for ABI purposes, enabling pass in registers instead of invisible references. The attribute is supported with `__attribute__((trivial_abi))` and `[[clang::trivial_abi]]` spellings. PR c++/107187 gcc/cp/ChangeLog: * cp-tree.h (has_trivial_abi_attribute): New function. (validate_trivial_abi_attribute): Declare. (classtype_has_non_deleted_copy_or_move_ctor): Declare. (cxx_clang_attribute_table): Declare. * tree.cc (handle_trivial_abi_attribute): New function. (handle_gnu_trivial_abi_attribute): New function. (classtype_has_trivial_abi): New function. (validate_trivial_abi_attribute): New function. (cxx_gnu_attributes): Add trivial_abi entry. (cxx_clang_attributes): New table for [[clang::trivial_abi]]. * class.cc (finish_struct_bits): Skip BLKmode for types with trivial_abi attribute. (classtype_has_non_deleted_copy_or_move_ctor): New function. (finish_struct_1): Call validate_trivial_abi_attribute before finish_struct_bits. * cp-objcp-common.h (cp_objcp_attribute_table): Register cxx_clang_attribute_table. * decl.cc (store_parm_decls): Register cleanups for trivial_abi parameters. gcc/ChangeLog: * doc/extend.texi: Document __attribute__((trivial_abi)). Signed-off-by: Yuxuan Chen Reviewed-by: Jason Merrill --- diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index 8beeb797a08..9f77ec68220 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -2420,8 +2420,9 @@ finish_struct_bits (tree t) mode to be BLKmode, and force its TREE_ADDRESSABLE bit to be nonzero. This will cause it to be passed by invisible reference and prevent it from being returned in a register. */ - if (type_has_nontrivial_copy_init (t) - || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)) + if (!has_trivial_abi_attribute (t) + && (type_has_nontrivial_copy_init (t) + || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))) { SET_DECL_MODE (TYPE_MAIN_DECL (t), BLKmode); SET_TYPE_MODE (t, BLKmode); @@ -6086,6 +6087,24 @@ classtype_has_non_deleted_move_ctor (tree t) return false; } +/* True iff T has a copy or move constructor that is not deleted. */ + +bool +classtype_has_non_deleted_copy_or_move_ctor (tree t) +{ + if (CLASSTYPE_LAZY_COPY_CTOR (t)) + lazily_declare_fn (sfk_copy_constructor, t); + if (CLASSTYPE_LAZY_MOVE_CTOR (t)) + lazily_declare_fn (sfk_move_constructor, t); + for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (t)); iter; ++iter) + { + tree fn = *iter; + if ((copy_fn_p (fn) || move_fn_p (fn)) && !DECL_DELETED_FN (fn)) + return true; + } + return false; +} + /* If T, a class, has a user-provided copy constructor, copy assignment operator, or destructor, returns that function. Otherwise, null. */ @@ -8085,6 +8104,8 @@ finish_struct_1 (tree t) } } + validate_trivial_abi_attribute (t); + finish_struct_bits (t); set_method_tm_attributes (t); diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h index 2999b36a5dd..ed29e65e4f3 100644 --- a/gcc/cp/cp-objcp-common.h +++ b/gcc/cp/cp-objcp-common.h @@ -127,6 +127,7 @@ static const scoped_attribute_specs *const cp_objcp_attribute_table[] = { &std_attribute_table, &cxx_gnu_attribute_table, + &cxx_clang_attribute_table, &c_common_gnu_attribute_table, &c_common_clang_attribute_table, &c_common_format_attribute_table, diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index ff8fcca72fe..8a4ac460059 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7444,6 +7444,7 @@ extern bool type_has_virtual_destructor (tree); extern bool type_has_non_deleted_trivial_default_ctor (tree); extern bool classtype_has_move_assign_or_move_ctor_p (tree, bool user_declared); extern bool classtype_has_non_deleted_move_ctor (tree); +extern bool classtype_has_non_deleted_copy_or_move_ctor (tree); extern tree classtype_has_depr_implicit_copy (tree); extern bool classtype_has_op (tree, tree_code); extern tree classtype_has_defaulted_op (tree, tree_code); @@ -8659,6 +8660,8 @@ extern bool std_layout_type_p (const_tree); extern bool trivial_type_p (const_tree); extern bool implicit_lifetime_type_p (tree); extern bool trivially_copyable_p (const_tree); +extern bool has_trivial_abi_attribute (tree); +extern void validate_trivial_abi_attribute (tree); extern bool type_has_unique_obj_representations (const_tree, bool = false); extern bool scalarish_type_p (const_tree); extern bool structural_type_p (tree, bool = false); @@ -8766,6 +8769,7 @@ extern bool is_dummy_object (const_tree); extern bool is_byte_access_type (tree); extern bool is_byte_access_type_not_plain_char (tree); extern const struct scoped_attribute_specs cxx_gnu_attribute_table; +extern const struct scoped_attribute_specs cxx_clang_attribute_table; extern const struct scoped_attribute_specs std_attribute_table; extern const struct scoped_attribute_specs internal_attribute_table; extern tree make_ptrmem_cst (tree, tree); diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 679a8007ca6..c3d589e8c23 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -20273,6 +20273,23 @@ store_parm_decls (tree current_function_parms) DECL_ARGUMENTS is not modified. */ current_binding_level->names = chainon (nonparms, DECL_ARGUMENTS (fndecl)); + /* Register cleanups for parameters with trivial_abi attribute, the cleanup + of which is the callee's responsibility. */ + for (tree parm = DECL_ARGUMENTS (fndecl); parm; parm = DECL_CHAIN (parm)) + { + if (TREE_CODE (parm) == PARM_DECL) + { + tree parm_type = TREE_TYPE (parm); + if (has_trivial_abi_attribute (parm_type)) + { + tree cleanup + = cxx_maybe_build_cleanup (parm, tf_warning_or_error); + if (cleanup && cleanup != error_mark_node) + finish_decl_cleanup (parm, cleanup); + } + } + } + if (use_eh_spec_block (current_function_decl)) current_eh_spec_block = begin_eh_spec_block (); } diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 26e12f848c6..c57587dbeee 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -48,6 +48,8 @@ static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *); static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *); static tree handle_no_dangling_attribute (tree *, tree, tree, int, bool *); static tree handle_annotation_attribute (tree *, tree, tree, int, bool *); +static tree handle_trivial_abi_attribute (tree *, tree, tree, int, bool *); +static tree handle_gnu_trivial_abi_attribute (tree *, tree, tree, int, bool *); /* If REF is an lvalue, returns the kind of lvalue that REF is. Otherwise, returns clk_none. */ @@ -5570,6 +5572,7 @@ handle_indeterminate_attribute (tree *node, tree name, tree, int, } /* Table of valid C++ attributes. */ +// clang-format off static const attribute_spec cxx_gnu_attributes[] = { /* { name, min_len, max_len, decl_req, type_req, fn_type_req, @@ -5580,7 +5583,10 @@ static const attribute_spec cxx_gnu_attributes[] = handle_abi_tag_attribute, NULL }, { "no_dangling", 0, 1, false, true, false, false, handle_no_dangling_attribute, NULL }, + { "trivial_abi", 0, 0, false, true, false, true, + handle_gnu_trivial_abi_attribute, NULL }, }; +// clang-format on const scoped_attribute_specs cxx_gnu_attribute_table = { @@ -5631,6 +5637,20 @@ const scoped_attribute_specs internal_attribute_table = "internal ", { internal_attributes } }; +/* Table of C++ attributes also recognized in the clang:: namespace. */ +// clang-format off +static const attribute_spec cxx_clang_attributes[] = +{ + { "trivial_abi", 0, 0, false, true, false, true, + handle_trivial_abi_attribute, NULL }, +}; + +const scoped_attribute_specs cxx_clang_attribute_table = +{ + "clang", { cxx_clang_attributes } +}; +// clang-format on + /* Handle an "init_priority" attribute; arguments as in struct attribute_spec.handler. */ static tree @@ -5982,6 +6002,142 @@ handle_annotation_attribute (tree *node, tree ARG_UNUSED (name), return NULL_TREE; } +/* Handle a "trivial_abi" attribute applied via [[gnu::trivial_abi]]. + We reject that spelling; suggest [[clang::trivial_abi]] or + __attribute__((trivial_abi)) instead. */ + +static tree +handle_gnu_trivial_abi_attribute (tree *node, tree name, tree args, int flags, + bool *no_add_attrs) +{ + if (flags & ATTR_FLAG_CXX11) + { + warning (OPT_Wattributes, + "%<[[gnu::trivial_abi]]%> is not supported; use " + "%<[[clang::trivial_abi]]%> or " + "%<__attribute__((trivial_abi))%> instead"); + *no_add_attrs = true; + return NULL_TREE; + } + return handle_trivial_abi_attribute (node, name, args, flags, no_add_attrs); +} + +/* Handle a "trivial_abi" attribute. */ + +static tree +handle_trivial_abi_attribute (tree *node, tree name, tree, int, + bool *no_add_attrs) +{ + tree type = *node; + + /* Only allow on class types (struct, class, union) */ + if (TREE_CODE (type) != RECORD_TYPE && TREE_CODE (type) != UNION_TYPE) + { + warning (OPT_Wattributes, "%qE attribute only applies to classes", name); + *no_add_attrs = true; + return NULL_TREE; + } + + return NULL_TREE; +} + +/* Return true if TYPE has the trivial_abi attribute. */ + +bool +has_trivial_abi_attribute (tree type) +{ + if (type == NULL_TREE || !TYPE_P (type)) + return false; + return lookup_attribute ("trivial_abi", TYPE_ATTRIBUTES (type)); +} + +/* Validate the trivial_abi attribute on a completed class type. + Called from finish_struct after the class is complete. */ + +void +validate_trivial_abi_attribute (tree type) +{ + if (!has_trivial_abi_attribute (type)) + return; + + gcc_assert (COMPLETE_TYPE_P (type)); + + /* Check for virtual bases. */ + if (CLASSTYPE_VBASECLASSES (type)) + { + if (warning (OPT_Wattributes, "% cannot be applied to %qT", + type)) + inform (input_location, "has a virtual base"); + TYPE_ATTRIBUTES (type) + = remove_attribute ("trivial_abi", TYPE_ATTRIBUTES (type)); + return; + } + + /* Check for virtual member functions. */ + if (TYPE_POLYMORPHIC_P (type)) + { + if (warning (OPT_Wattributes, "% cannot be applied to %qT", + type)) + inform (input_location, "is polymorphic"); + TYPE_ATTRIBUTES (type) + = remove_attribute ("trivial_abi", TYPE_ATTRIBUTES (type)); + return; + } + + /* Check for non-trivial base classes. */ + if (TYPE_BINFO (type)) + { + unsigned int n_bases = BINFO_N_BASE_BINFOS (TYPE_BINFO (type)); + for (unsigned int i = 0; i < n_bases; ++i) + { + tree base_binfo = BINFO_BASE_BINFO (TYPE_BINFO (type), i); + tree base_type = BINFO_TYPE (base_binfo); + + if (TREE_ADDRESSABLE (base_type)) + { + if (warning (OPT_Wattributes, + "% cannot be applied to %qT", type)) + inform (input_location, "has a non-trivial base class %qT", + base_type); + TYPE_ATTRIBUTES (type) + = remove_attribute ("trivial_abi", TYPE_ATTRIBUTES (type)); + return; + } + } + } + + /* Check for non-trivial member types. */ + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL && !DECL_ARTIFICIAL (field)) + { + tree field_type = strip_array_types (TREE_TYPE (field)); + + if (CLASS_TYPE_P (field_type) && TREE_ADDRESSABLE (field_type)) + { + if (warning (OPT_Wattributes, + "% cannot be applied to %qT", type)) + inform (input_location, "has a non-static data member " + "of non-trivial type %qT", field_type); + TYPE_ATTRIBUTES (type) + = remove_attribute ("trivial_abi", TYPE_ATTRIBUTES (type)); + return; + } + } + } + + /* Check that not all copy/move constructors are deleted. */ + if (!classtype_has_non_deleted_copy_or_move_ctor (type)) + { + if (warning (OPT_Wattributes, "% cannot be applied to %qT", + type)) + inform (input_location, + "copy constructors and move constructors are all deleted"); + TYPE_ATTRIBUTES (type) + = remove_attribute ("trivial_abi", TYPE_ATTRIBUTES (type)); + return; + } +} /* Return a new PTRMEM_CST of the indicated TYPE. The MEMBER is the thing pointed to by the constant. */ diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 47e0865c676..7de7eab3aa5 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -31209,6 +31209,76 @@ these attributes on a type has the effect of propagating it to every member function of the type, including implicit special member functions. @xref{Common Attributes}. +@cindex @code{trivial_abi} type attribute +@item trivial_abi + +The @code{trivial_abi} attribute can be applied to a C++ class, struct, +or union. It instructs the compiler to pass and return the type using +the C ABI for the underlying type when the type would otherwise be +considered non-trivial for the purposes of calls. + +The attribute can be specified either as @code{__attribute__ +((trivial_abi))} or @code{[[clang::trivial_abi]]} for compatibility +with the original implementation in Clang. To avoid portability +complications, @code{[[gnu::trivial_abi]]} is ignored with a warning. + +A class annotated with @code{trivial_abi} can have non-trivial destructors +or copy/move constructors without automatically becoming non-trivial for +the purposes of calls. For example: + +@smallexample +// A is trivial for the purposes of calls despite the user-provided +// special member functions. +struct __attribute__ ((trivial_abi)) A @{ + ~A(); + A(const A &); + A(A &&); + int x; +@}; + +// B is trivial for the purposes of calls because A is. +struct B @{ + A a; +@}; +@end smallexample + +If a type is trivial for the purposes of calls, has a non-trivial destructor, +and is passed as an argument by value, the convention is that the callee will +destroy the object before returning. The lifetime of the copy of the parameter +in the caller ends without a destructor call when the call begins. + +When a type marked with @code{trivial_abi} is used as a function argument, +the compiler may omit the call to the copy constructor. Thus, side effects +of the copy constructor are potentially not performed. For example, objects +that contain pointers to themselves or otherwise depend on their address +(or the address of their subobjects) should not be declared with +@code{trivial_abi}. + +Attribute @code{trivial_abi} has no effect in the following cases: + +@itemize @bullet +@item +The class has a virtual base or virtual member function. + +@item +Copy constructors and move constructors of the class are all deleted. + +@item +The class has a base class that is non-trivial for the purposes of calls. + +@item +The class has a non-static data member whose type is non-trivial for the +purposes of calls, which includes: + +@itemize @bullet +@item +classes that are non-trivial for the purposes of calls + +@item +arrays of any of the above +@end itemize +@end itemize + @end table @node Function Multiversioning diff --git a/gcc/testsuite/g++.dg/abi/invisiref3.C b/gcc/testsuite/g++.dg/abi/invisiref3.C new file mode 100644 index 00000000000..dbb817d4e53 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/invisiref3.C @@ -0,0 +1,28 @@ +// PR c++/107187 +// { dg-do compile { target c++11 } } +// { dg-additional-options "-fdump-tree-gimple" } +// { dg-final { scan-tree-dump-not "struct S &" "gimple" } } +// { dg-final { scan-tree-dump "S::~S" "gimple" } } + +struct __attribute__ ((__trivial_abi__)) S +{ + S () {} + ~S (); + int i; +}; + +int +foo (S s) +{ + return s.i; +} + +S getS (); + +void +bar () +{ + foo (getS ()); +} + +static_assert (!__is_trivially_copyable (S), ""); diff --git a/gcc/testsuite/g++.dg/abi/invisiref3a.C b/gcc/testsuite/g++.dg/abi/invisiref3a.C new file mode 100644 index 00000000000..427853fabe2 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/invisiref3a.C @@ -0,0 +1,26 @@ +// PR c++/107187 +// { dg-do compile { target c++11 } } +// { dg-additional-options "-Wabi=10 -fdump-tree-gimple" } +// { dg-final { scan-tree-dump "struct S &" "gimple" } } + +struct S +{ + ~S (); + int i; +}; + +int +foo (S s) +{ + return s.i; +} + +S getS (); + +void +bar () +{ + foo (getS ()); +} + +static_assert (!__is_trivially_copyable (S), ""); diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi1.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi1.C new file mode 100644 index 00000000000..deda9646d85 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi1.C @@ -0,0 +1,41 @@ +// { dg-do compile { target c++11 } } +// Test basic functionality of [[clang::trivial_abi]] attribute + +struct [[clang::trivial_abi]] TrivialAbi1 +{ + int x; + ~TrivialAbi1 () { + } // Non-trivial destructor, but should be allowed with trivial_abi +}; + +class [[clang::trivial_abi]] TrivialAbi2 +{ + int y; + +public: + TrivialAbi2 (const TrivialAbi2 &) { + } // Non-trivial copy constructor, but should be allowed + ~TrivialAbi2 () {} +}; + +// Test that the attribute is recognized and the types compile successfully + +// Test basic usage in function parameters and return values +TrivialAbi1 +func1 (TrivialAbi1 param) +{ + return param; +} + +TrivialAbi2 +func2 (TrivialAbi2 param) +{ + return param; +} + +union [[clang::trivial_abi]] TrivialUnion +{ + int a; + float b; + ~TrivialUnion () {} +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi2.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi2.C new file mode 100644 index 00000000000..76d47a5dc71 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi2.C @@ -0,0 +1,70 @@ +// { dg-do compile { target c++11 } } +// Test error cases and restrictions for __attribute__((trivial_abi)) attribute + +// Test: attribute rejected on non-class types +int __attribute__ ((trivial_abi)) +x; // { dg-warning "'trivial_abi' attribute only applies to classes" } +typedef int __attribute__ ((trivial_abi)) +int_t; // { dg-warning "'trivial_abi' attribute only applies to classes" } + +// Test: attribute rejected on types with virtual functions +struct __attribute__ ((trivial_abi)) +WithVirtual // { dg-warning "'trivial_abi' cannot be applied to 'WithVirtual'" } +{ + virtual void f () {} +}; + +// Test: attribute rejected on types with virtual bases +struct Base +{ +}; + +struct __attribute__ ((trivial_abi)) +WithVirtualBase // { dg-warning "'trivial_abi' cannot be applied" } + : virtual Base +{ + int x; +}; + +// Test: attribute rejected when all copy/move constructors are deleted +struct __attribute__ ((trivial_abi)) +AllDeleted // { dg-warning "'trivial_abi' cannot be applied to 'AllDeleted'" } +{ + AllDeleted (const AllDeleted &) = delete; + AllDeleted (AllDeleted &&) = delete; + int x; +}; + +// Test: attribute rejected on types with non-trivial base classes +struct NonTrivialBase +{ + NonTrivialBase (const NonTrivialBase &) {} // Non-trivial copy +}; + +struct __attribute__ ((trivial_abi)) +WithNonTrivialBase // { dg-warning "'trivial_abi' cannot be applied" } + : NonTrivialBase +{ + int x; +}; + +// Test: attribute rejected on types with non-trivial member types +struct NonTrivialMember +{ + NonTrivialMember (const NonTrivialMember &) {} // Non-trivial copy +}; + +struct __attribute__ ((trivial_abi)) +WithNonTrivialMember // { dg-warning "'trivial_abi' cannot be applied" } +{ + NonTrivialMember member; + int x; +}; + +// Test: attribute rejected on array members of non-trivial types +struct __attribute__ ((trivial_abi)) +WithNonTrivialArray // { dg-warning "'trivial_abi' cannot be applied" } +{ + NonTrivialMember arr[5]; + int x; +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi3.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi3.C new file mode 100644 index 00000000000..8be389b79b6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi3.C @@ -0,0 +1,58 @@ +// { dg-do compile { target c++11 } } +// Test ABI behavior of [[clang::trivial_abi]] attribute +// This test verifies that types with trivial_abi are passed by value +// instead of by invisible reference, even with non-trivial constructors + +struct [[clang::trivial_abi]] TrivialAbiType +{ + int data; + TrivialAbiType () : data (0) {} + TrivialAbiType (const TrivialAbiType &other) : data (other.data) {} + TrivialAbiType (TrivialAbiType &&other) : data (other.data) {} + ~TrivialAbiType () {} +}; + +struct NonTrivialType +{ + int data; + NonTrivialType () : data (0) {} + NonTrivialType (const NonTrivialType &other) : data (other.data) {} + NonTrivialType (NonTrivialType &&other) : data (other.data) {} + ~NonTrivialType () {} +}; + +// Test that trivial_abi types are considered trivially relocatable +// (ABI behavior is tested separately in invisiref3.C) + +// Function templates to test parameter passing +template +void +test_param_passing (T param) +{ + // Function body doesn't matter for ABI test +} + +// Test usage +void +test_functions () +{ + TrivialAbiType trivial_obj; + NonTrivialType non_trivial_obj; + + // Both should compile, but with different ABI behavior + test_param_passing (trivial_obj); // Should pass by value + test_param_passing (non_trivial_obj); // Should pass by invisible reference +} + +// Test return values +TrivialAbiType +return_trivial () +{ + return TrivialAbiType{}; +} + +NonTrivialType +return_non_trivial () +{ + return NonTrivialType{}; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi4.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi4.C new file mode 100644 index 00000000000..b9771ee45c7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi4.C @@ -0,0 +1,57 @@ +// { dg-do compile { target c++11 } } +// Test edge cases and inheritance with [[clang::trivial_abi]] attribute + +// Test: Derived classes with trivial_abi base classes +struct [[clang::trivial_abi]] TrivialBase +{ + int x; + ~TrivialBase () {} +}; + +// This should be allowed - base class has trivial_abi +struct [[clang::trivial_abi]] DerivedFromTrivial : TrivialBase +{ + int y; + ~DerivedFromTrivial () {} +}; + +// Test: Multiple inheritance with all trivial bases +struct [[clang::trivial_abi]] TrivialBase2 +{ + int z; + ~TrivialBase2 () {} +}; + +struct [[clang::trivial_abi]] MultipleInheritance : TrivialBase, TrivialBase2 +{ + int w; + ~MultipleInheritance () {} +}; + +// Test: Empty class with trivial_abi +struct [[clang::trivial_abi]] EmptyTrivialAbi +{ + ~EmptyTrivialAbi () {} +}; + +// Test: Class with only trivial members +struct [[clang::trivial_abi]] OnlyTrivialMembers +{ + int a; + float b; + char c[10]; + ~OnlyTrivialMembers () {} +}; + +// Test: Template class with trivial_abi +template struct [[clang::trivial_abi]] TemplateTrivialAbi +{ + T data; + ~TemplateTrivialAbi () {} +}; + +// This should work for scalar types +using IntTrivialAbi = TemplateTrivialAbi; + +// Test with another trivial_abi type as template parameter +using NestedTrivialAbi = TemplateTrivialAbi; diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi5.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi5.C new file mode 100644 index 00000000000..27abe9c8abc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi5.C @@ -0,0 +1,60 @@ +// { dg-do compile { target c++11 } } +// Test compatibility of [[clang::trivial_abi]] with other GCC features + +// Test: Compatibility with other attributes +struct [[clang::trivial_abi]] [[gnu::packed]] TrivialAbiPacked +{ + char a; + int b; + ~TrivialAbiPacked () {} +}; + +// Test: Compatibility with alignas +struct [[clang::trivial_abi]] alignas (16) TrivialAbiAligned +{ + int x; + ~TrivialAbiAligned () {} +}; + +// Test: Forward declaration and later definition +struct TrivialForward; +struct [[clang::trivial_abi]] TrivialForward +{ + int x; + ~TrivialForward () {} +}; + +// Test: typedef and using declarations +using TrivialAlias = TrivialAbiPacked; +typedef TrivialAbiAligned TrivialTypedef; + +// Test: Local classes +void +test_local_class () +{ + struct [[clang::trivial_abi]] LocalTrivial + { + int x; + ~LocalTrivial () {} + }; +} + +// Test: Named nested structs with trivial_abi +struct ContainerClass +{ + struct [[clang::trivial_abi]] NestedStruct + { + int nested_member; + ~NestedStruct () {} + }; +}; + +// Test: Nested classes +struct Outer +{ + struct [[clang::trivial_abi]] Nested + { + int value; + ~Nested () {} + }; +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi6.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi6.C new file mode 100644 index 00000000000..2f431d63e68 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi6.C @@ -0,0 +1,22 @@ +// { dg-do compile { target c++11 } } +// Test that [[gnu::trivial_abi]] is rejected + +struct [[gnu::trivial_abi]] S0 // { dg-warning "'..gnu::trivial_abi..' is not supported" } +{ + int a; + ~S0 () {} +}; + +// __attribute__((trivial_abi)) should still work +struct __attribute__ ((trivial_abi)) S1 +{ + int a; + ~S1 () {} +}; + +// [[clang::trivial_abi]] should still work +struct [[clang::trivial_abi]] S2 +{ + int a; + ~S2 () {} +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi_syntax.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi_syntax.C new file mode 100644 index 00000000000..38e737f7629 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi_syntax.C @@ -0,0 +1,150 @@ +// { dg-do compile { target c++11 } } +// Test comprehensive trivial_abi attribute behavior +// Based on clang's attr-trivial-abi.cpp test + +void __attribute__ ((trivial_abi)) +foo (); // { dg-warning "'trivial_abi' attribute only applies to classes" } + +// Should not crash. +template class __attribute__ ((trivial_abi)) a +{ + a (a &&); +}; + +struct [[clang::trivial_abi]] S0 +{ + int a; +}; + +struct __attribute__ ((trivial_abi)) S1 +{ + int a; +}; + +struct __attribute__ ((trivial_abi)) +S3 // { dg-warning "'trivial_abi' cannot be applied to 'S3'" } +{ // { dg-message "is polymorphic" "" { target *-*-* } .-1 } + virtual void m (); +}; + +struct S3_2 // { dg-warning "'trivial_abi' cannot be applied to 'S3_2'" } +{ // { dg-message "is polymorphic" "" { target *-*-* } .-1 } + virtual void m (); +} __attribute__ ((trivial_abi)); + +// clang-format off +struct __attribute__ ((trivial_abi)) +S3_3 // { dg-warning "'trivial_abi' cannot be applied to 'S3_3'" } +{ // { dg-message "has a non-static data member of non-trivial type 'S3_2'" "" { target *-*-* } .-1 } + S3_3 (S3_3 &&); + S3_2 s32; +}; +// clang-format on + +// Diagnose invalid trivial_abi even when the type is templated because it has a +// non-trivial field. Note: GCC only diagnoses this at template instantiation, +// unlike Clang which diagnoses it earlier. +template struct __attribute__ ((trivial_abi)) S3_4 +{ + S3_4 (S3_4 &&); + S3_2 s32; +}; + +struct S4 +{ + int a; +}; +static_assert (__is_trivially_copyable (S4), ""); + +struct __attribute__ (( + trivial_abi)) S5 // { dg-warning "'trivial_abi' cannot be applied to 'S5'" } + : public virtual S4 +// { dg-message "has a virtual base" "" { target *-*-* } .-2 } +{ +}; + +struct __attribute__ ((trivial_abi)) S9 : public S4 +{ +}; + +struct __attribute__ ((trivial_abi (1))) +S8 // { dg-error "wrong number of arguments" } +{ // { dg-message "expected 0, found 1" "" { target *-*-* } .-1 } + int a; +}; + +// Do not warn about deleted ctors when 'trivial_abi' is used to annotate a +// template class. +template struct __attribute__ ((trivial_abi)) S10 +{ + T p; +}; + +S10 p1; + +template struct S14 +{ + T a; +}; + +template struct __attribute__ ((trivial_abi)) S15 : S14 +{ +}; + +S15 s15; + +template struct __attribute__ ((trivial_abi)) S16 +{ + S14 a; +}; + +S16 s16; + +template struct __attribute__ ((trivial_abi)) S17 +{ +}; + +S17 s17; + +// clang-format off +namespace deletedCopyMoveConstructor { +struct __attribute__ ((trivial_abi)) +CopyMoveDeleted // { dg-warning "cannot be applied" } +{ // { dg-message "copy constructors and move constructors are all deleted" "" { target *-*-* } .-1 } + CopyMoveDeleted (const CopyMoveDeleted &) = delete; + CopyMoveDeleted (CopyMoveDeleted &&) = delete; +}; + +struct __attribute__ ((trivial_abi)) S18 // { dg-warning "cannot be applied" } +{ // { dg-message "has a non-static data member of non-trivial type 'deletedCopyMoveConstructor::CopyMoveDeleted'" "" { target *-*-* } .-1 } + CopyMoveDeleted a; +}; +// clang-format on + +struct __attribute__ ((trivial_abi)) CopyDeleted +{ + CopyDeleted (const CopyDeleted &) = delete; + CopyDeleted (CopyDeleted &&) = default; +}; + +struct __attribute__ ((trivial_abi)) MoveDeleted +{ + MoveDeleted (const MoveDeleted &) = default; + MoveDeleted (MoveDeleted &&) = delete; +}; + +// clang-format off +struct __attribute__ ((trivial_abi)) S19 // { dg-warning "cannot be applied" } +{ // { dg-message "copy constructors and move constructors are all deleted" "" { target *-*-* } .-1 } + CopyDeleted a; + MoveDeleted b; +}; +// clang-format on + +// This is fine since the move constructor isn't deleted. +struct __attribute__ ((trivial_abi)) S20 +{ + int &&a; // a member of rvalue reference type deletes the copy constructor. +}; + +} // namespace deletedCopyMoveConstructor