cxx-ada-enclosing-type-method-names

C++ Ada enclosing-type method names

The C++ Ada mapper emits a method whose name differs from its enclosing type only by case as an illegal duplicate Ada identifier.

AcceptedApplies in patchset orderSince 1.2.0

Where it applies.

How each patchset treats this bundle on each GCC major
PatchsetGCC 13GCC 14GCC 15GCC 16
1.2.0 (latest)Patchedgcc-13-1413.2.0Patchedgcc-13-1414.2.0Patchedgcc-15-1615.3.0Patchedgcc-15-1616.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 identifiers are case-insensitive. A C++ method whose spelling differs from its enclosing type only by case therefore collides with the generated Ada type.

struct Left
{
  virtual int left ();
};

The unpatched mapper produces invalid Ada:

package Class_Left is
   type Left is tagged limited record
      null;
   end record;

   function left (this : access Left) return int;
end Class_Left;

The corrected output gives only the colliding method a stable descriptive suffix while preserving its C++ external symbol:

package Class_Left is
   type Left is tagged limited record
      null;
   end record;

   function left_Method (this : access Left) return int
   with Import => True,
        Convention => CPP,
        External_Name => "_ZN4Left4leftEv";
end Class_Left;

The executable regression covers two independent collisions inside concrete base classes and also includes a multiply inherited class. At -O0 and -O2, the patched Ada compiles, calls both renamed C++ methods on C++-allocated base objects, and verifies their return values.

Patch.

Variant gcc-13-14

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

+68 −1 2 files

Download the patch

gcc/c-family/c-ada-spec.cc +28−1modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -3294 +3294 @@print_method_qualifiers (pretty_printer *buffer, tree t)
32943294 pp_string (buffer, "_Rvalue");
32953295}
32963296
3297Added line. /* Return true if METHOD_NAME collides with the enclosing C++ TYPE name after
3298Added line. Ada's case-insensitive identifier conversion. */
3299Added line.
3300Added line. static bool
3301Added line. method_name_matches_type (tree method_name, tree type)
3302Added line. {
3303Added line. if (!method_name || !RECORD_OR_UNION_TYPE_P (type) || !TYPE_NAME (type))
3304Added line. return false;
3305Added line.
3306Added line. tree type_decl = TYPE_NAME (type);
3307Added line. tree type_name_node = TREE_CODE (type_decl) == TYPE_DECL
3308Added line. ? DECL_NAME (type_decl) : type_decl;
3309Added line. if (!type_name_node || TREE_CODE (type_name_node) != IDENTIFIER_NODE)
3310Added line. return false;
3311Added line.
3312Added line. char *method_ada = to_ada_name (IDENTIFIER_POINTER (method_name), NULL);
3313Added line. char *type_ada = to_ada_name (IDENTIFIER_POINTER (type_name_node), NULL);
3314Added line. const bool matches = !strcasecmp (method_ada, type_ada);
3315Added line. free (method_ada);
3316Added line. free (type_ada);
3317Added line. return matches;
3318Added line. }
3319Added line.
32973320/* Return the name of type T. */
32983321
32993322static const char *
@@ -3636 +3659 @@dump_ada_declaration (pretty_printer *buffer, tree t, tree type, int spc)
36363659 const unsigned int suffix = overloading_index (decl_name);
36373660 pp_ada_tree_identifier (buffer, decl_name, t, false);
36383661 if (is_method)
3639Removed line. print_method_qualifiers (buffer, t);
3662Added line. {
3663Added line. print_method_qualifiers (buffer, t);
3664Added line. if (method_name_matches_type (decl_name, type))
3665Added line. pp_string (buffer, "_Method");
3666Added line. }
36403667 if (suffix > 1)
36413668 pp_decimal_int (buffer, suffix);
36423669 }
gcc/testsuite/g++.dg/ada-spec/enclosing-type-method-names.C +40−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/enclosing-type-method-names.C: original line, patched line, change, source
@@ -0 +1 @@
1Added line. /* { dg-do compile } */
2Added line. /* { dg-options "-fdump-ada-spec-slim" } */
3Added line. /* { dg-final { scan-file enclosing_type_method_names_c.ads "function left_Method" } } */
4Added line. /* { dg-final { scan-file enclosing_type_method_names_c.ads "function right_Method" } } */
5Added line.
6Added line. struct Left
7Added line. {
8Added line. virtual int left ();
9Added line. int l;
10Added line. };
11Added line.
12Added line. struct Right
13Added line. {
14Added line. virtual int right ();
15Added line. int r;
16Added line. };
17Added line.
18Added line. struct Both : Left, Right
19Added line. {
20Added line. int both;
21Added line. };
22Added line.
23Added line. int Left::left () { return l; }
24Added line. int Right::right () { return r; }
25Added line. extern "C" Left *cpp_create_left (int value)
26Added line. {
27Added line. Left *object = new Left;
28Added line. object->l = value;
29Added line. return object;
30Added line. }
31Added line. extern "C" Right *cpp_create_right (int value)
32Added line. {
33Added line. Right *object = new Right;
34Added line. object->r = value;
35Added line. return object;
36Added line. }
37Added line. extern "C" void cpp_delete_left (Left *object) { delete object; }
38Added line. extern "C" void cpp_delete_right (Right *object) { delete object; }
39Added line.
40Added line. /* { dg-final { cleanup-ada-spec } } */
41

