On Tue, Feb 10, 2026 at 06:14:02PM +0000, Boris Staletic wrote:
> On Tuesday, February 10th, 2026 at 5:38 PM, Marek Polacek 
> <[email protected]> wrote:
> 
> > On Tue, Feb 10, 2026 at 05:32:22PM +0000, Boris Staletic wrote:
> > > On Tuesday, February 10th, 2026 at 4:49 PM, Marek Polacek 
> > > <[email protected]> wrote:
> > >
> > > > On Tue, Feb 10, 2026 at 03:26:37PM +0000, Boris Staletic wrote:
> > > > > On Tuesday, February 10th, 2026 at 2:05 PM, Jason Merrill 
> > > > > <[email protected]> wrote:
> > > > >
> > > > > >
> > > > > >
> > > > >
> > > > > Snipped older discussions for brevity.
> > > > >
> > > > > > >
> > > > > > > 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)))
> > > > > >
> > > > > >
> > > > > > TREE_STATIC on a type isn't meaningful, the checks below are enough.
> > > > >
> > > > > I thought that was fishy... You're right, everything works without 
> > > > > that check.
> > > > >
> > > > > >
> > > > > > > + {
> > > > > > > + if (TREE_CODE (op) == BASELINK
> > > > > > > + && TREE_CODE (TREE_TYPE (op)) == METHOD_TYPE)
> > > > > >
> > > > > >
> > > > > > It shouldn't be necessary to check for METHOD_TYPE; any class 
> > > > > > member is
> > > > > > suitable for passing to build_offset_ref. If the argument is static,
> > > > > > it'll return it unchanged rather than actually build an OFFSET_REF.
> > > > >
> > > > > If I drop the METHOD_TYPE check, I get an ICE in both splice10.C and 
> > > > > splice11.C.
> > > > > splice10.C ICEs when calling `build_offset_ref()` on a static member 
> > > > > function (s::get_y).
> > > > > splice11.C ICEs when calling `build_offset_ref()` on a free function 
> > > > > (::f).
> > > > >
> > > > > That's why I was initially looking for a way to do this only for 
> > > > > non-static members.
> > > >
> > > > I think Jason meant the check for METHOD_TYPE for a BASELINK, which
> > > > is certainly not needed.  Please also use BASELINK_P instead of
> > > > TREE_CODE == BASELINK.
> > > >
> > > > We shouldn't call build_offset_ref on static member functions or
> > > > free functions.  So I think the second call to build_offset_ref
> > > > should be guarded by
> > > >
> > > >   TREE_CODE (op) == FIELD_DECL || DECL_OBJECT_MEMBER_FUNCTION_P (op)
> > > >
> > > > But maybe Jason prefers something else.
> > > >
> > > >
> > > > I would also move the SPLICE_EXPR_ADDRESS_P block after checking
> > > > dependent_splice_p and after check_splice_expr.
> > >
> > > Okay, all of that works. One question:
> > > Would it be more correct to guard the whole block with
> > >
> > > if (SPLICE_EXPR_ADDRESS_P (t))
> > >
> > > or
> > >
> > > if (SPLICE_EXPR_ADDRESS_P (op))
> > >
> > > Given what's in the dependent_splice_p block, I'm thinking latter, but 
> > > I'm not sure.
> > 
> > It should check t, because op won't be a SPLICE_EXPR (except in the
> > dependent_splice_p block).
> > 
> > Marek
> > 
> 
> In that case, here's the updated patch:

Thanks, the patch looks good to me now (but I can't approve it).

This seems like your first patch to GCC, and I'm not sure these
changes are trivial.  As documented at 
<https://gcc.gnu.org/contribute.html#legal>
we need either a copyright assignment with the FSF, or DCO (Developer
Certificate of Origin) sign-off (<https://gcc.gnu.org/dco.html>).  Since
you've included the Signed-off-by trailer it seems like you want to
contribute under the terms of the DCO.

> -- >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                            | 12 ++++++++++
>  gcc/testsuite/g++.dg/reflect/splice10.C | 28 ++++++++++++++++++++++++
>  gcc/testsuite/g++.dg/reflect/splice11.C | 29 +++++++++++++++++++++++++
>  3 files changed, 69 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..333ecc3130 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -16771,6 +16771,18 @@ tsubst_splice_expr (tree t, tree args, 
> tsubst_flags_t complain, tree in_decl)
>                            SPLICE_EXPR_MEMBER_ACCESS_P (t),
>                            (complain & tf_error)))
>      return error_mark_node;
> +
> +  if (SPLICE_EXPR_ADDRESS_P (t))
> +    {
> +      if (BASELINK_P (op))
> +     op = build_offset_ref (BINFO_TYPE (BASELINK_ACCESS_BINFO (op)), op,
> +                            /*address_p=*/true, complain);
> +      else if (TREE_CODE (op) == FIELD_DECL
> +            || DECL_OBJECT_MEMBER_FUNCTION_P (op))
> +     op = build_offset_ref (DECL_CONTEXT (op), op,
> +                            /*address_p=*/true, complain);
> +    }
> +
>    if (outer_automatic_var_p (op))
>      op = process_outer_var_ref (op, complain);
>    /* Like in cp_parser_splice_expression, for foo.[: bar :]
> 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
> 

Marek

Reply via email to