https://gcc.gnu.org/g:5686e69266cff73830551db19766c39bc83e3dd3

commit r16-8536-g5686e69266cff73830551db19766c39bc83e3dd3
Author: Marek Polacek <[email protected]>
Date:   Wed Mar 18 13:48:40 2026 -0400

    c++/reflection: ICE with invalid splice in member access expr [PR123726]
    
    This patch fixes a crash on invalid code.  In a class member access
    with a splice, the splice cannot designate something that is not
    a member of the object we're accessing.  check_splice_expr could also
    detect more invalid splices, such as accessing a class template or
    alias template member with . or ->.
    
            PR c++/123726
    
    gcc/cp/ChangeLog:
    
            * reflect.cc (check_splice_expr): Detect accessing a class template
            or alias template with . or ->.
            * typeck.cc (finish_class_member_access_expr): Check if the scope of
            the spliced entity in a member access expression is of a class type.
            Allow variable_template_p for context_for_name_lookup.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/reflect/member1.C: Adjust dg-error.
            * g++.dg/reflect/member22.C: New test.
    
    Co-authored-by: Boris Staletic <[email protected]>
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/reflect.cc                       |  9 ++++++++
 gcc/cp/typeck.cc                        | 17 +++++++++++++--
 gcc/testsuite/g++.dg/reflect/member1.C  |  3 +--
 gcc/testsuite/g++.dg/reflect/member22.C | 37 +++++++++++++++++++++++++++++++++
 4 files changed, 62 insertions(+), 4 deletions(-)

diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index b066576eba58..78d48db29ba2 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -8884,6 +8884,15 @@ check_splice_expr (location_t loc, location_t start_loc, 
tree t,
                  "through a splice", t);
       return false;
     }
+
+  /* One can't access a class template or alias template with . or ->.  */
+  if (member_access_p && DECL_TYPE_TEMPLATE_P (t))
+    {
+      if (complain_p)
+       error_at (loc, "invalid class member access of type template %qE", t);
+      return false;
+    }
+
   /* [expr.unary.op]/3.1 "If the operand [of unary &] is a qualified-id or
      splice-expression designating a non-static member m, other than an
      explicit object member function, m shall be a direct member of some
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index e019d6e22a96..b88b5e3217b2 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -3599,8 +3599,21 @@ finish_class_member_access_expr (cp_expr object, tree 
name, bool template_p,
                   || VAR_P (name)
                   || TREE_CODE (name) == CONST_DECL
                   || TREE_CODE (name) == FUNCTION_DECL
-                  || DECL_FUNCTION_TEMPLATE_P (OVL_FIRST (name))))
-       scope = context_for_name_lookup (OVL_FIRST (name));
+                  || DECL_FUNCTION_TEMPLATE_P (OVL_FIRST (name))
+                  || variable_template_p (name)))
+       {
+         scope = context_for_name_lookup (OVL_FIRST (name));
+         if (!CLASS_TYPE_P (scope))
+           {
+             if (complain & tf_error)
+               {
+                 auto_diagnostic_group d;
+                 error ("%q#D is not a member of %qT", name, object_type);
+                 inform (DECL_SOURCE_LOCATION (OVL_FIRST (name)), "declared 
here");
+               }
+             return error_mark_node;
+           }
+       }
 
       if (TREE_CODE (name) == TEMPLATE_ID_EXPR)
        {
diff --git a/gcc/testsuite/g++.dg/reflect/member1.C 
b/gcc/testsuite/g++.dg/reflect/member1.C
index cecb4a4204d9..124b74f12504 100644
--- a/gcc/testsuite/g++.dg/reflect/member1.C
+++ b/gcc/testsuite/g++.dg/reflect/member1.C
@@ -92,8 +92,7 @@ f ()
   sp.[: ^^S::x :] = 2; // { dg-error "which is of pointer type" }
   c.[: ^^C<char>::x :] = 1; // { dg-error "is not a base of" }
   cp->[: ^^C<char>::x :] = 1; // { dg-error "is not a base of" }
-  s.template [: ^^S::N :].t;  // { dg-error "expected a reflection of a 
function template" }
-  // { dg-message "but .S::N. is a class template" "" { target *-*-* } 20 }
+  s.template [: ^^S::N :].t;  // { dg-error "invalid class member access of 
type template .S::N." }
   S::template [: ^^S::N<int> :] e1;  // { dg-error "expected unqualified-id" }
   C<int>::template [: ^^S::N<int> :] e2;  // { dg-error "expected 
unqualified-id" }
   s.template [: ^^S::var<int> :] = 1;  // { dg-error "expected a reflection of 
a function template" }
diff --git a/gcc/testsuite/g++.dg/reflect/member22.C 
b/gcc/testsuite/g++.dg/reflect/member22.C
new file mode 100644
index 000000000000..8267e1846700
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/member22.C
@@ -0,0 +1,37 @@
+// PR c++/123726
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+struct S {
+  template<typename>
+  struct tt {};
+  template<typename T>
+  using tu = tt<T>;
+};
+int x;
+void f() {}
+
+template<typename>
+struct tt {};
+
+template<typename>
+void tf() {}
+
+template<typename>
+int tv = 5;
+
+template<typename T>
+using tu = tt<T>;
+
+int main() {
+  S s;
+  s.[:^^x:];           // { dg-error "not a member of .S." }
+  s.[:^^s:];           // { dg-error "not a member of .S." }
+  s.[:^^f:];           // { dg-error "not a member of .S." }
+  s.[:^^tt:];          // { dg-error "invalid class member access of type 
template .tt." }
+  s.template [:^^tf:]; // { dg-error "not a member of .S." }
+  s.[:^^tv:];          // { dg-error "not a member of .S." }
+  s.[:^^tu:];          // { dg-error "invalid class member access of type 
template .tu." }
+  s.[:^^S::tt:];       // { dg-error "invalid class member access of type 
template .S::tt." }
+  s.[:^^S::tu:];       // { dg-error "invalid class member access of type 
template .S::tu." }
+}

Reply via email to