Variant gcc-15-16

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

+68 −1 2 files

Download the patch

gcc/c-family/c-ada-spec.cc +28−1modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -3358 +3358 @@print_method_qualifiers (pretty_printer *pp, tree t)
33583358 pp_string (pp, "_Rvalue");
33593359}
33603360
3361Added line. /* Return true if METHOD_NAME collides with the enclosing C++ TYPE name after
3362Added line. Ada's case-insensitive identifier conversion. */
3363Added line.
3364Added line. static bool
3365Added line. method_name_matches_type (tree method_name, tree type)
3366Added line. {
3367Added line. if (!method_name || !RECORD_OR_UNION_TYPE_P (type) || !TYPE_NAME (type))
3368Added line. return false;
3369Added line.
3370Added line. tree type_decl = TYPE_NAME (type);
3371Added line. tree type_name_node = TREE_CODE (type_decl) == TYPE_DECL
3372Added line. ? DECL_NAME (type_decl) : type_decl;
3373Added line. if (!type_name_node || TREE_CODE (type_name_node) != IDENTIFIER_NODE)
3374Added line. return false;
3375Added line.
3376Added line. char *method_ada = to_ada_name (IDENTIFIER_POINTER (method_name), NULL);
3377Added line. char *type_ada = to_ada_name (IDENTIFIER_POINTER (type_name_node), NULL);
3378Added line. const bool matches = !strcasecmp (method_ada, type_ada);
3379Added line. free (method_ada);
3380Added line. free (type_ada);
3381Added line. return matches;
3382Added line. }
3383Added line.
33613384/* Return the name of type T. */
33623385
33633386static const char *
@@ -3700 +3723 @@dump_ada_declaration (pretty_printer *pp, tree t, tree type, int spc)
37003723 const unsigned int suffix = overloading_index (decl_name);
37013724 pp_ada_tree_identifier (pp, decl_name, t, false);
37023725 if (is_method)
3703Removed line. print_method_qualifiers (pp, t);
3726Added line. {
3727Added line. print_method_qualifiers (pp, t);
3728Added line. if (method_name_matches_type (decl_name, type))
3729Added line. pp_string (pp, "_Method");
3730Added line. }
37043731 if (suffix > 1)
37053732 pp_decimal_int (pp, suffix);
37063733 }
gcc/testsuite/g++.dg/ada-spec/enclosing-type-method-names.C +40−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/enclosing-type-method-names.C: original line, patched line, change, source
@@ -0 +1 @@
1Added line. /* { dg-do compile } */
2Added line. /* { dg-options "-fdump-ada-spec-slim" } */
3Added line. /* { dg-final { scan-file enclosing_type_method_names_c.ads "function left_Method" } } */
4Added line. /* { dg-final { scan-file enclosing_type_method_names_c.ads "function right_Method" } } */
5Added line.
6Added line. struct Left
7Added line. {
8Added line. virtual int left ();
9Added line. int l;
10Added line. };
11Added line.
12Added line. struct Right
13Added line. {
14Added line. virtual int right ();
15Added line. int r;
16Added line. };
17Added line.
18Added line. struct Both : Left, Right
19Added line. {
20Added line. int both;
21Added line. };
22Added line.
23Added line. int Left::left () { return l; }
24Added line. int Right::right () { return r; }
25Added line. extern "C" Left *cpp_create_left (int value)
26Added line. {
27Added line. Left *object = new Left;
28Added line. object->l = value;
29Added line. return object;
30Added line. }
31Added line. extern "C" Right *cpp_create_right (int value)
32Added line. {
33Added line. Right *object = new Right;
34Added line. object->r = value;
35Added line. return object;
36Added line. }
37Added line. extern "C" void cpp_delete_left (Left *object) { delete object; }
38Added line. extern "C" void cpp_delete_right (Right *object) { delete object; }
39Added line.
40Added line. /* { dg-final { cleanup-ada-spec } } */
41

