On Monday, February 9th, 2026 at 5:41 PM, Marek Polacek <[email protected]> wrote:
> > > On Mon, Feb 09, 2026 at 11:03:14PM +0900, Jason Merrill wrote: > > > 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. > > > Also thanks for your bug reports! Note that the PR is in the ASSIGNED > state indicating that someone, in this case me, is already working on > the problem. I didn't get to this PR yet so it's fine -- I thought it > would depend on another bug fix that is on my plate, namely, not passing > ctx to finish_id_expression. Looks like it doesn't. > I did notice that the bug report had the ASSIGNED state, but I thought you already had a lot to do and my intention was to help out. I first posted this patch to the bug tracker, but after no replies I thought it would have been fine to submit the patch for review. > > > 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. > > > Note that we have separate ChangeLogs for cp/ and for testsuite/. > It's best to use gcc-mklog to auto-generate a ChangeLog skeleton. > Feel free to contact me off-list. I did notice that and thought I got the format right. Anyway, fixed now. > > > > 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. > > > BASELINK_ACCESS_BINFO Done. > > > > + 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 (. > > > I would like this to be moved to tsubst_splice_expr (and I guess check > SPLICE_EXPR_ADDRESS_P). Done. > > > > + } > > > + } > > > 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 } } > > > I don't think this test needs to be dg-run, dg-compile should be enough. > I initially used dg-compile, but this pull request made me reconsider: https://gcc.gnu.org/pipermail/gcc-patches/2026-February/707791.html Anyway, changed to dg-dompile. > > > +// { 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 @@ > > > I'd also add // PR c++/123661 here. Done. > > > > +// { dg-do run { target c++26 } } > > > Also dg-compile. Done. > > > > +// { 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); > > > +} > > > Marek Here's a v2 of the patch, 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` - `splice10.C` test) and passing `^^T::member` as the template argument (in which case evaluating the `SPLICE_EXPR` returns a `BASELINK` - `splice11.C`). Signed-off-by: Boris Staletic <[email protected]> PR c++/123660 PR c++/123661 gcc/cp/ChangeLog: * pt.cc (tsubst_splice_expr): Handle pointers to non-static members from splice expressions gcc/testsuite/ChangeLog: * g++.dg/reflect/splice10.C: New test. * g++.dg/reflect/splice11.C: New test. --- gcc/cp/pt.cc | 14 ++++++++++++ gcc/testsuite/g++.dg/reflect/splice10.C | 28 ++++++++++++++++++++++++ gcc/testsuite/g++.dg/reflect/splice11.C | 29 +++++++++++++++++++++++++ 3 files changed, 71 insertions(+) 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..0c0076c9a4 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -16755,6 +16755,20 @@ tsubst_splice_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) op = splice (op); if (op == error_mark_node) return error_mark_node; + + if (SPLICE_EXPR_ADDRESS_P (t) && !TREE_STATIC (TREE_TYPE (op))) + { + if (TREE_CODE (op) == BASELINK + && TREE_CODE (TREE_TYPE (op)) == METHOD_TYPE) + op = build_offset_ref (BINFO_TYPE (BASELINK_ACCESS_BINFO (op)), op, + /*address_p=*/true, complain); + else if (TREE_CODE (op) == FIELD_DECL + || (TREE_CODE (op) == FUNCTION_DECL + && TREE_CODE (TREE_TYPE (op)) == METHOD_TYPE)) + op = build_offset_ref (DECL_CONTEXT (op), op, + /*address_p=*/true, complain); + } + if (dependent_splice_p (op)) { if (SPLICE_EXPR_EXPRESSION_P (t)) diff --git a/gcc/testsuite/g++.dg/reflect/splice10.C b/gcc/testsuite/g++.dg/reflect/splice10.C new file mode 100644 index 0000000000..2f335ea8fc --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/splice10.C @@ -0,0 +1,28 @@ +// PR c++/123660 +// PR c++/123661 +// { dg-do compile { 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]:]; +} + +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..b62fdcc5ee --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/splice11.C @@ -0,0 +1,29 @@ +// PR c++/123660 +// PR c++/123661 +// { dg-do compile { 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:]; +} + +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); -- 2.51.1
