]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++/reflection: ICE with &template [:members_of():] [PR124794]
authorMarek Polacek <polacek@redhat.com>
Fri, 31 Jul 2026 15:57:14 +0000 (11:57 -0400)
committerMarek Polacek <polacek@redhat.com>
Mon, 3 Aug 2026 21:26:43 +0000 (17:26 -0400)
Given

  struct C { template <class T> void f(T); };

we handle "&template [:^^C::f:]" correctly because the spliced
expression is

  BASELINK<OVERLOAD<TEMPLATE_DECL f>>, binfo C>

which is fine: we have an OVERLOAD around the TEMPLATE_DECL and
lookup_member wrapped the whole thing in a BASELINK.  But when
we're splicing members_of(^^C, ac)[0], we ended up with

  OVERLOAD<TEMPLATE_DECL f>>

and then go down the wrong path in cp_parser_splice_expression.

splice already correctly adds the missing OVERLOAD but it also
needs to (maybe) add a BASELINK.

This patch also adjusts baselink_for_fns to gain a parameter
controlling if we want to ignore currently_open_derived_class.
It matters when we're in a member function of a class derived
from C and the object argument of the -> is a different derived class,
as exercised in splice17.C: pd->[:g1:] (42); in D2::mfn.  There, if
we didn't ignore currently_open_derived_class, the BASELINK would
use D2 as the access_binfo, which is wrong because it has no
derivation relationship to the object type (here D1).  With this
patch access_binfo will be C, which is what members_of gave us.

PR c++/124794

gcc/cp/ChangeLog:

* cp-tree.h (baselink_for_fns): Adjust declaration.
* parser.cc (cp_parser_reflect_expression): Adjust the call to
baselink_for_fns.
* reflect.cc (splice): Call baselink_for_fns.
* semantics.cc (baselink_for_fns): Add a bool parameter.  If
it's true, ignore currently_open_derived_class.

gcc/testsuite/ChangeLog:

* g++.dg/reflect/splice17.C: New test.

Reviewed-by: Jason Merrill <jason@redhat.com>
gcc/cp/cp-tree.h
gcc/cp/parser.cc
gcc/cp/reflect.cc
gcc/cp/semantics.cc
gcc/testsuite/g++.dg/reflect/splice17.C [new file with mode: 0644]

index 87245184461423991370cff03cdcb379dc39b85a..a8af4d38945053adcc41bcbbc7a24a65aa3a900f 100644 (file)
@@ -8683,7 +8683,7 @@ extern void finish_transaction_stmt               (tree, tree, int, tree);
 extern tree build_transaction_expr             (location_t, tree, int, tree);
 extern bool cxx_omp_create_clause_info         (tree, tree, bool, bool,
                                                 bool, bool);
-extern tree baselink_for_fns                    (tree);
+extern tree baselink_for_fns                    (tree, bool = false);
 extern void finish_static_assert                (tree, tree, location_t,
                                                 bool, bool, bool = false);
 extern tree finish_decltype_type                (tree, bool, tsubst_flags_t);
index 19918cfa9de2e562aa70fcd312df80850a9140a4..67a1696f0262828540eabe2fa9c6dc021228bc00 100644 (file)
@@ -10233,7 +10233,7 @@ cp_parser_reflect_expression (cp_parser *parser)
        && !concept_check_p (t))
       t = finish_template_variable (t);
     else if (is_overloaded_fn (t))
-      t = baselink_for_fns (t);
+      t = baselink_for_fns (t, /*ignore_current_class_p=*/true);
     if (cp_parser_parse_definitely (parser))
       return get_reflection (loc, t);
   }
index 7b4142f2d4fb0e2883ca1933dbe1115644767309..d1cfeeacfa5f5d46aed476c3e9e8b42f4f6cfb74 100644 (file)
@@ -8790,6 +8790,11 @@ splice (tree refl)
      it comes from e.g. members_of it is not.  */
   if (DECL_FUNCTION_TEMPLATE_P (refl))
     refl = ovl_make (refl, NULL_TREE);
