cxx-ada-virtual-inheritance-layout

C++ Ada virtual-inheritance layout

The C++ Ada mapper drops the size, alignment, and component positions of classes with virtual bases.

StagedApplies in patchset orderStaged in 1.2.0

Why this is staged.

Virtual-base layout is C++ ABI-coupled and cannot be expressed as native Ada inheritance; dynamic virtual-base conversion stays an explicit ABI-wrapper boundary. Held out of the published patchset for separate upstream review.

Depends on

Where it applies.

How each patchset treats this bundle on each GCC major
PatchsetGCC 13GCC 14GCC 15GCC 16
1.2.0 (latest)Staged13.2.0Staged14.2.0Staged15.3.0Staged16.2.0
1.1.0Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.2.0
1.0.1Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.1.0
1.0.0Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.1.0

Explanation.

The Itanium C++ ABI gives a class with a virtual base an internal virtual-base table pointer and places the shared base subobject at a runtime-defined offset. The mapper emits the visible members but currently discards their C++ positions, size, and alignment.

For example, on the supported 64-bit targets this class is 16 bytes, aligned to 8 bytes, with derived_value at byte 8 and the virtual Root subobject at byte 12:

struct Root
{
  int root_value;
};

struct Virtual : virtual Root
{
  int derived_value;
};

The unpatched mapper omits the internal pointer and lets Ada place both visible components from byte zero. The generated object is therefore only 8 bytes and aligned to 4:

type Virtual is limited record
   derived_value : aliased int;
   field_2 : aliased Root;
end record
with Import => True,
     Convention => CPP;

The corrected output leaves the internal pointer as an opaque gap while fixing the complete size, alignment, and both visible components at their C++ ABI positions:

for Virtual'Size use 128;
for Virtual'Object_Size use 128;
for Virtual'Alignment use 8;
for Virtual use record
   derived_value at 8 range 0 .. 31;
   field_2 at 12 range 0 .. 31;
end record;

The executable regression runs at -O0 and -O2. C++ constructs the object so its hidden virtual-base pointer is valid. Ada then checks the complete layout, writes the direct member and virtual-base member through the generated view, and C++ reads both values back through virtual-base-aware access paths.

Patch.

Variant gcc-13

Applies to 13.2.0. Source flavors: linux, darwin_arm64.

+109 −14 4 files

Download the patch

