[gcc r16-8054] c++/reflection: class member access with [: T::BASELINK :] [PR124440]
Marek Polacek
mpolacek@gcc.gnu.org
Thu Mar 12 19:05:57 GMT 2026
https://gcc.gnu.org/g:8b626332027ad1ff21f33a5b465e02fcab0cbff9
commit r16-8054-g8b626332027ad1ff21f33a5b465e02fcab0cbff9
Author: Marek Polacek <polacek@redhat.com>
Date: Wed Mar 11 13:28:30 2026 -0400
c++/reflection: class member access with [: T::BASELINK :] [PR124440]
Let
struct B { consteval virtual int fn() const { return 1; } };
struct D : B { consteval int fn() const override { return 2; } };
and consider
d.[:^^B::fn:]() // #1
d.[:^^B:]::fn() // #2
then #1 should yield 2 and #2 should yield 1: #1 doesn't count as
qualified name lookup and so should be treated as "d.fn()" where
lookup_vfn_in_binfo in build_over_call finds D::fn. But #2 is like
"d.B::fn()" which should prevent a virtual function call and we
should be calling B::fn.
We already handle this correctly outside of templates: finish_call_expr
only checks IDK to see if it should disallow_virtual, but in tsubst_expr
it looks at BASELINK_QUALIFIED_P, which is wrongly set for #1.
BASELINK_QUALIFIED_P is always set by adjust_result_of_q_name_lookup,
so cp_parser_postfix_dot_deref_expression should clear it for case #1.
With this patch, we only set BASELINK_QUALIFIED_P if qualifying_scope
was not null.
Unfortunately we can't avoid the adjust_result_ call because then
lookup_vfn_in_binfo gets a binfo that isn't BINFO_PRIMARY_P and it won't
find D::fn. It seems it is necessary to have that adjust_result_ ->
lookup_base adjustment.
And then tsubst_expr has to do the same thing for when we have
d.[:^^T::fn:]()
where T is a tparm that is substituted with B.
PR c++/124440
gcc/cp/ChangeLog:
* parser.cc (cp_parser_postfix_dot_deref_expression): Pass
parser->scope to adjust_result_of_qualified_name_lookup even
when splice_p. Assign the result of
adjust_result_of_qualified_name_lookup to name.
* pt.cc (tsubst_expr): Call adjust_result_of_qualified_name_lookup
for BASELINKs in splice-expressions.
* search.cc (adjust_result_of_qualified_name_lookup): Return early
if decl is not a BASELINK. If qualifying_scope is null, use decl's
binfo. Set BASELINK_QUALIFIED_P only if qualifying_scope wasn't
null.
gcc/testsuite/ChangeLog:
* g++.dg/reflect/member21.C: Uncomment the commented out asserts.
Reviewed-by: Jason Merrill <jason@redhat.com>
Diff:
---
gcc/cp/parser.cc | 12 ++++--------
gcc/cp/pt.cc | 7 +++++++
gcc/cp/search.cc | 22 +++++++++++++---------
gcc/testsuite/g++.dg/reflect/member21.C | 5 ++---
4 files changed, 26 insertions(+), 20 deletions(-)
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 44ee3a5c3b79..ffb22aa776f6 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -9733,14 +9733,10 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser,
parser->object_scope = NULL_TREE;
}
if ((parser->scope || splice_p) && name && BASELINK_P (name))
- adjust_result_of_qualified_name_lookup
- (name,
- /* For obj->[:^^R:] we won't have parser->scope, but we still
- have to perform this adjustment. */
- (splice_p
- ? BINFO_TYPE (BASELINK_ACCESS_BINFO (name))
- : parser->scope),
- scope);
+ /* For obj->[:^^R:] we won't have parser->scope, but we still
+ have to perform this adjustment. */
+ name = (adjust_result_of_qualified_name_lookup
+ (name, parser->scope, scope));
postfix_expression
= finish_class_member_access_expr (postfix_expression, name,
template_p,
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index fc0f35e2062d..fa330159f3a9 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -22433,6 +22433,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
object_type = TREE_TYPE (object);
member = TREE_OPERAND (t, 1);
+ const bool splice_p = dependent_splice_p (member);
if (BASELINK_P (member))
member = tsubst_baselink (member,
non_reference (TREE_TYPE (object)),
@@ -22528,6 +22529,12 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
}
RETURN (error_mark_node);
}
+ else if (splice_p && BASELINK_P (member))
+ /* We need to call adjust_result_of_qualified_name_lookup when
+ we have obj->[:^^T::fn:], but we don't set BASELINK_QUALIFIED_P
+ so that we still get virtual function binding. */
+ member = (adjust_result_of_qualified_name_lookup
+ (member, NULL_TREE, object_type));
r = finish_class_member_access_expr (object, member,
/*template_p=*/false,
diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc
index 01561e6cfeaa..e098362fed77 100644
--- a/gcc/cp/search.cc
+++ b/gcc/cp/search.cc
@@ -1465,21 +1465,26 @@ adjust_result_of_qualified_name_lookup (tree decl,
tree qualifying_scope,
tree context_class)
{
- if (context_class && context_class != error_mark_node
+ if (!BASELINK_P (decl))
+ return decl;
+
+ const bool qualified_p = qualifying_scope != NULL_TREE;
+ if (!qualified_p)
+ qualifying_scope = BINFO_TYPE (BASELINK_ACCESS_BINFO (decl));
+
+ if (context_class
+ && context_class != error_mark_node
&& CLASS_TYPE_P (context_class)
&& CLASS_TYPE_P (qualifying_scope)
- && DERIVED_FROM_P (qualifying_scope, context_class)
- && BASELINK_P (decl))
+ && DERIVED_FROM_P (qualifying_scope, context_class))
{
- tree base;
-
/* Look for the QUALIFYING_SCOPE as a base of the CONTEXT_CLASS.
Because we do not yet know which function will be chosen by
overload resolution, we cannot yet check either accessibility
or ambiguity -- in either case, the choice of a static member
function might make the usage valid. */
- base = lookup_base (context_class, qualifying_scope,
- ba_unique, NULL, tf_none);
+ tree base = lookup_base (context_class, qualifying_scope,
+ ba_unique, NULL, tf_none);
if (base && base != error_mark_node)
{
BASELINK_ACCESS_BINFO (decl) = base;
@@ -1491,8 +1496,7 @@ adjust_result_of_qualified_name_lookup (tree decl,
}
}
- if (BASELINK_P (decl))
- BASELINK_QUALIFIED_P (decl) = true;
+ BASELINK_QUALIFIED_P (decl) = qualified_p;
return decl;
}
diff --git a/gcc/testsuite/g++.dg/reflect/member21.C b/gcc/testsuite/g++.dg/reflect/member21.C
index 48ce3f1631d8..fd69621dac27 100644
--- a/gcc/testsuite/g++.dg/reflect/member21.C
+++ b/gcc/testsuite/g++.dg/reflect/member21.C
@@ -16,11 +16,10 @@ f ()
static_assert(d.U::fn() == 2);
static_assert(d.D::fn() == 2);
static_assert(d.fn() == 2);
- // FIXME PR124440
- //static_assert(d.[:^^T::fn:]() == 2);
+ static_assert(d.[:^^T::fn:]() == 2);
static_assert(d.[:^^T:]::fn() == 1);
static_assert(d.T::fn() == 1);
- //static_assert(d.[:^^B::fn:]() == 2);
+ static_assert(d.[:^^B::fn:]() == 2);
static_assert(d.[:^^B:]::fn() == 1);
static_assert(d.B::fn() == 1);
}
More information about the Gcc-cvs
mailing list