cxx-ada-concrete-multiple-inheritance

C++ Ada concrete multiple inheritance

The C++ Ada mapper emits multiple concrete tagged bases as illegal Ada progenitors and omits their secondary subobject storage.

StagedApplies in patchset orderStaged in 1.2.0

Why this is staged.

Concrete secondary bases are represented as fixed nested as-base storage rather than native Ada multiple inheritance, so the binding is ABI-faithful but not fully type-safe. 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.

Ada tagged types can have one concrete parent and any number of interface progenitors, but C++ permits several concrete bases. The mapper currently prints every tagged C++ base as an Ada progenitor and omits their storage from the record body.

For example, this class has a primary Left subobject, a secondary Right subobject, and a member that reuses Right's complete-object tail padding:

class Left { virtual int left_value (); int l; };
class Right { virtual int right_value (); int r; };

class Both : public Left, public Right
{
  int left_value () override;
  int right_value () override;
  int both;
};

The unpatched mapper produces an illegal Ada derivation. Right is a concrete tagged type, not an interface, and no component describes its secondary subobject:

type Both is limited new Left and Right with record
   both : aliased int;
end record
with Import => True,
     Convention => CPP;

The corrected mapper uses the address-zero C++ primary base as Ada's one concrete parent. It emits the secondary base as a nested storage view whose 12-byte size excludes complete-object tail padding, then fixes every visible component at its C++ ABI offset:

type Right_As_Base is record
   r : int;
end record
with Convention => C_Pass_By_Copy;
pragma Component_Alignment (Storage_Unit, Right_As_Base);
for Right_As_Base'Size use 96;
for Right_As_Base'Object_Size use 96;
for Right_As_Base'Alignment use 4;
for Right_As_Base use record
   r at 8 range 0 .. 31;
end record;

type Both is limited new Left with record
   field_2 : aliased Right_As_Base;
   both : aliased int;
end record
with Import => True,
     Convention => CPP;
for Both'Object_Size use 256;
for Both use record
   field_2 at 16 range 0 .. 95;
   both at 28 range 0 .. 31;
end record;

The storage view intentionally is not another Ada tagged parent. To dispatch through the secondary C++ base, Ada converts field_2'Unchecked_Access from an access-to-storage type to access Right'Class with an instantiation of Ada.Unchecked_Conversion. The address does not change: the secondary vtable entry performs C++'s required adjustment back to Both.

Interoperability boundary

Fixed-layout concrete multiple inheritance does not require a C++ wrapper for secondary-base field access or virtual dispatch. Ada can take the address of the generated nested storage view and dispatch through the converted access Right'Class value directly.

This is ABI-faithful binding support, not native or fully type-safe Ada multiple inheritance. The conversion is deliberately unchecked, and its validity depends on the generated component retaining the compiler-provided C++ subobject offset and layout. The regression uses C++ helpers to construct and destroy a real C++ object and to provide an independent cross-language oracle; those helpers are not needed merely to invoke Right's virtual method from Ada.

A virtual base is different: its address may require a runtime lookup through the object's C++ ABI metadata. Such a dynamic virtual-base conversion cannot be represented by a fixed nested component and still requires an imported C++ wrapper or equivalent ABI-aware thunk.

The executable regression runs at -O0 and -O2. It compares C++ sizeof and the secondary-base offset with Ada's object size and component address, dispatches through the primary, complete, and converted secondary views, writes every visible field from Ada, and reads all values through C++. Dynamic virtual-base conversions remain an ABI operation rather than a fixed nested-component view.

Patch.

Variant gcc-13-14

Applies to 13.2.0, 14.2.0. Source flavors: linux, darwin_arm64.

+273 −45 4 files

Download the patch

