cxx-ada-qualified-method-names

C++ Ada qualified method names

The C++ Ada spec dumper emits colliding names for cv/ref-qualified methods and for copy/move assignment operators.

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 profiles cannot overload methods solely by the C++ cv/ref qualification of their implicit object parameter, and the mapper also gives copy and move assignment the same synthetic name.

For example:

struct Accessor {
  int inspect();
  int inspect() const;
  int inspect() volatile;
  int category() &;
  int category() &&;
};

struct Value {
  Value& operator=(const Value&);
  Value& operator=(Value&&);
};

The unpatched mapper emits colliding Ada declarations:

function inspect (this : access Accessor) return int;
function inspect (this : access constant Accessor) return int;
function inspect (this : access Accessor) return int;

function category (this : access Accessor) return int;
function category (this : access Accessor) return int;

function Assign_Value (...) return access Value;
function Assign_Value (...) return access Value;

The corrected output preserves the otherwise-lost C++ identity in stable Ada suffixes:

function inspect (this : access Accessor) return int;
function inspect_Const (this : access constant Accessor) return int;
function inspect_Volatile (this : access Accessor) return int;

function category_Lvalue (this : access Accessor) return int;
function category_Rvalue (this : access Accessor) return int;

function Assign_Value (...) return access Value;
function Assign_Value_Move (...) return access Value;

Const, volatile, lvalue-ref, and rvalue-ref suffixes are combined in that order. The names expose distinctions that Ada cannot encode in the profiles; callers remain responsible for satisfying the C++ object-category semantics. The executable regression calls every corrected binding and checks copy/move assignment effects at -O0 and -O2.

Patch.

Variant gcc-13-14

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

+115 −0 4 files

Download the patch