Tests.

enclosing-type-method-names.C C++ · 40 lines
/* { dg-do compile } */
/* { dg-options "-fdump-ada-spec-slim" } */
/* { dg-final { scan-file enclosing_type_method_names_c.ads "function left_Method" } } */
/* { dg-final { scan-file enclosing_type_method_names_c.ads "function right_Method" } } */

struct Left
{
  virtual int left ();
  int l;
};

struct Right
{
  virtual int right ();
  int r;
};

struct Both : Left, Right
{
  int both;
};

int Left::left () { return l; }
int Right::right () { return r; }
extern "C" Left *cpp_create_left (int value)
{
  Left *object = new Left;
  object->l = value;
  return object;
}
extern "C" Right *cpp_create_right (int value)
{
  Right *object = new Right;
  object->r = value;
  return object;
}
extern "C" void cpp_delete_left (Left *object) { delete object; }
extern "C" void cpp_delete_right (Right *object) { delete object; }

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

Download · View in repository

enclosing_type_method_names_consumer.adb Ada · 22 lines
with Ada.Text_IO;
with Enclosing_Type_Method_Names_C;
with Interfaces.C; use Interfaces.C;

procedure Enclosing_Type_Method_Names_Consumer is
   package Bindings renames Enclosing_Type_Method_Names_C;
   Left_Object : access Bindings.Class_Left.Left :=
     Bindings.cpp_create_left (41);
   Right_Object : access Bindings.Class_Right.Right :=
     Bindings.cpp_create_right (73);
begin
   if Left_Object = null or else Right_Object = null
     or else Bindings.Class_Left.left_Method (Left_Object) /= 41
     or else Bindings.Class_Right.right_Method (Right_Object) /= 73
   then
      raise Program_Error with "renamed C++ method call differs";
   end if;

   Bindings.cpp_delete_left (Left_Object);
   Bindings.cpp_delete_right (Right_Object);
   Ada.Text_IO.Put_Line ("MATCH enclosing type and method names");
end Enclosing_Type_Method_Names_Consumer;

Download · View in repository

run-test.sh shell · 34 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-enclosing-type-method-names/tests/enclosing-type-method-names.C"
ada="$root/bundles/cxx-ada-enclosing-type-method-names/tests/enclosing_type_method_names_consumer.adb"
work=$(mktemp -d "${TMPDIR:-/tmp}/gnat-cxx-ada-type-method.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 enclosing-type-method-names.C)
  if [[ "$state" == unpatched ]]; then
    set +e
    (cd "$dir"; "${REGRESSION_ENV[@]}" "$REGRESSION_GNATMAKE" -q -f -c enclosing_type_method_names_consumer.adb) >"$dir/build.log" 2>&1
    status=$?; set -e
    [[ $status -ne 0 ]] || { echo "error: unpatched binding compiled" >&2; exit 1; }
    grep -Eiq '(left|right).*conflicts' "$dir/build.log"
    echo "cxx-ada-enclosing-type-method-names -O$optimization: expected collision (GCC $version)"
  else
    (cd "$dir"; "${REGRESSION_ENV[@]}" "$REGRESSION_GNATMAKE" -q -f "-O$optimization" enclosing_type_method_names_consumer.adb -largs enclosing-type-method-names.o -lstdc++; "${REGRESSION_ENV[@]}" ./enclosing_type_method_names_consumer) >"$dir/output.log" 2>&1 || { cat "$dir/output.log"; exit 1; }
    grep -F "function left_Method" "$dir/enclosing_type_method_names_c.ads"
    grep -F "function right_Method" "$dir/enclosing_type_method_names_c.ads"
    grep -Fx "MATCH enclosing type and method names" "$dir/output.log"
    echo "cxx-ada-enclosing-type-method-names -O$optimization: patched (GCC $version)"
  fi
done

Download · View in repository

Commands.

Apply the patch
patch --fuzz=0 -p1 -i bundles/cxx-ada-enclosing-type-method-names/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.