On 2/8/26 6:43 PM, Boris Staletic wrote:
Bootstrapped and tested on x86_64-pc-linux-gnu.
-- >8 --
In case of expressions like `&[:expr:]` where `expr` depends on a
template parameter, and the splice expression represents a `FIELD_DECL` or
a non-static member `FUNCTION_DECL`, that's exactly what we'd pass on.
However, `build_x_unary_op()` for these expressions is expecting an
`OFFSET_REF`. `OFFSET_REF` is also what gets passed to
`build_x_unary_op()` when templates are not involved.
There's also a difference between the template argument being a type and
using `members_of()` to get to the reflections of members (in which case
evaluating the `SPLICE_EXPR` returns a `FUNCTION_DECL` - `splice8.C`
test) and passing `^^T::member` as the template argument (in which case
evaluating the `SPLICE_EXPR` returns a `BASELINK` - `splice9.C`).
Thanks for the patch! A few commments below.
2026-02-08 Boris Staletic <[email protected]>
PR c++/123660
PR c++/123661
* pt.cc (tsubst_expr): Handle pointers to non-static members
from splice expressions.
* g++.dg/reflect/splice10.C: New test.
* g++.dg/reflect/splice11.C: New test.
Signed-off-by: Boris Staletic <[email protected]>
---
gcc/cp/pt.cc | 23 +++++++++++++++++--
gcc/testsuite/g++.dg/reflect/splice10.C | 30 +++++++++++++++++++++++++
gcc/testsuite/g++.dg/reflect/splice11.C | 29 ++++++++++++++++++++++++
3 files changed, 80 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/reflect/splice10.C
create mode 100644 gcc/testsuite/g++.dg/reflect/splice11.C
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 049bbf07e0..fb1cd386e8 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -21454,8 +21454,27 @@ tsubst_expr (tree t, tree args, tsubst_flags_t
complain, tree in_decl)
op1 = tsubst_qualified_id (op1, args, complain, in_decl,
/*done=*/true, /*address_p=*/true);
else
- op1 = tsubst_non_call_postfix_expression (op1, args, complain,
- in_decl);
+ {
+ tree old_op1 = op1;
+ op1 = tsubst_non_call_postfix_expression (op1, args, complain,
+ in_decl);
+ if (TREE_CODE (old_op1) == SPLICE_EXPR)
+ {
+ if (TREE_CODE (op1) == BASELINK)
+ {
+ tree fn = BASELINK_FUNCTIONS (op1);
+ if (TREE_CODE (fn) == FUNCTION_DECL &&
+ TREE_CODE (TREE_TYPE (op1) ) == METHOD_TYPE &&
+ !TREE_STATIC (op1))
+ op1 = fn;
+ }
It should be fine to just pass the BASELINK on to build_offset_ref. You
can use BINFO_TYPE (BINFO_ACCESS_BINFO as the type argument.
+ if (TREE_CODE (op1) == FIELD_DECL ||
+ (TREE_CODE (op1) == FUNCTION_DECL &&
+ TREE_CODE (TREE_TYPE (op1)) == METHOD_TYPE &&
+ !TREE_STATIC (TREE_TYPE (op1))))
Operators like ||/&& go at the beginning of the line in our coding style.
+ op1 = build_offset_ref(DECL_CONTEXT(op1), op1, true, complain);
And space before (.
+ }
+ }
RETURN (build_x_unary_op (input_location, ADDR_EXPR, op1,
templated_operator_saved_lookups (t),
complain|decltype_flag));
diff --git a/gcc/testsuite/g++.dg/reflect/splice10.C
b/gcc/testsuite/g++.dg/reflect/splice10.C
new file mode 100644
index 0000000000..cbed421021
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/splice10.C
@@ -0,0 +1,30 @@
+// PR c++/123660
+// PR c++/123661
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+struct s {
+ int get_z(this s) { return 4; }
+ static int get_y() { return 4; }
+ int get_x() { return 3; }
+ int xx;
+ static int xxx;
+};
+
+int s::xxx = 5;
+
+template<typename T, decltype(0uz) I>
+constexpr auto test() {
+ constexpr auto ctx = std::meta::access_context::current();
+ return &[:members_of(^^T, ctx)[I]:];
+}
+
+int main() {
+ static_assert(test<s, 0uz>() == &s::get_z);
+ static_assert(test<s, 1uz>() == &s::get_y);
+ static_assert(test<s, 2uz>() == &s::get_x);
+ static_assert(test<s, 3uz>() == &s::xx);
+ static_assert(test<s, 4uz>() == &s::xxx);
+}
diff --git a/gcc/testsuite/g++.dg/reflect/splice11.C
b/gcc/testsuite/g++.dg/reflect/splice11.C
new file mode 100644
index 0000000000..6568afed07
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/splice11.C
@@ -0,0 +1,29 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+void f() {}
+struct s {
+ int get_z(this s) { return 4; }
+ static int get_y() { return 4; }
+ int get_x() { return 3; }
+ int xx;
+ static int xxx;
+};
+
+int s::xxx = 5;
+
+template<std::meta::info refl_expr>
+constexpr auto test() {
+ return &[:refl_expr:];
+}
+
+int main() {
+ static_assert(test<^^s::get_z>() == &s::get_z);
+ static_assert(test<^^s::get_y>() == &s::get_y);
+ static_assert(test<^^s::get_x>() == &s::get_x);
+ static_assert(test<^^s::xx>() == &s::xx);
+ static_assert(test<^^s::xxx>() == &s::xxx);
+ static_assert(test<^^f>() == &f);
+}