On 12/11/25 5:17 AM, Marek Polacek wrote:
On Fri, Dec 05, 2025 at 10:18:34PM +0530, Jason Merrill wrote:
+static tree
+cp_parser_reflection_name (cp_parser *parser)
+{
+  /* Look for the optional `::' operator.  */
+  bool global_scope_p
+    = (cp_parser_global_scope_opt (parser,
+                                  /*current_scope_valid_p=*/false)
+       != NULL_TREE);
+  /* And the optional nested-name-specifier.  */
+  bool nested_name_specifier_p
+    = (cp_parser_nested_name_specifier_opt (parser,
+                                           /*typename_keyword_p=*/false,
+                                           /*check_dependency_p=*/true,
+                                           /*type_p=*/false,
+                                           /*is_declaration=*/false,
+                                           /*template_keyword_p=*/false,
+                                           global_scope_p)
+       != NULL_TREE);
+  /* Look for the optional `template' keyword.  */
+  if (cp_parser_optional_template_keyword (parser)
+      && !global_scope_p
+      && !nested_name_specifier_p)
+    /* Only "template identifier" isn't valid.  */
+    cp_parser_simulate_error (parser);

This could be a cp_parser_error without a comment?

Done.  Though I think we'll never see that error because here we're
always parsing tentatively.

Agreed.

+  /* Nope.  Well then, maybe it's a type-id.  */
+  cp_parser_parse_tentatively (parser);
+
+  /* Unfortunately we need to distinguish in
+     template <typename> struct cls_tmpl {};
+     template <typename T> using cls_tmpl_alias = const cls_tmpl <T>;
+
+     ^^cls_tmpl_alias <int> which is a type alias from
+     ^^const cls_tmpl_alias <int>
+     ^^cls_tmpl_alias <int> const
+     ^^cls_tmpl_alias <int> *
+     etc. which are just types, not type aliases.  Parse tentatively
+     type specifiers and check that there is just ds_type_spec specified
+     and it is a specialization of a template alias, in that case later
+     on if cp_parser_type_id parses the same tokens don't strip typedefs.  */
+  if (!talias)
+    {
+      cp_decl_specifier_seq type_specifier_seq;
+
+      /* Parse the type-specifier-seq.  */
+      cp_parser_type_specifier_seq (parser, CP_PARSER_FLAGS_NONE,
+                                   /*is_declaration=*/false, false,
+                                   &type_specifier_seq);
+      if (is_typedef_decl (type_specifier_seq.type)
+         && TYPE_ALIAS_TEMPLATE_INFO (TREE_TYPE (type_specifier_seq.type)))
+       {
+         int i;
+         for (i = ds_first; i < ds_last; ++i)
+           if (i != ds_type_spec && type_specifier_seq.locations[i])
+             break;
+         if (i == ds_last)
+           {
+             talias = type_specifier_seq.type;
+             next = cp_lexer_peek_token (parser->lexer);
+           }
+       }
+      cp_parser_abort_tentative_parse (parser);
+      cp_parser_parse_tentatively (parser);
+    }
+
+  t = cp_parser_type_id (parser);
+  if (cp_parser_parse_definitely (parser))
+    {
+      if (TYPE_P (t))
+       {
+         /* With using A = int; ^^A is a type alias but
+            ^^const A or ^^A & or ^^A const is not.
+            With template <typename T> using B = C <T>;
+            ^^B <int> is a type alias though.  */
+         if (talias == NULL_TREE
+             || cp_lexer_peek_token (parser->lexer) != next)
+           t = strip_typedefs (t);
+       }
+      return get_reflection (loc, t);
+    }

I think it would be simpler to handle more of this in cp_parser_type_id_1,
which already handles other special cases, rather than parsing the
type-specifiers twice?

I took a crack at this in
<https://forge.sourceware.org/marek/gcc/commit/e2a9ca89a628d54897ffe30054191b0e14dfd1c4>
and it seemed to work.

Looks good, except I think I'd rather call _type_id_1 directly rather than add a special-case parameter to _type_id.

+         typename_token = token;
+         splice_token = cp_lexer_peek_nth_token (parser->lexer, 2);
+       }
+      else if (!processing_template_decl)
        error_at (token->location,
                  "keyword %<typename%> not allowed outside of templates");
        else
@@ -30501,10 +31381,31 @@ cp_parser_base_specifier (cp_parser* parser)
    class_scope_p = (parser->scope && TYPE_P (parser->scope));
    template_p = class_scope_p && cp_parser_optional_template_keyword (parser);
