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.

        PR c++/107187

V3 changes from v2:
    - Handling cases where bases can be dependent.
    - gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi_syntax.C now uses the same
      test as Clang to ensure no difference in semantics.

V2 changes from v1:
    - Addressing code review comments.
    - Registered cleanup in callee for trivial abi. Added test
      `gcc/testsuite/g++.dg/abi/invisiref3a.C`
    - Added doc in `gcc/doc/extend.texi`. Pretty much the same as in
      Clang's attributes documentation.
From 1ed8f7b49c1f676a4f8b1b331ff7eac140ebaa4b Mon Sep 17 00:00:00 2001
From: Yuxuan Chen <[email protected]>
Date: Tue, 7 Oct 2025 14:17:35 -0700
Subject: [PATCH] c++: Add support for [[gnu::trivial_abi]] attribute
 [PR107187]

Changes from v2:
- Handling cases where bases can be dependent.
- gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi_syntax.C now uses the same
  test as Clang to ensure no difference in semantics.

Changes from v1:
- Addressing code review comments.
- Registered cleanup in callee for trivial abi. Added test
  `gcc/testsuite/g++.dg/abi/invisiref3a.C`
- Added doc in `gcc/doc/extend.texi`. Pretty much the same as in
  Clang's attributes documentation.

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.

    PR c++/107187

gcc/cp/ChangeLog:

    * cp-tree.h (struct lang_type): Add has_trivial_abi flag.
    * tree.cc (trivially_relocatable_type_p): Check for
    trivial_abi attribute.
    (handle_trivial_abi_attribute): Implement attribute validation.
    (cxx_gnu_attributes): Add trivial_abi entry.
    * class.cc (finish_struct_bits): Respect trivial_for_calls_p.
    * decl.cc (store_parm_decls): Register cleanups for trivial_abi
    parameters.
    * pt.cc (instantiate_class_template): Propagate
    CLASSTYPE_HAS_TRIVIAL_ABI in template instantiation.
    * module.cc (trees_out::lang_type_bools, trees_in::lang_type_bools):
    Serialize has_trivial_abi.

Signed-off-by: Yuxuan Chen <[email protected]>
---
 gcc/cp/ChangeLog                              |  16 ++
 gcc/cp/class.cc                               |  25 ++-
 gcc/cp/cp-tree.h                              |  12 +-
 gcc/cp/decl.cc                                |  17 ++
 gcc/cp/module.cc                              |   2 +
 gcc/cp/pt.cc                                  |   1 +
 gcc/cp/tree.cc                                | 143 +++++++++++++++-
 gcc/doc/extend.texi                           |  69 ++++++++
 gcc/testsuite/g++.dg/abi/invisiref3.C         |  28 +++
 gcc/testsuite/g++.dg/abi/invisiref3a.C        |  26 +++
 .../g++.dg/cpp0x/attr-trivial_abi1.C          |  48 ++++++
 .../g++.dg/cpp0x/attr-trivial_abi2.C          |  70 ++++++++
 .../g++.dg/cpp0x/attr-trivial_abi3.C          |  61 +++++++
 .../g++.dg/cpp0x/attr-trivial_abi4.C          |  75 ++++++++
 .../g++.dg/cpp0x/attr-trivial_abi5.C          |  83 +++++++++
 .../g++.dg/cpp0x/attr-trivial_abi_syntax.C    | 161 ++++++++++++++++++
 16 files changed, 826 insertions(+), 11 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/abi/invisiref3.C
 create mode 100644 gcc/testsuite/g++.dg/abi/invisiref3a.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi4.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi5.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi_syntax.C

diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 95b471d4984..e18c0812f56 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,19 @@
+2025-11-05  Yuxuan Chen  <[email protected]>
+
+	PR c++/107187
+	* cp-tree.h (struct lang_type): Add has_trivial_abi flag.
+	* tree.cc (trivially_relocatable_type_p): Check for
+	trivial_abi attribute.
+	(handle_trivial_abi_attribute): Implement attribute validation.
+	(cxx_gnu_attributes): Add trivial_abi entry.
+	* class.cc (finish_struct_bits): Respect trivial_for_calls_p.
+	* decl.cc (store_parm_decls): Register cleanups for trivial_abi
+	parameters.
+	* pt.cc (instantiate_class_template): Propagate
+	CLASSTYPE_HAS_TRIVIAL_ABI in template instantiation.
+	* module.cc (trees_out::lang_type_bools, trees_in::lang_type_bools):
+	Serialize has_trivial_abi.
+
 2025-11-05  Nathaniel Shead  <[email protected]>
 
 	PR c++/121574
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index b56cc518a11..7903ddb4f33 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -2415,8 +2415,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 (!trivial_for_calls_p (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);
@@ -6072,6 +6073,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.  */
 
@@ -8296,6 +8315,8 @@ finish_struct (tree t, tree attributes)
     finish_struct_1 (t);
   /* COMPLETE_TYPE_P is now true.  */
 
+  validate_trivial_abi_attribute (t);
+
   maybe_warn_about_overly_private_class (t);
 
   if (is_std_init_list (t))
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 4f077e70151..cb40bff0746 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2529,6 +2529,8 @@ struct GTY(()) lang_type {
   bool replaceable : 1;
   bool replaceable_computed : 1;
 
+  bool has_trivial_abi : 1;
+
   /* When adding a flag here, consider whether or not it ought to
      apply to a template instance if it applies to the template.  If
      so, make sure to copy it in instantiate_class_template!
@@ -2538,7 +2540,7 @@ struct GTY(()) lang_type {
   /* There are some bits left to fill out a 32-bit word.  Keep track
      of this by updating the size of this bitfield whenever you add or
      remove a flag.  */
-  unsigned dummy : 30;
+  unsigned dummy : 29;
 
   tree primary_base;
   vec<tree_pair_s, va_gc> *vcall_indices;
@@ -2908,6 +2910,11 @@ struct GTY(()) lang_type {
    already.  */
 #define CLASSTYPE_REPLACEABLE_COMPUTED(NODE) \
   (LANG_TYPE_CLASS_CHECK (NODE)->replaceable_computed)
+
+/* True if the class is trivial for the purpose of calls.  */
+#define CLASSTYPE_HAS_TRIVIAL_ABI(NODE)                                        \
+  (LANG_TYPE_CLASS_CHECK (NODE)->has_trivial_abi)
+
 
 /* Additional macros for inheritance information.  */
 
@@ -7203,6 +7210,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);
@@ -8380,6 +8388,8 @@ extern bool trivially_relocatable_type_p	(tree);
 extern bool replaceable_type_p			(tree);
 extern bool implicit_lifetime_type_p		(tree);
 extern bool trivially_copyable_p		(const_tree);
+extern bool trivial_for_calls_p (const_tree);
+extern void validate_trivial_abi_attribute (tree);
 extern bool type_has_unique_obj_representations (const_tree);
 extern bool scalarish_type_p			(const_tree);
 extern bool structural_type_p			(tree, bool = false);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 751ba40fc7f..2863e3b997f 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -19966,6 +19966,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 (CLASS_TYPE_P (parm_type) && trivial_for_calls_p (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/module.cc b/gcc/cp/module.cc
index 0610ee5f65a..adadd9e05b1 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -6240,6 +6240,7 @@ trees_out::lang_type_bools (tree t, bits_out& bits)
 
   WB (lang->replaceable);
   WB (lang->replaceable_computed);
+  WB (lang->has_trivial_abi);
 #undef WB
 }
 
@@ -6318,6 +6319,7 @@ trees_in::lang_type_bools (tree t, bits_in& bits)
 
   RB (lang->replaceable);
   RB (lang->replaceable_computed);
+  RB (lang->has_trivial_abi);
 #undef RB
   return !get_overrun ();
 }
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index c233bb95cad..6b7d59cc4c5 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -12718,6 +12718,7 @@ instantiate_class_template (tree type)
 	= CLASSTYPE_REPLACEABLE_BIT (pattern);
       CLASSTYPE_REPLACEABLE_COMPUTED (type)
 	= CLASSTYPE_REPLACEABLE_COMPUTED (pattern);
+      CLASSTYPE_HAS_TRIVIAL_ABI (type) = CLASSTYPE_HAS_TRIVIAL_ABI (pattern);
     }
 
   pbinfo = TYPE_BINFO (pattern);
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 3edb74a256f..d2daf8da681 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -48,6 +48,7 @@ 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_contract_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_dangling_attribute (tree *, tree, tree, int, bool *);
+static tree handle_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.  */
@@ -4810,6 +4811,17 @@ trivial_type_p (const_tree t)
     return scalarish_type_p (t);
 }
 
