cxx-ada-profile-formal-type-names

C++ Ada profile formal/type names

A C++ formal parameter can hide a type used later in the generated Ada subprogram profile, causing GNAT to reject the binding.

AcceptedApplies standaloneSince 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 formal parameters are visible throughout the rest of their subprogram profile. A parameter therefore cannot have the same case-insensitive name as a type used by a later parameter or by the result subtype.

This C++ uses all three affected forms: a constructor result, a function result, and a later formal parameter type:

struct Result { int value; };
struct Widget
{
  Widget (int widget);
  int value;
};

extern "C" Result *make_result (int result);
extern "C" int inspect (int result, Result *item);

The unpatched mapper checks a formal only against that formal's own type and produces illegal Ada profiles:

function New_Widget (widget : int) return Widget;
function make_result (result : int) return access Result;
function inspect (result : int; item : access Result) return int;

GNAT rejects these because widget hides Widget and result hides Result. The corrected mapper compares each formal's converted Ada name with every type from that point to the end of the profile, including the result, and prefixes only a colliding formal:

function New_Widget (the_widget : int) return Widget;
function make_result (the_result : int) return access Result;
function inspect (the_result : int; item : access Result) return int;

The executable regression runs at -O0 and -O2. Stock GCC must generate a binding that GNAT rejects with the profile visibility error. With the patch, Ada creates a C++ result object, passes it through the later-type profile, checks the computed value, and releases it through C++.

Patch.

Variant gcc-13-14

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

+91 −1 2 files

Download the patch

