]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++/reflection: mangling dependent splices [PR123237]
authorMarek Polacek <polacek@redhat.com>
Fri, 13 Mar 2026 12:19:26 +0000 (08:19 -0400)
committerMarek Polacek <polacek@redhat.com>
Mon, 13 Apr 2026 23:09:16 +0000 (19:09 -0400)
This patch adds a mangling for dependent splices.  Since the ABI discussion
at <https://github.com/itanium-cxx-abi/cxx-abi/issues/208> hasn't gelled
yet, this mangling might change in the future.  In this patch I'm adding

  <splice> ::= DS <expression> [ <template-args> ] E

for all dependent splices.

When we have ^^T::x, we can't say what kind of reflection this
will be, so this patch emits the "de" prefix.  For template template
parameters we emit "tt".

PR c++/123237
PR c++/124771
PR c++/124842

gcc/cp/ChangeLog:

* mangle.cc (write_splice): New.
(write_prefix): Call write_splice for SPLICE_SCOPE.
(write_type): Likewise.
(write_expression): Call write_splice for dependent_splice_p.
(write_reflection): Handle the "tt" and "de" prefixes.
* reflect.cc (reflection_mangle_prefix): For
DECL_TEMPLATE_TEMPLATE_PARM_P, use the prefix "tt".  For
dependent reflections, use "de".

gcc/testsuite/ChangeLog:

* g++.dg/reflect/mangle2.C: New test.
* g++.dg/reflect/mangle3.C: New test.
* g++.dg/reflect/mangle4.C: New test.
* g++.dg/reflect/mangle5.C: New test.

Reviewed-by: Jason Merrill <jason@redhat.com>
gcc/cp/mangle.cc
gcc/cp/reflect.cc
gcc/testsuite/g++.dg/reflect/mangle2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/reflect/mangle3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/reflect/mangle4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/reflect/mangle5.C [new file with mode: 0644]

index d1301789ef4d4e09e1cb984b9e3cda10eff6fb57..1cd5470a8f97ac3bf0ed20039c46466541f4bfd9 100644 (file)
@@ -240,6 +240,7 @@ static void write_local_name (tree, const tree, const tree);
 static void dump_substitution_candidates (void);
 static tree mangle_decl_string (const tree);
 static void maybe_check_abi_tags (tree, tree = NULL_TREE, int = 10);
+static void write_splice (tree);
 
 /* Control functions.  */
 
@@ -1299,7 +1300,8 @@ write_nested_name (const tree decl)
            ::= <template-prefix> <template-args>
            ::= <decltype>
            ::= # empty
-           ::= <substitution>  */
+           ::= <substitution>
+           ::= <splice>            # C++26 dependent splice [proposed]  */
 
 static void
 write_prefix (const tree node)
@@ -1319,6 +1321,12 @@ write_prefix (const tree node)
       return;
     }
 
+  if (TREE_CODE (node) == SPLICE_SCOPE)
+    {
+      write_splice (node);
+      return;
+    }
+
   if (find_substitution (node))
     return;
 
