cxx-ada-char8-type

C++ Ada char8_t mapping

The C++ Ada spec dumper emits char8_t references even though GNAT's Interfaces.C has no such Ada type.

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.

C++20 gives char8_t a distinct fundamental type with the size, alignment, and unsigned representation of unsigned char. The Ada spec dumper prints the C++ spelling directly, but GNAT's Interfaces.C has no char8_t declaration.

The offending C++ is a function with a char8_t parameter and result:

char8_t
char8_value (char8_t value)
{
  return value;
}

The unpatched mapper produces an Ada type name that does not exist:

function char8_value (value : char8_t) return char8_t
with Import => True,
     Convention => CPP,
     External_Name => "_Z11char8_valueDu";

The correct generated binding uses the ABI-equivalent type already provided by Interfaces.C while retaining the C++ mangled name that identifies char8_t:

function char8_value (value : unsigned_char) return unsigned_char
with Import => True,
     Convention => CPP,
     External_Name => "_Z11char8_valueDu";

The executable regression compiles the generated Ada and round-trips a value through the C++ function at -O0 and -O2. The existing wchar_t, char16_t, and char32_t mappings remain unchanged.

Run it against an unpatched or patched compiler root:

./bundles/cxx-ada-char8-type/run-test.sh \
  TOOLCHAIN_ROOT GCC_VERSION unpatched
./bundles/cxx-ada-char8-type/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.

+19 −0 2 files

Download the patch

gcc/c-family/c-ada-spec.cc +7−0modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -2189 +2189 @@dump_ada_node (pretty_printer *buffer, tree node, tree type, int spc,
21892189 case FIXED_POINT_TYPE:
21902190 case BOOLEAN_TYPE:
21912191 if (TYPE_NAME (node)
2192Added line. && ((TREE_CODE (TYPE_NAME (node)) == IDENTIFIER_NODE
2193Added line. && id_equal (TYPE_NAME (node), "char8_t"))
2194Added line. || (TREE_CODE (TYPE_NAME (node)) == TYPE_DECL
2195Added line. && DECL_NAME (TYPE_NAME (node))
2196Added line. && id_equal (DECL_NAME (TYPE_NAME (node)), "char8_t"))))
2197Added line. pp_string (buffer, "unsigned_char");
2198Added line. else if (TYPE_NAME (node)
21922199 && !(TREE_CODE (TYPE_NAME (node)) == TYPE_DECL
21932200 && !strcmp (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (node))),
21942201 "__int128")))
gcc/testsuite/g++.dg/ada-spec/char8-type.C +12−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/char8-type.C: original line, patched line, change, source
@@ -0 +1 @@
1Added line. /* { dg-do compile { target c++20 } } */
2Added line. /* { dg-options "-fdump-ada-spec-slim" } */
3Added line. /* { dg-final { scan-file char8_type_c.ads "value : unsigned_char" } } */
4Added line. /* { dg-final { scan-file char8_type_c.ads "return unsigned_char" } } */
5Added line.
6Added line. char8_t
7Added line. char8_value (char8_t value)
8Added line. {
9Added line. return value;
10Added line. }
11Added line.
12Added line. /* { dg-final { cleanup-ada-spec } } */
13

Variant gcc-15-16

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

+19 −0 2 files

Download the patch

gcc/c-family/c-ada-spec.cc +7−0modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -2254 +2254 @@dump_ada_node (pretty_printer *pp, tree node, tree type, int spc,
22542254 case FIXED_POINT_TYPE:
22552255 case BOOLEAN_TYPE:
22562256 if (TYPE_NAME (node)
2257Added line. && ((TREE_CODE (TYPE_NAME (node)) == IDENTIFIER_NODE
2258Added line. && id_equal (TYPE_NAME (node), "char8_t"))
2259Added line. || (TREE_CODE (TYPE_NAME (node)) == TYPE_DECL
2260Added line. && DECL_NAME (TYPE_NAME (node))
2261Added line. && id_equal (DECL_NAME (TYPE_NAME (node)), "char8_t"))))
2262Added line. pp_string (pp, "unsigned_char");
2263Added line. else if (TYPE_NAME (node)
22572264 && !(TREE_CODE (TYPE_NAME (node)) == TYPE_DECL
22582265 && !strncmp (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (node))),
22592266 "__int128", 8)))
gcc/testsuite/g++.dg/ada-spec/char8-type.C +12−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/char8-type.C: original line, patched line, change, source
@@ -0 +1 @@
1Added line. /* { dg-do compile { target c++20 } } */
2Added line. /* { dg-options "-fdump-ada-spec-slim" } */
3Added line. /* { dg-final { scan-file char8_type_c.ads "value : unsigned_char" } } */
4Added line. /* { dg-final { scan-file char8_type_c.ads "return unsigned_char" } } */
5Added line.
6Added line. char8_t
7Added line. char8_value (char8_t value)
8Added line. {
9Added line. return value;
10Added line. }
11Added line.
12Added line. /* { dg-final { cleanup-ada-spec } } */
13

Tests.

char8-type.C C++ · 12 lines
/* { dg-do compile { target c++20 } } */
/* { dg-options "-fdump-ada-spec-slim" } */
/* { dg-final { scan-file char8_type_c.ads "value : unsigned_char" } } */
/* { dg-final { scan-file char8_type_c.ads "return unsigned_char" } } */

char8_t
char8_value (char8_t value)
{
  return value;
}

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

Download · View in repository

char8_type_consumer.adb Ada · 11 lines
with Interfaces.C; use Interfaces.C;
with Char8_Type_C;

procedure Char8_Type_Consumer is
   package Bindings renames Char8_Type_C;
   Value : constant unsigned_char := 16#A5#;
begin
   if Bindings.char8_value (Value) /= Value then
      raise Program_Error with "char8_t value did not round-trip";
   end if;
end Char8_Type_Consumer;

Download · View in repository

run-test.sh shell · 77 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-char8-type/tests/char8-type.C"
ada_fixture="$root/bundles/cxx-ada-char8-type/tests/char8_type_consumer.adb"
work=$(mktemp -d "${TMPDIR:-/tmp}/gnat-cxx-ada-char8-type.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 char8-type.C
  )

  spec="$case_dir/char8_type_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" char8_type_consumer.adb \
      -largs char8-type.o -lstdc++
    "${REGRESSION_ENV[@]}" ./char8_type_consumer
  ) >"$case_dir/run.log" 2>&1
  run_status=$?
  set -e

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

  [[ $run_status -eq 0 ]] || {
    cat "$case_dir/run.log"
    exit 1
  }
  grep -Eq 'value : unsigned_char' "$spec"
  grep -Eq 'return unsigned_char' "$spec"
  echo "cxx-ada-char8-type -O$optimization: patched (GCC $version)"
done

Download · View in repository

Commands.

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