gcc/c-family/c-ada-spec.cc +56−1modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -1680 +1680 @@dump_ada_import (pretty_printer *buffer, tree t, int spc)
16801680 pp_string (buffer, "\";");
16811681}
16821682
1683Added line. /* Return true if NAME and TYPE use the same Ada identifier. Strip anonymous
1684Added line. pointer and reference layers because the generated profile names their
1685Added line. designated type directly. */
1686Added line.
1687Added line. static bool
1688Added line. identifier_matches_type (tree name, tree type)
1689Added line. {
1690Added line. if (!name || !type || type == error_mark_node)
1691Added line. return false;
1692Added line.
1693Added line. while ((TREE_CODE (type) == POINTER_TYPE
1694Added line. || TREE_CODE (type) == REFERENCE_TYPE)
1695Added line. && !TYPE_NAME (type))
1696Added line. type = TREE_TYPE (type);
1697Added line.
1698Added line. if (TREE_CODE (type) == FUNCTION_TYPE || !TYPE_NAME (type))
1699Added line. return false;
1700Added line.
1701Added line. tree type_name_node = TYPE_NAME (type);
1702Added line. if (TREE_CODE (type_name_node) == TYPE_DECL)
1703Added line. type_name_node = DECL_NAME (type_name_node);
1704Added line. if (!type_name_node || TREE_CODE (type_name_node) != IDENTIFIER_NODE)
1705Added line. return false;
1706Added line.
1707Added line. char *decl_ada = to_ada_name (IDENTIFIER_POINTER (name), NULL);
1708Added line. char *type_ada = to_ada_name (IDENTIFIER_POINTER (type_name_node), NULL);
1709Added line. const bool matches = !strcasecmp (decl_ada, type_ada);
1710Added line. free (decl_ada);
1711Added line. free (type_ada);
1712Added line. return matches;
1713Added line. }
1714Added line.
16831715/* Check whether T and its type have different names, and append "the_"
16841716 otherwise in BUFFER. */
16851717
@@ -1709 +1741 @@check_type_name_conflict (pretty_printer *buffer, tree t)
17091741 }
17101742}
17111743
1744Added line. /* Return true if PARAMETER would hide a type used by its own or a later
1745Added line. profile element. Ada formal parameters are visible through the remainder
1746Added line. of the profile, including its return type. */
1747Added line.
1748Added line. static bool
1749Added line. parameter_name_conflicts_with_profile (tree parameter, tree func,
1750Added line. bool is_constructor)
1751Added line. {
1752Added line. tree name = DECL_NAME (parameter);
1753Added line. if (!name)
1754Added line. return false;
1755Added line.
1756Added line. for (tree arg = parameter; arg; arg = DECL_CHAIN (arg))
1757Added line. if (identifier_matches_type (name, TREE_TYPE (arg)))
1758Added line. return true;
1759Added line.
1760Added line. tree return_type = is_constructor
1761Added line. ? DECL_CONTEXT (func) : TREE_TYPE (TREE_TYPE (func));
1762Added line. return identifier_matches_type (name, return_type);
1763Added line. }
1764Added line.
17121765/* Dump in BUFFER a function declaration FUNC in Ada syntax.
17131766 IS_METHOD indicates whether FUNC is a C++ method.
17141767 IS_CONSTRUCTOR whether FUNC is a C++ constructor.
@@ -1786 +1839 @@dump_ada_function_declaration (pretty_printer *buffer, tree func,
17861839 {
17871840 if (DECL_NAME (arg))
17881841 {
1789Removed line. check_type_name_conflict (buffer, arg);
1842Added line. if (parameter_name_conflicts_with_profile (arg, func,
1843Added line. is_constructor))
1844Added line. pp_string (buffer, "the_");
17901845 pp_ada_tree_identifier (buffer, DECL_NAME (arg), NULL_TREE,
17911846 false);
17921847 pp_string (buffer, " : ");
gcc/testsuite/g++.dg/ada-spec/profile-formal-type-names.C +35−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/profile-formal-type-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 profile_formal_type_names_c.ads "function New_Widget \(the_widget : int\)" } } */
4Added line. /* { dg-final { scan-file profile_formal_type_names_c.ads "function make_result \(the_result : int\)" } } */
5Added line. /* { dg-final { scan-file profile_formal_type_names_c.ads "function inspect \(the_result : int; item : access Result\)" } } */
6Added line.
7Added line. struct Result
8Added line. {
9Added line. int value;
10Added line. };
11Added line.
12Added line. struct Widget
13Added line. {
14Added line. Widget (int widget);
15Added line. int value;
16Added line. };
17Added line.
18Added line. Widget::Widget (int widget) : value (widget) {}
19Added line.
20Added line. extern "C" Result *make_result (int result)
21Added line. {
22Added line. return new Result { result };
23Added line. }
24Added line.
25Added line. extern "C" int inspect (int result, Result *item)
26Added line. {
27Added line. return result + item->value;
28Added line. }
29Added line.
30Added line. extern "C" void delete_result (Result *item)
31Added line. {
32Added line. delete item;
33Added line. }
34Added line.
35Added line. /* { dg-final { cleanup-ada-spec } } */
36

Variant gcc-15-16

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

+91 −1 2 files

Download the patch

