On Tue, Dec 09, 2025 at 08:38:26PM +0800, Jason Merrill wrote:
> This suggests that we're doing the lookup in the context of "d.", which is
> wrong. That is, we should reject
>
> struct A { static int x; };
> int q = A().[:^^x:]; // error, no 'x' in scope
>
> but the branch currently accepts this. Under
> https://eel.is/c++draft/basic.lookup.qual#general-2 'x' is not a
> member-qualified name.
This is about parser->context->object_type.
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.
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. */
+ 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