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 exact GCC target
PatchsetGCC 1313.2.0GCC 1414.2.0GCC 1515.3.0GCC 1616.1.0GCC 1616.2.0
1.2.0 (latest)Patchedgcc-13-1413.2.0Patchedgcc-13-1414.2.0Patchedgcc-15-1615.3.0Not coveredPatchedgcc-15-1616.2.0
1.1.1Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.1.0Not applicable16.2.0
1.1.0Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not coveredNot applicable16.2.0
1.0.1Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.1.0Not covered
1.0.0Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.1.0Not covered

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.