gcc/c-family/c-ada-spec.cc +51−14modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -3115 +3115 @@is_abi_ignored_empty_field (tree node, tree field)
31153115 return false;
31163116}
31173117
3118Added line. /* Return true if FIELD is represented as a component in the generated Ada
3119Added line. record. Keep this in step with dump_ada_structure. */
3120Added line.
3121Added line. static bool
3122Added line. is_ada_record_field (tree node, tree field)
3123Added line. {
3124Added line. if (TREE_CODE (field) != FIELD_DECL
3125Added line. || DECL_VIRTUAL_P (field)
3126Added line. || is_abi_ignored_empty_field (node, field))
3127Added line. return false;
3128Added line.
3129Added line. return DECL_NAME (field) || !is_tagged_type (TREE_TYPE (field));
3130Added line. }
3131Added line.
31183132/* Dump the value and object sizes of an empty C++ class. Ada's value size is
31193133 zero so an empty base consumes no storage, while Object_Size preserves the
31203134 byte occupied by a complete C++ object. */
@@ -2874 +2888 @@static bool
28742888has_constant_field_layout (tree node)
28752889{
28762890 for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
2877Removed line. if (TREE_CODE (field) == FIELD_DECL
2878Removed line. && DECL_NAME (field)
2879Removed line. && !DECL_VIRTUAL_P (field)
2891Added line. if (is_ada_record_field (node, field)
28802892 && (!DECL_FIELD_OFFSET (field)
28812893 || !DECL_FIELD_BIT_OFFSET (field)
28822894 || !DECL_SIZE (field)
@@ -2889 +2901 @@has_constant_field_layout (tree node)
28892901 return true;
28902902}
28912903
2892Removed line. /* Dump the size and component clauses needed to preserve NODE's C++ tail
2893Removed line. padding layout. TYPE is the declaration used for Ada name qualification. */
2904Added line. /* Dump the size and component clauses needed to preserve NODE's C++ layout.
2905Added line. For virtual inheritance the generated record contains the virtual bases and
2906Added line. therefore uses the complete-object size; other calls preserve the as-base
2907Added line. size needed for reusable tail padding. TYPE is the declaration used for
2908Added line. Ada name qualification. */
28942909
28952910static void
2896Removed line. dump_ada_tail_padding_layout (pretty_printer *buffer, tree node, tree type,
2897Removed line. int spc)
2911Added line. dump_ada_cpp_layout (pretty_printer *buffer, tree node, tree type, int spc,
2912Added line. bool has_virtual_base)
28982913{
28992914 const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
29002915 const HOST_WIDE_INT object_size = int_size_in_bytes (node);
2916Added line. const HOST_WIDE_INT value_size = has_virtual_base ? object_size : data_size;
29012917
29022918 gcc_assert (data_size >= 0 && object_size >= 0);
29032919
@@ -2905 +2921 @@dump_ada_tail_padding_layout (pretty_printer *buffer, tree node, tree type,
29052921 pp_string (buffer, "for ");
29062922 dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
29072923 pp_string (buffer, "'Size use ");
2908Removed line. pp_wide_integer (buffer, (HOST_WIDE_INT) data_size * BITS_PER_UNIT);
2924Added line. pp_wide_integer (buffer, value_size * BITS_PER_UNIT);
29092925 pp_semicolon (buffer);
29102926
29112927 newline_and_indent (buffer, spc);
@@ -2915 +2931 @@dump_ada_tail_padding_layout (pretty_printer *buffer, tree node, tree type,
29152931 pp_wide_integer (buffer, object_size * BITS_PER_UNIT);
29162932 pp_semicolon (buffer);
29172933
2934Added line. if (has_virtual_base)
2935Added line. {
2936Added line. newline_and_indent (buffer, spc);
2937Added line. pp_string (buffer, "for ");
2938Added line. dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
2939Added line. pp_string (buffer, "'Alignment use ");
2940Added line. pp_decimal_int (buffer, TYPE_ALIGN (node) / BITS_PER_UNIT);
2941Added line. pp_semicolon (buffer);
2942Added line. }
2943Added line.
29182944 newline_and_indent (buffer, spc);
29192945 pp_string (buffer, "for ");
29202946 dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
29212947 pp_string (buffer, " use record");
29222948
2949Added line. int field_num = 0;
29232950 for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
2924Removed line. if (TREE_CODE (field) == FIELD_DECL
2925Removed line. && DECL_NAME (field)
2926Removed line. && !DECL_VIRTUAL_P (field))
2951Added line. if (is_ada_record_field (node, field))
29272952 {
2953Added line. field_num++;
29282954 const unsigned HOST_WIDE_INT bitpos
29292955 = tree_to_uhwi (DECL_FIELD_OFFSET (field)) * BITS_PER_UNIT
29302956 + tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field));
@@ -2934 +2960 @@dump_ada_tail_padding_layout (pretty_printer *buffer, tree node, tree type,
29342960 = first_bit + tree_to_uhwi (DECL_SIZE (field)) - 1;
29352961
29362962 newline_and_indent (buffer, spc + INDENT_INCR);
2937Removed line. dump_ada_decl_name (buffer, field, false);
2963Added line. if (DECL_NAME (field))
2964Added line. dump_ada_decl_name (buffer, field, false);
2965Added line. else if (field_num == 1)
2966Added line. pp_string (buffer, "parent");
2967Added line. else
2968Added line. {
2969Added line. char buf[32];
2970Added line. sprintf (buf, "field_%d", field_num);
2971Added line. pp_string (buffer, buf);
2972Added line. }
29382973 pp_string (buffer, " at ");
29392974 pp_unsigned_wide_integer (buffer, position);
29402975 pp_string (buffer, " range ");
@@ -3981 +4016 @@dump_ada_structure (pretty_printer *buffer, tree node, tree type, bool nested,
39814016 pp_newline (buffer);
39824017 dump_ada_empty_class_layout (buffer, node, type, spc);
39834018 }
3984Removed line. else if (needs_tail_padding_layout (node)
4019Added line. else if ((needs_tail_padding_layout (node)
4020Added line. || (cpp_check && cpp_check (node, HAS_VIRTUAL_BASE)))
39854021 && has_constant_field_layout (node))
39864022 {
39874023 if (need_semicolon)
@@ -3991 +4027 @@dump_ada_structure (pretty_printer *buffer, tree node, tree type, bool nested,
39914027 }
39924028
39934029 pp_newline (buffer);
3994Removed line. dump_ada_tail_padding_layout (buffer, node, type, spc);
4030Added line. dump_ada_cpp_layout (buffer, node, type, spc,
4031Added line. cpp_check (node, HAS_VIRTUAL_BASE));
39954032 }
39964033
39974034 /* Print the static fields of the structure, if any. */
gcc/c-family/c-ada-spec.h +1−0modified
Unified diff for gcc/c-family/c-ada-spec.h: original line, patched line, change, source
@@ -27 +27 @@along with GCC; see the file COPYING3. If not see
2727enum cpp_operation {
2828 GET_AS_BASE_SIZE,
2929 GET_FUNCTION_QUALIFIERS,
30Added line. HAS_VIRTUAL_BASE,
3031 HAS_DEPENDENT_TEMPLATE_ARGS,
3132 IS_ABSTRACT,
3233 IS_ASSIGNMENT_OPERATOR,
gcc/cp/decl2.cc +3−0modified
Unified diff for gcc/cp/decl2.cc: original line, patched line, change, source
@@ -4439 +4439 @@cpp_check (tree t, cpp_operation op)
44394439
44404440 return qualifiers;
44414441 }
4442Added line. case HAS_VIRTUAL_BASE:
4443Added line. return (CLASS_TYPE_P (t) && COMPLETE_TYPE_P (t)
4444Added line. && CLASSTYPE_VBASECLASSES (t));
44424445 case HAS_DEPENDENT_TEMPLATE_ARGS:
44434446 {
44444447 tree ti = CLASSTYPE_TEMPLATE_INFO (t);
gcc/testsuite/g++.dg/ada-spec/virtual-inheritance-layout.C +54−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/virtual-inheritance-layout.C: original line, patched line, change, source
@@ -0 +1 @@
1Added line. /* { dg-do compile } */
2Added line. /* { dg-require-effective-target lp64 } */
3Added line. /* { dg-options "-fdump-ada-spec-slim" } */
4Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "for Virtual'Size use 128;" } } */
5Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "for Virtual'Object_Size use 128;" } } */
6Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "for Virtual'Alignment use 8;" } } */
7Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "derived_value at 8 range 0 .. 31;" } } */
8Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "field_2 at 12 range 0 .. 31;" } } */
9Added line.
10Added line. struct Root
11Added line. {
12Added line. int root_value;
13Added line. };
14Added line.
15Added line. struct Virtual : virtual Root
16Added line. {
17Added line. int derived_value;
18Added line. Virtual (int root, int derived);
19Added line. };
20Added line.
21Added line. Virtual::Virtual (int root, int derived)
22Added line. : Root {root}, derived_value {derived}
23Added line. {}
24Added line.
25Added line. extern "C" unsigned long cpp_root_size () { return sizeof (Root); }
26Added line. extern "C" unsigned long cpp_virtual_size () { return sizeof (Virtual); }
27Added line. extern "C" unsigned long cpp_virtual_alignment () { return alignof (Virtual); }
28Added line. extern "C" unsigned long cpp_derived_offset ()
29Added line. {
30Added line. Virtual object (0, 0);
31Added line. return reinterpret_cast<char *> (&object.derived_value)
32Added line. - reinterpret_cast<char *> (&object);
33Added line. }
34Added line. extern "C" unsigned long cpp_root_offset ()
35Added line. {
36Added line. Virtual object (0, 0);
37Added line. return reinterpret_cast<char *> (&object.root_value)
38Added line. - reinterpret_cast<char *> (&object);
39Added line. }
40Added line. extern "C" Virtual *cpp_create (int root, int derived)
41Added line. {
42Added line. return new Virtual (root, derived);
43Added line. }
44Added line. extern "C" void cpp_delete (Virtual *object) { delete object; }
45Added line. extern "C" int cpp_root_value (const Virtual *object)
46Added line. {
47Added line. return object->root_value;
48Added line. }
49Added line. extern "C" int cpp_derived_value (const Virtual *object)
50Added line. {
51Added line. return object->derived_value;
52Added line. }
53Added line.
54Added line. /* { dg-final { cleanup-ada-spec } } */
55

