On 5/15/26 12:11 PM, feedable wrote:
On 12/05/2026 23:04, Jason Merrill wrote:
On 4/29/26 10:09 AM, feedable wrote:
On 29/04/2026 05:27, Jason Merrill wrote:
On 4/28/26 4:17 PM, feedable wrote:
On 28/04/2026 22:10, Jason Merrill wrote:
On 4/27/26 7:14 PM, feedable wrote:
On 28/04/2026 01:03, Jason Merrill wrote:
On 4/27/26 2:14 PM, feedable wrote:
+ else if (BASELINK_P (expr))
+ expr = build_baselink (access_path,
BASELINK_BINFO (expr),
Extra space before =. Also I guess BASELINK_ACCESS_BINFO
instead of
BASELINK_BINFO?
We want to use access_path (relative to the actual
object_type) in place of
BASELINK_ACCESS_BINFO, but indeed the access path should be
the second
argument rather than the first in both this call and the
existing one.
If I understand correctly, access_path is the endpoint of the
search, so it
should go into BASELINK_BINFO, and it's the object type that
should go
into BASELINK_ACCESS_BINFO (in both cases, actually), since
it's the
place where we started the search.
Ah, true.
Apparently, the start would actually correspond to the time
when we got
the reflection in the first place (as we are essentially
performing
x.C::m, but with C::m being spliced); so obtaining the access
binfo from the
reflection here is correct.
I think you were right before; the reflection of a member does
not include any information about how it was produced, whether
from ^^ directly or otherwise. And normally
BASELINK_ACCESS_BINFO needs to represent either the object type
or a base thereof.
I believe that information is set in https://
forge.sourceware.org/ gcc/ gcc-TEST/src/commit/
c607c686100689e3e68487cd8097c2fbd3904168/ gcc/cp/
parser.cc#L10181, at least for the ^^ case.
So using TYPE_BINFO (object_type) as in v3 makes sense. Of
course we're suppressing access checking anyway so it doesn't
much matter, but better to be correct if it isn't a burden.
Consider this case:
```
struct X {};
struct Y { template<class T> T f(T); };
auto _ = X{}.[:^^Y::f:]<int>(1);
```
Here, in v3 we build a BASELINK with the start point at X, even
though X has nothing to do with Y::f, we couldn't've possibly
found Y::F through X.
Hmm, in that case lookup_base should fail so we don't build a
BASELINK at all.
The call to lookup_base probably should fail, yes. That said, we
still do build the BASELINK even if the result is NULL. We want
that because code later really really wants that BASELINK even if
it has no BINFO, to perform access checks on.
Ah, yes, I was forgetting null return for "not a base". But I would
think we want to error immediately in that case and set expr =
error_mark_node rather than build a BASELINK.
finish_class_member_access_expr does that for us already if we just
leave the BINFO as NULL in such cases.
Only accidentally; better to give an error here as well rather than
build a broken BASELINK that's likely to cause crashes if something
looks at it closely.
We still can't emit error_mark_node here, since we wouldn't be able to
parse `(` in `obj.[:memfn:]<args>()` otherwise, since we won't know that
it's a function.
We can emit the function without the baselink, however, but that also
changes the error in finish_class_member_access_expr to "is not a base
of" one, which is worse than the "is not a member of" that was issued
there previously.
I guess that error is directed at the case of `b.A::x` where it would
say "A is not a base of B", but for `obj.[:member:]` it makes much less
sense.
I also don't really want to change that since that was being built even
before the patch, and it seems to work just fine.
Fair enough. But then let's use TYPE_BINFO (scope) instead of NULL_TREE
to get the same behavior without a nonsense BASELINK. I'll make that
change in a followup.
@@ -16944,8 +16971,12 @@ tsubst_splice_expr (tree t, tree args, tsubst_flags_t
complain, tree in_decl)
SET_SPLICE_EXPR_TEMPLATE_P (op, true);
if (SPLICE_EXPR_TARGS_P (t))
SET_SPLICE_EXPR_TARGS_P (op, true);
- return op;
+ return apply_template (op);
}
+
+ /* We have to form a template-id for checking too */
+ op = apply_template (op);
Missing period.
From your v5, I fixed the period, word-wrapped the commit message at
76, and shortened the subject line, so this is what I'm pushing, along
with my followup. Thanks!
Jason
From 4df1b833623097b5910cd73dd87b7f152d0edf9f Mon Sep 17 00:00:00 2001
From: feedable <[email protected]>
Date: Wed, 29 Apr 2026 03:07:15 +0300
Subject: [PATCH] c++/reflection: member function template splicing [PR124794]
To: [email protected]
cp_parser_splice_expression is stripping BASELINKs before resolving the
expr; checks if the result is a BASELINK after that (by which point it never
is). To fix it, stop stripping the BASELINK, add handling to
check_splice_expr instead.
In cp_parser_splice_specifier, the additional template param parsing fails
to detect that the reflection is a template if it's wrapped in a BASELINKs,
and decides not to parse the splice-specialization-specifier if the
'template' keyword is missing. Grab the data from the reflection instead.
During template instantiation, we blindly substitute the template part of a
TEMPLATE_ID_EXPR, even if it's a SPLICE_EXPR. This, in turn, substitutes the
SPLICE_EXPR as-if it had no template arguments and finishes up the
expression, which interferes with later processing by TEMPLATE_ID_EXPR. To
fix, we defer such TEMPLATE_ID_EXPRs to tsubst_splice_expr, which itself
performs the substitution of a TEMPLATE_ID_EXPR and finishes up the
expression.
check_splice_expr now also accepts TEMPLATE_ID_EXPRs in tsubst_splice_expr;
this is uniform with cp_parser_splice_expression, and required in order to
handle `template[:dep:]<>` where `dep` does not reflect a template.
PR c++/124794
PR c++/125069
gcc/cp/ChangeLog:
* parser.cc (cp_parser_splice_specifier): Do not strip BASELINKs.
(cp_parser_splice_expression): Add parsing for member function
template specializations without the "template" keyword.
* pt.cc (tsubst_splice_expr): Handle TEMPLATE_ID_EXPR where the
template part is a SPLICE_EXPR.
(tsubst_expr): Defer to tsubst_splice_expr when the template part of
TEMPLATE_ID_EXPR is a SPLICE_EXPR.
* reflect.cc (check_splice_expr): Add handling for BASELINKs.
gcc/testsuite/ChangeLog:
* g++.dg/reflect/member19.C: Enable tests.
* g++.dg/reflect/splice15.C: New test.
* g++.dg/reflect/splice16.C: New test.
Reviewed-by: Jason Merrill <[email protected]>
---
gcc/cp/parser.cc | 21 +++++---
gcc/cp/pt.cc | 49 ++++++++++++++---
gcc/cp/reflect.cc | 14 +++--
gcc/testsuite/g++.dg/reflect/member19.C | 18 -------
gcc/testsuite/g++.dg/reflect/splice15.C | 70 +++++++++++++++++++++++++
gcc/testsuite/g++.dg/reflect/splice16.C | 34 ++++++++++++
6 files changed, 170 insertions(+), 36 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/reflect/splice15.C
create mode 100644 gcc/testsuite/g++.dg/reflect/splice16.C
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 03b1ea17683..6641dbba33d 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -6179,6 +6179,8 @@ cp_parser_splice_specifier (cp_parser *parser, bool template_p = false,
/* Get the reflected operand. */
expr = splice (expr);
+ tree expr_real = maybe_get_first_fn (expr);
+
/* If the next token is a <, it could be a splice-specialization-specifier.
But we need to handle "[:r:] < 42" where the < doesn't start a template
argument list. [temp.names]/3: A < is interpreted as the delimiter of
@@ -6190,30 +6192,34 @@ cp_parser_splice_specifier (cp_parser *parser, bool template_p = false,
/* As a courtesy to the user, if there is a < after a template
name, parse the construct as an s-s-s and warn about the missing
'template'; it can't be anything else. */
- && (template_p
- || typename_p
- || TREE_CODE (OVL_FIRST (expr)) == TEMPLATE_DECL))
+ && (template_p || typename_p || TREE_CODE (expr_real) == TEMPLATE_DECL))
{
/* For member access splice-specialization-specifier, try to wrap
non-dependent splice for function template into a BASELINK so
that cp_parser_template_id can handle it. */
if (object_type
- && DECL_FUNCTION_TEMPLATE_P (OVL_FIRST (expr))
+ && reflection_function_template_p (expr_real)
&& !dependent_type_p (object_type))
{
- tree scope = DECL_CONTEXT (OVL_FIRST (expr));
+ tree scope = DECL_CONTEXT (expr_real);
if (scope && CLASS_TYPE_P (scope))
{
tree access_path = lookup_base (object_type, scope, ba_unique,
NULL, tf_warning_or_error);
if (access_path == error_mark_node)
expr = error_mark_node;
+ else if (BASELINK_P (expr))
+ expr = build_baselink (access_path,
+ BASELINK_ACCESS_BINFO (expr),
+ BASELINK_FUNCTIONS (expr),
+ BASELINK_OPTYPE (expr));
else
expr
= build_baselink (access_path, TYPE_BINFO (object_type),
expr,
- IDENTIFIER_CONV_OP_P (OVL_NAME (expr))
- ? TREE_TYPE (OVL_NAME (expr)) : NULL_TREE);
+ IDENTIFIER_CONV_OP_P (OVL_NAME (expr_real))
+ ? TREE_TYPE (OVL_NAME (expr_real))
+ : NULL_TREE);
}
}
/* Let cp_parser_template_id parse the template arguments. */
@@ -6316,7 +6322,6 @@ cp_parser_splice_expression (cp_parser *parser, bool template_p,
tree t = expr.get_value ();
STRIP_ANY_LOCATION_WRAPPER (t);
tree unresolved = t;
- t = MAYBE_BASELINK_FUNCTIONS (t);
t = resolve_nondeduced_context (t, tf_warning_or_error);
if (dependent_splice_p (t))
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index da45a4971a7..b334882de81 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -16942,7 +16942,34 @@ tsubst_splice_scope (tree t, tree args, tsubst_flags_t complain, tree in_decl)
static tree
tsubst_splice_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
{
- tree op = tsubst_expr (TREE_OPERAND (t, 0), args, complain, in_decl);
+ tree template_id = NULL_TREE;
+ auto apply_template = [&](tree templ)
+ {
+ if (!template_id)
+ return templ;
+ template_id = copy_node (template_id);
+ tree ret = template_id;
+
+ /* follow the example of lookup_template_function, but for all
+ templates. */
+ if (BASELINK_P (templ))
+ {
+ ret = copy_node (templ);
+ BASELINK_FUNCTIONS (ret) = template_id;
+ templ = BASELINK_FUNCTIONS (templ);
+ }
+ TREE_OPERAND (template_id, 0) = templ;
+ return ret;
+ };
+
+ if (TREE_CODE (t) == TEMPLATE_ID_EXPR)
+ {
+ template_id = t;
+ t = TREE_OPERAND (t, 0);
+ }
+
+ tree op = tsubst_expr (TREE_OPERAND (t, 0), args,
+ (complain & ~tf_no_name_lookup), in_decl);
if (op == error_mark_node)
return error_mark_node;
op = splice (op);
@@ -16960,8 +16987,12 @@ tsubst_splice_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
SET_SPLICE_EXPR_TEMPLATE_P (op, true);
if (SPLICE_EXPR_TARGS_P (t))
SET_SPLICE_EXPR_TARGS_P (op, true);
- return op;
+ return apply_template (op);
}
+
+ /* We have to form a template-id for checking too. */
+ op = apply_template (op);
+
if (SPLICE_EXPR_EXPRESSION_P (t)
&& !check_splice_expr (input_location, UNKNOWN_LOCATION, op,
SPLICE_EXPR_ADDRESS_P (t),
@@ -16971,6 +17002,11 @@ tsubst_splice_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
(complain & tf_error)))
return error_mark_node;
+ /* For the template-id case, we have to substitute only after checking, to
+ reject the case where the template part is a type. */
+ if (template_id)
+ op = tsubst_expr (op, args, complain, in_decl);
+
if (SPLICE_EXPR_ADDRESS_P (t))
{
push_deferring_access_checks (dk_no_check);
@@ -21472,11 +21508,12 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
tree object;
tree templ = TREE_OPERAND (t, 0);
tree targs = TREE_OPERAND (t, 1);
+ tsubst_flags_t complain_lookup = complain | no_name_lookup_flag;
- if (no_name_lookup_flag)
- templ = tsubst_name (templ, args, complain, in_decl);
- else
- templ = tsubst_expr (templ, args, complain, in_decl);
+ if (TREE_CODE (templ) == SPLICE_EXPR)
+ return tsubst_splice_expr (t, args, complain_lookup, in_decl);
+
+ templ = tsubst_expr (templ, args, complain_lookup, in_decl);
if (targs)
targs = tsubst_template_args (targs, args, complain, in_decl);
diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index 34750f2aa25..673b79d0bde 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -9060,6 +9060,12 @@ check_splice_expr (location_t loc, location_t start_loc, tree t,
bool address_p, bool member_access_p, bool template_p,
bool targs_p, bool complain_p)
{
+ t = MAYBE_BASELINK_FUNCTIONS (t);
+ tree expr = t;
+ if (TREE_CODE (t) == TEMPLATE_ID_EXPR)
+ t = TREE_OPERAND (t, 0);
+ t = OVL_FIRST (t);
+
/* We may not have gotten an expression. */
if (TREE_CODE (t) == TYPE_DECL
|| TREE_CODE (t) == NAMESPACE_DECL
@@ -9095,7 +9101,7 @@ check_splice_expr (location_t loc, location_t start_loc, tree t,
/* [expr.prim.splice]/2 For a splice-expression of the form
splice-specifier, the expression is ill-formed if it is: */
/* -- a constructor or a destructor */
- if (TREE_CODE (t) == FUNCTION_DECL
+ if (TREE_CODE (STRIP_TEMPLATE (t)) == FUNCTION_DECL
&& (DECL_CONSTRUCTOR_P (t) || DECL_DESTRUCTOR_P (t)))
{
if (complain_p)
@@ -9134,7 +9140,7 @@ check_splice_expr (location_t loc, location_t start_loc, tree t,
}
if (member_access_p
- && !valid_splice_for_member_access_p (t, /*decls_only_p=*/false))
+ && !valid_splice_for_member_access_p (expr, /*decls_only_p=*/false))
{
if (complain_p)
error_at (loc, "cannot use %qE to access a class member", t);
@@ -9232,8 +9238,8 @@ check_splice_expr (location_t loc, location_t start_loc, tree t,
return false;
}
gcc_checking_assert (reflection_function_template_p (t)
- || get_template_info (t)
- || TREE_CODE (t) == TEMPLATE_ID_EXPR
+ || get_template_info (expr)
+ || TREE_CODE (expr) == TEMPLATE_ID_EXPR
|| variable_template_p (t)
|| dependent_splice_p (t));
}
diff --git a/gcc/testsuite/g++.dg/reflect/member19.C b/gcc/testsuite/g++.dg/reflect/member19.C
index 8523ee55215..765b456a1f2 100644
--- a/gcc/testsuite/g++.dg/reflect/member19.C
+++ b/gcc/testsuite/g++.dg/reflect/member19.C
@@ -92,28 +92,19 @@ baz ()
static_assert (s.a <43> == 43);
static_assert (s.template [:members_of (^^A, uctx)[0]:] <44> == 44);
static_assert (s.template [:members_of (^^A, uctx)[0]:] <45> == 45);
-#if 0
- // TODO: This doesn't work yet.
static_assert (s.template [:^^A::a:] <44> == 44);
static_assert (s.template [:^^A::a:] <45> == 45);
-#endif
constexpr T t;
static_assert (t.a <42> == 142);
static_assert (t.a <43> == 143);
static_assert (t.template [:members_of (^^A, uctx)[0]:] <44> == 44);
static_assert (t.template [:members_of (^^A, uctx)[0]:] <45> == 45);
-#if 0
- // TODO: This doesn't work yet.
static_assert (t.template [:^^A::a:] <44> == 44);
static_assert (t.template [:^^A::a:] <45> == 45);
-#endif
static_assert (t.template [:members_of (^^B, uctx)[0]:] <44> == 144);
static_assert (t.template [:members_of (^^B, uctx)[0]:] <45> == 145);
-#if 0
- // TODO: This doesn't work yet.
static_assert (t.template [:^^B::a:] <44> == 144);
static_assert (t.template [:^^B::a:] <45> == 145);
-#endif
}
template <typename A, typename B>
@@ -125,28 +116,19 @@ qux ()
static_assert (s.template a <43> == 43);
static_assert (s.template [:members_of (^^A, uctx)[0]:] <44> == 44);
static_assert (s.template [:members_of (^^A, uctx)[0]:] <45> == 45);
-#if 0
- // TODO: This doesn't work yet.
static_assert (s.template [:^^A::a:] <44> == 44);
static_assert (s.template [:^^A::a:] <45> == 45);
-#endif
constexpr B t;
static_assert (t.template a <42> == 142);
static_assert (t.template a <43> == 143);
static_assert (t.template [:members_of (^^A, uctx)[0]:] <44> == 44);
static_assert (t.template [:members_of (^^A, uctx)[0]:] <45> == 45);
-#if 0
- // TODO: This doesn't work yet.
static_assert (t.template [:^^A::a:] <44> == 44);
static_assert (t.template [:^^A::a:] <45> == 45);
-#endif
static_assert (t.template [:members_of (^^B, uctx)[0]:] <44> == 144);
static_assert (t.template [:members_of (^^B, uctx)[0]:] <45> == 145);
-#if 0
- // TODO: This doesn't work yet.
static_assert (t.template [:^^B::a:] <44> == 144);
static_assert (t.template [:^^B::a:] <45> == 145);
-#endif
}
void
diff --git a/gcc/testsuite/g++.dg/reflect/splice15.C b/gcc/testsuite/g++.dg/reflect/splice15.C
new file mode 100644
index 00000000000..f72e3f83373
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/splice15.C
@@ -0,0 +1,70 @@
+// PR c++/124794
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection -Wno-error=missing-template-keyword" }
+
+struct C{
+ template <class T> void f(T);
+ void g(int);
+};
+
+void (C::*p0)(int) = &template[:^^C::f:]<int>;
+void (C::*p1)(int) = template[:^^C::f:]<int>; // { dg-error "cannot implicitly reference a class member" }
+void (C::*p2)(int) = &template[:^^C::f:];
+void (C::*p3)(int) = template[:^^C::f:]; // { dg-error "cannot implicitly reference a class member" }
+void (C::*p4)(int) = &[:^^C::f:]<int>; // { dg-warning "keyword before dependent template name" }
+void (C::*p5)(int) = [:^^C::f:]<int>; // { dg-error "cannot implicitly reference a class member" }
+void (C::*p6)(int) = &[:^^C::f:]; // { dg-warning "keyword before dependent template name" }
+void (C::*p7)(int) = [:^^C::f:]; // { dg-error "cannot implicitly reference a class member" }
+
+template <auto r> void (C::*tp0)(int) = &template[:r:]<int>;
+template <auto r> void (C::*tp1)(int) = template[:r:]<int>; // { dg-error "cannot implicitly reference a class member" }
+template <auto r> void (C::*tp2)(int) = &template[:r:];
+template <auto r> void (C::*tp3)(int) = template[:r:]; // { dg-error "cannot implicitly reference a class member" }
+/* tp4 and tp5 intentionally omitted as they are not applicable in a template context */
+template <auto r> void (C::*tp6)(int) = &[:r:];
+template <auto r> void (C::*tp7)(int) = [:r:]; // { dg-error "cannot implicitly reference a class member" }
+template <auto r> void (C::*tp0n)(int) = &template[:r:]<int>; // { dg-error "no matches converting function" }
+template <auto r> void (C::*tp1n)(int) = template[:r:]<int>; // { dg-error "cannot implicitly reference a class member" }
+template <auto r> void (C::*tp2n)(int) = &template[:r:]; // { dg-error "expected a reflection of a function template" }
+template <auto r> void (C::*tp3n)(int) = template[:r:]; // { dg-error "cannot implicitly reference a class member" }
+/* tp4u and tp5u intentionally omitted as they are not applicable in a template context */
+template <auto r> void (C::*tp6n)(int) = &[:r:];
+template <auto r> void (C::*tp7n)(int) = [:r:]; // { dg-error "cannot implicitly reference a class member" }
+
+static_assert((
+ tp0<^^C::f>,
+ tp1<^^C::f>,
+ tp2<^^C::f>,
+ tp3<^^C::f>,
+ tp6<^^C::f>,
+ tp7<^^C::f>,
+ tp0n<^^C::g>,
+ tp1n<^^C::g>,
+ tp2n<^^C::g>,
+ tp3n<^^C::g>,
+ tp6n<^^C::g>,
+ tp7n<^^C::g>,
+ true));
+
+struct Base1{
+ template<class T>
+ constexpr T f(T x) {
+ return x;
+ }
+};
+struct Base2: Base1 {
+ template<class T>
+ constexpr T g(T x) {
+ return x;
+ }
+};
+struct Base3: Base1, Base2 {}; // { dg-warning "inaccessible" }
+
+static_assert(Base1{}.[:^^Base1::f:](4) == 4); // { dg-warning "keyword before dependent template name" }
+static_assert(Base1{}.[:^^Base1::f:]<int>(13) == 13); // { dg-warning "keyword before dependent template name" }
+static_assert(Base3{}.[:^^Base2::g:](42) == 42); // { dg-warning "keyword before dependent template name" }
+static_assert(Base3{}.[:^^Base2::g:]<int>(67) == 67); // { dg-warning "keyword before dependent template name" }
+constexpr int invalid1 = Base3{}.[:^^Base2::f:]; // { dg-error "cannot resolve overloaded function" }
+// { dg-warning "keyword before dependent template name" "" { target *-*-* } .-1 }
+constexpr int invalid2 = Base3{}.[:^^Base1::f:]; // { dg-error "cannot resolve overloaded function" }
+// { dg-warning "keyword before dependent template name" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/reflect/splice16.C b/gcc/testsuite/g++.dg/reflect/splice16.C
new file mode 100644
index 00000000000..a3eb13c5f69
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/splice16.C
@@ -0,0 +1,34 @@
+// PR c++/125069
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection -Wno-error=missing-template-keyword" }
+
+#include <meta>
+struct x{ x(auto); };
+
+constexpr auto ac = std::meta::access_context::unchecked();
+
+template<auto r>
+constexpr auto t = (
+ [:r:], // { dg-error "cannot use constructor or destructor" }
+ &[:r:], // { dg-error "cannot use constructor or destructor" }
+ template [:r:], // { dg-error "cannot use constructor or destructor" }
+ &template [:r:], // { dg-error "cannot use constructor or destructor" }
+ template [:r:]<int>, // { dg-error "cannot use constructor or destructor" }
+ &template [:r:]<int>, // { dg-error "cannot use constructor or destructor" }
+ 1);
+
+
+int main() {
+ constexpr auto r = members_of(^^x, ac)[0];
+
+ t<r>;
+
+ [:r:]; // { dg-error "cannot use constructor or destructor" }
+ &[:r:]; // { dg-error "cannot use constructor or destructor" }
+ [:r:]<int>; // { dg-error "cannot use constructor or destructor" }
+ &[:r:]<int>; // { dg-error "cannot use constructor or destructor" }
+ template [:r:]; // { dg-error "cannot use constructor or destructor" }
+ &template [:r:]; // { dg-error "cannot use constructor or destructor" }
+ template [:r:]<int>; // { dg-error "cannot use constructor or destructor" }
+ &template [:r:]<int>; // { dg-error "cannot use constructor or destructor" }
+}
--
2.54.0
From 0ef61166ed2ee4bf5194f95b918b1da510f5ad6c Mon Sep 17 00:00:00 2001
From: Jason Merrill <[email protected]>
Date: Mon, 1 Jun 2026 18:37:38 -0400
Subject: [PATCH] c++/reflection: template splicing tweak
To: [email protected]
Discussion of the 124794 patch observed that access_path can be null if
object_type isn't derived from scope, which means building a nonsensical
BASELINK with null BASELINK_BINFO.
PR c++/124794
gcc/cp/ChangeLog:
* parser.cc (cp_parser_splice_specifier): Use TYPE_BINFO (scope)
for not-derived case.
* search.cc (build_baselink): Assert that binfos aren't null.
---
gcc/cp/parser.cc | 10 +++++++---
gcc/cp/search.cc | 1 +
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 6641dbba33d..c2bcaa97392 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -6204,18 +6204,22 @@ cp_parser_splice_specifier (cp_parser *parser, bool template_p = false,
tree scope = DECL_CONTEXT (expr_real);
if (scope && CLASS_TYPE_P (scope))
{
+ /* Use the same BASELINK_BINFO and BASELINK_ACCESS_BINFO since
+ we don't do access checking for a splice. */
tree access_path = lookup_base (object_type, scope, ba_unique,
NULL, tf_warning_or_error);
+ if (!access_path)
+ /* Not a base, access directly for the error. */
+ access_path = TYPE_BINFO (scope);
if (access_path == error_mark_node)
expr = error_mark_node;
else if (BASELINK_P (expr))
- expr = build_baselink (access_path,
- BASELINK_ACCESS_BINFO (expr),
+ expr = build_baselink (access_path, access_path,
BASELINK_FUNCTIONS (expr),
BASELINK_OPTYPE (expr));
else
expr
- = build_baselink (access_path, TYPE_BINFO (object_type),
+ = build_baselink (access_path, access_path,
expr,
IDENTIFIER_CONV_OP_P (OVL_NAME (expr_real))
? TREE_TYPE (OVL_NAME (expr_real))
diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc
index 5d00ce3d826..a51fd1cd6a7 100644
--- a/gcc/cp/search.cc
+++ b/gcc/cp/search.cc
@@ -1144,6 +1144,7 @@ build_baselink (tree binfo, tree access_binfo, tree functions, tree optype)
{
tree baselink;
+ gcc_checking_assert (binfo && access_binfo);
gcc_assert (OVL_P (functions) || TREE_CODE (functions) == TEMPLATE_ID_EXPR);
gcc_assert (!optype || TYPE_P (optype));
gcc_assert (TREE_TYPE (functions));
--
2.54.0