gcc/c-family/c-ada-spec.h +9−0modified
Unified diff for gcc/c-family/c-ada-spec.h: original line, patched line, change, source
@@ -25 +25 @@
2525/* In c-ada-spec.cc */
2626
2727enum cpp_operation {
28Added line. GET_FUNCTION_QUALIFIERS,
2829 HAS_DEPENDENT_TEMPLATE_ARGS,
2930 IS_ABSTRACT,
3031 IS_ASSIGNMENT_OPERATOR,
@@ -36 +37 @@enum cpp_operation {
3637 IS_TRIVIAL
3738};
3839
40Added line. enum cpp_function_qualifier {
41Added line. CPP_FUNCTION_CONST = 1 << 0,
42Added line. CPP_FUNCTION_VOLATILE = 1 << 1,
43Added line. CPP_FUNCTION_LVALUE = 1 << 2,
44Added line. CPP_FUNCTION_RVALUE = 1 << 3,
45Added line. CPP_FUNCTION_MOVE_ASSIGNMENT = 1 << 4
46Added line. };
47Added line.
3948extern void collect_ada_nodes (tree, const char *);
4049extern void collect_source_ref (const char *);
4150extern void dump_ada_specs (void (*)(const char *),
gcc/c-family/c-ada-spec.cc +23−0modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -2869 +2869 @@print_assignment_operator (pretty_printer *buffer, tree t, tree type)
28692869
28702870 pp_string (buffer, "Assign_");
28712871 pp_ada_tree_identifier (buffer, decl_name, t, false);
2872Added line.
2873Added line. if (cpp_check (t, GET_FUNCTION_QUALIFIERS)
2874Added line. & CPP_FUNCTION_MOVE_ASSIGNMENT)
2875Added line. pp_string (buffer, "_Move");
2876Added line. }
2877Added line.
2878Added line. /* Append the C++ method qualifiers that Ada profiles cannot represent. */
2879Added line.
2880Added line. static void
2881Added line. print_method_qualifiers (pretty_printer *buffer, tree t)
2882Added line. {
2883Added line. const int qualifiers = cpp_check (t, GET_FUNCTION_QUALIFIERS);
2884Added line.
2885Added line. if (qualifiers & CPP_FUNCTION_CONST)
2886Added line. pp_string (buffer, "_Const");
2887Added line. if (qualifiers & CPP_FUNCTION_VOLATILE)
2888Added line. pp_string (buffer, "_Volatile");
2889Added line. if (qualifiers & CPP_FUNCTION_LVALUE)
2890Added line. pp_string (buffer, "_Lvalue");
2891Added line. else if (qualifiers & CPP_FUNCTION_RVALUE)
2892Added line. pp_string (buffer, "_Rvalue");
28722893}
28732894
28742895/* Return the name of type T. */
@@ -3175 +3196 @@dump_ada_declaration (pretty_printer *buffer, tree t, tree type, int spc)
31753196 {
31763197 const unsigned int suffix = overloading_index (decl_name);
31773198 pp_ada_tree_identifier (buffer, decl_name, t, false);
3199Added line. if (is_method)
3200Added line. print_method_qualifiers (buffer, t);
31783201 if (suffix > 1)
31793202 pp_decimal_int (buffer, suffix);
31803203 }
gcc/cp/decl2.cc +22−0modified
Unified diff for gcc/cp/decl2.cc: original line, patched line, change, source
@@ -4408 +4408 @@cpp_check (tree t, cpp_operation op)
44084408{
44094409 switch (op)
44104410 {
4411Added line. case GET_FUNCTION_QUALIFIERS:
4412Added line. {
4413Added line. int qualifiers = 0;
4414Added line. tree type = TREE_TYPE (t);
4415Added line.
4416Added line. if (TREE_CODE (type) == METHOD_TYPE)
4417Added line. {
4418Added line. if (type_memfn_quals (type) & TYPE_QUAL_CONST)
4419Added line. qualifiers |= CPP_FUNCTION_CONST;
4420Added line. if (type_memfn_quals (type) & TYPE_QUAL_VOLATILE)
4421Added line. qualifiers |= CPP_FUNCTION_VOLATILE;
4422Added line. if (FUNCTION_REF_QUALIFIED (type))
4423Added line. qualifiers |= FUNCTION_RVALUE_QUALIFIED (type)
4424Added line. ? CPP_FUNCTION_RVALUE : CPP_FUNCTION_LVALUE;
4425Added line. }
4426Added line.
4427Added line. if (DECL_ASSIGNMENT_OPERATOR_P (t)
4428Added line. && special_function_p (t) == sfk_move_assignment)
4429Added line. qualifiers |= CPP_FUNCTION_MOVE_ASSIGNMENT;
4430Added line.
4431Added line. return qualifiers;
4432Added line. }
44114433 case HAS_DEPENDENT_TEMPLATE_ARGS:
44124434 {
44134435 tree ti = CLASSTYPE_TEMPLATE_INFO (t);
gcc/testsuite/g++.dg/ada-spec/qualified-method-names.C +61−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/qualified-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 qualified_method_names_c.ads "function inspect_Const" } } */
4Added line. /* { dg-final { scan-file qualified_method_names_c.ads "function inspect_Volatile" } } */
5Added line. /* { dg-final { scan-file qualified_method_names_c.ads "function category_Lvalue" } } */
6Added line. /* { dg-final { scan-file qualified_method_names_c.ads "function category_Rvalue" } } */
7Added line. /* { dg-final { scan-file qualified_method_names_c.ads "function Assign_Value_Move" } } */
8Added line.
9Added line. class Accessor
10Added line. {
11Added line. public:
12Added line. explicit Accessor (int value);
13Added line. int inspect ();
14Added line. int inspect () const;
15Added line. int inspect () volatile;
16Added line. int category () &;
17Added line. int category () &&;
18Added line.
19Added line. private:
20Added line. int value_;
21Added line. };
22Added line.
23Added line. Accessor::Accessor (int value) : value_ (value) {}
24Added line. int Accessor::inspect () { return value_ + 1; }
25Added line. int Accessor::inspect () const { return value_ + 2; }
26Added line. int Accessor::inspect () volatile { return value_ + 3; }
27Added line. int Accessor::category () & { return value_ + 4; }
28Added line. int Accessor::category () && { return value_ + 5; }
29Added line.
30Added line. class Value
31Added line. {
32Added line. public:
33Added line. explicit Value (int initial);
34Added line. Value &operator= (const Value &other);
35Added line. Value &operator= (Value &&other);
36Added line. int get () const;
37Added line.
38Added line. private:
39Added line. int value_;
40Added line. };
41Added line.
42Added line. Value::Value (int initial) : value_ (initial) {}
43Added line.
44Added line. Value &
45Added line. Value::operator= (const Value &other)
46Added line. {
47Added line. value_ = other.value_;
48Added line. return *this;
49Added line. }
50Added line.
51Added line. Value &
52Added line. Value::operator= (Value &&other)
53Added line. {
54Added line. value_ = other.value_;
55Added line. other.value_ = -1;
56Added line. return *this;
57Added line. }
58Added line.
59Added line. int Value::get () const { return value_; }
60Added line.
61Added line. /* { dg-final { cleanup-ada-spec } } */
62

Variant gcc-15-16

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

+115 −0 4 files

Download the patch

gcc/c-family/c-ada-spec.h +9−0modified
Unified diff for gcc/c-family/c-ada-spec.h: original line, patched line, change, source
@@ -25 +25 @@
2525/* In c-ada-spec.cc */
2626
2727enum cpp_operation {
28Added line. GET_FUNCTION_QUALIFIERS,
2829 HAS_DEPENDENT_TEMPLATE_ARGS,
2930 IS_ABSTRACT,
3031 IS_ASSIGNMENT_OPERATOR,
@@ -36 +37 @@enum cpp_operation {
3637 IS_TRIVIAL
3738};
3839
40Added line. enum cpp_function_qualifier {
41Added line. CPP_FUNCTION_CONST = 1 << 0,
42Added line. CPP_FUNCTION_VOLATILE = 1 << 1,
43Added line. CPP_FUNCTION_LVALUE = 1 << 2,
44Added line. CPP_FUNCTION_RVALUE = 1 << 3,
45Added line. CPP_FUNCTION_MOVE_ASSIGNMENT = 1 << 4
46Added line. };
47Added line.
3948extern void collect_ada_nodes (tree, const char *);
4049extern void collect_source_ref (const char *);
4150extern void dump_ada_specs (void (*)(const char *),
gcc/c-family/c-ada-spec.cc +23−0modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -2936 +2936 @@print_assignment_operator (pretty_printer *pp, tree t, tree type)
29362936
29372937 pp_string (pp, "Assign_");
29382938 pp_ada_tree_identifier (pp, decl_name, t, false);
2939Added line.
2940Added line. if (cpp_check (t, GET_FUNCTION_QUALIFIERS)
2941Added line. & CPP_FUNCTION_MOVE_ASSIGNMENT)
2942Added line. pp_string (pp, "_Move");
2943Added line. }
2944Added line.
2945Added line. /* Append the C++ method qualifiers that Ada profiles cannot represent. */
2946Added line.
2947Added line. static void
2948Added line. print_method_qualifiers (pretty_printer *pp, tree t)
2949Added line. {
2950Added line. const int qualifiers = cpp_check (t, GET_FUNCTION_QUALIFIERS);
2951Added line.
2952Added line. if (qualifiers & CPP_FUNCTION_CONST)
2953Added line. pp_string (pp, "_Const");
2954Added line. if (qualifiers & CPP_FUNCTION_VOLATILE)
2955Added line. pp_string (pp, "_Volatile");
2956Added line. if (qualifiers & CPP_FUNCTION_LVALUE)
2957Added line. pp_string (pp, "_Lvalue");
2958Added line. else if (qualifiers & CPP_FUNCTION_RVALUE)
2959Added line. pp_string (pp, "_Rvalue");
29392960}
29402961
29412962/* Return the name of type T. */
@@ -3242 +3263 @@dump_ada_declaration (pretty_printer *pp, tree t, tree type, int spc)
32423263 {
32433264 const unsigned int suffix = overloading_index (decl_name);
32443265 pp_ada_tree_identifier (pp, decl_name, t, false);
3266Added line. if (is_method)
3267Added line. print_method_qualifiers (pp, t);
32453268 if (suffix > 1)
32463269 pp_decimal_int (pp, suffix);
32473270 }
gcc/cp/decl2.cc +22−0modified
Unified diff for gcc/cp/decl2.cc: original line, patched line, change, source
@@ -4957 +4957 @@cpp_check (tree t, cpp_operation op)
49574957{
49584958 switch (op)
49594959 {
4960Added line. case GET_FUNCTION_QUALIFIERS:
4961Added line. {
4962Added line. int qualifiers = 0;
4963Added line. tree type = TREE_TYPE (t);
4964Added line.
4965Added line. if (TREE_CODE (type) == METHOD_TYPE)
4966Added line. {
4967Added line. if (type_memfn_quals (type) & TYPE_QUAL_CONST)
4968Added line. qualifiers |= CPP_FUNCTION_CONST;
4969Added line. if (type_memfn_quals (type) & TYPE_QUAL_VOLATILE)
4970Added line. qualifiers |= CPP_FUNCTION_VOLATILE;
4971Added line. if (FUNCTION_REF_QUALIFIED (type))
4972Added line. qualifiers |= FUNCTION_RVALUE_QUALIFIED (type)
4973Added line. ? CPP_FUNCTION_RVALUE : CPP_FUNCTION_LVALUE;
4974Added line. }
4975Added line.
4976Added line. if (DECL_ASSIGNMENT_OPERATOR_P (t)
4977Added line. && special_function_p (t) == sfk_move_assignment)
4978Added line. qualifiers |= CPP_FUNCTION_MOVE_ASSIGNMENT;
4979Added line.
4980Added line. return qualifiers;
4981Added line. }
49604982 case HAS_DEPENDENT_TEMPLATE_ARGS:
49614983 {
49624984 tree ti = CLASSTYPE_TEMPLATE_INFO (t);
gcc/testsuite/g++.dg/ada-spec/qualified-method-names.C +61−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/qualified-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 qualified_method_names_c.ads "function inspect_Const" } } */
4Added line. /* { dg-final { scan-file qualified_method_names_c.ads "function inspect_Volatile" } } */
5Added line. /* { dg-final { scan-file qualified_method_names_c.ads "function category_Lvalue" } } */
6Added line. /* { dg-final { scan-file qualified_method_names_c.ads "function category_Rvalue" } } */
7Added line. /* { dg-final { scan-file qualified_method_names_c.ads "function Assign_Value_Move" } } */
8Added line.
9Added line. class Accessor
10Added line. {
11Added line. public:
12Added line. explicit Accessor (int value);
13Added line. int inspect ();
14Added line. int inspect () const;
15Added line. int inspect () volatile;
16Added line. int category () &;
17Added line. int category () &&;
18Added line.
19Added line. private:
20Added line. int value_;
21Added line. };
22Added line.
23Added line. Accessor::Accessor (int value) : value_ (value) {}
24Added line. int Accessor::inspect () { return value_ + 1; }
25Added line. int Accessor::inspect () const { return value_ + 2; }
26Added line. int Accessor::inspect () volatile { return value_ + 3; }
27Added line. int Accessor::category () & { return value_ + 4; }
28Added line. int Accessor::category () && { return value_ + 5; }
29Added line.
30Added line. class Value
31Added line. {
32Added line. public:
33Added line. explicit Value (int initial);
34Added line. Value &operator= (const Value &other);
35Added line. Value &operator= (Value &&other);
36Added line. int get () const;
37Added line.
38Added line. private:
39Added line. int value_;
40Added line. };
41Added line.
42Added line. Value::Value (int initial) : value_ (initial) {}
43Added line.
44Added line. Value &
45Added line. Value::operator= (const Value &other)
46Added line. {
47Added line. value_ = other.value_;
48Added line. return *this;
49Added line. }
50Added line.
51Added line. Value &
52Added line. Value::operator= (Value &&other)
53Added line. {
54Added line. value_ = other.value_;
55Added line. other.value_ = -1;
56Added line. return *this;
57Added line. }
58Added line.
59Added line. int Value::get () const { return value_; }
60Added line.
61Added line. /* { dg-final { cleanup-ada-spec } } */
62

Tests.

qualified-method-names.C C++ · 61 lines
/* { dg-do compile } */
/* { dg-options "-fdump-ada-spec-slim" } */
/* { dg-final { scan-file qualified_method_names_c.ads "function inspect_Const" } } */
/* { dg-final { scan-file qualified_method_names_c.ads "function inspect_Volatile" } } */
/* { dg-final { scan-file qualified_method_names_c.ads "function category_Lvalue" } } */
/* { dg-final { scan-file qualified_method_names_c.ads "function category_Rvalue" } } */
/* { dg-final { scan-file qualified_method_names_c.ads "function Assign_Value_Move" } } */

class Accessor
{
public:
  explicit Accessor (int value);
  int inspect ();
  int inspect () const;
  int inspect () volatile;
  int category () &;
  int category () &&;

private:
  int value_;
};

Accessor::Accessor (int value) : value_ (value) {}
int Accessor::inspect () { return value_ + 1; }
int Accessor::inspect () const { return value_ + 2; }
int Accessor::inspect () volatile { return value_ + 3; }
int Accessor::category () & { return value_ + 4; }
int Accessor::category () && { return value_ + 5; }

class Value
{
public:
  explicit Value (int initial);
  Value &operator= (const Value &other);
  Value &operator= (Value &&other);
  int get () const;

private:
  int value_;
};

Value::Value (int initial) : value_ (initial) {}

Value &
Value::operator= (const Value &other)
{
  value_ = other.value_;
  return *this;
}

Value &
Value::operator= (Value &&other)
{
  value_ = other.value_;
  other.value_ = -1;
  return *this;
}

int Value::get () const { return value_; }

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

Download · View in repository

qualified_method_names_consumer.adb Ada · 43 lines
with Interfaces.C; use Interfaces.C;
with Qualified_Method_Names_C;

procedure Qualified_Method_Names_Consumer is
   package Bindings renames Qualified_Method_Names_C;

   Object : aliased Bindings.Class_Accessor.Accessor :=
     Bindings.Class_Accessor.New_Accessor (10);
   Target : aliased Bindings.Class_Value.Value :=
     Bindings.Class_Value.New_Value (20);
   Copy_Source : aliased Bindings.Class_Value.Value :=
     Bindings.Class_Value.New_Value (30);
   Move_Source : aliased Bindings.Class_Value.Value :=
     Bindings.Class_Value.New_Value (40);
   Result : access Bindings.Class_Value.Value;
begin
   if Bindings.Class_Accessor.inspect (Object'Access) /= 11
     or else Bindings.Class_Accessor.inspect_Const (Object'Access) /= 12
     or else Bindings.Class_Accessor.inspect_Volatile (Object'Access) /= 13
     or else Bindings.Class_Accessor.category_Lvalue (Object'Access) /= 14
     or else Bindings.Class_Accessor.category_Rvalue (Object'Access) /= 15
   then
      raise Program_Error with "qualified methods called the wrong symbols";
   end if;

   Result := Bindings.Class_Value.Assign_Value
     (Target'Access, Copy_Source'Access);
   if Bindings.Class_Value.get_Const (Result) /= 30
     or else Bindings.Class_Value.get_Const (Target'Access) /= 30
     or else Bindings.Class_Value.get_Const (Copy_Source'Access) /= 30
   then
      raise Program_Error with "copy assignment mapping failed";
   end if;

   Result := Bindings.Class_Value.Assign_Value_Move
     (Target'Access, Move_Source'Access);
   if Bindings.Class_Value.get_Const (Result) /= 40
     or else Bindings.Class_Value.get_Const (Target'Access) /= 40
     or else Bindings.Class_Value.get_Const (Move_Source'Access) /= -1
   then
      raise Program_Error with "move assignment mapping failed";
   end if;
end Qualified_Method_Names_Consumer;

Download · View in repository

run-test.sh shell · 70 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-qualified-method-names/tests/qualified-method-names.C"
ada_fixture="$root/bundles/cxx-ada-qualified-method-names/tests/qualified_method_names_consumer.adb"
work=$(mktemp -d "${TMPDIR:-/tmp}/gnat-cxx-ada-qualified-methods.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" -c "-O$optimization" \
      -fdump-ada-spec-slim qualified-method-names.C
  )

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

  if [[ "$state" == unpatched ]]; then
    [[ $run_status -ne 0 ]] || {
      echo "error: unpatched qualified-method regression unexpectedly passed" >&2
      exit 1
    }
    grep -Eiq 'conflicts with declaration|not declared|undefined' \
      "$case_dir/run.log" || {
      cat "$case_dir/run.log"
      exit 1
    }
    echo "cxx-ada-qualified-method-names -O$optimization: expected rejection (GCC $version)"
    continue
  fi

  [[ $run_status -eq 0 ]] || {
    cat "$case_dir/run.log"
    exit 1
  }
  echo "cxx-ada-qualified-method-names -O$optimization: patched (GCC $version)"
done

Download · View in repository

Commands.

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