Variant gcc-14

Applies to 14.2.0. Source flavors: linux, darwin_arm64.

+109 −14 4 files

Download the patch

gcc/c-family/c-ada-spec.cc +51−14modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -3118 +3118 @@is_abi_ignored_empty_field (tree node, tree field)
31183118 return false;
31193119}
31203120
3121Added line. /* Return true if FIELD is represented as a component in the generated Ada
3122Added line. record. Keep this in step with dump_ada_structure. */
3123Added line.
3124Added line. static bool
3125Added line. is_ada_record_field (tree node, tree field)
3126Added line. {
3127Added line. if (TREE_CODE (field) != FIELD_DECL
3128Added line. || DECL_VIRTUAL_P (field)
3129Added line. || is_abi_ignored_empty_field (node, field))
3130Added line. return false;
3131Added line.
3132Added line. return DECL_NAME (field) || !is_tagged_type (TREE_TYPE (field));
3133Added line. }
3134Added line.
31213135/* Dump the value and object sizes of an empty C++ class. Ada's value size is
31223136 zero so an empty base consumes no storage, while Object_Size preserves the
31233137 byte occupied by a complete C++ object. */
@@ -2877 +2891 @@static bool
28772891has_constant_field_layout (tree node)
28782892{
28792893 for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
2880Removed line. if (TREE_CODE (field) == FIELD_DECL
2881Removed line. && DECL_NAME (field)
2882Removed line. && !DECL_VIRTUAL_P (field)
2894Added line. if (is_ada_record_field (node, field)
28832895 && (!DECL_FIELD_OFFSET (field)
28842896 || !DECL_FIELD_BIT_OFFSET (field)
28852897 || !DECL_SIZE (field)
@@ -2892 +2904 @@has_constant_field_layout (tree node)
28922904 return true;
28932905}
28942906
2895Removed line. /* Dump the size and component clauses needed to preserve NODE's C++ tail
2896Removed line. padding layout. TYPE is the declaration used for Ada name qualification. */
2907Added line. /* Dump the size and component clauses needed to preserve NODE's C++ layout.
2908Added line. For virtual inheritance the generated record contains the virtual bases and
2909Added line. therefore uses the complete-object size; other calls preserve the as-base
2910Added line. size needed for reusable tail padding. TYPE is the declaration used for
2911Added line. Ada name qualification. */
28972912
28982913static void
2899Removed line. dump_ada_tail_padding_layout (pretty_printer *buffer, tree node, tree type,
2900Removed line. int spc)
2914Added line. dump_ada_cpp_layout (pretty_printer *buffer, tree node, tree type, int spc,
2915Added line. bool has_virtual_base)
29012916{
29022917 const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
29032918 const HOST_WIDE_INT object_size = int_size_in_bytes (node);
2919Added line. const HOST_WIDE_INT value_size = has_virtual_base ? object_size : data_size;
29042920
29052921 gcc_assert (data_size >= 0 && object_size >= 0);
29062922
@@ -2908 +2924 @@dump_ada_tail_padding_layout (pretty_printer *buffer, tree node, tree type,
29082924 pp_string (buffer, "for ");
29092925 dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
29102926 pp_string (buffer, "'Size use ");
2911Removed line. pp_wide_integer (buffer, (HOST_WIDE_INT) data_size * BITS_PER_UNIT);
2927Added line. pp_wide_integer (buffer, value_size * BITS_PER_UNIT);
29122928 pp_semicolon (buffer);
29132929
29142930 newline_and_indent (buffer, spc);
@@ -2918 +2934 @@dump_ada_tail_padding_layout (pretty_printer *buffer, tree node, tree type,
29182934 pp_wide_integer (buffer, object_size * BITS_PER_UNIT);
29192935 pp_semicolon (buffer);
29202936
2937Added line. if (has_virtual_base)
2938Added line. {
2939Added line. newline_and_indent (buffer, spc);
2940Added line. pp_string (buffer, "for ");
2941Added line. dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
2942Added line. pp_string (buffer, "'Alignment use ");
2943Added line. pp_decimal_int (buffer, TYPE_ALIGN (node) / BITS_PER_UNIT);
2944Added line. pp_semicolon (buffer);
2945Added line. }
2946Added line.
29212947 newline_and_indent (buffer, spc);
29222948 pp_string (buffer, "for ");
29232949 dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
29242950 pp_string (buffer, " use record");
29252951
2952Added line. int field_num = 0;
29262953 for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
2927Removed line. if (TREE_CODE (field) == FIELD_DECL
2928Removed line. && DECL_NAME (field)
2929Removed line. && !DECL_VIRTUAL_P (field))
2954Added line. if (is_ada_record_field (node, field))
29302955 {
2956Added line. field_num++;
29312957 const unsigned HOST_WIDE_INT bitpos
29322958 = tree_to_uhwi (DECL_FIELD_OFFSET (field)) * BITS_PER_UNIT
29332959 + tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field));
@@ -2937 +2963 @@dump_ada_tail_padding_layout (pretty_printer *buffer, tree node, tree type,
29372963 = first_bit + tree_to_uhwi (DECL_SIZE (field)) - 1;
29382964
29392965 newline_and_indent (buffer, spc + INDENT_INCR);
2940Removed line. dump_ada_decl_name (buffer, field, false);
2966Added line. if (DECL_NAME (field))
2967Added line. dump_ada_decl_name (buffer, field, false);
2968Added line. else if (field_num == 1)
2969Added line. pp_string (buffer, "parent");
2970Added line. else
2971Added line. {
2972Added line. char buf[32];
2973Added line. sprintf (buf, "field_%d", field_num);
2974Added line. pp_string (buffer, buf);
2975Added line. }
29412976 pp_string (buffer, " at ");
29422977 pp_unsigned_wide_integer (buffer, position);
29432978 pp_string (buffer, " range ");
@@ -3984 +4019 @@dump_ada_structure (pretty_printer *buffer, tree node, tree type, bool nested,
39844019 pp_newline (buffer);
39854020 dump_ada_empty_class_layout (buffer, node, type, spc);
39864021 }
3987Removed line. else if (needs_tail_padding_layout (node)
4022Added line. else if ((needs_tail_padding_layout (node)
4023Added line. || (cpp_check && cpp_check (node, HAS_VIRTUAL_BASE)))
39884024 && has_constant_field_layout (node))
39894025 {
39904026 if (need_semicolon)
@@ -3994 +4030 @@dump_ada_structure (pretty_printer *buffer, tree node, tree type, bool nested,
39944030 }
39954031
39964032 pp_newline (buffer);
3997Removed line. dump_ada_tail_padding_layout (buffer, node, type, spc);
4033Added line. dump_ada_cpp_layout (buffer, node, type, spc,
4034Added line. cpp_check (node, HAS_VIRTUAL_BASE));
39984035 }
39994036
40004037 /* Print the static fields of the structure, if any. */
gcc/c-family/c-ada-spec.h +1−0modified
Unified diff for gcc/c-family/c-ada-spec.h: original line, patched line, change, source
@@ -27 +27 @@along with GCC; see the file COPYING3. If not see
2727enum cpp_operation {
2828 GET_AS_BASE_SIZE,
2929 GET_FUNCTION_QUALIFIERS,
30Added line. HAS_VIRTUAL_BASE,
3031 HAS_DEPENDENT_TEMPLATE_ARGS,
3132 IS_ABSTRACT,
3233 IS_ASSIGNMENT_OPERATOR,
gcc/cp/decl2.cc +3−0modified
Unified diff for gcc/cp/decl2.cc: original line, patched line, change, source
@@ -4576 +4576 @@cpp_check (tree t, cpp_operation op)
45764576
45774577 return qualifiers;
45784578 }
4579Added line. case HAS_VIRTUAL_BASE:
4580Added line. return (CLASS_TYPE_P (t) && COMPLETE_TYPE_P (t)
4581Added line. && CLASSTYPE_VBASECLASSES (t));
45794582 case HAS_DEPENDENT_TEMPLATE_ARGS:
45804583 {
45814584 tree ti = CLASSTYPE_TEMPLATE_INFO (t);
gcc/testsuite/g++.dg/ada-spec/virtual-inheritance-layout.C +54−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/virtual-inheritance-layout.C: original line, patched line, change, source
@@ -0 +1 @@
1Added line. /* { dg-do compile } */
2Added line. /* { dg-require-effective-target lp64 } */
3Added line. /* { dg-options "-fdump-ada-spec-slim" } */
4Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "for Virtual'Size use 128;" } } */
5Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "for Virtual'Object_Size use 128;" } } */
6Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "for Virtual'Alignment use 8;" } } */
7Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "derived_value at 8 range 0 .. 31;" } } */
8Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "field_2 at 12 range 0 .. 31;" } } */
9Added line.
10Added line. struct Root
11Added line. {
12Added line. int root_value;
13Added line. };
14Added line.
15Added line. struct Virtual : virtual Root
16Added line. {
17Added line. int derived_value;
18Added line. Virtual (int root, int derived);
19Added line. };
20Added line.
21Added line. Virtual::Virtual (int root, int derived)
22Added line. : Root {root}, derived_value {derived}
23Added line. {}
24Added line.
25Added line. extern "C" unsigned long cpp_root_size () { return sizeof (Root); }
26Added line. extern "C" unsigned long cpp_virtual_size () { return sizeof (Virtual); }
27Added line. extern "C" unsigned long cpp_virtual_alignment () { return alignof (Virtual); }
28Added line. extern "C" unsigned long cpp_derived_offset ()
29Added line. {
30Added line. Virtual object (0, 0);
31Added line. return reinterpret_cast<char *> (&object.derived_value)
32Added line. - reinterpret_cast<char *> (&object);
33Added line. }
34Added line. extern "C" unsigned long cpp_root_offset ()
35Added line. {
36Added line. Virtual object (0, 0);
37Added line. return reinterpret_cast<char *> (&object.root_value)
38Added line. - reinterpret_cast<char *> (&object);
39Added line. }
40Added line. extern "C" Virtual *cpp_create (int root, int derived)
41Added line. {
42Added line. return new Virtual (root, derived);
43Added line. }
44Added line. extern "C" void cpp_delete (Virtual *object) { delete object; }
45Added line. extern "C" int cpp_root_value (const Virtual *object)
46Added line. {
47Added line. return object->root_value;
48Added line. }
49Added line. extern "C" int cpp_derived_value (const Virtual *object)
50Added line. {
51Added line. return object->derived_value;
52Added line. }
53Added line.
54Added line. /* { dg-final { cleanup-ada-spec } } */
55

