On 12/10/25 8:23 PM, Jakub Jelinek wrote:
On Tue, Dec 09, 2025 at 08:38:26PM +0800, Jason Merrill wrote:
+          [:^^B::fn:]()  // do not disable virtual dispatch
+          [:^^B:]::fn()  // disable virtual dispatch
+
+        so we check SPLICE_P.  */
+      if (parser->scope && !splice_p)
        *idk = CP_ID_KIND_QUALIFIED;

Hmm, it seems wrong for parser->scope to still be set in the former case.

So this is tested in reflect/member9.C.  I thought that d.[:^^B::fn:]()
should behave just like d.B::fn() which also leaves parser->scope set
to B.

I don't think it should, as above the splice is a separate lookup context.

I agree with the comments and the testcase, but we should be clearing
parser->scope after the splice so we don't need any change here.

I've tried:

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 0d59f6ec5a5..bc31eabf474 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -6120,6 +6120,15 @@ cp_parser_splice_specifier (cp_parser *parser, bool 
template_p = false,
        return error_mark_node;
      }
+if (parser->scope || parser->qualifying_scope || parser->object_scope)
+{
+FILE *f = fopen ("/tmp/splice", "a");
+fprintf (f, "%s %s %d %d %d %d\n",
+main_input_filename ? main_input_filename : "-", current_function_name (),
+2, (int) !!parser->scope, (int) !!parser->qualifying_scope, (int) 
!!parser->object_scope);
+fclose (f);
+}
+
    tree expr = cp_parser_constant_expression (parser,
                                             /*allow_non_constant_p=*/false,
                                             /*non_constant_p=*/nullptr,
@@ -6215,6 +6224,14 @@ cp_parser_splice_expression (cp_parser *parser, bool 
template_p,
    if (member_access_p)
      push_deferring_access_checks (dk_no_check);
+if (parser->scope || parser->qualifying_scope || parser->object_scope)
+{
+FILE *f = fopen ("/tmp/splice", "a");
+fprintf (f, "%s %s %d %d %d %d\n",
+main_input_filename ? main_input_filename : "-", current_function_name (),
+member_access_p, (int) !!parser->scope, (int) !!parser->qualifying_scope, (int) 
!!parser->object_scope);
+fclose (f);
+}
    cp_expr expr = cp_parser_splice_specifier (parser, template_p, &targs_p);
if (member_access_p)

and this only triggers on type8.C testcase, reduced to
template<class, class> struct same_type;
template<class T> struct same_type<T, T> {};

struct S {
   static const int &&mem();
   static int i;
   int j;
} s;

same_type<decltype([: ^^S::i :]), int> s5b;
where it seems to be parsing [: ^^S::i :] tentatively, sets
parser->scope and parser->qualifying_scope to S during that
parsing and then when it is parsing it again sees that.
So guess the question is where to clear those 3 (e.g. whether
at the start of cp_parser_splice_expression and also at the end
of those?)

I think so.

and/or if there are some spots where it should be temporarily
and where it should be reset back.

I don't think so.

And then there is also parser->context->object_type, guess for that one
we should save it and restore it, so that it can be taken into account
for s.[: ^^S::foo :] <int> (); etc. parsing.
...
The following WIP patch fixes that.
The reason for saving/restoring rather than just clearing is so that
it can be used for the splice-specialization-specifier case later on
if the splice-specifier is non-dependent.

Why is this needed? The template arguments aren't member-qualified, e.g. this is properly rejected:

struct A {
  static const int i = 42;
  template <int I> void f();
};

int main()
{
  A().f<i>(); // error, no 'i' in scope
}

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 0d59f6ec5a5..4e10fec64e2 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -6102,6 +6102,7 @@ cp_parser_next_tokens_can_start_splice_scope_spec_p 
(cp_parser *parser)
static cp_expr
 cp_parser_splice_specifier (cp_parser *parser, bool template_p = false,
+                           bool member_access_p = false,
                            bool *targs_p = nullptr)
 {
   /* Get the location of the '[:'.  */
@@ -6120,11 +6121,22 @@ cp_parser_splice_specifier (cp_parser *parser, bool 
template_p = false,
       return error_mark_node;
     }
+ /* For member_access_p, temporarily clear parser->context->object_type. */

Why conditional on member_access_p?

+  tree save_object_type = NULL_TREE;
+  if (member_access_p)
+    {
+      save_object_type = parser->context->object_type;
+      parser->context->object_type = NULL_TREE;
+    }
+
   tree expr = cp_parser_constant_expression (parser,
                                             /*allow_non_constant_p=*/false,
                                             /*non_constant_p=*/nullptr,
                                             /*strict_p=*/true);
+ if (member_access_p)
+    parser->context->object_type = save_object_type;
+
   /* Get the location of the ':]'.  */
   location_t finish_loc = cp_lexer_peek_token (parser->lexer)->location;
@@ -6215,7 +6227,8 @@ cp_parser_splice_expression (cp_parser *parser, bool template_p,
   if (member_access_p)
     push_deferring_access_checks (dk_no_check);
- cp_expr expr = cp_parser_splice_specifier (parser, template_p, &targs_p);
+  cp_expr expr = cp_parser_splice_specifier (parser, template_p,
+                                            member_access_p, &targs_p);
if (member_access_p)
     pop_deferring_access_checks ();
@@ -6385,7 +6398,8 @@ cp_parser_splice_scope_specifier (cp_parser *parser, bool 
typename_p,
                                  bool template_p)
 {
   bool targs_p = false;
-  cp_expr scope = cp_parser_splice_specifier (parser, template_p, &targs_p);
+  cp_expr scope = cp_parser_splice_specifier (parser, template_p, false,
+                                             &targs_p);
   const location_t loc = scope.get_location ();
   if (TREE_CODE (scope) == TYPE_DECL)
     scope = TREE_TYPE (scope);
diff --git a/gcc/testsuite/g++.dg/reflect/member1.C 
b/gcc/testsuite/g++.dg/reflect/member1.C
index c64b39f0938..e9d9a64d0eb 100644
--- a/gcc/testsuite/g++.dg/reflect/member1.C
+++ b/gcc/testsuite/g++.dg/reflect/member1.C
@@ -95,5 +95,5 @@ f ()
   s.template [: ^^S::N :].t;  // { dg-error "reflection .S::N. not usable in a 
template splice" }
   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 [: ^^var<int> :] = 1;  // { dg-error "reflection .S::var<int>. not 
usable in a template splice" }
+  s.template [: ^^S::var<int> :] = 1;  // { dg-error "reflection .S::var<int>. not 
usable in a template splice" }
 }
diff --git a/gcc/testsuite/g++.dg/reflect/splice3.C 
b/gcc/testsuite/g++.dg/reflect/splice3.C
new file mode 100644
index 00000000000..88434444a2d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/splice3.C
@@ -0,0 +1,5 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+struct A { static int x; };
+int q = A ().[: ^^x :];  // { dg-error "'x' has not been declared" }


        Jakub


Reply via email to