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

Reply via email to