Variant gcc-15-16

Applies to 15.3.0, 16.1.0, 16.2.0. Source flavors: linux, darwin_arm64.

+109 −13 4 files

Download the patch

gcc/c-family/c-ada-spec.cc +51−13modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -3189 +3189 @@is_abi_ignored_empty_field (tree node, tree field)
31893189 return false;
31903190}
31913191
3192Added line. /* Return true if FIELD is represented as a component in the generated Ada
3193Added line. record. Keep this in step with dump_ada_structure. */
3194Added line.
3195Added line. static bool
3196Added line. is_ada_record_field (tree node, tree field)
3197Added line. {
3198Added line. if (TREE_CODE (field) != FIELD_DECL
3199Added line. || DECL_VIRTUAL_P (field)
3200Added line. || is_abi_ignored_empty_field (node, field))
3201Added line. return false;
3202Added line.
3203Added line. return DECL_NAME (field) || !is_tagged_type (TREE_TYPE (field));
3204Added line. }
3205Added line.
31923206/* Dump the value and object sizes of an empty C++ class. Ada's value size is
31933207 zero so an empty base consumes no storage, while Object_Size preserves the
31943208 byte occupied by a complete C++ object. */
@@ -2947 +2961 @@static bool
29472961has_constant_field_layout (tree node)
29482962{
29492963 for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
2950Removed line. if (TREE_CODE (field) == FIELD_DECL
2951Removed line. && DECL_NAME (field)
2952Removed line. && !DECL_VIRTUAL_P (field)
2964Added line. if (is_ada_record_field (node, field)
29532965 && (!DECL_FIELD_OFFSET (field)
29542966 || !DECL_FIELD_BIT_OFFSET (field)
29552967 || !DECL_SIZE (field)
@@ -2962 +2974 @@has_constant_field_layout (tree node)
29622974 return true;
29632975}
29642976
2965Removed line. /* Dump the size and component clauses needed to preserve NODE's C++ tail
2966Removed line. padding layout. TYPE is the declaration used for Ada name qualification. */
2977Added line. /* Dump the size and component clauses needed to preserve NODE's C++ layout.
2978Added line. For virtual inheritance the generated record contains the virtual bases and
2979Added line. therefore uses the complete-object size; other calls preserve the as-base
2980Added line. size needed for reusable tail padding. TYPE is the declaration used for
2981Added line. Ada name qualification. */
29672982
29682983static void
2969Removed line. dump_ada_tail_padding_layout (pretty_printer *pp, tree node, tree type, int spc)
2984Added line. dump_ada_cpp_layout (pretty_printer *pp, tree node, tree type, int spc,
2985Added line. bool has_virtual_base)
29702986{
29712987 const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
29722988 const HOST_WIDE_INT object_size = int_size_in_bytes (node);
2989Added line. const HOST_WIDE_INT value_size = has_virtual_base ? object_size : data_size;
29732990
29742991 gcc_assert (data_size >= 0 && object_size >= 0);
29752992
@@ -2977 +2994 @@dump_ada_tail_padding_layout (pretty_printer *pp, tree node, tree type, int spc)
29772994 pp_string (pp, "for ");
29782995 dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
29792996 pp_string (pp, "'Size use ");
2980Removed line. pp_wide_integer (pp, (HOST_WIDE_INT) data_size * BITS_PER_UNIT);
2997Added line. pp_wide_integer (pp, value_size * BITS_PER_UNIT);
29812998 pp_semicolon (pp);
29822999
29833000 newline_and_indent (pp, spc);
@@ -2987 +3004 @@dump_ada_tail_padding_layout (pretty_printer *pp, tree node, tree type, int spc)
29873004 pp_wide_integer (pp, object_size * BITS_PER_UNIT);
29883005 pp_semicolon (pp);
29893006
3007Added line. if (has_virtual_base)
3008Added line. {
3009Added line. newline_and_indent (pp, spc);
3010Added line. pp_string (pp, "for ");
3011Added line. dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
3012Added line. pp_string (pp, "'Alignment use ");
3013Added line. pp_decimal_int (pp, TYPE_ALIGN (node) / BITS_PER_UNIT);
3014Added line. pp_semicolon (pp);
3015Added line. }
3016Added line.
29903017 newline_and_indent (pp, spc);
29913018 pp_string (pp, "for ");
29923019 dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
29933020 pp_string (pp, " use record");
29943021
3022Added line. int field_num = 0;
29953023 for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
2996Removed line. if (TREE_CODE (field) == FIELD_DECL
2997Removed line. && DECL_NAME (field)
2998Removed line. && !DECL_VIRTUAL_P (field))
3024Added line. if (is_ada_record_field (node, field))
29993025 {
3026Added line. field_num++;
30003027 const unsigned HOST_WIDE_INT bitpos
30013028 = tree_to_uhwi (DECL_FIELD_OFFSET (field)) * BITS_PER_UNIT
30023029 + tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field));
@@ -3006 +3033 @@dump_ada_tail_padding_layout (pretty_printer *pp, tree node, tree type, int spc)
30063033 = first_bit + tree_to_uhwi (DECL_SIZE (field)) - 1;
30073034
30083035 newline_and_indent (pp, spc + INDENT_INCR);
3009Removed line. dump_ada_decl_name (pp, field, false);
3036Added line. if (DECL_NAME (field))
3037Added line. dump_ada_decl_name (pp, field, false);
3038Added line. else if (field_num == 1)
3039Added line. pp_string (pp, "parent");
3040Added line. else
3041Added line. {
3042Added line. char buf[32];
3043Added line. sprintf (buf, "field_%d", field_num);
3044Added line. pp_string (pp, buf);
3045Added line. }
30103046 pp_string (pp, " at ");
30113047 pp_unsigned_wide_integer (pp, position);
30123048 pp_string (pp, " range ");
@@ -4063 +4099 @@dump_ada_structure (pretty_printer *pp, tree node, tree type, bool nested,
40634099 pp_newline (pp);
40644100 dump_ada_empty_class_layout (pp, node, type, spc);
40654101 }
4066Removed line. else if (needs_tail_padding_layout (node)
4102Added line. else if ((needs_tail_padding_layout (node)
4103Added line. || (cpp_check && cpp_check (node, HAS_VIRTUAL_BASE)))
40674104 && has_constant_field_layout (node))
40684105 {
40694106 if (need_semicolon)
@@ -4073 +4110 @@dump_ada_structure (pretty_printer *pp, tree node, tree type, bool nested,
40734110 }
40744111
40754112 pp_newline (pp);
4076Removed line. dump_ada_tail_padding_layout (pp, node, type, spc);
4113Added line. dump_ada_cpp_layout (pp, node, type, spc,
4114Added line. cpp_check (node, HAS_VIRTUAL_BASE));
40774115 }
40784116
40794117 /* Print the static fields of the structure, if any. */
gcc/c-family/c-ada-spec.h +1−0modified
Unified diff for gcc/c-family/c-ada-spec.h: original line, patched line, change, source
@@ -27 +27 @@along with GCC; see the file COPYING3. If not see
2727enum cpp_operation {
2828 GET_AS_BASE_SIZE,
2929 GET_FUNCTION_QUALIFIERS,
30Added line. HAS_VIRTUAL_BASE,
3031 HAS_DEPENDENT_TEMPLATE_ARGS,
3132 IS_ABSTRACT,
3233 IS_ASSIGNMENT_OPERATOR,
gcc/cp/decl2.cc +3−0modified
Unified diff for gcc/cp/decl2.cc: original line, patched line, change, source
@@ -5288 +5288 @@cpp_check (tree t, cpp_operation op)
52885288
52895289 return qualifiers;
52905290 }
5291Added line. case HAS_VIRTUAL_BASE:
5292Added line. return (CLASS_TYPE_P (t) && COMPLETE_TYPE_P (t)
5293Added line. && CLASSTYPE_VBASECLASSES (t));
52915294 case HAS_DEPENDENT_TEMPLATE_ARGS:
52925295 {
52935296 tree ti = CLASSTYPE_TEMPLATE_INFO (t);
gcc/testsuite/g++.dg/ada-spec/virtual-inheritance-layout.C +54−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/virtual-inheritance-layout.C: original line, patched line, change, source
@@ -0 +1 @@
1Added line. /* { dg-do compile } */
2Added line. /* { dg-require-effective-target lp64 } */
3Added line. /* { dg-options "-fdump-ada-spec-slim" } */
4Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "for Virtual'Size use 128;" } } */
5Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "for Virtual'Object_Size use 128;" } } */
6Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "for Virtual'Alignment use 8;" } } */
7Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "derived_value at 8 range 0 .. 31;" } } */
8Added line. /* { dg-final { scan-file virtual_inheritance_layout_c.ads "field_2 at 12 range 0 .. 31;" } } */
9Added line.
10Added line. struct Root
11Added line. {
12Added line. int root_value;
13Added line. };
14Added line.
15Added line. struct Virtual : virtual Root
16Added line. {
17Added line. int derived_value;
18Added line. Virtual (int root, int derived);
19Added line. };
20Added line.
21Added line. Virtual::Virtual (int root, int derived)
22Added line. : Root {root}, derived_value {derived}
23Added line. {}
24Added line.
25Added line. extern "C" unsigned long cpp_root_size () { return sizeof (Root); }
26Added line. extern "C" unsigned long cpp_virtual_size () { return sizeof (Virtual); }
27Added line. extern "C" unsigned long cpp_virtual_alignment () { return alignof (Virtual); }
28Added line. extern "C" unsigned long cpp_derived_offset ()
29Added line. {
30Added line. Virtual object (0, 0);
31Added line. return reinterpret_cast<char *> (&object.derived_value)
32Added line. - reinterpret_cast<char *> (&object);
33Added line. }
34Added line. extern "C" unsigned long cpp_root_offset ()
35Added line. {
36Added line. Virtual object (0, 0);
37Added line. return reinterpret_cast<char *> (&object.root_value)
38Added line. - reinterpret_cast<char *> (&object);
39Added line. }
40Added line. extern "C" Virtual *cpp_create (int root, int derived)
41Added line. {
42Added line. return new Virtual (root, derived);
43Added line. }
44Added line. extern "C" void cpp_delete (Virtual *object) { delete object; }
45Added line. extern "C" int cpp_root_value (const Virtual *object)
46Added line. {
47Added line. return object->root_value;
48Added line. }
49Added line. extern "C" int cpp_derived_value (const Virtual *object)
50Added line. {
51Added line. return object->derived_value;
52Added line. }
53Added line.
54Added line. /* { dg-final { cleanup-ada-spec } } */
55