gcc/c-family/c-ada-spec.cc +189−45modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -40 +40 @@static void dump_ada_structure (pretty_printer *, tree, tree, bool, int);
4040static void dump_nested_types (pretty_printer *, tree, int);
4141static char *to_ada_name (const char *, bool *);
4242static bool needs_empty_class_layout (tree);
43Added line. static bool maps_to_ada_tagged_type (tree);
4344
4445#define INDENT(SPACE) \
4546 do { int i; for (i = 0; i<SPACE; i++) pp_space (buffer); } while (0)
@@ -1230 +1231 @@is_tagged_type (const_tree type)
12301231 return false;
12311232}
12321233
1234Added line. /* Return whether TYPE maps to an Ada interface: it contains only a virtual
1235Added line. table field and abstract methods. */
1236Added line.
1237Added line. static bool
1238Added line. is_ada_interface_type (tree type)
1239Added line. {
1240Added line. if (!cpp_check || !RECORD_OR_UNION_TYPE_P (type))
1241Added line. return false;
1242Added line.
1243Added line. bool has_fields = false;
1244Added line. bool is_interface = false;
1245Added line. for (tree fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld))
1246Added line. {
1247Added line. if (TREE_CODE (fld) == FIELD_DECL)
1248Added line. {
1249Added line. if (!has_fields && DECL_VIRTUAL_P (fld))
1250Added line. is_interface = true;
1251Added line. else
1252Added line. is_interface = false;
1253Added line. has_fields = true;
1254Added line. }
1255Added line. else if (TREE_CODE (fld) == FUNCTION_DECL && !DECL_ARTIFICIAL (fld)
1256Added line. && !cpp_check (fld, IS_ABSTRACT))
1257Added line. is_interface = false;
1258Added line. }
1259Added line.
1260Added line. return is_interface;
1261Added line. }
1262Added line.
12331263/* Return whether TYPE has non-trivial methods, i.e. methods that do something
12341264 for the objects of TYPE. In C++, all classes have implicit special methods,
12351265 e.g. constructors and destructors, but they can be trivial if the type is
@@ -2084 +2114 @@dump_ada_function_declaration (pretty_printer *buffer, tree func,
20842114 slot in the virtual table or is a constructor. */
20852115 if (TREE_TYPE (arg)
20862116 && POINTER_TYPE_P (TREE_TYPE (arg))
2087Removed line. && is_tagged_type (TREE_TYPE (TREE_TYPE (arg)))
2117Added line. && maps_to_ada_tagged_type (TREE_TYPE (TREE_TYPE (arg)))
20882118 && !(num == 1 && is_method && (DECL_VINDEX (func) || is_constructor)))
20892119 pp_string (buffer, "'Class");
20902120
@@ -2373 +2403 @@dump_ada_template (pretty_printer *buffer, tree t, int spc)
23732403 dump_ada_node (buffer, instance, t, spc, false, true);
23742404 package_prefix = true;
23752405
2376Removed line. if (is_tagged_type (instance))
2406Added line. if (maps_to_ada_tagged_type (instance))
23772407 pp_string (buffer, " is tagged limited ");
23782408 else
23792409 pp_string (buffer, " is limited ");
@@ -3010 +3040 @@dump_ada_node (pretty_printer *buffer, tree node, tree type, int spc,
30103040 dump_ada_decl_name (buffer, node, limited_access);
30113041 else
30123042 {
3013Removed line. if (is_tagged_type (TREE_TYPE (node)))
3043Added line. if (maps_to_ada_tagged_type (TREE_TYPE (node)))
30143044 {
30153045 int first = true;
3046Added line. tree node_type = TREE_TYPE (node);
30163047
3017Removed line. /* Look for ancestors. */
3018Removed line. for (tree fld = TYPE_FIELDS (TREE_TYPE (node));
3048Added line. /* Ada supports one concrete tagged parent. Use the C++ primary
3049Added line. base, whose address is the complete object's address. */
3050Added line. for (tree fld = TYPE_FIELDS (node_type);
30193051 fld;
30203052 fld = TREE_CHAIN (fld))
3021Removed line. {
3022Removed line. if (!DECL_NAME (fld) && is_tagged_type (TREE_TYPE (fld)))
3023Removed line. {
3024Removed line. if (first)
3025Removed line. {
3026Removed line. pp_string (buffer, "limited new ");
3027Removed line. first = false;
3028Removed line. }
3029Removed line. else
3030Removed line. pp_string (buffer, " and ");
3053Added line. if (cpp_check (fld, IS_PRIMARY_BASE_FIELD)
3054Added line. && is_tagged_type (TREE_TYPE (fld)))
3055Added line. {
3056Added line. pp_string (buffer, "limited new ");
3057Added line. dump_ada_decl_name (buffer, TYPE_NAME (TREE_TYPE (fld)), false);
3058Added line. first = false;
3059Added line. break;
3060Added line. }
30313061
3032Removed line. dump_ada_decl_name (buffer, TYPE_NAME (TREE_TYPE (fld)),
3033Removed line. false);
3034Removed line. }
3035Removed line. }
3062Added line. /* Data-free abstract secondary bases map to Ada interfaces. */
3063Added line. for (tree fld = TYPE_FIELDS (node_type);
3064Added line. fld;
3065Added line. fld = TREE_CHAIN (fld))
3066Added line. if (cpp_check (fld, IS_BASE_FIELD)
3067Added line. && !cpp_check (fld, IS_PRIMARY_BASE_FIELD)
3068Added line. && is_ada_interface_type (TREE_TYPE (fld)))
3069Added line. {
3070Added line. pp_string (buffer, first ? "limited new " : " and ");
3071Added line. dump_ada_decl_name (buffer, TYPE_NAME (TREE_TYPE (fld)), false);
3072Added line. first = false;
3073Added line. }
30363074
30373075 pp_string (buffer, first ? "tagged limited " : " with ");
30383076 }
@@ -3173 +3211 @@is_abi_ignored_empty_field (tree node, tree field)
31733211 return false;
31743212}
31753213
3176Removed line. /* Return true if FIELD is represented as a component in the generated Ada
3177Removed line. record. Keep this in step with dump_ada_structure. */
3214Added line. /* Return true when NODE places another field in FIELD's complete-object tail
3215Added line. padding. Ada extensions cannot reproduce that overlap with their parent. */
3216Added line.
3217Added line. static bool
3218Added line. primary_base_tail_reused (tree node, tree field)
3219Added line. {
3220Added line. const HOST_WIDE_INT object_size = int_size_in_bytes (TREE_TYPE (field));
3221Added line. if (object_size <= 0)
3222Added line. return false;
3223Added line.
3224Added line. for (tree other = TYPE_FIELDS (node); other; other = TREE_CHAIN (other))
3225Added line. if (other != field && TREE_CODE (other) == FIELD_DECL
3226Added line. && !DECL_VIRTUAL_P (other) && DECL_FIELD_OFFSET (other)
3227Added line. && DECL_FIELD_BIT_OFFSET (other)
3228Added line. && tree_fits_uhwi_p (DECL_FIELD_OFFSET (other))
3229Added line. && tree_fits_uhwi_p (DECL_FIELD_BIT_OFFSET (other)))
3230Added line. {
3231Added line. const unsigned HOST_WIDE_INT bitpos
3232Added line. = tree_to_uhwi (DECL_FIELD_OFFSET (other)) * BITS_PER_UNIT
3233Added line. + tree_to_uhwi (DECL_FIELD_BIT_OFFSET (other));
3234Added line. if (bitpos > 0
3235Added line. && bitpos < ((unsigned HOST_WIDE_INT) object_size
3236Added line. * BITS_PER_UNIT))
3237Added line. return true;
3238Added line. }
3239Added line.
3240Added line. return false;
3241Added line. }
3242Added line.
3243Added line. /* Return true if FIELD is a concrete C++ base that NODE must represent as a
3244Added line. component. Ada inheritance can represent the primary base only when NODE
3245Added line. maps to a tagged type without overlapping the primary base's tail. */
31783246
31793247static bool
3180Removed line. is_ada_record_field (tree node, tree field)
3248Added line. is_concrete_nested_base_field (tree node, tree field)
3249Added line. {
3250Added line. return (cpp_check && TREE_CODE (field) == FIELD_DECL
3251Added line. && cpp_check (field, IS_BASE_FIELD)
3252Added line. && (!is_tagged_type (node)
3253Added line. || !cpp_check (field, IS_PRIMARY_BASE_FIELD)
3254Added line. || primary_base_tail_reused (node, field))
3255Added line. && !is_ada_interface_type (TREE_TYPE (field))
3256Added line. && is_tagged_type (TREE_TYPE (field)));
3257Added line. }
3258Added line.
3259Added line. /* Return true if NODE can use Ada tagged inheritance without losing a C++
3260Added line. primary base's reused tail padding. */
3261Added line.
3262Added line. static bool
3263Added line. maps_to_ada_tagged_type (tree node)
3264Added line. {
3265Added line. if (!is_tagged_type (node))
3266Added line. return false;
3267Added line.
3268Added line. for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
3269Added line. if (cpp_check && cpp_check (field, IS_PRIMARY_BASE_FIELD)
3270Added line. && is_concrete_nested_base_field (node, field))
3271Added line. return false;
3272Added line. return true;
3273Added line. }
3274Added line.
3275Added line. /* Return true if NODE has a tagged C++ primary base represented by Ada
3276Added line. inheritance rather than an explicit record component. */
3277Added line.
3278Added line. static bool
3279Added line. has_tagged_primary_base (tree node)
3280Added line. {
3281Added line. if (!maps_to_ada_tagged_type (node))
3282Added line. return false;
3283Added line.
3284Added line. for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
3285Added line. if (cpp_check && cpp_check (field, IS_PRIMARY_BASE_FIELD)
3286Added line. && is_tagged_type (TREE_TYPE (field)))
3287Added line. return true;
3288Added line. return false;
3289Added line. }
3290Added line.
3291Added line. /* Return true if NODE has a concrete C++ base that needs a component. */
3292Added line.
3293Added line. static bool
3294Added line. has_concrete_nested_base (tree node)
3295Added line. {
3296Added line. for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
3297Added line. if (is_concrete_nested_base_field (node, field))
3298Added line. return true;
3299Added line. return false;
3300Added line. }
3301Added line.
3302Added line. /* Return true if FIELD is represented as a component in NODE's generated
3303Added line. Ada record. Keep this in step with dump_ada_structure. */
3304Added line.
3305Added line. static bool
3306Added line. is_ada_record_field (tree node, tree field)
31813307{
31823308 if (TREE_CODE (field) != FIELD_DECL
31833309 || DECL_VIRTUAL_P (field)
31843310 || is_abi_ignored_empty_field (node, field))
31853311 return false;
31863312
3187Removed line. return DECL_NAME (field) || !is_tagged_type (TREE_TYPE (field));
3313Added line. return (DECL_NAME (field) || !is_tagged_type (TREE_TYPE (field))
3314Added line. || is_concrete_nested_base_field (node, field));
31883315}
31893316
31903317/* Return true if NODE needs a distinct storage type when used as a base
3191Removed line. subobject. A non-polymorphic class with virtual bases contains those bases
3192Removed line. as a complete object, but excludes them from its as-base size. */
3318Added line. subobject. A class can exclude virtual bases or complete-object tail
3319Added line. padding from its as-base size. */
31933320
31943321static bool
3195Removed line. needs_virtual_as_base_type (tree node)
3322Added line. needs_as_base_storage_type (tree node)
31963323{
3197Removed line. if (!cpp_check || !RECORD_OR_UNION_TYPE_P (node) || is_tagged_type (node)
3198Removed line. || !cpp_check (node, HAS_VIRTUAL_BASE))
3324Added line. if (!cpp_check || !RECORD_OR_UNION_TYPE_P (node))
31993325 return false;
32003326
32013327 const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
@@ -3174 +3300 @@needs_virtual_as_base_type (tree node)
31743300 for this component. */
31753301
31763302static bool
3177Removed line. is_shortened_virtual_base_field (tree field)
3303Added line. is_shortened_base_field (tree field)
31783304{
31793305 if (TREE_CODE (field) != FIELD_DECL || DECL_NAME (field)
3180Removed line. || !needs_virtual_as_base_type (TREE_TYPE (field))
3306Added line. || !needs_as_base_storage_type (TREE_TYPE (field))
31813307 || !DECL_SIZE (field) || !tree_fits_uhwi_p (DECL_SIZE (field)))
31823308 return false;
31833309
@@ -3194 +3320 @@static void
31943320dump_ada_record_field_type (pretty_printer *buffer, tree field)
31953321{
31963322 dump_ada_decl_name (buffer, TYPE_NAME (TREE_TYPE (field)), false);
3197Removed line. if (is_shortened_virtual_base_field (field))
3323Added line. if (is_shortened_base_field (field))
31983324 pp_string (buffer, "_As_Base");
31993325}
32003326
@@ -3204 +3330 @@static bool
32043330field_fits_as_base (tree node, tree field)
32053331{
32063332 const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
3207Removed line. if (data_size < 0 || !is_ada_record_field (node, field)
3333Added line. if (data_size < 0 || !is_ada_record_field (node, field)
32083334 || !DECL_FIELD_OFFSET (field) || !DECL_FIELD_BIT_OFFSET (field)
32093335 || !DECL_SIZE (field)
32103336 || !tree_fits_uhwi_p (DECL_FIELD_OFFSET (field))
@@ -3225 +3351 @@field_fits_as_base (tree node, tree field)
32253351 padding is not part of a base subobject. */
32263352
32273353static unsigned int
3228Removed line. virtual_as_base_alignment (tree node, unsigned int data_size)
3354Added line. as_base_alignment (tree node, unsigned int data_size)
32293355{
32303356 unsigned int alignment = TYPE_ALIGN (node) / BITS_PER_UNIT;
32313357 while (alignment > 1 && data_size % alignment != 0)
@@ -3237 +3363 @@virtual_as_base_alignment (tree node, unsigned int data_size)
32373363 keeps each direct field addressable while excluding shared virtual bases. */
32383364
32393365static void
3240Removed line. dump_ada_virtual_as_base_type (pretty_printer *buffer, tree node, tree type,
3241Removed line. int spc)
3366Added line. dump_ada_as_base_storage_type (pretty_printer *buffer, tree node, tree type,
3367Added line. int spc)
32423368{
32433369 const unsigned int data_size = cpp_check (node, GET_AS_BASE_SIZE);
3244Removed line. int field_num = 0;
3370Added line. int field_num = has_tagged_primary_base (node) ? 1 : 0;
32453371
32463372 pp_newline (buffer);
32473373 newline_and_indent (buffer, spc);
@@ -3297 +3423 @@dump_ada_virtual_as_base_type (pretty_printer *buffer, tree node, tree type,
32973423 newline_and_indent (buffer, spc);
32983424 pp_string (buffer, "with Convention => C_Pass_By_Copy;");
32993425
3426Added line. if (as_base_alignment (node, data_size)
3427Added line. < TYPE_ALIGN (node) / BITS_PER_UNIT)
3428Added line. {
3429Added line. newline_and_indent (buffer, spc);
3430Added line. pp_string (buffer, "pragma Component_Alignment (Storage_Unit, ");
3431Added line. dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
3432Added line. pp_string (buffer, "_As_Base);");
3433Added line. }
3434Added line.
33003435 newline_and_indent (buffer, spc);
33013436 pp_string (buffer, "for ");
33023437 dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
@@ -3315 +3450 @@dump_ada_virtual_as_base_type (pretty_printer *buffer, tree node, tree type,
33153450 pp_string (buffer, "for ");
33163451 dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
33173452 pp_string (buffer, "_As_Base'Alignment use ");
3318Removed line. pp_decimal_int (buffer, virtual_as_base_alignment (node, data_size));
3453Added line. pp_decimal_int (buffer, as_base_alignment (node, data_size));
33193454 pp_semicolon (buffer);
33203455
33213456 newline_and_indent (buffer, spc);
@@ -3395 +3530 @@static bool
33953530has_constant_field_layout (tree node)
33963531{
33973532 for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
3398Removed line. if (is_ada_record_field (node, field)
3533Added line. if (is_ada_record_field (node, field)
33993534 && (!DECL_FIELD_OFFSET (field)
34003535 || !DECL_FIELD_BIT_OFFSET (field)
34013536 || !DECL_SIZE (field)
@@ -3453 +3588 @@dump_ada_cpp_layout (pretty_printer *buffer, tree node, tree type, int spc,
34533588 dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
34543589 pp_string (buffer, " use record");
34553590
3456Removed line. int field_num = 0;
3591Added line. int field_num = has_tagged_primary_base (node) ? 1 : 0;
34573592 for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
3458Removed line. if (is_ada_record_field (node, field))
3593Added line. if (is_ada_record_field (node, field))
34593594 {
34603595 field_num++;
34613596 const unsigned HOST_WIDE_INT bitpos
@@ -4389 +4524 @@dump_ada_structure (pretty_printer *buffer, tree node, tree type, bool nested,
43894524 const bool is_union = (TREE_CODE (node) == UNION_TYPE);
43904525 char buf[32];
43914526 int field_num = 0;
4527Added line. int fields_emitted = 0;
43924528 int field_spc = spc + INDENT_INCR;
43934529 int need_semicolon;
43944530
@@ -4415 +4551 @@dump_ada_structure (pretty_printer *buffer, tree node, tree type, bool nested,
44154551 /* Add parent field if needed. */
44164552 if (!DECL_NAME (tmp))
44174553 {
4418Removed line. if (!is_tagged_type (TREE_TYPE (tmp)))
4554Added line. if (maps_to_ada_tagged_type (node) && cpp_check
4555Added line. && cpp_check (tmp, IS_PRIMARY_BASE_FIELD)
4556Added line. && is_tagged_type (TREE_TYPE (tmp)))
4557Added line. field_num++;
4558Added line. else if (!is_tagged_type (TREE_TYPE (tmp))
4559Added line. || is_concrete_nested_base_field (node, tmp))
44194560 {
44204561 if (!TYPE_NAME (TREE_TYPE (tmp)))
44214562 dump_ada_declaration (buffer, tmp, type, field_spc);
@@ -4436 +4577 @@dump_ada_structure (pretty_printer *buffer, tree node, tree type, bool nested,
44364577
44374578 pp_newline (buffer);
44384579 field_num++;
4580Added line. fields_emitted++;
44394581 }
44404582 }
44414583 else if (TREE_CODE (tmp) == FIELD_DECL)
@@ -4461 +4603 @@dump_ada_structure (pretty_printer *buffer, tree node, tree type, bool nested,
44614603 {
44624604 pp_newline (buffer);
44634605 field_num++;
4606Added line. fields_emitted++;
44644607 }
44654608 }
44664609 }
@@ -4473 +4616 @@dump_ada_structure (pretty_printer *buffer, tree node, tree type, bool nested,
44734616 pp_newline (buffer);
44744617 }
44754618
4476Removed line. if (field_num == 0)
4619Added line. if (fields_emitted == 0)
44774620 {
44784621 INDENT (spc + INDENT_INCR);
44794622 pp_string (buffer, "null;");
@@ -4543 +4686 @@dump_ada_structure (pretty_printer *buffer, tree node, tree type, bool nested,
45434686 dump_ada_empty_class_layout (buffer, node, type, spc);
45444687 }
45454688 else if ((needs_tail_padding_layout (node)
4546Removed line. || (cpp_check && cpp_check (node, HAS_VIRTUAL_BASE)))
4689Added line. || (cpp_check && cpp_check (node, HAS_VIRTUAL_BASE))
4690Added line. || has_concrete_nested_base (node))
45474691 && has_constant_field_layout (node))
45484692 {
45494693 if (need_semicolon)
@@ -4556 +4700 @@dump_ada_structure (pretty_printer *buffer, tree node, tree type, bool nested,
45564700 dump_ada_cpp_layout (buffer, node, type, spc,
45574701 cpp_check (node, HAS_VIRTUAL_BASE));
45584702
4559Removed line. if (needs_virtual_as_base_type (node))
4560Removed line. dump_ada_virtual_as_base_type (buffer, node, type, spc);
4703Added line. if (needs_as_base_storage_type (node))
4704Added line. dump_ada_as_base_storage_type (buffer, node, type, spc);
45614705 }
45624706
45634707 /* Print the static fields of the structure, if any. */
gcc/c-family/c-ada-spec.h +2−0modified
Unified diff for gcc/c-family/c-ada-spec.h: original line, patched line, change, source
@@ -31 +31 @@enum cpp_operation {
3131 HAS_DEPENDENT_TEMPLATE_ARGS,
3232 IS_ABSTRACT,
3333 IS_ASSIGNMENT_OPERATOR,
34Added line. IS_BASE_FIELD,
3435 IS_CONSTRUCTOR,
3536 IS_DESTRUCTOR,
3637 IS_COPY_CONSTRUCTOR,
3738 IS_MOVE_CONSTRUCTOR,
39Added line. IS_PRIMARY_BASE_FIELD,
3840 IS_TEMPLATE,
3941 IS_TRIVIAL,
4042 REUSES_BASE_TAIL_PADDING
gcc/cp/decl2.cc +18−0modified
Unified diff for gcc/cp/decl2.cc: original line, patched line, change, source
@@ -4593 +4593 @@cpp_check (tree t, cpp_operation op)
45934593 return DECL_PURE_VIRTUAL_P (t);
45944594 case IS_ASSIGNMENT_OPERATOR:
45954595 return DECL_ASSIGNMENT_OPERATOR_P (t);
4596Added line. case IS_BASE_FIELD:
4597Added line. return (TREE_CODE (t) == FIELD_DECL && DECL_FIELD_IS_BASE (t));
45964598 case IS_CONSTRUCTOR:
45974599 return DECL_CONSTRUCTOR_P (t);
45984600 case IS_DESTRUCTOR:
@@ -4601 +4603 @@cpp_check (tree t, cpp_operation op)
46014603 return DECL_COPY_CONSTRUCTOR_P (t);
46024604 case IS_MOVE_CONSTRUCTOR:
46034605 return DECL_MOVE_CONSTRUCTOR_P (t);
4606Added line. case IS_PRIMARY_BASE_FIELD:
4607Added line. {
4608Added line. if (TREE_CODE (t) != FIELD_DECL || !DECL_FIELD_IS_BASE (t)
4609Added line. || !CLASS_TYPE_P (DECL_CONTEXT (t)))
4610Added line. return 0;
4611Added line.
4612Added line. tree binfo;
4613Added line. unsigned int i = 0;
4614Added line. vec<tree, va_gc> *vbases =
4615Added line. CLASSTYPE_VBASECLASSES (DECL_CONTEXT (t));
4616Added line. for (; vec_safe_iterate (vbases, i, &binfo); i++)
4617Added line. if (TREE_TYPE (t) == CLASSTYPE_AS_BASE (BINFO_TYPE (binfo)))
4618Added line. return 0;
4619Added line.
4620Added line. return tree_int_cst_equal (bit_position (t), bitsize_zero_node);
4621Added line. }
46044622 case IS_TEMPLATE:
46054623 return TREE_CODE (t) == TEMPLATE_DECL;
46064624 case IS_TRIVIAL:
gcc/testsuite/g++.dg/ada-spec/concrete-multiple-inheritance.C +64−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/concrete-multiple-inheritance.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 concrete_multiple_inheritance_c.ads "type Both is limited new Left with record" } } */
5Added line. /* { dg-final { scan-file concrete_multiple_inheritance_c.ads "field_2 : aliased Right_As_Base;" } } */
6Added line. /* { dg-final { scan-file concrete_multiple_inheritance_c.ads "for Right_As_Base'Object_Size use 96;" } } */
7Added line. /* { dg-final { scan-file concrete_multiple_inheritance_c.ads "field_2 at 16 range 0 .. 95;" } } */
8Added line.
9Added line. class Left
10Added line. {
11Added line. public:
12Added line. virtual ~Left () = default;
13Added line. virtual int left_value () { return l; }
14Added line. int l;
15Added line. };
16Added line.
17Added line. class Right
18Added line. {
19Added line. public:
20Added line. virtual ~Right () = default;
21Added line. virtual int right_value () { return r; }
22Added line. int r;
23Added line. };
24Added line.
25Added line. class Both : public Left, public Right
26Added line. {
27Added line. public:
28Added line. Both (int l_value, int r_value, int both_value);
29Added line. ~Both () override = default;
30Added line. int left_value () override { return l + both; }
31Added line. int right_value () override { return r + both; }
32Added line. int both;
33Added line. };
34Added line.
35Added line. Both::Both (int l_value, int r_value, int both_value)
36Added line. {
37Added line. l = l_value;
38Added line. r = r_value;
39Added line. both = both_value;
40Added line. }
41Added line.
42Added line. extern "C" Both *cpp_create_both (int l, int r, int both_value)
43Added line. {
44Added line. return new Both (l, r, both_value);
45Added line. }
46Added line.
47Added line. extern "C" void cpp_delete_both (Both *object) { delete object; }
48Added line. extern "C" unsigned long cpp_both_size () { return sizeof (Both); }
49Added line. extern "C" unsigned long cpp_right_offset (Both *object)
50Added line. {
51Added line. return reinterpret_cast<char *> (static_cast<Right *> (object))
52Added line. - reinterpret_cast<char *> (object);
53Added line. }
54Added line. extern "C" int cpp_call_left (Both *object) { return object->left_value (); }
55Added line. extern "C" int cpp_call_right (Right *object) { return object->right_value (); }
56Added line. extern "C" int cpp_call_right_from_both (Both *object)
57Added line. {
58Added line. return static_cast<Right *> (object)->right_value ();
59Added line. }
60Added line. extern "C" int cpp_read_left (Both *object) { return object->l; }
61Added line. extern "C" int cpp_read_right (Both *object) { return object->r; }
62Added line. extern "C" int cpp_read_both (Both *object) { return object->both; }
63Added line.
64Added line. /* { dg-final { cleanup-ada-spec } } */
65

Variant gcc-15-16

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

+273 −45 4 files

Download the patch

gcc/c-family/c-ada-spec.cc +189−45modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -40 +40 @@static void dump_ada_structure (pretty_printer *, tree, tree, bool, int);
4040static void dump_nested_types (pretty_printer *, tree, int);
4141static char *to_ada_name (const char *, bool *);
4242static bool needs_empty_class_layout (tree);
43Added line. static bool maps_to_ada_tagged_type (tree);
4344
4445#define INDENT(SPACE) \
4546 do { int i; for (i = 0; i<SPACE; i++) pp_space (pp); } while (0)
@@ -1283 +1284 @@is_tagged_type (const_tree type)
12831284 return false;
12841285}
12851286
1287Added line. /* Return whether TYPE maps to an Ada interface: it contains only a virtual
1288Added line. table field and abstract methods. */
1289Added line.
1290Added line. static bool
1291Added line. is_ada_interface_type (tree type)
1292Added line. {
1293Added line. if (!cpp_check || !RECORD_OR_UNION_TYPE_P (type))
1294Added line. return false;
1295Added line.
1296Added line. bool has_fields = false;
1297Added line. bool is_interface = false;
1298Added line. for (tree fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld))
1299Added line. {
1300Added line. if (TREE_CODE (fld) == FIELD_DECL)
1301Added line. {
1302Added line. if (!has_fields && DECL_VIRTUAL_P (fld))
1303Added line. is_interface = true;
1304Added line. else
1305Added line. is_interface = false;
1306Added line. has_fields = true;
1307Added line. }
1308Added line. else if (TREE_CODE (fld) == FUNCTION_DECL && !DECL_ARTIFICIAL (fld)
1309Added line. && !cpp_check (fld, IS_ABSTRACT))
1310Added line. is_interface = false;
1311Added line. }
1312Added line.
1313Added line. return is_interface;
1314Added line. }
1315Added line.
12861316/* Return whether TYPE has non-trivial methods, i.e. methods that do something
12871317 for the objects of TYPE. In C++, all classes have implicit special methods,
12881318 e.g. constructors and destructors, but they can be trivial if the type is
@@ -2137 +2167 @@dump_ada_function_declaration (pretty_printer *pp, tree func,
21372167 slot in the virtual table or is a constructor. */
21382168 if (TREE_TYPE (arg)
21392169 && POINTER_TYPE_P (TREE_TYPE (arg))
2140Removed line. && is_tagged_type (TREE_TYPE (TREE_TYPE (arg)))
2170Added line. && maps_to_ada_tagged_type (TREE_TYPE (TREE_TYPE (arg)))
21412171 && !(num == 1 && is_method && (DECL_VINDEX (func) || is_constructor)))
21422172 pp_string (pp, "'Class");
21432173
@@ -2416 +2446 @@dump_ada_template (pretty_printer *pp, tree t, int spc)
24162446 dump_ada_node (pp, instance, t, spc, false, true);
24172447 package_prefix = true;
24182448
2419Removed line. if (is_tagged_type (instance))
2449Added line. if (maps_to_ada_tagged_type (instance))
24202450 pp_string (pp, " is tagged limited ");
24212451 else
24222452 pp_string (pp, " is limited ");
@@ -3081 +3111 @@dump_ada_node (pretty_printer *pp, tree node, tree type, int spc,
30813111 dump_ada_decl_name (pp, node, limited_access);
30823112 else
30833113 {
3084Removed line. if (is_tagged_type (TREE_TYPE (node)))
3114Added line. if (maps_to_ada_tagged_type (TREE_TYPE (node)))
30853115 {
30863116 int first = true;
3117Added line. tree node_type = TREE_TYPE (node);
30873118
3088Removed line. /* Look for ancestors. */
3089Removed line. for (tree fld = TYPE_FIELDS (TREE_TYPE (node));
3119Added line. /* Ada supports one concrete tagged parent. Use the C++ primary
3120Added line. base, whose address is the complete object's address. */
3121Added line. for (tree fld = TYPE_FIELDS (node_type);
30903122 fld;
30913123 fld = TREE_CHAIN (fld))
3092Removed line. {
3093Removed line. if (!DECL_NAME (fld) && is_tagged_type (TREE_TYPE (fld)))
3094Removed line. {
3095Removed line. if (first)
3096Removed line. {
3097Removed line. pp_string (pp, "limited new ");
3098Removed line. first = false;
3099Removed line. }
3100Removed line. else
3101Removed line. pp_string (pp, " and ");
3124Added line. if (cpp_check (fld, IS_PRIMARY_BASE_FIELD)
3125Added line. && is_tagged_type (TREE_TYPE (fld)))
3126Added line. {
3127Added line. pp_string (pp, "limited new ");
3128Added line. dump_ada_decl_name (pp, TYPE_NAME (TREE_TYPE (fld)), false);
3129Added line. first = false;
3130Added line. break;
3131Added line. }
31023132
3103Removed line. dump_ada_decl_name (pp, TYPE_NAME (TREE_TYPE (fld)),
3104Removed line. false);
3105Removed line. }
3106Removed line. }
3133Added line. /* Data-free abstract secondary bases map to Ada interfaces. */
3134Added line. for (tree fld = TYPE_FIELDS (node_type);
3135Added line. fld;
3136Added line. fld = TREE_CHAIN (fld))
3137Added line. if (cpp_check (fld, IS_BASE_FIELD)
3138Added line. && !cpp_check (fld, IS_PRIMARY_BASE_FIELD)
3139Added line. && is_ada_interface_type (TREE_TYPE (fld)))
3140Added line. {
3141Added line. pp_string (pp, first ? "limited new " : " and ");
3142Added line. dump_ada_decl_name (pp, TYPE_NAME (TREE_TYPE (fld)), false);
3143Added line. first = false;
3144Added line. }
31073145
31083146 pp_string (pp, first ? "tagged limited " : " with ");
31093147 }
@@ -3244 +3282 @@is_abi_ignored_empty_field (tree node, tree field)
32443282 return false;
32453283}
32463284
3247Removed line. /* Return true if FIELD is represented as a component in the generated Ada
3248Removed line. record. Keep this in step with dump_ada_structure. */
3285Added line. /* Return true when NODE places another field in FIELD's complete-object tail
3286Added line. padding. Ada extensions cannot reproduce that overlap with their parent. */
3287Added line.
3288Added line. static bool
3289Added line. primary_base_tail_reused (tree node, tree field)
3290Added line. {
3291Added line. const HOST_WIDE_INT object_size = int_size_in_bytes (TREE_TYPE (field));
3292Added line. if (object_size <= 0)
3293Added line. return false;
3294Added line.
3295Added line. for (tree other = TYPE_FIELDS (node); other; other = TREE_CHAIN (other))
3296Added line. if (other != field && TREE_CODE (other) == FIELD_DECL
3297Added line. && !DECL_VIRTUAL_P (other) && DECL_FIELD_OFFSET (other)
3298Added line. && DECL_FIELD_BIT_OFFSET (other)
3299Added line. && tree_fits_uhwi_p (DECL_FIELD_OFFSET (other))
3300Added line. && tree_fits_uhwi_p (DECL_FIELD_BIT_OFFSET (other)))
3301Added line. {
3302Added line. const unsigned HOST_WIDE_INT bitpos
3303Added line. = tree_to_uhwi (DECL_FIELD_OFFSET (other)) * BITS_PER_UNIT
3304Added line. + tree_to_uhwi (DECL_FIELD_BIT_OFFSET (other));
3305Added line. if (bitpos > 0
3306Added line. && bitpos < ((unsigned HOST_WIDE_INT) object_size
3307Added line. * BITS_PER_UNIT))
3308Added line. return true;
3309Added line. }
3310Added line.
3311Added line. return false;
3312Added line. }
3313Added line.
3314Added line. /* Return true if FIELD is a concrete C++ base that NODE must represent as a
3315Added line. component. Ada inheritance can represent the primary base only when NODE
3316Added line. maps to a tagged type without overlapping the primary base's tail. */
32493317
32503318static bool
3251Removed line. is_ada_record_field (tree node, tree field)
3319Added line. is_concrete_nested_base_field (tree node, tree field)
3320Added line. {
3321Added line. return (cpp_check && TREE_CODE (field) == FIELD_DECL
3322Added line. && cpp_check (field, IS_BASE_FIELD)
3323Added line. && (!is_tagged_type (node)
3324Added line. || !cpp_check (field, IS_PRIMARY_BASE_FIELD)
3325Added line. || primary_base_tail_reused (node, field))
3326Added line. && !is_ada_interface_type (TREE_TYPE (field))
3327Added line. && is_tagged_type (TREE_TYPE (field)));
3328Added line. }
3329Added line.
3330Added line. /* Return true if NODE can use Ada tagged inheritance without losing a C++
3331Added line. primary base's reused tail padding. */
3332Added line.
3333Added line. static bool
3334Added line. maps_to_ada_tagged_type (tree node)
3335Added line. {
3336Added line. if (!is_tagged_type (node))
3337Added line. return false;
3338Added line.
3339Added line. for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
3340Added line. if (cpp_check && cpp_check (field, IS_PRIMARY_BASE_FIELD)
3341Added line. && is_concrete_nested_base_field (node, field))
3342Added line. return false;
3343Added line. return true;
3344Added line. }
3345Added line.
3346Added line. /* Return true if NODE has a tagged C++ primary base represented by Ada
3347Added line. inheritance rather than an explicit record component. */
3348Added line.
3349Added line. static bool
3350Added line. has_tagged_primary_base (tree node)
3351Added line. {
3352Added line. if (!maps_to_ada_tagged_type (node))
3353Added line. return false;
3354Added line.
3355Added line. for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
3356Added line. if (cpp_check && cpp_check (field, IS_PRIMARY_BASE_FIELD)
3357Added line. && is_tagged_type (TREE_TYPE (field)))
3358Added line. return true;
3359Added line. return false;
3360Added line. }
3361Added line.
3362Added line. /* Return true if NODE has a concrete C++ base that needs a component. */
3363Added line.
3364Added line. static bool
3365Added line. has_concrete_nested_base (tree node)
3366Added line. {
3367Added line. for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
3368Added line. if (is_concrete_nested_base_field (node, field))
3369Added line. return true;
3370Added line. return false;
3371Added line. }
3372Added line.
3373Added line. /* Return true if FIELD is represented as a component in NODE's generated
3374Added line. Ada record. Keep this in step with dump_ada_structure. */
3375Added line.
3376Added line. static bool
3377Added line. is_ada_record_field (tree node, tree field)
32523378{
32533379 if (TREE_CODE (field) != FIELD_DECL
32543380 || DECL_VIRTUAL_P (field)
32553381 || is_abi_ignored_empty_field (node, field))
32563382 return false;
32573383
3258Removed line. return DECL_NAME (field) || !is_tagged_type (TREE_TYPE (field));
3384Added line. return (DECL_NAME (field) || !is_tagged_type (TREE_TYPE (field))
3385Added line. || is_concrete_nested_base_field (node, field));
32593386}
32603387
32613388/* Return true if NODE needs a distinct storage type when used as a base
3262Removed line. subobject. A non-polymorphic class with virtual bases contains those bases
3263Removed line. as a complete object, but excludes them from its as-base size. */
3389Added line. subobject. A class can exclude virtual bases or complete-object tail
3390Added line. padding from its as-base size. */
32643391
32653392static bool
3266Removed line. needs_virtual_as_base_type (tree node)
3393Added line. needs_as_base_storage_type (tree node)
32673394{
3268Removed line. if (!cpp_check || !RECORD_OR_UNION_TYPE_P (node) || is_tagged_type (node)
3269Removed line. || !cpp_check (node, HAS_VIRTUAL_BASE))
3395Added line. if (!cpp_check || !RECORD_OR_UNION_TYPE_P (node))
32703396 return false;
32713397
32723398 const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
@@ -3245 +3371 @@needs_virtual_as_base_type (tree node)
32453371 for this component. */
32463372
32473373static bool
3248Removed line. is_shortened_virtual_base_field (tree field)
3374Added line. is_shortened_base_field (tree field)
32493375{
32503376 if (TREE_CODE (field) != FIELD_DECL || DECL_NAME (field)
3251Removed line. || !needs_virtual_as_base_type (TREE_TYPE (field))
3377Added line. || !needs_as_base_storage_type (TREE_TYPE (field))
32523378 || !DECL_SIZE (field) || !tree_fits_uhwi_p (DECL_SIZE (field)))
32533379 return false;
32543380
@@ -3265 +3391 @@static void
32653391dump_ada_record_field_type (pretty_printer *pp, tree field)
32663392{
32673393 dump_ada_decl_name (pp, TYPE_NAME (TREE_TYPE (field)), false);
3268Removed line. if (is_shortened_virtual_base_field (field))
3394Added line. if (is_shortened_base_field (field))
32693395 pp_string (pp, "_As_Base");
32703396}
32713397
@@ -3275 +3401 @@static bool
32753401field_fits_as_base (tree node, tree field)
32763402{
32773403 const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
3278Removed line. if (data_size < 0 || !is_ada_record_field (node, field)
3404Added line. if (data_size < 0 || !is_ada_record_field (node, field)
32793405 || !DECL_FIELD_OFFSET (field) || !DECL_FIELD_BIT_OFFSET (field)
32803406 || !DECL_SIZE (field)
32813407 || !tree_fits_uhwi_p (DECL_FIELD_OFFSET (field))
@@ -3296 +3422 @@field_fits_as_base (tree node, tree field)
32963422 padding is not part of a base subobject. */
32973423
32983424static unsigned int
3299Removed line. virtual_as_base_alignment (tree node, unsigned int data_size)
3425Added line. as_base_alignment (tree node, unsigned int data_size)
33003426{
33013427 unsigned int alignment = TYPE_ALIGN (node) / BITS_PER_UNIT;
33023428 while (alignment > 1 && data_size % alignment != 0)
@@ -3308 +3434 @@virtual_as_base_alignment (tree node, unsigned int data_size)
33083434 keeps each direct field addressable while excluding shared virtual bases. */
33093435
33103436static void
3311Removed line. dump_ada_virtual_as_base_type (pretty_printer *pp, tree node, tree type,
3312Removed line. int spc)
3437Added line. dump_ada_as_base_storage_type (pretty_printer *pp, tree node, tree type,
3438Added line. int spc)
33133439{
33143440 const unsigned int data_size = cpp_check (node, GET_AS_BASE_SIZE);
3315Removed line. int field_num = 0;
3441Added line. int field_num = has_tagged_primary_base (node) ? 1 : 0;
33163442
33173443 pp_newline (pp);
33183444 newline_and_indent (pp, spc);
@@ -3368 +3494 @@dump_ada_virtual_as_base_type (pretty_printer *pp, tree node, tree type,
33683494 newline_and_indent (pp, spc);
33693495 pp_string (pp, "with Convention => C_Pass_By_Copy;");
33703496
3497Added line. if (as_base_alignment (node, data_size)
3498Added line. < TYPE_ALIGN (node) / BITS_PER_UNIT)
3499Added line. {
3500Added line. newline_and_indent (pp, spc);
3501Added line. pp_string (pp, "pragma Component_Alignment (Storage_Unit, ");
3502Added line. dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
3503Added line. pp_string (pp, "_As_Base);");
3504Added line. }
3505Added line.
33713506 newline_and_indent (pp, spc);
33723507 pp_string (pp, "for ");
33733508 dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
@@ -3386 +3521 @@dump_ada_virtual_as_base_type (pretty_printer *pp, tree node, tree type,
33863521 pp_string (pp, "for ");
33873522 dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
33883523 pp_string (pp, "_As_Base'Alignment use ");
3389Removed line. pp_decimal_int (pp, virtual_as_base_alignment (node, data_size));
3524Added line. pp_decimal_int (pp, as_base_alignment (node, data_size));
33903525 pp_semicolon (pp);
33913526
33923527 newline_and_indent (pp, spc);
@@ -3465 +3600 @@static bool
34653600has_constant_field_layout (tree node)
34663601{
34673602 for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
3468Removed line. if (is_ada_record_field (node, field)
3603Added line. if (is_ada_record_field (node, field)
34693604 && (!DECL_FIELD_OFFSET (field)
34703605 || !DECL_FIELD_BIT_OFFSET (field)
34713606 || !DECL_SIZE (field)
@@ -3523 +3658 @@dump_ada_cpp_layout (pretty_printer *pp, tree node, tree type, int spc,
35233658 dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
35243659 pp_string (pp, " use record");
35253660
3526Removed line. int field_num = 0;
3661Added line. int field_num = has_tagged_primary_base (node) ? 1 : 0;
35273662 for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
3528Removed line. if (is_ada_record_field (node, field))
3663Added line. if (is_ada_record_field (node, field))
35293664 {
35303665 field_num++;
35313666 const unsigned HOST_WIDE_INT bitpos
@@ -4469 +4604 @@dump_ada_structure (pretty_printer *pp, tree node, tree type, bool nested,
44694604 const bool is_union = (TREE_CODE (node) == UNION_TYPE);
44704605 char buf[32];
44714606 int field_num = 0;
4607Added line. int fields_emitted = 0;
44724608 int field_spc = spc + INDENT_INCR;
44734609 int need_semicolon;
44744610
@@ -4495 +4631 @@dump_ada_structure (pretty_printer *pp, tree node, tree type, bool nested,
44954631 /* Add parent field if needed. */
44964632 if (!DECL_NAME (tmp))
44974633 {
4498Removed line. if (!is_tagged_type (TREE_TYPE (tmp)))
4634Added line. if (maps_to_ada_tagged_type (node) && cpp_check
4635Added line. && cpp_check (tmp, IS_PRIMARY_BASE_FIELD)
4636Added line. && is_tagged_type (TREE_TYPE (tmp)))
4637Added line. field_num++;
4638Added line. else if (!is_tagged_type (TREE_TYPE (tmp))
4639Added line. || is_concrete_nested_base_field (node, tmp))
44994640 {
45004641 if (!TYPE_NAME (TREE_TYPE (tmp)))
45014642 dump_ada_declaration (pp, tmp, type, field_spc);
@@ -4516 +4657 @@dump_ada_structure (pretty_printer *pp, tree node, tree type, bool nested,
45164657
45174658 pp_newline (pp);
45184659 field_num++;
4660Added line. fields_emitted++;
45194661 }
45204662 }
45214663 else if (TREE_CODE (tmp) == FIELD_DECL)
@@ -4541 +4683 @@dump_ada_structure (pretty_printer *pp, tree node, tree type, bool nested,
45414683 {
45424684 pp_newline (pp);
45434685 field_num++;
4686Added line. fields_emitted++;
45444687 }
45454688 }
45464689 }
@@ -4553 +4696 @@dump_ada_structure (pretty_printer *pp, tree node, tree type, bool nested,
45534696 pp_newline (pp);
45544697 }
45554698
4556Removed line. if (field_num == 0)
4699Added line. if (fields_emitted == 0)
45574700 {
45584701 INDENT (spc + INDENT_INCR);
45594702 pp_string (pp, "null;");
@@ -4623 +4766 @@dump_ada_structure (pretty_printer *pp, tree node, tree type, bool nested,
46234766 dump_ada_empty_class_layout (pp, node, type, spc);
46244767 }
46254768 else if ((needs_tail_padding_layout (node)
4626Removed line. || (cpp_check && cpp_check (node, HAS_VIRTUAL_BASE)))
4769Added line. || (cpp_check && cpp_check (node, HAS_VIRTUAL_BASE))
4770Added line. || has_concrete_nested_base (node))
46274771 && has_constant_field_layout (node))
46284772 {
46294773 if (need_semicolon)
@@ -4636 +4780 @@dump_ada_structure (pretty_printer *pp, tree node, tree type, bool nested,
46364780 dump_ada_cpp_layout (pp, node, type, spc,
46374781 cpp_check (node, HAS_VIRTUAL_BASE));
46384782
4639Removed line. if (needs_virtual_as_base_type (node))
4640Removed line. dump_ada_virtual_as_base_type (pp, node, type, spc);
4783Added line. if (needs_as_base_storage_type (node))
4784Added line. dump_ada_as_base_storage_type (pp, node, type, spc);
46414785 }
46424786
46434787 /* Print the static fields of the structure, if any. */
gcc/c-family/c-ada-spec.h +2−0modified
Unified diff for gcc/c-family/c-ada-spec.h: original line, patched line, change, source
@@ -31 +31 @@enum cpp_operation {
3131 HAS_DEPENDENT_TEMPLATE_ARGS,
3232 IS_ABSTRACT,
3333 IS_ASSIGNMENT_OPERATOR,
34Added line. IS_BASE_FIELD,
3435 IS_CONSTRUCTOR,
3536 IS_DESTRUCTOR,
3637 IS_COPY_CONSTRUCTOR,
3738 IS_MOVE_CONSTRUCTOR,
39Added line. IS_PRIMARY_BASE_FIELD,
3840 IS_TEMPLATE,
3941 IS_TRIVIAL,
4042 REUSES_BASE_TAIL_PADDING
gcc/cp/decl2.cc +18−0modified
Unified diff for gcc/cp/decl2.cc: original line, patched line, change, source
@@ -5305 +5305 @@cpp_check (tree t, cpp_operation op)
53055305 return DECL_PURE_VIRTUAL_P (t);
53065306 case IS_ASSIGNMENT_OPERATOR:
53075307 return DECL_ASSIGNMENT_OPERATOR_P (t);
5308Added line. case IS_BASE_FIELD:
5309Added line. return (TREE_CODE (t) == FIELD_DECL && DECL_FIELD_IS_BASE (t));
53085310 case IS_CONSTRUCTOR:
53095311 return DECL_CONSTRUCTOR_P (t);
53105312 case IS_DESTRUCTOR:
@@ -5313 +5315 @@cpp_check (tree t, cpp_operation op)
53135315 return DECL_COPY_CONSTRUCTOR_P (t);
53145316 case IS_MOVE_CONSTRUCTOR:
53155317 return DECL_MOVE_CONSTRUCTOR_P (t);
5318Added line. case IS_PRIMARY_BASE_FIELD:
5319Added line. {
5320Added line. if (TREE_CODE (t) != FIELD_DECL || !DECL_FIELD_IS_BASE (t)
5321Added line. || !CLASS_TYPE_P (DECL_CONTEXT (t)))
5322Added line. return 0;
5323Added line.
5324Added line. tree binfo;
5325Added line. unsigned int i = 0;
5326Added line. vec<tree, va_gc> *vbases =
5327Added line. CLASSTYPE_VBASECLASSES (DECL_CONTEXT (t));
5328Added line. for (; vec_safe_iterate (vbases, i, &binfo); i++)
5329Added line. if (TREE_TYPE (t) == CLASSTYPE_AS_BASE (BINFO_TYPE (binfo)))
5330Added line. return 0;
5331Added line.
5332Added line. return tree_int_cst_equal (bit_position (t), bitsize_zero_node);
5333Added line. }
53165334 case IS_TEMPLATE:
53175335 return TREE_CODE (t) == TEMPLATE_DECL;
53185336 case IS_TRIVIAL:
gcc/testsuite/g++.dg/ada-spec/concrete-multiple-inheritance.C +64−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/concrete-multiple-inheritance.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 concrete_multiple_inheritance_c.ads "type Both is limited new Left with record" } } */
5Added line. /* { dg-final { scan-file concrete_multiple_inheritance_c.ads "field_2 : aliased Right_As_Base;" } } */
6Added line. /* { dg-final { scan-file concrete_multiple_inheritance_c.ads "for Right_As_Base'Object_Size use 96;" } } */
7Added line. /* { dg-final { scan-file concrete_multiple_inheritance_c.ads "field_2 at 16 range 0 .. 95;" } } */
8Added line.
9Added line. class Left
10Added line. {
11Added line. public:
12Added line. virtual ~Left () = default;
13Added line. virtual int left_value () { return l; }
14Added line. int l;
15Added line. };
16Added line.
17Added line. class Right
18Added line. {
19Added line. public:
20Added line. virtual ~Right () = default;
21Added line. virtual int right_value () { return r; }
22Added line. int r;
23Added line. };
24Added line.
25Added line. class Both : public Left, public Right
26Added line. {
27Added line. public:
28Added line. Both (int l_value, int r_value, int both_value);
29Added line. ~Both () override = default;
30Added line. int left_value () override { return l + both; }
31Added line. int right_value () override { return r + both; }
32Added line. int both;
33Added line. };
34Added line.
35Added line. Both::Both (int l_value, int r_value, int both_value)
36Added line. {
37Added line. l = l_value;
38Added line. r = r_value;
39Added line. both = both_value;
40Added line. }
41Added line.
42Added line. extern "C" Both *cpp_create_both (int l, int r, int both_value)
43Added line. {
44Added line. return new Both (l, r, both_value);
45Added line. }
46Added line.
47Added line. extern "C" void cpp_delete_both (Both *object) { delete object; }
48Added line. extern "C" unsigned long cpp_both_size () { return sizeof (Both); }
49Added line. extern "C" unsigned long cpp_right_offset (Both *object)
50Added line. {
51Added line. return reinterpret_cast<char *> (static_cast<Right *> (object))
52Added line. - reinterpret_cast<char *> (object);
53Added line. }
54Added line. extern "C" int cpp_call_left (Both *object) { return object->left_value (); }
55Added line. extern "C" int cpp_call_right (Right *object) { return object->right_value (); }
56Added line. extern "C" int cpp_call_right_from_both (Both *object)
57Added line. {
58Added line. return static_cast<Right *> (object)->right_value ();
59Added line. }
60Added line. extern "C" int cpp_read_left (Both *object) { return object->l; }
61Added line. extern "C" int cpp_read_right (Both *object) { return object->r; }
62Added line. extern "C" int cpp_read_both (Both *object) { return object->both; }
63Added line.
64Added line. /* { dg-final { cleanup-ada-spec } } */
65

Tests.

concrete-multiple-inheritance.C C++ · 64 lines
/* { dg-do compile } */
/* { dg-require-effective-target lp64 } */
/* { dg-options "-fdump-ada-spec-slim" } */
/* { dg-final { scan-file concrete_multiple_inheritance_c.ads "type Both is limited new Left with record" } } */
/* { dg-final { scan-file concrete_multiple_inheritance_c.ads "field_2 : aliased Right_As_Base;" } } */
/* { dg-final { scan-file concrete_multiple_inheritance_c.ads "for Right_As_Base'Object_Size use 96;" } } */
/* { dg-final { scan-file concrete_multiple_inheritance_c.ads "field_2 at 16 range 0 .. 95;" } } */

class Left
{
public:
  virtual ~Left () = default;
  virtual int left_value () { return l; }
  int l;
};

class Right
{
public:
  virtual ~Right () = default;
  virtual int right_value () { return r; }
  int r;
};

class Both : public Left, public Right
{
public:
  Both (int l_value, int r_value, int both_value);
  ~Both () override = default;
  int left_value () override { return l + both; }
  int right_value () override { return r + both; }
  int both;
};

Both::Both (int l_value, int r_value, int both_value)
{
  l = l_value;
  r = r_value;
  both = both_value;
}

extern "C" Both *cpp_create_both (int l, int r, int both_value)
{
  return new Both (l, r, both_value);
}

extern "C" void cpp_delete_both (Both *object) { delete object; }
extern "C" unsigned long cpp_both_size () { return sizeof (Both); }
extern "C" unsigned long cpp_right_offset (Both *object)
{
  return reinterpret_cast<char *> (static_cast<Right *> (object))
    - reinterpret_cast<char *> (object);
}
extern "C" int cpp_call_left (Both *object) { return object->left_value (); }
extern "C" int cpp_call_right (Right *object) { return object->right_value (); }
extern "C" int cpp_call_right_from_both (Both *object)
{
  return static_cast<Right *> (object)->right_value ();
}
extern "C" int cpp_read_left (Both *object) { return object->l; }
extern "C" int cpp_read_right (Both *object) { return object->r; }
extern "C" int cpp_read_both (Both *object) { return object->both; }

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

Download · View in repository

concrete_multiple_inheritance_consumer.adb Ada · 54 lines
with Ada.Text_IO;
with Ada.Unchecked_Conversion;
with Concrete_Multiple_Inheritance_C;
with Interfaces.C; use Interfaces.C;
with System;
with System.Storage_Elements; use System.Storage_Elements;

procedure Concrete_Multiple_Inheritance_Consumer is
   package Bindings renames Concrete_Multiple_Inheritance_C;
   Object : access Bindings.Class_Both.Both :=
     Bindings.cpp_create_both (10, 20, 3);

   type Right_Base_Access is
     access all Bindings.Class_Right.Right_As_Base;
   type Right_Class_Access is access all Bindings.Class_Right.Right'Class;
   function To_Right is new Ada.Unchecked_Conversion
     (Right_Base_Access, Right_Class_Access);

   Right_Object : constant Right_Class_Access :=
     To_Right (Object.field_2'Unchecked_Access);
   Ada_Right_Offset : constant Storage_Offset :=
     Object.field_2'Address - Object.all'Address;
begin
   if Object = null
     or else Bindings.Class_Both.Both'Object_Size / System.Storage_Unit
       /= Bindings.cpp_both_size
     or else unsigned_long (Ada_Right_Offset)
       /= Bindings.cpp_right_offset (Object)
     or else Bindings.Class_Both.left_value (Object) /= 13
     or else Bindings.Class_Both.right_value (Object) /= 23
     or else Bindings.Class_Right.right_value (Right_Object) /= 23
     or else Bindings.cpp_call_left (Object) /= 13
     or else Bindings.cpp_call_right (Right_Object) /= 23
     or else Bindings.cpp_call_right_from_both (Object) /= 23
   then
      raise Program_Error with "nested concrete base dispatch differs";
   end if;

   Object.l := 30;
   Object.field_2.r := 40;
   Object.both := 5;

   if Bindings.cpp_read_left (Object) /= 30
     or else Bindings.cpp_read_right (Object) /= 40
     or else Bindings.cpp_read_both (Object) /= 5
     or else Bindings.cpp_call_left (Object) /= 35
     or else Bindings.cpp_call_right (Right_Object) /= 45
   then
      raise Program_Error with "nested concrete base storage differs";
   end if;

   Bindings.cpp_delete_both (Object);
   Ada.Text_IO.Put_Line ("MATCH nested concrete multiple inheritance");
end Concrete_Multiple_Inheritance_Consumer;

Download · View in repository

run-test.sh shell · 36 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 ]] || exit 2
gxx="$REGRESSION_TOOLCHAIN/bin/g++"
cxx="$root/bundles/cxx-ada-concrete-multiple-inheritance/tests/concrete-multiple-inheritance.C"
ada="$root/bundles/cxx-ada-concrete-multiple-inheritance/tests/concrete_multiple_inheritance_consumer.adb"
work=$(mktemp -d "${TMPDIR:-/tmp}/gnat-cxx-ada-concrete-mi.XXXXXX")
trap 'rm -rf "$work"' EXIT

for optimization in 0 2; do
  dir="$work/O$optimization"; mkdir -p "$dir"; cp "$cxx" "$ada" "$dir/"
  (cd "$dir"; "${REGRESSION_ENV[@]}" "$gxx" -c "-O$optimization" -fdump-ada-spec-slim concrete-multiple-inheritance.C)
  if [[ "$state" == unpatched ]]; then
    grep -F "type Both is limited new Left and Right with record" "$dir/concrete_multiple_inheritance_c.ads"
    set +e
    (cd "$dir"; "${REGRESSION_ENV[@]}" "$REGRESSION_GNATMAKE" -q -f -c concrete_multiple_inheritance_consumer.adb) >"$dir/build.log" 2>&1
    status=$?; set -e
    [[ $status -ne 0 ]] || { echo "error: unpatched concrete MI binding compiled" >&2; exit 1; }
    grep -Eiq '(interface|progenitor)' "$dir/build.log"
    echo "cxx-ada-concrete-multiple-inheritance -O$optimization: expected rejection (GCC $version)"
  else
    grep -F "type Both is limited new Left with record" "$dir/concrete_multiple_inheritance_c.ads"
    grep -F "field_2 : aliased Right_As_Base;" "$dir/concrete_multiple_inheritance_c.ads"
    grep -F "field_2 at 16 range 0 .. 95;" "$dir/concrete_multiple_inheritance_c.ads"
    (cd "$dir"; "${REGRESSION_ENV[@]}" "$REGRESSION_GNATMAKE" -q -f "-O$optimization" concrete_multiple_inheritance_consumer.adb -largs concrete-multiple-inheritance.o -lstdc++; "${REGRESSION_ENV[@]}" ./concrete_multiple_inheritance_consumer) >"$dir/output.log" 2>&1 || { cat "$dir/output.log"; exit 1; }
    grep -Fx "MATCH nested concrete multiple inheritance" "$dir/output.log"
    echo "cxx-ada-concrete-multiple-inheritance -O$optimization: patched (GCC $version)"
  fi
done

Download · View in repository

Commands.

Apply the patch
patch --fuzz=0 -p1 -i bundles/cxx-ada-concrete-multiple-inheritance/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.