[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