Tests.

virtual-inheritance-layout.C C++ · 54 lines
/* { dg-do compile } */
/* { dg-require-effective-target lp64 } */
/* { dg-options "-fdump-ada-spec-slim" } */
/* { dg-final { scan-file virtual_inheritance_layout_c.ads "for Virtual'Size use 128;" } } */
/* { dg-final { scan-file virtual_inheritance_layout_c.ads "for Virtual'Object_Size use 128;" } } */
/* { dg-final { scan-file virtual_inheritance_layout_c.ads "for Virtual'Alignment use 8;" } } */
/* { dg-final { scan-file virtual_inheritance_layout_c.ads "derived_value at 8 range 0 .. 31;" } } */
/* { dg-final { scan-file virtual_inheritance_layout_c.ads "field_2 at 12 range 0 .. 31;" } } */

struct Root
{
  int root_value;
};

struct Virtual : virtual Root
{
  int derived_value;
  Virtual (int root, int derived);
};

Virtual::Virtual (int root, int derived)
  : Root {root}, derived_value {derived}
{}

extern "C" unsigned long cpp_root_size () { return sizeof (Root); }
extern "C" unsigned long cpp_virtual_size () { return sizeof (Virtual); }
extern "C" unsigned long cpp_virtual_alignment () { return alignof (Virtual); }
extern "C" unsigned long cpp_derived_offset ()
{
  Virtual object (0, 0);
  return reinterpret_cast<char *> (&object.derived_value)
    - reinterpret_cast<char *> (&object);
}
extern "C" unsigned long cpp_root_offset ()
{
  Virtual object (0, 0);
  return reinterpret_cast<char *> (&object.root_value)
    - reinterpret_cast<char *> (&object);
}
extern "C" Virtual *cpp_create (int root, int derived)
{
  return new Virtual (root, derived);
}
extern "C" void cpp_delete (Virtual *object) { delete object; }
extern "C" int cpp_root_value (const Virtual *object)
{
  return object->root_value;
}
extern "C" int cpp_derived_value (const Virtual *object)
{
  return object->derived_value;
}