+  /* Also add a BASELINK so that we handle &[:R:].  Since R was already
+     resolved (e.g. via members_of), we don't want to consider the enclosing
+     class for the access path.  */
+  if (is_overloaded_fn (refl))
+    refl = baselink_for_fns (refl, /*ignore_current_class_p=*/true);
 
   return refl;
 }
index 2274c6ab9b5b7da9840f9bf378d673b330257a50..7907668da36ebf18e8bdd904f01f585aa0c327b9 100644 (file)
@@ -4564,23 +4564,22 @@ finish_base_specifier (tree base, tree access, bool virtual_p,
 /* If FNS is a member function, a set of member functions, or a
    template-id referring to one or more member functions, return a
    BASELINK for FNS, incorporating the current access context.
-   Otherwise, return FNS unchanged.  */
+   Otherwise, return FNS unchanged.  If IGNORE_CURRENT_CLASS_P is
+   true, we do not consider the currently open derived class.  */
 
 tree
-baselink_for_fns (tree fns)
+baselink_for_fns (tree fns, bool ignore_current_class_p/*=false*/)
 {
-  tree scope;
-  tree cl;
-
-  if (BASELINK_P (fns)
-      || error_operand_p (fns))
+  if (BASELINK_P (fns) || error_operand_p (fns))
     return fns;
 
-  scope = ovl_scope (fns);
+  tree scope = ovl_scope (fns);
   if (!CLASS_TYPE_P (scope))
     return fns;
 
-  cl = currently_open_derived_class (scope);
+  tree cl = (ignore_current_class_p
+            ? NULL_TREE
+            : currently_open_derived_class (scope));
   if (!cl)
     cl = scope;
   tree access_path = TYPE_BINFO (cl);
diff --git a/gcc/testsuite/g++.dg/reflect/splice17.C b/gcc/testsuite/g++.dg/reflect/splice17.C
new file mode 100644 (file)
index 0000000..634b585
--- /dev/null
@@ -0,0 +1,72 @@
+// PR c++/124794
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+struct C {
+  template <class T> void f(T);
+  void g (int);
+
+  static constexpr int val = 42;
+};
+
+constexpr auto ac = std::meta::access_context::current();
+constexpr auto f1 = members_of(^^C, ac)[0];
+constexpr auto f2 = ^^C::f;
+void (C::*p1)(int) = &template [:f1:];
+void (C::*p2)(int) = &template [:f2:];
+
+constexpr auto g1 = members_of(^^C, ac)[1];
+constexpr auto g2 = ^^C::g;
+void (C::*p3)(int) = &[:g1:];
+void (C::*p4)(int) = &[:g2:];
+
+void
+g (C *pc)
+{
+  auto p = &pc->[: ^^C::val :];
+  auto q = &pc->C::val;
+
+  pc->f (42);
+  pc->template [:f1:](42);
+  pc->template [:f2:](42);
+  pc->g (42);
+  pc->[:g1:] (42);
+  pc->[:g2:] (42);
+}
+
+struct D1 : C {
+  void mfn (D1 *pd)
+  {
+    auto p = &pd->[: ^^C::val :];
+    auto q = &pd->C::val;
+
+    pd->f (42);
+    pd->template [:f1:](42);
+    pd->template [:f2:](42);
+    pd->g (42);
+    pd->[:g1:] (42);
+    pd->[:g2:] (42);
+  }
+};
+
+struct D2 : C {
+  void mfn (D1 *pd)
+  {
+    auto p = &pd->[: ^^C::val :];
+    auto q = &pd->C::val;
+
+    constexpr auto rg = ^^C::g;
+    pd->[:rg:] (42);
+    constexpr auto rf = ^^C::f;
+    pd->template [:rf:] (42);
+
+    pd->f (42);
+    pd->template [:f1:](42);
+    pd->template [:f2:](42);
+    pd->g (42);
+    pd->[:g1:] (42);
+    pd->[:g2:] (42);
+  }
+};