@@ -2444,7 +2452,7 @@ write_local_name (tree function, const tree local_entity,
            ::= G <type>    # imaginary (C 2000)     [not supported]
            ::= U <source-name> <type>   # vendor extended type qualifier
 
-   C++0x extensions
+   C++11 extensions
 
      <type> ::= RR <type>   # rvalue reference-to
      <type> ::= Dt <expression> # decltype of an id-expression or
@@ -2452,6 +2460,7 @@ write_local_name (tree function, const tree local_entity,
      <type> ::= DT <expression> # decltype of an expression
      <type> ::= Dn              # decltype of nullptr
      <type> ::= Dm             # decltype of ^^int
+     <type> ::= <splice>       # C++26 dependent splice [proposed]
 
    TYPE is a type node.  */
 
@@ -2734,6 +2743,10 @@ write_type (tree type)
              ++is_builtin_type;
              break;
 
+           case SPLICE_SCOPE:
+             write_splice (type);
+             break;
+
            case TYPEOF_TYPE:
              sorry ("mangling %<typeof%>, use %<decltype%> instead");
              break;
@@ -3441,7 +3454,10 @@ range_expr_nelts (tree expr)
                  ::= L <mangled-name> E                # external name
                  ::= st <type>                         # sizeof
                  ::= sr <type> <unqualified-name>      # dependent name
-                 ::= sr <type> <unqualified-name> <template-args> */
+                 ::= sr <type> <unqualified-name> <template-args>
+                 ::= L Dm <value reflection> E         # C++26 reflection
+                                                       # value [proposed]
+                 ::= <splice>          # C++26 dependent splice [proposed]  */
 
 static void
 write_expression (tree expr)
@@ -3699,6 +3715,8 @@ write_expression (tree expr)
        write_string ("on");
       write_unqualified_id (expr);
     }
+  else if (dependent_splice_p (expr))
+    write_splice (expr);
   else if (TREE_CODE (expr) == TEMPLATE_ID_EXPR)
     {
       tree fn = TREE_OPERAND (expr, 0);
@@ -4174,7 +4192,9 @@ write_expression (tree expr)
                  ::= co [ <prefix> ] <unqualified-name> # concept
                  ::= na [ <prefix> ] <unqualified-name> # namespace alias
                  ::= ns [ <prefix> ] <unqualified-name> # namespace
-                 ::= gs                                # ^^::
+                 ::= gs                                 # ^^::
+                 ::= tt <template-template-param>       # templ templ param
+                 ::= de <expression>                    # dependent expr
                  ::= ba [ <nonnegative number> ] _ <type> # dir. base cls rel
                  ::= ds <type> _ [ <unqualified-name> ] _
                      [ <alignment number> ] _ [ <bit-width number> ] _
@@ -4286,6 +4306,41 @@ write_reflection (tree refl)
       for (int i = 5; i < TREE_VEC_LENGTH (arg); ++i)
        write_template_arg (REFLECT_EXPR_HANDLE (TREE_VEC_ELT (arg, i)));
     }
+  else if (strcmp (prefix, "tt") == 0)
+    {
+      gcc_assert (DECL_TEMPLATE_TEMPLATE_PARM_P (arg));
+      write_template_template_param (TREE_TYPE (arg));
+    }
+  else if (strcmp (prefix, "de") == 0)
+    write_expression (arg);
+  else
+    gcc_unreachable ();
+}
+
+/* Mangle a dependent splice.
+
+     <splice> ::= DS <expression> [ <template-args> ] E
+
+   TODO: This is only a proposed mangling.
+   See <https://github.com/itanium-cxx-abi/cxx-abi/issues/208>.  */
+
+static void
+write_splice (tree sp)
+{
+  write_string ("DS");
+
+  if (TREE_CODE (sp) == SPLICE_SCOPE)
+    sp = SPLICE_SCOPE_EXPR (sp);
+  gcc_assert (dependent_splice_p (sp));
+  if (TREE_CODE (sp) == TEMPLATE_ID_EXPR)
+    {
+      write_expression (TREE_OPERAND (TREE_OPERAND (sp, 0), 0));
+      write_template_args (TREE_OPERAND (sp, 1));
+    }
+  else
+    write_expression (TREE_OPERAND (sp, 0));
+
+  write_char ('E');
 }
 
 /* Literal subcase of non-terminal <template-arg>.
index 06bfae2774832cc77a0eee7e6d74b48b93d843f8..4ef340f0726cebb6114c79e4eee66ffaec3eef9d 100644 (file)
@@ -9179,6 +9179,21 @@ reflection_mangle_prefix (tree refl, char prefix[3])
       strcpy (prefix, "ds");
       return h;
     }
+  /* We don't have a metafunction for template template parameters.  */
+  if (DECL_TEMPLATE_TEMPLATE_PARM_P (h))
+    {
+      strcpy (prefix, "tt");
+      return h;
+    }
+  /* For ^^T::x, we can't say what the reflection is going to be.  Just say
+     it's something dependent.  This is at the end rather than the beginning
+     because potential_constant_expression_1 doesn't handle codes like
+     TREE_BINFO.  */
+  if (uses_template_parms (h))
+    {
+      strcpy (prefix, "de");
+      return h;
+    }
   gcc_unreachable ();
 }
 