+  if (typename_token && cp_lexer_peek_token (parser->lexer) != splice_token)
+    {
+      /* Emit deferred diagnostics for invalid typename keyword if
+        cp_parser_nested_name_specifier_opt parsed splice-scope-specifier.  */
+      if (!processing_template_decl)
+       error_at (typename_token->location,
+                 "keyword %<typename%> not allowed outside of templates");
Pre-existig issue, but this is not true anymore.  For instance, this is
fine:

struct A { struct B {}; };
typename A::B b;

+      else
+       error_at (typename_token->location,
+                 "keyword %<typename%> not allowed in this context "
+                 "(the base class is implicitly a type)");

...so let's only give this error.

Happy to, but I think it should be done outside the Reflection
patch.  parse/typename6.C checks the first error.

Agreed.

@@ -17478,6 +17601,9 @@ tsubst (tree t, tree args, tsubst_flags_t complain, 
tree in_decl)
  static tree
  tsubst_scope (tree t, tree args, tsubst_flags_t complain, tree in_decl)
  {
+  /* This could be a dependent namespace.  */
+  if (TREE_CODE (t) == NAMESPACE_DECL)
+    return tsubst_expr (t, args, complain | tf_qualifying_scope, in_decl);
    gcc_checking_assert (TYPE_P (t));
    return tsubst (t, args, complain | tf_qualifying_scope, in_decl);
  }

Why doesn't tsubst handle NAMESPACE_DECL?  A namespace isn't an expression,
so tsubst_expr seems wrong.  More context below.

NAMESPACE_DECL used to be handled by tsubst_copy and since r14-4796 by
tsubst_expr.

Moved case NAMESPACE_DECL into tsubst_decl here:
<https://forge.sourceware.org/marek/gcc/commit/950ad2a58b10542fb41978f2c657235f292e49a2>
@@ -20943,6 +21068,38 @@ tsubst_expr (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
            RETURN (r);
          }
+       /* Normally, we only expect a template function at this point.  But
+          for [:X:]<arg>, we don't really know what [:X:] means until we
+          substitute it.  */
+       if (DECL_TYPE_TEMPLATE_P (templ)
+           || DECL_TEMPLATE_TEMPLATE_PARM_P (templ))
+         {
+           tree op = TREE_OPERAND (t, 0);
+           gcc_assert (TREE_CODE (op) == SPLICE_EXPR);
+           tree r = finish_template_type (templ, targs,
+                                          /*entering_scope=*/false);
+           if (TREE_CODE (r) == TYPE_DECL)
+             r = TREE_TYPE (r);
+           if (SPLICE_EXPR_EXPRESSION_P (op)
+               && !check_splice_expr (input_location, UNKNOWN_LOCATION, r,
+                                      SPLICE_EXPR_ADDRESS_P (op),
+                                      SPLICE_EXPR_MEMBER_ACCESS_P (op),
+                                      /*complain_p=*/false))
+             {
+               if (complain & tf_error)
+                 {
+                   auto_diagnostic_group d;
+                   error_at (cp_expr_loc_or_input_loc (t),
+                             "%qE is not usable in a splice expression", r);
+                   if (TYPE_P (r))
+                     inform (input_location, "add %<typename%> to denote a "
+                             "type outside a type-only context");
+                 }
+               RETURN (error_mark_node);
+             }
+           RETURN (r);

How can we get here such that it isn't an error?
<tests>
aha, in

  typename [: R :]<int> c1;

the type is SPLICE_SCOPE where SPLICE_SCOPE_EXPR is [:R:]<int>, and we pass
it to tsubst_expr.

But the comment is wrong; we do know what [:R:] means, we know it needs to
be a type because it's a SPLICE_SCOPE, so we shouldn't have needed to pass
it to tsubst_expr in the first place.

Yes: every valid usage of [:R:] is coming from tsubst_splice_scope, so we
know it needs to expand to a type/scope.

Can we instead add SPLICE_EXPR and TEMPLATE_ID_EXPR cases to tsubst for the
type cases, as well as moving the NAMESPACE_DECL case there?

Hopefully done in
<https://forge.sourceware.org/marek/gcc/commit/0726f7476b187bccd4a113a80900fb8125486313>

Looks good.

@@ -3437,9 +3444,24 @@ finish_class_member_access_expr (cp_expr object, tree 
name, bool template_p,
        return error_mark_node;
      }
+  /* For OBJECT.[:S::fn:] the BASELINK can be inside a SCOPE_REF.
+     This happens, but, until Reflection, not for a class member access.  */
+  if (TREE_CODE (name) == SCOPE_REF && BASELINK_P (TREE_OPERAND (name, 1)))
+    name = TREE_OPERAND (name, 1);

Why does this happen?  Why isn't the SCOPE_REF folded into the BASELINK?

Happens in:

   struct S {
     void foo () {}
   };

   template <typename T>
   void qux ()
   {
     S s {};
     s.[:(T) ^^S::foo:] ();
   }

   void
   corge ()
   {
     qux <decltype (^^int)> ();
   }

In cp_parser_postfix_dot_deref_expression:
* cp_parser_splice_expression gives us a SPLICE_EXPR (~it's dependent)

Yes.

* since parser->scope is S, we create a SCOPE_REF in build_qualified_name

As discussed in _dot_deref, parser->scope shouldn't be set by the time we get back there, so I guess fixing that should make this hunk unnecessary.

* need to tsubst, so tsubst_expr/SCOPE_REF does tsubst_scope + tsubst_name,
   then build_qualified_name creates a new SCOPE_REF
* tsubst_expr calls finish_class_member_access_expr with that SCOPE_REF

Does this have to change?

+/* The DIE for C++26 'decltype(^^int)' fundamental type.  */
+static GTY(()) dw_die_ref meta_type_die;

Why do we need this?

I don't think the current auto handling is a great model to copy.

I wonder about, in is_base_type and gen_type_die_with_usage, just handling
everything past END_OF_BASE_TREE_CODES like LANG_TYPE?

Resolved in
<https://forge.sourceware.org/marek/gcc/commit/06e3ccc259570c2e2c7ab4e997d38326e8afc53d>

I'd still prefer END_OF_BASE_TREE_CODES rather than the (equal) LAST_AND_UNUSED_TREE_CODE.

Jason

Reply via email to