+/* Returns 1 iff type T is trivial for the purpose of calls.
+   This includes types that are otherwise trivial, or types
+   that have the trivial_abi attribute.  */
+
+bool
+trivial_for_calls_p (const_tree t)
+{
+  t = strip_array_types (CONST_CAST_TREE (t));
+  return CLASSTYPE_HAS_TRIVIAL_ABI (t);
+}
+
 /* Returns 1 iff type T is a default-movable type, as defined in
    [class.prop].  */
 
@@ -4937,6 +4949,14 @@ trivially_relocatable_type_p (tree t)
   if (!COMPLETE_TYPE_P (t))
     return false;
 
+  /* trivial_abi attribute makes the class trivially relocatable.  */
+  if (CLASSTYPE_HAS_TRIVIAL_ABI (t))
+    {
+      CLASSTYPE_TRIVIALLY_RELOCATABLE_BIT (t) = 1;
+      CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED (t) = 1;
+      return true;
+    }
+
   if (!CLASSTYPE_TRIVIALLY_RELOCATABLE_BIT (t)
       && !union_with_no_declared_special_member_fns (t)
       && !default_movable_type_p (t))
@@ -5659,16 +5679,16 @@ handle_indeterminate_attribute (tree *node, tree name, tree, int,
 }
 
 /* Table of valid C++ attributes.  */