diff --git a/gcc/testsuite/g++.dg/reflect/mangle2.C b/gcc/testsuite/g++.dg/reflect/mangle2.C
new file mode 100644 (file)
index 0000000..452e172
--- /dev/null
@@ -0,0 +1,48 @@
+// PR c++/123237
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+template<typename T>
+using A0 = [:^^T:];
+
+template<typename T>
+using A1 = [:parent_of (^^T):]::B;
+
+namespace N {
+  using B = int;
+  struct S {};
+}
+namespace D {
+  using B = long;
+  struct S {};
+}
+
+template <typename T>
+using A2 = decltype (T ().[:std::meta::nonstatic_data_members_of (^^T, std::meta::access_context::unchecked ())[0]:]);
+
+struct B
+{
+  long a;
+  int b;
+};
+
+struct C
+{
+  int c;
+  long d;
+};
+
+
+void
+g ()
+{
+  []<typename T = int> requires(requires { A0<T> {}; }) {}();
+  []<typename T = N::S> requires (requires { A1<T> {}; }) {} ();
+  []<typename T = C> requires (requires { A2<T> {}; }) {} ();
+}
+
+// { dg-final { scan-assembler "_ZZ1gvENKUlTyQrqXtlDSLDmtyT_EEEEvE_clIiEEDav" } }
+// { dg-final { scan-assembler "_ZZ1gvENKUlTyQrqXtlNDSclL_ZNSt4meta9parent_ofEDmELDmtyT_EEE1BEEEvE0_clIN1N1SEEEDav" } }
+// { dg-final { scan-assembler "_ZZ1gvENKUlTyQrqXtlDtdtcvT__EDScldtclL_ZNSt4meta25nonstatic_data_members_ofEDmNS0_14access_contextEELDmtyS_EclL_ZNS1_9uncheckedEvEEEL_ZNSt6vectorIDmSaIDmEEixEmELi0EEEEEEvE1_clI1CEEDav" } }
diff --git a/gcc/testsuite/g++.dg/reflect/mangle3.C b/gcc/testsuite/g++.dg/reflect/mangle3.C
new file mode 100644 (file)
index 0000000..2f901c1
--- /dev/null
@@ -0,0 +1,241 @@
+// PR c++/123237
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+// Test mangling dependent splices.
+
+#include <meta>
+using namespace std::meta;
+
+int i;
+int arr[] = {1, 2, 3};
+auto [a1, a2, a3] = arr;
+enum Enum { A };
+using Alias = int;
+
+template<auto>
+struct TCls { };
+
+template <auto> int TVar;
+
+template<typename T>
+struct B {
+  T t;
+  constexpr T fn () { return 42; }
+  template<typename>
+  struct C { };
+  template<typename U>
+  using U = C<U>;
+};
+
+struct Y {
+  int i;
+  using type = int;
+};
+
+struct S : Y { };
+
+namespace N {
+  struct NY {
+    int i;
+  };
+}
+
+namespace NSAlias = N;
+template <auto> concept Concept = requires { true; };
+constexpr auto ctx = std::meta::access_context::current ();
+
+template<typename T>
+using US = [:^^T:];
+
+template<info R>
+constexpr auto f1 (typename [:R:] x) { return x; }
+
+template<info R>
+constexpr auto f2 (typename [:R:]::NY x) { return x; }
+
+template<info R>
+constexpr auto f3 () -> TCls<sizeof(typename [:R:])> { return {}; }
+
+template<info R>
+constexpr auto f4 () -> TCls<sizeof([:R:])> { return {}; }
+
+template<info R>
+constexpr auto f5 () -> decltype([:R:]) { return {}; }
+
+template<typename T>
+constexpr auto f6 (US<T> y) { return y; }
+
+template<info R>
+constexpr auto f7 (typename [:R:]<0> x) { return x; }
+
+template<typename T>
+constexpr auto f8 (typename [:^^T:] x) { return x; }
+
+template<info R>
+constexpr typename [:R:] f9 (int i) { return i; }
+
+template<typename T>
+constexpr T foo (T t) { return t; }
+
+template<info R>
+constexpr auto f10 () -> decltype([:R:](42)) { return {}; }
+
+template<info R>
+constexpr auto f11 (TCls<[:R:](42)> a) { return a; }
+
+template<typename T>
+consteval info id () { return ^^T; }
+
+template<typename T>
+constexpr typename [:id<T>():] f12 () { return 42; }
+
+template<info R>
+constexpr auto f13 (typename [:R:]::type x) { return x; }
+
+template<info R>
+constexpr auto f14 () -> [:R:] { return {}; }
+
+template<typename T, info R>
+constexpr auto f15 () -> decltype(T{}.[:R:]) { return {}; }
+
+template<typename T, info R>
+constexpr auto f16 () -> decltype(T{}.[:R:]()) { return {}; }
+
+template<info O, info M>
+constexpr auto f17 () -> decltype(typename [:O:]{}.[:M:]) { return {}; }
+
+template<info M>
+constexpr auto f18 () -> decltype(Y{}.[:M:]) { return {}; }
+
+template<template<typename> class T>
+constexpr auto f19 () -> [:^^T:]<int> { return {}; }
+
+template<typename T>
+constexpr auto f20 () -> [:^^typename T::type:] { return {}; }
+
+template<typename T>
+constexpr auto f21 () -> decltype(&[:^^T::i:]) { return {}; }
+
+template<typename T>
+constexpr auto f22 () -> decltype(&[:^^T::fn:]) { return {}; }
+
+template<typename T>
+constexpr auto f23 () -> [:^^typename T::C<int>:] { return {}; }
+
+template<typename T>
+constexpr auto f24 () -> [:^^typename T::U<int>:] { return {}; }
+
+template<info R>
+constexpr auto f25 () -> decltype([:R:])* { return {}; }
+
+template<typename T>
+consteval auto f26 (typename [:^^T:] x) { return x; }
+
+template<info R>
+constexpr auto f27 (typename [:R:]::Alias x) { return x; }
+
+void
+g (int p)
+{
+  f1<^^Y>(Y{ 42 });
+// { dg-final { scan-assembler "_Z2f1ILDmty1YEEDaDST_E" } }
+  f1<^^Enum>(A);
+// { dg-final { scan-assembler "_Z2f1ILDmty4EnumEEDaDST_E" } }
+  f2<^^N>(N::NY{ 42 });
+// { dg-final { scan-assembler "_Z2f2ILDmns1NEEDaNDST_E2NYE" } }
+  f2<^^NSAlias>(NSAlias::NY{ 42 });
+// { dg-final { scan-assembler "_Z2f2ILDmna7NSAliasEEDaNDST_E2NYE" } }
+  f3<^^int>();
+// { dg-final { scan-assembler "_Z2f3ILDmtyiEE4TClsIXstDST_EEEv" } }
+  f3<^^Alias>();
+// { dg-final { scan-assembler "_Z2f3ILDmtyiEE4TClsIXstDST_EEEv" } }
+  f4<^^i>();
+// { dg-final { scan-assembler "_Z2f4ILDmvr1iEE4TClsIXszDST_EEEv" } }
+  f4<^^arr>();
+// { dg-final { scan-assembler "_Z2f4ILDmvr3arrEE4TClsIXszDST_EEEv" } }
+  f4<^^a1>();
+// { dg-final { scan-assembler "_Z2f4ILDmsb2a1EE4TClsIXszDST_EEEv" } }
+  f4<^^A>();
+// { dg-final { scan-assembler "_Z2f4ILDmen4Enum1AEE4TClsIXszDST_EEEv" } }
+  f4<^^TVar>();
+// { dg-final { scan-assembler "_Z2f4ILDmvt4TVarEE4TClsIXszDST_EEEv" } }
+  f5<^^i>();
+// { dg-final { scan-assembler "_Z2f5ILDmvr1iEEDtDST_EEv" } }
+  f5<^^a1>();
+// { dg-final { scan-assembler "_Z2f5ILDmsb2a1EEDtDST_EEv" } }
+  f5<^^A>();
+// { dg-final { scan-assembler "_Z2f5ILDmen4Enum1AEEDtDST_EEv" } }
+  f5<^^TVar>();
+// { dg-final { scan-assembler "_Z2f5ILDmvt4TVarEEDtDST_EEv" } }
+  f5<^^Concept>();
+// { dg-final { scan-assembler "_Z2f5ILDmco7ConceptEEDtDST_EEv" } }
+  f5<^^p>();
+// { dg-final { scan-assembler "_Z2f5ILDmvrZ1giE1pEEDtDST_EEv" } }
+  f5<std::meta::reflect_constant (42)>();
+// { dg-final { scan-assembler "_Z2f5ILDmvlLi42EEEDtDST_EEv" } }
+  f5<std::meta::reflect_object (arr[1])>();
+// { dg-final { scan-assembler "_Z2f5ILDmobixL_Z3arrELl1EEEDtDST_EEv" } }
+  f5<parameters_of (^^g)[0]>();
+// { dg-final { scan-assembler "_Z2f5ILDmpa_1giEEDtDST_EEv" } }
+  f5<bases_of (^^S, ctx)[0]>();
+// { dg-final { scan-assembler "_Z2f5ILDmba_1SEEDtDST_EEv" } }
+  f6<Y>(Y{42});
+// { dg-final { scan-assembler "_Z2f6I1YEDaDSLDmtyT_EE" } }
+  f7<^^TCls>(TCls<0>{});
+// { dg-final { scan-assembler "_Z2f7ILDmct4TClsEEDaDST_ILi0EEE" } }
+  f8<int>(42);
+// { dg-final { scan-assembler "_Z2f8IiEDaDSLDmtyT_EE" } }
+  f8<Alias>(42);
+// { dg-final { scan-assembler "_Z2f8IiEDaDSLDmtyT_EE" } }
+  f9<^^int>(42);
+// { dg-final { scan-assembler "_Z2f9ILDmtyiEEDST_Ei" } }
+  f9<^^Alias>(42);
+// { dg-final { scan-assembler "_Z2f9ILDmtyiEEDST_Ei" } }
+  f10<^^foo<int>>();
+// { dg-final { scan-assembler "_Z3f10ILDmfn3fooIiET_S1_EEDTclDST_ELi42EEEv" } }
+  f10<^^foo<Alias>>();
+// { dg-final { scan-assembler "_Z3f10ILDmfn3fooIiET_S1_EEDTclDST_ELi42EEEv" } }
+  f11<^^foo<int>>(TCls<42>{});
+// { dg-final { scan-assembler "_Z3f11ILDmfn3fooIiET_S1_EEDa4TClsIXclDST_ELi42EEEE" } }
+  f11<^^foo<Alias>>(TCls<42>{});
+// { dg-final { scan-assembler "_Z3f11ILDmfn3fooIiET_S1_EEDa4TClsIXclDST_ELi42EEEE" } }
+  f12<int>();
+// { dg-final { scan-assembler "_Z3f12IiEDScl2idIT_EEEv" } }
+  f12<Alias>();
+// { dg-final { scan-assembler "_Z3f12IiEDScl2idIT_EEEv" } }
+  f13<^^Y>(42);
+// { dg-final { scan-assembler "_Z3f13ILDmty1YEEDaNDST_E4typeE" } }
+  f14<^^int>();
+// { dg-final { scan-assembler "_Z3f14ILDmtyiEEDST_Ev" } }
+  f14<^^TCls<1>>();
+// { dg-final { scan-assembler "_Z3f14ILDmty4TClsILi1EEEEDST_Ev" } }
+  f15<Y, ^^Y::i>();
+// { dg-final { scan-assembler "_Z3f15I1YLDmdmS0_1iEEDtdttlT_EDST0_EEv" } }
+  f15<B<int>, ^^B<int>::t>();
+// { dg-final { scan-assembler "_Z3f15I1BIiELDmdmS1_1tEEDtdttlT_EDST0_EEv" } }
+  f16<B<int>, ^^B<int>::fn>();
+// { dg-final { scan-assembler "_Z3f16I1BIiELDmfnNS1_2fnEvEEDTcldttlT_EDST0_EEEv" } }
+  f17<^^Y, ^^Y::i>();
+// { dg-final { scan-assembler "_Z3f17ILDmty1YELDmdmS0_1iEEDtdttlDST_EEDST0_EEv" } }
+  f18<^^Y::i>();
+// { dg-final { scan-assembler "_Z3f18ILDmdm1Y1iEEDtdttlS0_EDST_EEv" } }
+  f19<B>();
+// { dg-final { scan-assembler "_Z3f19I1BEDSLDmttT_EIiEEv" } }
+  f20<Y>();
+// { dg-final { scan-assembler "_Z3f20I1YEDSLDmtyNT_4typeEEEv" } }
+  f21<Y>();
+// { dg-final { scan-assembler "_Z3f21I1YEDTadDSLDmdesrT_1iEEEv" } }
+  f22<B<int>>();
+// { dg-final { scan-assembler "_Z3f22I1BIiEEDTadDSLDmdesrT_2fnEEEv" } }
+  f23<B<int>>();
+// { dg-final { scan-assembler "_Z3f23I1BIiEEDSLDmtyNT_1CIiEEEEv" } }
+  f24<B<int>>();
+// { dg-final { scan-assembler "_Z3f24I1BIiEEDSLDmtyNT_1UIiEEEEv" } }
+  f24<B<Alias>>();
+// { dg-final { scan-assembler "_Z3f24I1BIiEEDSLDmtyNT_1UIiEEEEv" } }
+  f25<^^arr>();
+// { dg-final { scan-assembler "_Z3f25ILDmvr3arrEEPDtDST_EEv" } }
+  constexpr auto r = f26<std::meta::info>({});
+  f27<^^::>(42);
+// { dg-final { scan-assembler "_Z3f27ILDmgsEEDaNDST_E5AliasE" } }
+}
diff --git a/gcc/testsuite/g++.dg/reflect/mangle4.C b/gcc/testsuite/g++.dg/reflect/mangle4.C
new file mode 100644 (file)
index 0000000..3880468
--- /dev/null
@@ -0,0 +1,26 @@
+// PR c++/124771
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <mdspan>
+#include <meta>
+
+namespace std {
+template<random_access_iterator I>
+  struct iterator_accessor {
+    using element_type     = typename [: 
+      [] { return ^^iter_value_t<I>; }() 
+    :];
+
+    constexpr iterator_accessor() noexcept = default;
+    constexpr operator default_accessor<const element_type>() noexcept
+      requires contiguous_iterator<I> && (!is_const_v<element_type>)
+        { return {}; }
+  };
+}
+
+int main() {
+  // ??? It's not clear which of these should error.
+  std::default_accessor<int>       _ = std::iterator_accessor<int*>();
+  std::default_accessor<const int> _ = std::iterator_accessor<int*>(); // { dg-error "conversion" }
+}
diff --git a/gcc/testsuite/g++.dg/reflect/mangle5.C b/gcc/testsuite/g++.dg/reflect/mangle5.C
new file mode 100644 (file)
index 0000000..4863563
--- /dev/null
@@ -0,0 +1,25 @@
+// PR c++/124842
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+inline constexpr int x = 1;
+template <auto r>
+typename[: std::meta::type_of(r) :] f() {
+  return 1;
+}
+
+int
+f1 ()
+{
+  return f<^^x>();
+// { dg-final { scan-assembler "_Z1fITnDaLDmvr1xEEDScl7type_ofT_EEv" } }
+}
+
+int
+f2 ()
+{
+  return f<std::meta::reflect_constant(1)>();
+// { dg-final { scan-assembler "_Z1fITnDaLDmvlLi1EEEDScl7type_ofT_EEv" } }
+}