On Sat, Dec 13, 2025 at 06:28:59PM +0700, Jason Merrill wrote:
> On 12/13/25 3:27 AM, Jakub Jelinek wrote:
> > On Fri, Dec 12, 2025 at 11:25:57AM -0500, Marek Polacek wrote:
> > > @@ -6120,6 +6119,9 @@ cp_parser_splice_specifier (cp_parser *parser, bool
> > > template_p = false,
> > > return error_mark_node;
> > > }
> > > + /* Remember if we are accessing a member of some object. */
> > > + const bool member_access_p = !!parser->context->object_type;
> > > +
> > > tree expr;
> > > {
> > > /* Temporarily clear parser->context->object_type. E.g.,
> >
> > I think this isn't even needed, you can then just drop the
> > member_access_p && part of
> > /* For member access splice-specialization-specifier, try to wrap
> > non-dependent splice for function template into a BASELINK so
> > that cp_parser_template_id can handle it. */
> > if (member_access_p
> > && parser->context->object_type
> > && DECL_FUNCTION_TEMPLATE_P (OVL_FIRST (expr))
> > && !dependent_type_p (parser->context->object_type))
> > Because object_type is only temporarily overridden, it will be restored
> > to what it was at the beginning and so can be just tested here.
>
> I still think object_type should be null after cp_parser_splice_specifier
> like it is after cp_parser_template_name; nothing that comes after the
> splice is member-qualified.
I think you're right.
While cp_parser_postfix_dot_deref_expression will clear it:
/* We no longer need to look up names in the scope of the object on
the left-hand side of the `.' or `->' operator. */
parser->context->object_type = NULL_TREE;
both for the splice-expression and for normal identifier cases,
for the non-splice case
constexpr int x = 42;
struct S { static constexpr int x = 20; template <int N> static constexpr int a
= N; };
static_assert (S {}.template a<x> == 42);
parser->context->object_type is cleared by
/* Now that we have looked up the name, the OBJECT_TYPE (if any) is
no longer valid. Note that if we are parsing tentatively, and
the parse fails, OBJECT_TYPE will be automatically restored. */
parser->context->object_type = NULL_TREE;
#0 cp_parser_lookup_name (parser=0x7ffff7fbb0c8, name=<identifier_node
0x7fffe99e0bc0 a>, tag_type=none_type, is_template=2, is_namespace=false,
check_dependency=true, ambiguous_decls=0x0, name_location=1070848) at
../../gcc/cp/parser.cc:34068
#1 0x0000000000716462 in cp_parser_template_name (parser=0x7ffff7fbb0c8,
template_keyword_p=true, check_dependency_p=true, is_declaration=false,
tag_type=none_type, is_identifier=0x7fffffffb6fe) at
../../gcc/cp/parser.cc:20668
#2 0x00000000007156de in cp_parser_template_id (parser=0x7ffff7fbb0c8,
template_keyword_p=true, check_dependency_p=true, tag_type=none_type,
is_declaration=false) at ../../gcc/cp/parser.cc:20275
#3 0x0000000000727f4c in cp_parser_class_name (parser=0x7ffff7fbb0c8,
typename_keyword_p=false, template_keyword_p=true, tag_type=none_type,
check_dependency_p=true, class_head_p=false, is_declaration=false,
enum_ok=true) at ../../gcc/cp/parser.cc:28544
#4 0x00000000006f60dc in cp_parser_qualifying_entity (parser=0x7ffff7fbb0c8,
typename_keyword_p=false, template_keyword_p=true, check_dependency_p=true,
type_p=false, is_declaration=false) at ../../gcc/cp/parser.cc:7791
#5 0x00000000006f55a6 in cp_parser_nested_name_specifier_opt
(parser=0x7ffff7fbb0c8, typename_keyword_p=false, check_dependency_p=true,
type_p=false, is_declaration=false, template_keyword_p=true, global_p=false) at
../../gcc/cp/parser.cc:7477
#6 0x00000000006f3805 in cp_parser_id_expression (parser=0x7ffff7fbb0c8,
template_keyword_p=true, check_dependency_p=true, template_p=0x7fffffffbbdf,
declarator_p=false, optional_p=false) at ../../gcc/cp/parser.cc:6797
#7 0x00000000006fa74c in cp_parser_postfix_dot_deref_expression
(parser=0x7ffff7fbb0c8, token_type=CPP_DOT, postfix_expression=...,
for_offsetof=false, idk=0x7fffffffbf1c, location=1069056) at
../../gcc/cp/parser.cc:9111
and that clearing didn't happen for the splice-expression case, so for
constexpr int x = 42;
struct S { static constexpr int x = 20; template <int N> static constexpr int a
= N; };
static_assert (S {}.template [:^^S::a:]<x> == 42);
we looked up the wrong x and ICEd when parsing the template argument.
https://forge.sourceware.org/marek/gcc/commit/85d5adcc79c6d0a190f87d78cb5de06ef222cdcc
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 69393d63c35..6eb3e0368bf 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -6119,19 +6119,17 @@ cp_parser_splice_specifier (cp_parser *parser, bool
template_p = false,
return error_mark_node;
}
- tree expr;
- {
- /* Temporarily clear parser->context->object_type. E.g.,
- struct A { static int x; };
- int q = A ().[: ^^x :];
- should be an error -- x is not a member-qualified name and isn't
- in scope. */
- auto ot = make_temp_override (parser->context->object_type, NULL_TREE);
- expr = cp_parser_constant_expression (parser,
- /*allow_non_constant_p=*/false,
- /*non_constant_p=*/nullptr,
- /*strict_p=*/true);
- }
+ tree object_type = parser->context->object_type;
+ /* Clear parser->context->object_type. E.g.,
+ struct A { static int x; };
+ int q = A ().[: ^^x :];
+ should be an error -- x is not a member-qualified name and isn't
+ in scope. */
+ parser->context->object_type = NULL;
+ tree expr = cp_parser_constant_expression (parser,
+ /*allow_non_constant_p=*/false,
+ /*non_constant_p=*/nullptr,
+ /*strict_p=*/true);
/* Get the location of the ':]'. */
location_t finish_loc = cp_lexer_peek_token (parser->lexer)->location;
@@ -6149,14 +6147,13 @@ cp_parser_splice_specifier (cp_parser *parser, bool
template_p = false,
/* For member access splice-specialization-specifier, try to wrap
non-dependent splice for function template into a BASELINK so
that cp_parser_template_id can handle it. */
- if (parser->context->object_type
+ if (object_type
&& DECL_FUNCTION_TEMPLATE_P (OVL_FIRST (expr))
- && !dependent_type_p (parser->context->object_type))
+ && !dependent_type_p (object_type))
{
tree scope = DECL_CONTEXT (OVL_FIRST (expr));
if (scope && CLASS_TYPE_P (scope))
{
- tree object_type = parser->context->object_type;
tree access_path = lookup_base (object_type, scope, ba_unique,
NULL, tf_warning_or_error);
if (access_path == error_mark_node)
diff --git a/gcc/testsuite/g++.dg/reflect/splice7.C
b/gcc/testsuite/g++.dg/reflect/splice7.C
new file mode 100644
index 00000000000..50426c9785e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/splice7.C
@@ -0,0 +1,6 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+constexpr int x = 42;
+struct S { static constexpr int x = 20; template <int N> static constexpr int
a = N; };
+static_assert (S {}.template [:^^S::a:]<x> == 42);
Jakub