gcc/c-family/c-ada-spec.cc +56−1modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -1734 +1734 @@dump_ada_import (pretty_printer *pp, tree t, int spc)
17341734 pp_string (pp, "\";");
17351735}
17361736
1737Added line. /* Return true if NAME and TYPE use the same Ada identifier. Strip anonymous
1738Added line. pointer and reference layers because the generated profile names their
1739Added line. designated type directly. */
1740Added line.
1741Added line. static bool
1742Added line. identifier_matches_type (tree name, tree type)
1743Added line. {
1744Added line. if (!name || !type || type == error_mark_node)
1745Added line. return false;
1746Added line.
1747Added line. while ((TREE_CODE (type) == POINTER_TYPE
1748Added line. || TREE_CODE (type) == REFERENCE_TYPE)
1749Added line. && !TYPE_NAME (type))
1750Added line. type = TREE_TYPE (type);
1751Added line.
1752Added line. if (TREE_CODE (type) == FUNCTION_TYPE || !TYPE_NAME (type))
1753Added line. return false;
1754Added line.
1755Added line. tree type_name_node = TYPE_NAME (type);
1756Added line. if (TREE_CODE (type_name_node) == TYPE_DECL)
1757Added line. type_name_node = DECL_NAME (type_name_node);
1758Added line. if (!type_name_node || TREE_CODE (type_name_node) != IDENTIFIER_NODE)
1759Added line. return false;
1760Added line.
1761Added line. char *decl_ada = to_ada_name (IDENTIFIER_POINTER (name), NULL);
1762Added line. char *type_ada = to_ada_name (IDENTIFIER_POINTER (type_name_node), NULL);
1763Added line. const bool matches = !strcasecmp (decl_ada, type_ada);
1764Added line. free (decl_ada);
1765Added line. free (type_ada);
1766Added line. return matches;
1767Added line. }
1768Added line.
17371769/* Check whether T and its type have different names, and append "the_"
17381770 otherwise in PP. */
17391771
@@ -1765 +1797 @@check_type_name_conflict (pretty_printer *pp, tree t)
17651797 }
17661798}
17671799
1800Added line. /* Return true if PARAMETER would hide a type used by its own or a later
1801Added line. profile element. Ada formal parameters are visible through the remainder
1802Added line. of the profile, including its return type. */
1803Added line.
1804Added line. static bool
1805Added line. parameter_name_conflicts_with_profile (tree parameter, tree func,
1806Added line. bool is_constructor)
1807Added line. {
1808Added line. tree name = DECL_NAME (parameter);
1809Added line. if (!name)
1810Added line. return false;
1811Added line.
1812Added line. for (tree arg = parameter; arg; arg = DECL_CHAIN (arg))
1813Added line. if (identifier_matches_type (name, TREE_TYPE (arg)))
1814Added line. return true;
1815Added line.
1816Added line. tree return_type = is_constructor
1817Added line. ? DECL_CONTEXT (func) : TREE_TYPE (TREE_TYPE (func));
1818Added line. return identifier_matches_type (name, return_type);
1819Added line. }
1820Added line.
17681821/* Dump in PP a function declaration FUNC in Ada syntax.
17691822 IS_METHOD indicates whether FUNC is a C++ method.
17701823 IS_CONSTRUCTOR whether FUNC is a C++ constructor.
@@ -1842 +1895 @@dump_ada_function_declaration (pretty_printer *pp, tree func,
18421895 {
18431896 if (DECL_NAME (arg))
18441897 {
1845Removed line. check_type_name_conflict (pp, arg);
1898Added line. if (parameter_name_conflicts_with_profile (arg, func,
1899Added line. is_constructor))
1900Added line. pp_string (pp, "the_");
18461901 pp_ada_tree_identifier (pp, DECL_NAME (arg), NULL_TREE,
18471902 false);
18481903 pp_string (pp, " : ");
gcc/testsuite/g++.dg/ada-spec/profile-formal-type-names.C +35−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/profile-formal-type-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 profile_formal_type_names_c.ads "function New_Widget \(the_widget : int\)" } } */
4Added line. /* { dg-final { scan-file profile_formal_type_names_c.ads "function make_result \(the_result : int\)" } } */
5Added line. /* { dg-final { scan-file profile_formal_type_names_c.ads "function inspect \(the_result : int; item : access Result\)" } } */
6Added line.
7Added line. struct Result
8Added line. {
9Added line. int value;
10Added line. };
11Added line.
12Added line. struct Widget
13Added line. {
14Added line. Widget (int widget);
15Added line. int value;
16Added line. };
17Added line.
18Added line. Widget::Widget (int widget) : value (widget) {}
19Added line.
20Added line. extern "C" Result *make_result (int result)
21Added line. {
22Added line. return new Result { result };
23Added line. }
24Added line.
25Added line. extern "C" int inspect (int result, Result *item)
26Added line. {
27Added line. return result + item->value;
28Added line. }
29Added line.
30Added line. extern "C" void delete_result (Result *item)
31Added line. {
32Added line. delete item;
33Added line. }
34Added line.
35Added line. /* { dg-final { cleanup-ada-spec } } */
36