-static const attribute_spec cxx_gnu_attributes[] =
-{
+static const attribute_spec cxx_gnu_attributes[] = {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
-  { "init_priority",  1, 1, true,  false, false, false,
-    handle_init_priority_attribute, NULL },
-  { "abi_tag", 1, -1, false, false, false, true,
-    handle_abi_tag_attribute, NULL },
-  { "no_dangling", 0, 1, false, true, false, false,
-    handle_no_dangling_attribute, NULL },
+  {"init_priority", 1, 1, true, false, false, false,
+   handle_init_priority_attribute, NULL},
+  {"abi_tag", 1, -1, false, false, false, true, 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_trivial_abi_attribute,
+   NULL},
 };
 
 const scoped_attribute_specs cxx_gnu_attribute_table =
@@ -6002,6 +6022,113 @@ handle_no_dangling_attribute (tree *node, tree name, tree args, int,
   return NULL_TREE;
 }
 
+/* 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;
+    }
+
+  /* Mark the type with the attribute. Validation will happen when the
+     type is complete in finish_struct.  */
+  if (CLASS_TYPE_P (type) && LANG_TYPE_CLASS_CHECK (type))
+    CLASSTYPE_HAS_TRIVIAL_ABI (type) = 1;
+
+  return NULL_TREE;
+}
+
+/* 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 (!CLASSTYPE_HAS_TRIVIAL_ABI (type))
+    return;
+
+  gcc_assert (COMPLETE_TYPE_P (type));
+
+  /* Check for virtual bases.  */
+  if (CLASSTYPE_VBASECLASSES (type))
+    {
+      if (warning (OPT_Wattributes, "%<trivial_abi%> cannot be applied to %qT",
+		   type))
+	inform (input_location, "has a virtual base");
+      CLASSTYPE_HAS_TRIVIAL_ABI (type) = 0;
+      return;
+    }
+
+  /* Check for virtual member functions.  */
+  if (TYPE_POLYMORPHIC_P (type))
+    {
+      if (warning (OPT_Wattributes, "%<trivial_abi%> cannot be applied to %qT",
+		   type))
+	inform (input_location, "is polymorphic");
+      CLASSTYPE_HAS_TRIVIAL_ABI (type) = 0;
+      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 (!trivial_for_calls_p (base_type))
+	    {
+	      if (warning (OPT_Wattributes,
+			   "%<trivial_abi%> cannot be applied to %qT", type))
+		inform (input_location,
+			"has a field of a non-trivial class type");
+	      CLASSTYPE_HAS_TRIVIAL_ABI (type) = 0;
+	      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) && !trivial_for_calls_p (field_type))
+	    {
+	      if (warning (OPT_Wattributes,
+			   "%<trivial_abi%> cannot be applied to %qT", type))
+		inform (input_location,
+			"has a field of a non-trivial class type");
+	      CLASSTYPE_HAS_TRIVIAL_ABI (type) = 0;
+	      return;
+	    }
+	}
+    }
+
+  /* Check that not all copy/move constructors are deleted.  */
+  if (!classtype_has_non_deleted_copy_or_move_ctor (type))
+    {
+      if (warning (OPT_Wattributes, "%<trivial_abi%> cannot be applied to %qT",
+		   type))
+	inform (input_location,
+		"copy constructors and move constructors are all deleted");
+      CLASSTYPE_HAS_TRIVIAL_ABI (type) = 0;
+      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 74278258206..cc22a2b3820 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -30965,6 +30965,75 @@ precedence and the @code{hot} attribute is not propagated.
 For the effects of the @code{hot} attribute on functions, see
 @ref{Common Function 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 purpose of calls.
+
+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 because trivial_abi makes the
+// user-provided special functions trivial.
+struct [[gnu::trivial_abi]] A @{
+  ~A();
+  A(const A &);
+  A(A &&);
+  int x;
+@};
+
+// B's destructor and copy/move constructor are considered trivial for the
+// purpose of calls because A is trivial.
+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.
+
+If a type is trivial for the purpose of calls, it is assumed to be trivially
+relocatable.
+
+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 directly declares a virtual base or virtual methods.
+
+@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..d547efa554b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi1.C
@@ -0,0 +1,48 @@
+// { dg-do compile { target c++11 } }
+// Test basic functionality of [[gnu::trivial_abi]] attribute
+
+struct [[gnu::trivial_abi]] TrivialAbi1
+{
+  int x;
+  ~TrivialAbi1 () {
+  } // Non-trivial destructor, but should be allowed with trivial_abi
+};
+
+class [[gnu::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 stored
+static_assert (__builtin_is_trivially_relocatable (TrivialAbi1),
+	       "TrivialAbi1 should be trivially relocatable");
+static_assert (__builtin_is_trivially_relocatable (TrivialAbi2),
+	       "TrivialAbi2 should be trivially relocatable");
+
+// Test basic usage in function parameters and return values
+TrivialAbi1
+func1 (TrivialAbi1 param)
+{
+  return param;
+}
+
+TrivialAbi2
+func2 (TrivialAbi2 param)
+{
+  return param;
+}
+
+union [[gnu::trivial_abi]] TrivialUnion
+{
+  int a;
+  float b;
+  ~TrivialUnion () {}
+};
+
+static_assert (__builtin_is_trivially_relocatable (TrivialUnion),
+	       "TrivialUnion should be trivially relocatable");
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..c671d7ca349
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi3.C
@@ -0,0 +1,61 @@
+// { dg-do compile { target c++11 } }
+// Test ABI behavior of [[gnu::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 [[gnu::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
+static_assert (__builtin_is_trivially_relocatable (TrivialAbiType),
+	       "TrivialAbiType should be trivially relocatable");
+static_assert (!__builtin_is_trivially_relocatable (NonTrivialType),
+	       "NonTrivialType should not be trivially relocatable");
+
+// Function templates to test parameter passing
+template <typename T>
+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..410c03646d0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi4.C
@@ -0,0 +1,75 @@
+// { dg-do compile { target c++11 } }
+// Test edge cases and inheritance with [[gnu::trivial_abi]] attribute
+
+// Test: Derived classes with trivial_abi base classes
+struct [[gnu::trivial_abi]] TrivialBase
+{
+  int x;
+  ~TrivialBase () {}
+};
+
+// This should be allowed - base class has trivial_abi
+struct [[gnu::trivial_abi]] DerivedFromTrivial : TrivialBase
+{
+  int y;
+  ~DerivedFromTrivial () {}
+};
+
+static_assert (__builtin_is_trivially_relocatable (TrivialBase),
+	       "TrivialBase should be trivially relocatable");
+static_assert (__builtin_is_trivially_relocatable (DerivedFromTrivial),
+	       "DerivedFromTrivial should be trivially relocatable");
+
+// Test: Multiple inheritance with all trivial bases
+struct [[gnu::trivial_abi]] TrivialBase2
+{
+  int z;
+  ~TrivialBase2 () {}
+};
+
+struct [[gnu::trivial_abi]] MultipleInheritance : TrivialBase, TrivialBase2
+{
+  int w;
+  ~MultipleInheritance () {}
+};
+
+static_assert (__builtin_is_trivially_relocatable (MultipleInheritance),
+	       "MultipleInheritance should be trivially relocatable");
+
+// Test: Empty class with trivial_abi
+struct [[gnu::trivial_abi]] EmptyTrivialAbi
+{
+  ~EmptyTrivialAbi () {}
+};
+
+static_assert (__builtin_is_trivially_relocatable (EmptyTrivialAbi),
+	       "EmptyTrivialAbi should be trivially relocatable");
+
+// Test: Class with only trivial members
+struct [[gnu::trivial_abi]] OnlyTrivialMembers
+{
+  int a;
+  float b;
+  char c[10];
+  ~OnlyTrivialMembers () {}
+};
+
+static_assert (__builtin_is_trivially_relocatable (OnlyTrivialMembers),
+	       "OnlyTrivialMembers should be trivially relocatable");
+
+// Test: Template class with trivial_abi
+template <typename T> struct [[gnu::trivial_abi]] TemplateTrivialAbi
+{
+  T data;
+  ~TemplateTrivialAbi () {}
+};
+
+// This should work for scalar types
+using IntTrivialAbi = TemplateTrivialAbi<int>;
+static_assert (__builtin_is_trivially_relocatable (IntTrivialAbi),
+	       "IntTrivialAbi should be trivially relocatable");
+
+// Test with another trivial_abi type as template parameter
+using NestedTrivialAbi = TemplateTrivialAbi<TrivialBase>;
+static_assert (__builtin_is_trivially_relocatable (NestedTrivialAbi),
+	       "NestedTrivialAbi should be trivially relocatable");
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..8ceb3d2f58a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi5.C
@@ -0,0 +1,83 @@
+// { dg-do compile { target c++11 } }
+// Test compatibility of [[gnu::trivial_abi]] with other GCC features
+
+// Test: Compatibility with other attributes
+struct [[gnu::trivial_abi, gnu::packed]] TrivialAbiPacked
+{
+  char a;
+  int b;
+  ~TrivialAbiPacked () {}
+};
+
+static_assert (__builtin_is_trivially_relocatable (TrivialAbiPacked),
+	       "TrivialAbiPacked should be trivially relocatable");
+
+// Test: Compatibility with alignas
+struct [[gnu::trivial_abi]] alignas (16) TrivialAbiAligned
+{
+  int x;
+  ~TrivialAbiAligned () {}
+};
+
+static_assert (__builtin_is_trivially_relocatable (TrivialAbiAligned),
+	       "TrivialAbiAligned should be trivially relocatable");
+
+// Test: Forward declaration and later definition
+struct TrivialForward;
+struct [[gnu::trivial_abi]] TrivialForward
+{
+  int x;
+  ~TrivialForward () {}
+};
+
+static_assert (__builtin_is_trivially_relocatable (TrivialForward),
+	       "TrivialForward should be trivially relocatable");
+
+// Test: typedef and using declarations
+using TrivialAlias = TrivialAbiPacked;
+typedef TrivialAbiAligned TrivialTypedef;
+
+static_assert (__builtin_is_trivially_relocatable (TrivialAlias),
+	       "TrivialAlias should be trivially relocatable");
+static_assert (__builtin_is_trivially_relocatable (TrivialTypedef),
+	       "TrivialTypedef should be trivially relocatable");
+
+// Test: Local classes
+void
+test_local_class ()
+{
+  struct [[gnu::trivial_abi]] LocalTrivial
+  {
+    int x;
+    ~LocalTrivial () {}
+  };
+
+  static_assert (__builtin_is_trivially_relocatable (LocalTrivial),
+		 "LocalTrivial should be trivially relocatable");
+}
+
+// Test: Named nested structs with trivial_abi
+struct ContainerClass
+{
+  struct [[gnu::trivial_abi]] NestedStruct
+  {
+    int nested_member;
+    ~NestedStruct () {}
+  };
+};
+
+// Test: Nested classes
+struct Outer
+{
+  struct [[gnu::trivial_abi]] Nested
+  {
+    int value;
+    ~Nested () {}
+  };
+};
+
+static_assert (
+  __builtin_is_trivially_relocatable (ContainerClass::NestedStruct),
+  "ContainerClass::NestedStruct should be trivially relocatable");
+static_assert (__builtin_is_trivially_relocatable (Outer::Nested),
+	       "Outer::Nested should be trivially relocatable");
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..cba2119442a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi_syntax.C
@@ -0,0 +1,161 @@
+// { 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> class __attribute__ ((trivial_abi)) a
+{
+  a (a &&);
+};
+
+struct [[gnu::trivial_abi]] S0
+{
+  int a;
+};
+static_assert (__builtin_is_trivially_relocatable (S0), "");
+
+struct __attribute__ ((trivial_abi)) S1
+{
+  int a;
+};
+static_assert (__builtin_is_trivially_relocatable (S1), "");
+
+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 field of a non-trivial class type" "" { 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 <class T> struct __attribute__ ((trivial_abi)) S3_4
+{
+  S3_4 (S3_4 &&);
+  S3_2 s32;
+};
+
+struct S4
+{
+  int a;
+};
+static_assert (__builtin_is_trivially_relocatable (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
+{
+};
+static_assert (__builtin_is_trivially_relocatable (S9), "");
+
+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 <class T> struct __attribute__ ((trivial_abi)) S10
+{
+  T p;
+};
+
+S10<int *> p1;
+static_assert (__builtin_is_trivially_relocatable (S10<int>), "");
+static_assert (__builtin_is_trivially_relocatable (S10<S3>), "");
+
+template <class T> struct S14
+{
+  T a;
+};
+
+template <class T> struct __attribute__ ((trivial_abi)) S15 : S14<T>
+{
+};
+
+S15<int> s15;
+static_assert (__builtin_is_trivially_relocatable (S15<int>), "");
+static_assert (__builtin_is_trivially_relocatable (S15<S3>), "");
+
+template <class T> struct __attribute__ ((trivial_abi)) S16
+{
+  S14<T> a;
+};
+static_assert (__builtin_is_trivially_relocatable (S16<int>), "");
+static_assert (__builtin_is_trivially_relocatable (S16<S3>), "");
+
+S16<int> s16;
+
+template <class T> struct __attribute__ ((trivial_abi)) S17
+{
+};
+
+S17<int> s17;
+static_assert (__builtin_is_trivially_relocatable (S17<int>), "");
+static_assert (__builtin_is_trivially_relocatable (S17<S3>), "");
+
+// 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 field of a non-trivial class type" "" { 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
-- 
2.51.1

Reply via email to