cxx-ada-template-nested-types

C++ Ada template nested types

Concrete C++ class-template packages reference anonymous array field types without declaring them in the generated Ada specification.

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.

dump_ada_template writes each concrete class-template instance into a nested Ada package, but it bypasses the nested-type prepass used for ordinary record declarations. Array fields then refer to an anonymous Ada array type that the package never declares.

Both a non-type argument and its defaulted form reach the same broken path:

template <typename T, unsigned N>
struct Buffer { T values[N]; };
template struct Buffer<int, 4>;

template <typename T = int, unsigned N = 2>
struct Defaults { T values[N]; };
template struct Defaults<>;

The unpatched mapper emits references to internal anon_array... names but no declarations for them (the numeric suffix is compiler-internal and varies):

package Buffer_int_4 is
   type Buffer is limited record
      values : aliased anon_array1712;
   end record
   with Convention => C_Pass_By_Copy;
end;

package Defaults_int_2 is
   type Defaults is limited record
      values : aliased anon_array1713;
   end record
   with Convention => C_Pass_By_Copy;
end;

The corrected output declares each nested type before the record that uses it:

package Buffer_int_4 is
   type anon_array1712 is array (0 .. 3) of aliased int;
   type Buffer is limited record
      values : aliased anon_array1712;
   end record
   with Convention => C_Pass_By_Copy;
end;

package Defaults_int_2 is
   type anon_array1713 is array (0 .. 1) of aliased int;
   type Defaults is limited record
      values : aliased anon_array1713;
   end record
   with Convention => C_Pass_By_Copy;
end;

The patch invokes the existing recursive nested-type prepass before printing a concrete template record. The executable regression covers both template forms, compiles their generated Ada, and passes both arrays to C++ at -O0 and -O2 to verify layout as well as syntax.

Run it against an unpatched or patched compiler root:

./bundles/cxx-ada-template-nested-types/run-test.sh \
  TOOLCHAIN_ROOT GCC_VERSION unpatched
./bundles/cxx-ada-template-nested-types/run-test.sh \
  TOOLCHAIN_ROOT GCC_VERSION patched

Patch.

Variant gcc-13-14

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

+39 −0 2 files

Download the patch

gcc/c-family/c-ada-spec.cc +2−0modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -37 +37 @@along with GCC; see the file COPYING3. If not see
3737static int dump_ada_node (pretty_printer *, tree, tree, int, bool, bool);
3838static int dump_ada_declaration (pretty_printer *, tree, tree, int);
3939static void dump_ada_structure (pretty_printer *, tree, tree, bool, int);
40Added line. static void dump_nested_types (pretty_printer *, tree, int);
4041static char *to_ada_name (const char *, bool *);
4142
4243#define INDENT(SPACE) \
@@ -1909 +1910 @@dump_ada_template (pretty_printer *buffer, tree t, int spc)
19091910 TREE_VISITED (get_underlying_decl (instance)) = 1;
1911Added line. dump_nested_types (buffer, TYPE_NAME (instance), spc);
19101912 pp_string (buffer, "type ");
gcc/testsuite/g++.dg/ada-spec/template-nested-types.C +37−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/template-nested-types.C: original line, patched line, change, source
@@ -0 +1 @@
1Added line. /* { dg-do compile } */
2Added line. /* { dg-options "-std=gnu++20 -fdump-ada-spec-slim" } */
3Added line. /* { dg-final { scan-file template_nested_types_c.ads "package Buffer_int_4 is" } } */
4Added line. /* { dg-final { scan-file template_nested_types_c.ads "package Defaults_int_2 is" } } */
5Added line. /* { dg-final { scan-file template_nested_types_c.ads "type anon_array[0-9]+ is array \(0 \.\. 3\) of aliased int;" } } */
6Added line. /* { dg-final { scan-file template_nested_types_c.ads "type anon_array[0-9]+ is array \(0 \.\. 1\) of aliased int;" } } */
7Added line.
8Added line. template <typename T, unsigned N>
9Added line. struct Buffer
10Added line. {
11Added line. T values[N];
12Added line. };
13Added line.
14Added line. template struct Buffer<int, 4>;
15Added line.
16Added line. template <typename T = int, unsigned N = 2>
17Added line. struct Defaults
18Added line. {
19Added line. T values[N];
20Added line. };
21Added line.
22Added line. template struct Defaults<>;
23Added line.
24Added line. extern "C" int
25Added line. buffer_sum (const Buffer<int, 4> *value)
26Added line. {
27Added line. return value->values[0] + value->values[1]
28Added line. + value->values[2] + value->values[3];
29Added line. }
30Added line.
31Added line. extern "C" int
32Added line. defaults_sum (const Defaults<> *value)
33Added line. {
34Added line. return value->values[0] + value->values[1];
35Added line. }
36Added line.
37Added line. /* { dg-final { cleanup-ada-spec } } */
38