Tests.

profile-formal-type-names.C C++ · 35 lines
/* { dg-do compile } */
/* { dg-options "-fdump-ada-spec-slim" } */
/* { dg-final { scan-file profile_formal_type_names_c.ads "function New_Widget \(the_widget : int\)" } } */
/* { dg-final { scan-file profile_formal_type_names_c.ads "function make_result \(the_result : int\)" } } */
/* { dg-final { scan-file profile_formal_type_names_c.ads "function inspect \(the_result : int; item : access Result\)" } } */

struct Result
{
  int value;
};

struct Widget
{
  Widget (int widget);
  int value;
};

Widget::Widget (int widget) : value (widget) {}

extern "C" Result *make_result (int result)
{
  return new Result { result };
}

extern "C" int inspect (int result, Result *item)
{
  return result + item->value;
}

extern "C" void delete_result (Result *item)
{
  delete item;
}

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

Download · View in repository

profile_formal_type_names_consumer.adb Ada · 15 lines
with Ada.Text_IO;
with Profile_Formal_Type_Names_C;
with Interfaces.C; use Interfaces.C;

procedure Profile_Formal_Type_Names_Consumer is
   package Bindings renames Profile_Formal_Type_Names_C;
   Object : access Bindings.Result := Bindings.make_result (41);
begin
   if Object = null or else Bindings.inspect (7, Object) /= 48 then
      raise Program_Error with "profile formal/type collision changed result";
   end if;

   Bindings.delete_result (Object);
   Ada.Text_IO.Put_Line ("MATCH profile formal and type names");
end Profile_Formal_Type_Names_Consumer;

Download · View in repository

run-test.sh shell · 35 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-profile-formal-type-names/tests/profile-formal-type-names.C"
ada="$root/bundles/cxx-ada-profile-formal-type-names/tests/profile_formal_type_names_consumer.adb"
work=$(mktemp -d "${TMPDIR:-/tmp}/gnat-cxx-ada-profile-name.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 profile-formal-type-names.C)
  if [[ "$state" == unpatched ]]; then
    set +e
    (cd "$dir"; "${REGRESSION_ENV[@]}" "$REGRESSION_GNATMAKE" -q -f -c profile_formal_type_names_consumer.adb) >"$dir/build.log" 2>&1
    status=$?; set -e
    [[ $status -ne 0 ]] || { echo "error: unpatched binding compiled" >&2; exit 1; }
    grep -Eiq 'formal parameter.*cannot be used before end of specification' "$dir/build.log"
    echo "cxx-ada-profile-formal-type-names -O$optimization: expected collision (GCC $version)"
  else
    (cd "$dir"; "${REGRESSION_ENV[@]}" "$REGRESSION_GNATMAKE" -q -f "-O$optimization" profile_formal_type_names_consumer.adb -largs profile-formal-type-names.o -lstdc++; "${REGRESSION_ENV[@]}" ./profile_formal_type_names_consumer) >"$dir/output.log" 2>&1 || { cat "$dir/output.log"; exit 1; }
    grep -F "function New_Widget (the_widget : int)" "$dir/profile_formal_type_names_c.ads"
    grep -F "function make_result (the_result : int)" "$dir/profile_formal_type_names_c.ads"
    grep -F "function inspect (the_result : int; item : access Result)" "$dir/profile_formal_type_names_c.ads"
    grep -Fx "MATCH profile formal and type names" "$dir/output.log"
    echo "cxx-ada-profile-formal-type-names -O$optimization: patched (GCC $version)"
  fi
done

Download · View in repository

Commands.

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