/* { dg-final { cleanup-ada-spec } } */

Download · View in repository

virtual_inheritance_layout_consumer.adb Ada · 45 lines
with Ada.Text_IO;
with Interfaces.C; use Interfaces.C;
with System;
with Virtual_Inheritance_Layout_C;

procedure Virtual_Inheritance_Layout_Consumer is
   package Bindings renames Virtual_Inheritance_Layout_C;
   subtype Virtual is Bindings.Class_Virtual.Virtual;
   Object : access Virtual := Bindings.cpp_create (101, 202);

   function Bytes (Bits : Natural) return unsigned_long is
     (unsigned_long (Bits / System.Storage_Unit));

   Layout_Matches : constant Boolean :=
     Bytes (Virtual'Object_Size) = Bindings.cpp_virtual_size
     and then unsigned_long (Virtual'Alignment) =
       Bindings.cpp_virtual_alignment
     and then unsigned_long (Object.derived_value'Position) =
       Bindings.cpp_derived_offset
     and then unsigned_long (Object.field_2'Position) =
       Bindings.cpp_root_offset;
begin
   if Object = null
     or else Bytes (Bindings.Root'Object_Size) /= Bindings.cpp_root_size
     or else Bindings.cpp_root_value (Object) /= 101
     or else Bindings.cpp_derived_value (Object) /= 202
   then
      raise Program_Error with "C++ virtual object setup differs";
   end if;

   if Layout_Matches then
      Object.derived_value := 303;
      Object.field_2.root_value := 404;
      if Bindings.cpp_derived_value (Object) /= 303
        or else Bindings.cpp_root_value (Object) /= 404
      then
         raise Program_Error with "Ada writes use the wrong virtual layout";
      end if;
      Ada.Text_IO.Put_Line ("MATCH C++ Ada virtual inheritance");
   else
      Ada.Text_IO.Put_Line ("MISMATCH C++ Ada virtual inheritance");
   end if;

   Bindings.cpp_delete (Object);
end Virtual_Inheritance_Layout_Consumer;

Download · View in repository

run-test.sh shell · 65 lines
#!/usr/bin/env bash
set -euo pipefail

if [[ $# -ne 3 ]]; then
  echo "usage: $0 TOOLCHAIN_ROOT GCC_VERSION unpatched|patched" >&2
  exit 2
fi

root=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
source "$root/scripts/regression-common.sh"
resolve_regression_toolchain "$1"
version=$2
state=$3
[[ "$state" == unpatched || "$state" == patched ]] || {
  echo "error: state must be unpatched or patched" >&2
  exit 2
}

gxx="$REGRESSION_TOOLCHAIN/bin/g++"
[[ -x "$gxx" ]] || {
  echo "error: no g++ in $REGRESSION_TOOLCHAIN" >&2
  exit 1
}

cxx_fixture="$root/bundles/cxx-ada-virtual-inheritance-layout/tests/virtual-inheritance-layout.C"
ada_fixture="$root/bundles/cxx-ada-virtual-inheritance-layout/tests/virtual_inheritance_layout_consumer.adb"
work=$(mktemp -d "${TMPDIR:-/tmp}/gnat-cxx-ada-virtual-layout.XXXXXX")
trap 'rm -rf "$work"' EXIT

for optimization in 0 2; do
  case_dir="$work/O$optimization"
  mkdir -p "$case_dir"
  cp "$cxx_fixture" "$ada_fixture" "$case_dir/"
  (
    cd "$case_dir"
    "${REGRESSION_ENV[@]}" "$gxx" -c "-O$optimization" \
      -fdump-ada-spec-slim virtual-inheritance-layout.C
    "${REGRESSION_ENV[@]}" "$REGRESSION_GNATMAKE" -q -f \
      "-O$optimization" virtual_inheritance_layout_consumer.adb \
      -largs virtual-inheritance-layout.o -lstdc++
    "${REGRESSION_ENV[@]}" ./virtual_inheritance_layout_consumer
  ) >"$case_dir/output.log" 2>&1 || {
    cat "$case_dir/output.log"
    exit 1
  }

  spec="$case_dir/virtual_inheritance_layout_c.ads"
  if [[ "$state" == unpatched ]]; then
    grep -Fx "MISMATCH C++ Ada virtual inheritance" "$case_dir/output.log"
    if grep -Fq "for Virtual'Object_Size use" "$spec"; then
      echo "error: unpatched mapper unexpectedly emitted virtual layout" >&2
      exit 1
    fi
    echo "cxx-ada-virtual-inheritance-layout -O$optimization: expected mismatch (GCC $version)"
    continue
  fi

  grep -F "for Virtual'Size use" "$spec"
  grep -F "for Virtual'Object_Size use" "$spec"
  grep -F "for Virtual'Alignment use" "$spec"
  grep -F "derived_value at" "$spec"
  grep -F "field_2 at" "$spec"
  grep -Fx "MATCH C++ Ada virtual inheritance" "$case_dir/output.log"
  echo "cxx-ada-virtual-inheritance-layout -O$optimization: patched (GCC $version)"
done

Download · View in repository

Commands.

Apply the patch
patch --fuzz=0 -p1 -i bundles/cxx-ada-virtual-inheritance-layout/patches/VARIANT.patch
Build the compiler
PATH=/path/to/bootstrap/bin:$PATH ./scripts/build-gnat.sh SOURCE BUILD INSTALL
Run the regression
./scripts/run-regressions.sh INSTALL 1.2.0 GCC_MAJOR patched

Metadata.