Variant gcc-15-16

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

+39 −0 2 files

Download the patch

gcc/c-family/c-ada-spec.cc +2−0modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -37 +37 @@along with GCC; see the file COPYING3. If not see
3737static int dump_ada_node (pretty_printer *, tree, tree, int, bool, bool);
3838static int dump_ada_declaration (pretty_printer *, tree, tree, int);
3939static void dump_ada_structure (pretty_printer *, tree, tree, bool, int);
40Added line. static void dump_nested_types (pretty_printer *, tree, int);
4041static char *to_ada_name (const char *, bool *);
4142
4243#define INDENT(SPACE) \
@@ -1956 +1957 @@dump_ada_template (pretty_printer *pp, tree t, int spc)
19561957 TREE_VISITED (get_underlying_decl (instance)) = 1;
1958Added line. dump_nested_types (pp, TYPE_NAME (instance), spc);
19571959 pp_string (pp, "type ");
gcc/testsuite/g++.dg/ada-spec/template-nested-types.C +37−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/template-nested-types.C: original line, patched line, change, source
@@ -0 +1 @@
1Added line. /* { dg-do compile } */
2Added line. /* { dg-options "-std=gnu++20 -fdump-ada-spec-slim" } */
3Added line. /* { dg-final { scan-file template_nested_types_c.ads "package Buffer_int_4 is" } } */
4Added line. /* { dg-final { scan-file template_nested_types_c.ads "package Defaults_int_2 is" } } */
5Added line. /* { dg-final { scan-file template_nested_types_c.ads "type anon_array[0-9]+ is array \(0 \.\. 3\) of aliased int;" } } */
6Added line. /* { dg-final { scan-file template_nested_types_c.ads "type anon_array[0-9]+ is array \(0 \.\. 1\) of aliased int;" } } */
7Added line.
8Added line. template <typename T, unsigned N>
9Added line. struct Buffer
10Added line. {
11Added line. T values[N];
12Added line. };
13Added line.
14Added line. template struct Buffer<int, 4>;
15Added line.
16Added line. template <typename T = int, unsigned N = 2>
17Added line. struct Defaults
18Added line. {
19Added line. T values[N];
20Added line. };
21Added line.
22Added line. template struct Defaults<>;
23Added line.
24Added line. extern "C" int
25Added line. buffer_sum (const Buffer<int, 4> *value)
26Added line. {
27Added line. return value->values[0] + value->values[1]
28Added line. + value->values[2] + value->values[3];
29Added line. }
30Added line.
31Added line. extern "C" int
32Added line. defaults_sum (const Defaults<> *value)
33Added line. {
34Added line. return value->values[0] + value->values[1];
35Added line. }
36Added line.
37Added line. /* { dg-final { cleanup-ada-spec } } */
38

Tests.

template-nested-types.C C++ · 37 lines
/* { dg-do compile } */
/* { dg-options "-std=gnu++20 -fdump-ada-spec-slim" } */
/* { dg-final { scan-file template_nested_types_c.ads "package Buffer_int_4 is" } } */
/* { dg-final { scan-file template_nested_types_c.ads "package Defaults_int_2 is" } } */
/* { dg-final { scan-file template_nested_types_c.ads "type anon_array[0-9]+ is array \(0 \.\. 3\) of aliased int;" } } */
/* { dg-final { scan-file template_nested_types_c.ads "type anon_array[0-9]+ is array \(0 \.\. 1\) of aliased int;" } } */

template <typename T, unsigned N>
struct Buffer
{
  T values[N];
};

template struct Buffer<int, 4>;

template <typename T = int, unsigned N = 2>
struct Defaults
{
  T values[N];
};

template struct Defaults<>;

extern "C" int
buffer_sum (const Buffer<int, 4> *value)
{
  return value->values[0] + value->values[1]
    + value->values[2] + value->values[3];
}

extern "C" int
defaults_sum (const Defaults<> *value)
{
  return value->values[0] + value->values[1];
}

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

Download · View in repository

template_nested_types_consumer.adb Ada · 17 lines
with Interfaces.C; use Interfaces.C;
with Template_Nested_Types_C;

procedure Template_Nested_Types_Consumer is
   package Bindings renames Template_Nested_Types_C;

   Buffer_Value : aliased Bindings.Buffer_Int_4.Buffer :=
     (values => (1, 2, 3, 4));
   Defaults_Value : aliased Bindings.Defaults_Int_2.Defaults :=
     (values => (5, 6));
begin
   if Bindings.buffer_sum (Buffer_Value'Access) /= 10
     or else Bindings.defaults_sum (Defaults_Value'Access) /= 11
   then
      raise Program_Error with "template array layout or binding is wrong";
   end if;
end Template_Nested_Types_Consumer;

Download · View in repository

run-test.sh shell · 79 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-template-nested-types/tests/template-nested-types.C"
ada_fixture="$root/bundles/cxx-ada-template-nested-types/tests/template_nested_types_consumer.adb"
work=$(mktemp -d "${TMPDIR:-/tmp}/gnat-cxx-ada-template-nested.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" -std=gnu++20 -c "-O$optimization" \
      -fdump-ada-spec-slim template-nested-types.C
  )

  spec="$case_dir/template_nested_types_c.ads"
  [[ -f "$spec" ]] || {
    echo "error: g++ did not generate $spec" >&2
    exit 1
  }

  set +e
  (
    cd "$case_dir"
    "${REGRESSION_ENV[@]}" "$REGRESSION_GNATMAKE" -q -f \
      "-O$optimization" template_nested_types_consumer.adb \
      -largs template-nested-types.o -lstdc++
    "${REGRESSION_ENV[@]}" ./template_nested_types_consumer
  ) >"$case_dir/run.log" 2>&1
  run_status=$?
  set -e

  if [[ "$state" == unpatched ]]; then
    [[ $run_status -ne 0 ]] || {
      echo "error: unpatched template nested-type regression unexpectedly passed" >&2
      exit 1
    }
    grep -Eiq 'anon_array.*undefined' "$case_dir/run.log" || {
      cat "$case_dir/run.log"
      exit 1
    }
    echo "cxx-ada-template-nested-types -O$optimization: expected missing type (GCC $version)"
    continue
  fi

  [[ $run_status -eq 0 ]] || {
    cat "$case_dir/run.log"
    exit 1
  }
  [[ $(grep -Ec 'type anon_array[0-9]+ is array' "$spec") -eq 2 ]] || {
    echo "error: expected two package-local anonymous array declarations" >&2
    exit 1
  }
  echo "cxx-ada-template-nested-types -O$optimization: patched (GCC $version)"
done

Download · View in repository

Commands.

Apply the patch
patch --fuzz=0 -p1 -i bundles/cxx-ada-template-nested-types/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.