On 11/30/25 11:00 PM, Jason Merrill wrote:
On 11/29/25 7:04 PM, Jakub Jelinek wrote:
On Sat, Nov 29, 2025 at 06:37:03PM +0530, Jason Merrill wrote:
Unfortunately it is not.  ATTR_ID here is just the attribute name, so
lookup_attribute_spec attempts to find it in the gnu namespace rather than
in the "internal " namespace in which it is present, and fails.

In order to make it work I guess is_late_template_attribute would need to pass
TREE_PURPOSE (attr) instead of name to attribute_takes_identifier_p,
similarly tsubst_attribute pass TREE_PURPOSE (t) instead of
get_attribute_name (t), and attribute_takes_identifier_p would then need to do if (TREE_CODE (attr_id) == TREE_LIST) attr_id = TREE_VALUE (attr_id); for the targetm.* call at the end (which still expects just IDENTIFIER_NODE.
Similarly maybe is_late_template_attribute should again call
lookup_attribute_spec on TREE_PURPOSE (attr) rather than name and then the
if (annotation_p (attr)) return true; stuff could be done after the
if (!spec) return false; checking.

Sounds good, perhaps as a separate patch.

Ok, will do a separate patch for that.

But the current behavior of merge_attributes reverses the order of the a2 list while leaving the a1 list in the same order.  This behavior seems too chaotic to be worth trying to preserve; keeping both lists in order seems
strictly better.

Ok, will change.

So, shall it use attribute_value_equal instead?

Yes, that seems like it would be an improvement, perhaps along with the
is_late_template_attribute cleanup or by itself.

Ok, another incremental patch.

Why does this need to loop?

+        c = BINFO_INHERITANCE_CHAIN (c);

The current patchset records direct base relationship reflection
as REFLECT_BASE with a TREE_BINFO as REFLECT_EXPR_HANDLE.
That looked to me best as it is an existing tree, instead of having
to create say a TREE_VEC with 2 elements or TREE_LIST, where one
operand of that would be the derived class and the other the base
class.  The base class is immediately accessible from the TREE_BINFO,
it is BINFO_TYPE (REFLECT_EXPR_HANDLE (t)).  The derived class is
usually BINFO_TYPE (BINFO_INHERITANCE_CHAIN (REFLECT_EXPR_HANDLE (t))),
but not always, sometimes there is longer BINFO_INHERITANCE_CHAIN
chain before reaching the final one (which has NULL
BINFO_INHERITANCE_CHAIN).

Sure, I'm looking for a comment about when there can be a longer chain. Is
it when a direct virtual base is a primary base of a non-virtual base?

E.g. g++.dg/reflect/bases_of2.C
struct A {};
struct B : virtual A {};
struct C : virtual A {};
struct D : virtual B, virtual C {};
struct E : virtual D {};
struct F : virtual D, virtual E {};

The (F, D) direct base relationship TREE_BINFO has
BINFO_INHERITANCE_CHAIN (BINFO_INHERITANCE_CHAIN (base_binfo)) non- NULL and
only BINFO_TYPE of that is F.

Sure, the added comment could just mention multiple virtual inheritance.

And some further feedback:

+cp_parser_reflect_expression (cp_parser *parser)
+{
+  if (!flag_reflection)
+    {
+      error_at (cp_lexer_peek_token (parser->lexer)->location,
+        "reflection is only available with %<-freflection%>");
+      return error_mark_node;
+    }
+
+  /* Consume the '^^'.  */
+  cp_lexer_consume_token (parser->lexer);
+
+  /* Get the location of the operand.  */
+  const location_t loc = cp_lexer_peek_token (parser->lexer)->location;
+
+  auto s = make_temp_override (cp_preserve_using_decl, true);

This flag seems kind of a kludge; since we only need it to apply to a single lookup, I wonder how difficult it would be to use a LOOK_want flag instead?

Failing that, setting the flag should at least move into cp_parser_reflection_name, it seems dangerous to have it set while parsing template arguments.

+    /* This can happen for std::meta::info(^^int) where the cast has no
+       meaning.  */
+    if (REFLECTION_TYPE_P (type) && REFLECT_EXPR_P (op))
+      {
+        r = op;
+        break;
+      }

Why is this needed?  I'd expect the existing code to work fine for a cast to the same type.

@@ -10658,8 +10815,11 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
     }

   /* Check that immediate invocation does not return an expression referencing
-     any immediate function decls.  */
-  if (!non_constant_p && cxx_dialect >= cxx20)
+     any immediate function decls.  But allow
+       consteval int fn () { return 42; }
+       constexpr auto r = ^^fn;
+     which is OK to do.  */
+  if (!non_constant_p && cxx_dialect >= cxx20 && !REFLECT_EXPR_P (r))
     if (tree immediate_fndecl
     = cp_walk_tree_without_duplicates (&r, find_immediate_fndecl,
                        NULL))

This change seems redundant with the one in find_immediate_fndecl.

  tree ctx = decl_namespace_context (fndecl);
  if (!DECL_NAMESPACE_STD_META_P (ctx))
    return false;

This pattern seems to be the only use of DECL_NAMESPACE_STD_META_P, how about dropping that macro and instead defining decl_in_std_meta_p?

+/* Return true iff the next tokens start a splice-type-specifier.
+   If REQUIRE_TYPENAME_P, we only return true if there is a preceding
+   typename keyword.  */
+
+static bool
+cp_parser_next_tokens_start_splice_type_spec_p (cp_parser *parser,
+                        bool require_typename_p)
+{
+  if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SPLICE))
+    return !require_typename_p;
+  return (cp_lexer_next_token_is_keyword (parser->lexer, RID_TYPENAME)
+      && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SPLICE));
+}

Doesn't this need to handle the optional typename template [: case?

+/* Return true iff the next tokens start a splice-scope-specifier.  */
+
+static bool
+cp_parser_next_tokens_start_splice_scope_spec_p (cp_parser *parser)
+{
+  if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SPLICE))
+    return true;
+  return (cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE)
+      && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SPLICE));
+}

Since these tokens could also start a splice-expression, maybe this should be called "...can_start..."?

+static tree
+cp_parser_splice_type_specifier (cp_parser *parser)
+{
+  /* Consume the optional typename.  */
+  if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TYPENAME))
+    cp_lexer_consume_token (parser->lexer);
+
+  tree type = cp_parser_splice_specifier (parser);
+
+  if (TREE_CODE (type) == TYPE_DECL)
+    type = TREE_TYPE (type);
+
+  /* When we see [:T:] or [:T:]<arg> we don't know what it'll turn out
+     to be.  */
+  if (dependent_splice_p (type))
+    return make_splice_scope (type, /*type_p=*/true);
+
+  if (!valid_splice_type_p (type))
+    {
+      cp_parser_error (parser, "reflection not usable in a splice type");
+      type = NULL_TREE;
+    }

It seems unfortunate not to print what the problematic reflection is here; maybe we can use a regular error (if !simulate) instead of cp_parser_error?

+cp_parser_splice_expression (cp_parser *parser, bool template_p,
+                 bool address_p, bool template_arg_p,
+                 bool member_access_p, cp_id_kind *idk)
+{
+  bool targs_p = false;
+
+  /* [class.access.base]/5: A member m is accessible at the point R when
+     designated in class N if
+     -- m is designated by a splice-expression  */
+  if (member_access_p)
+    push_deferring_access_checks (dk_no_check);
+
+  cp_expr expr = cp_parser_splice_specifier (parser, template_p, &targs_p);
+
+  if (member_access_p)
+    pop_deferring_access_checks ();

I don't understand messing with access around parsing the splice; where is the access check that would be affected?

+  if (template_p)
+    {
+      /* [expr.prim.splice] For a splice-expression of the form template
+     splice-specifier, the splice-specifier shall designate a function
+     template.  */
+      if (!targs_p)
+    {
+      if (!really_overloaded_fn (t))
+        {
+          auto_diagnostic_group d;
+          error_at (loc, "reflection not usable in a template splice");
+          inform (loc, "only function templates are allowed here");
+          return error_mark_node;
+        }
+    }
+      /* [expr.prim.splice] For a splice-expression of the form
+     template splice-specialization-specifier, the splice-specifier of the
+     splice-specialization-specifier shall designate a template.  */
+      else
+    {
+      if (really_overloaded_fn (t)
+          || get_template_info (t)
+          || TREE_CODE (t) == TEMPLATE_ID_EXPR)
+        /* OK */;
+      else
+        {
+          auto_diagnostic_group d;
+          error_at (loc, "reflection not usable in a template splice");
+          inform (loc, "only templates are allowed here");
+          return error_mark_node;
+        }
+    }
+    }
+  else if (/* No 'template' but there were template arguments?  */
+       targs_p
+       /* No 'template' but the splice-specifier designates a template?  */
+       || really_overloaded_fn (t))
+    {
+      auto_diagnostic_group d;
+      if (targs_p)
+    error_at (loc, "reflection not usable in a splice expression with "
+          "template arguments");
+      else
+    error_at (loc, "reflection not usable in a splice expression");
+      location_t sloc = expr.get_start ();
+      rich_location richloc (line_table, sloc);
+      richloc.add_fixit_insert_before (sloc, "template ");
+      inform (&richloc, "add %<template%> to denote a template");
+      return error_mark_node;

These "reflection not usable" errors also need to print the reflection.

+  /* [expr.prim.splice]/2: "The expression is ill-formed if S [the construct
+     designated by splice-specifier] is
+     -- a local entity such that there is a lambda scope that intervenes
+     between the expression and the point at which S was introduced"  */
+  // TODO This should be moved to check_splice_expr.

TODO

+  if (outer_automatic_var_p (t))
+    {
+      auto_diagnostic_group d;
+      error_at (loc, "cannot splice local entity %qD for which there is an "
+        "intervening lambda expression", t);
+      inform (DECL_SOURCE_LOCATION (t), "%qD declared here", t);
+      return error_mark_node;
+    }

Also, outer_automatic_var_p doesn't necessarily mean there are any lambdas involved; we could be in a local class member function.

+  /* When doing foo.[: bar :], cp_parser_postfix_dot_deref_expression wants +     to see an identifier or a TEMPLATE_ID_EXPR, if we have something like +     s.template [: ^^S::var :]<int> where S::var is a variable template.  */
+  if (member_access_p)
+    {
+      /* Grab the unresolved expression then.  */
+      t = unresolved;
+      if (DECL_P (t))
+    /* We cannot forget what context we came from, so build up
+       a SCOPE_REF.  */
+    t = build_qualified_name (/*type=*/NULL_TREE, CP_DECL_CONTEXT (t),
+                  DECL_NAME (t), /*template_p=*/false);
+      else if (OVL_P (t))
+    t = OVL_NAME (t);

It seems wrong to throw away the result of our earlier name lookup, can we instead improve ...dot_deref... to handle these forms as arguments?

+    {
+      /* We may have to instantiate; for instance, if we're dealing with
+     a variable template.  For &[: ^^S::x :], we have to create an
+     OFFSET_REF.  For a VAR_DECL, we need the convert_from_reference.  */
+      cp_unevaluated u;
+      /* CWG 3109 adjusted [class.protected] to say that checking access to +     protected non-static members is disabled for members designated by a
+     splice-expression.  */
+      push_deferring_access_checks (dk_no_check);

I'm not sure where an access check would happen here, either? finish_id_expression_1 already does this push/pop for the FIELD_DECL case.

+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);
+  location_t loc = scope.get_location ();
+  if (TREE_CODE (scope) == TYPE_DECL)
+    scope = TREE_TYPE (scope);
+
+  if (template_p && !targs_p)
+    {
+      error_at (loc, "extra %<template%> in a scope splice");
+      return error_mark_node;
+    }
+  /* [expr.prim.id.qual] The template may only be omitted from the
+     form template(opt) splice-specialization-specifier :: when the
+     splice-specialization-specifier is preceded by typename.  */
+  if (targs_p && !typename_p)
+    {
+      // TODO add error
+    }

TODO

+  if (!valid_splice_scope_p (scope))
+    {
+      auto_diagnostic_group d;
+      error_at (loc, "reflection not usable in a splice scope");
+      if (TYPE_P (scope))
+    inform (loc, "%qT is not a class, namespace, or enumeration",
+        tree (scope));

Let's print the reflection even if it isn't a type.

+/* Skip tokens until a non-nested closing CLOSE_TOKEN is the next
+   token, or there are no more tokens.  Return true in the first case,
+   false otherwise.  */
+
+// TODO: use instead of cp_parser_skip_to_closing_brace and
+// cp_parser_skip_up_to_closing_square_bracket

Or perhaps you could enhance/use cp_parser_skip_balanced_tokens here instead of adding a new skip function?

+/* We know the next token is '[:' (optionally preceded by a template or
+   typename) and we are wondering if a '::' follows right after the
+   closing ':]', or after the possible '<...>' after the ':]'.  Return
+   true if yes, false otherwise.  */
+
+static bool
+cp_parser_splice_spec_is_nns_p (cp_parser *parser)

All the callers first establish that we're looking at a splice and then call this function; I'd think we could reduce that to a single call to cp_parser_nth_token_starts_splice_nns_p?

+  /* ??? It'd be nice to use saved_token_sentinel, but its rollback
+     uses cp_lexer_previous_token, but we may be the first token in the
+     file so there are no previous tokens.  Sigh.  */
+  cp_lexer_save_tokens (parser->lexer);

That sounds pretty simple to fix?

@ -7098,6 +7577,20 @@ cp_parser_unqualified_id (cp_parser* parser,
         if (cp_parser_parse_definitely (parser))
           done = true;
       }
+    /* Allow r.~typename [:R:].  */
+    else if (!done
+         && cp_parser_next_tokens_start_splice_type_spec_p
+             (parser, /*require_typename_p=*/true))
+      {
+        parser->scope = object_scope;
+        parser->object_scope = NULL_TREE;
+        parser->qualifying_scope = NULL_TREE;
+        type_decl = cp_parser_splice_type_specifier (parser);
+        if (!type_decl)
+          return error_mark_node;
+        /* We don't have a TYPE_DECL, so return early.  */
+        return build_min_nt_loc (loc, BIT_NOT_EXPR, type_decl);

The comment seems to belong one line higher.

@@ -9037,15 +9565,32 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser,
      ordinary class member access expression, rather than a
      pseudo-destructor-name.  */
       bool template_p;
+      bool template_keyword_p = cp_parser_optional_template_keyword (parser);
       cp_token *token = cp_lexer_peek_token (parser->lexer);
-      /* Parse the id-expression.  */
-      name = (cp_parser_id_expression
-          (parser,
-           cp_parser_optional_template_keyword (parser),
-           /*check_dependency_p=*/true,
-           &template_p,
-           /*declarator_p=*/false,
-           /*optional_p=*/false));
+      /* See if there was this->[:R:].  Note that this->[: ^^S :]::i;
+     is not a splice-expression.  */

"Note that [in] this->.... [the rhs] is not a splice-expression."

+       [:^^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.

@@ -9087,13 +9641,17 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser,
           parser->qualifying_scope = NULL_TREE;
           parser->object_scope = NULL_TREE;
         }
-      if (parser->scope && name && BASELINK_P (name))
+      if ((parser->scope || splice_p) && name && BASELINK_P (name))
         adjust_result_of_qualified_name_lookup
-          (name, parser->scope, scope);
+          (name,
+           /* For obj->[:^^R:] we won't have parser->scope, but we still
+          have to perform this adjustment.  */
+           (splice_p ? BINFO_TYPE (BASELINK_BINFO (name)) : parser- >scope),
+           scope);

I think BASELINK_ACCESS_BINFO would be more accurate.

Still more to come...

+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?

+  /* We don't know what this might be.  Try and see what works.  */
+  cp_parser_parse_tentatively (parser);
+  tree t = cp_parser_reflection_name (parser);
+  tree talias = NULL_TREE;
+  cp_token *next = NULL;
+  if (TREE_CODE (t) == TYPE_DECL && !cp_parser_error_occurred (parser))
+    {
+      /* Need to call cp_parser_type_id, because say
+        using A = int;
+        ^^A &
+        should parse the type id rather than reflection-name.
+        Though, remember the TYPE_DECL and next token in that case
+        if it is a type alias and if cp_parser_type_id parses the
+        same tokens, don't strip_typedefs.  */
+      if (is_typedef_decl (t))
+       {
+         talias = TREE_TYPE (t);
+         next = cp_lexer_peek_token (parser->lexer);
+       }
+      cp_parser_simulate_error (parser);
+    }
+  if (cp_parser_parse_definitely (parser))
+    return get_reflection (loc, t);

It seems like we avoid returning too early for ^^tmpl<args> somewhat accidentally, because looking for a nested-name-specifier turns the template-id into a CPP_TEMPLATE_ID. That could use a comment.

+  /* 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?

     case RID_TYPENAME:
-      /* Look for an elaborated-type-specifier.  */
-      type_spec
-       = (cp_parser_elaborated_type_specifier
-          (parser,
-           decl_spec_seq_has_spec_p (decl_specs, ds_friend),
-           is_declaration));
-      if (decl_specs)
-       cp_parser_set_decl_spec_type (decl_specs,
-                                     type_spec,
-                                     token,
-                                     /*type_definition_p=*/false);
-      return type_spec;
+      {
+       /* If we see 'typename [:', this could be a typename-specifier.
+          But if there's no '::' after the '[:x:]' then it is probably
+          a simple-type-specifier.  */
+       if (keyword == RID_TYPENAME
+           && cp_lexer_peek_nth_token (parser->lexer, 2)->type
+               == CPP_OPEN_SPLICE
+           && !cp_parser_splice_spec_is_nns_p (parser))
+         break;
+
+       /* Look for an elaborated-type-specifier.  */
+       type_spec
+         = (cp_parser_elaborated_type_specifier
+            (parser,
+             decl_spec_seq_has_spec_p (decl_specs, ds_friend),
+             is_declaration));
+
+       if (decl_specs)
+         cp_parser_set_decl_spec_type (decl_specs,
+                                       type_spec,
+                                       token,
+                                       /*type_definition_p=*/false);
+       return type_spec;
+      }

I don't see the need for the added braces / indentation change?

@@ -30467,7 +31339,15 @@ cp_parser_base_specifier (cp_parser* parser)
   if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TYPENAME))
     {
       token = cp_lexer_peek_token (parser->lexer);
-      if (!processing_template_decl)
+      if (cp_parser_next_tokens_start_splice_type_spec_p (parser, true))
+       {
+         /* If typename is followed by [:, don't diagnose it just yet,
+            but defer it depending on whether it is splice-scope-specifier
+            or splice-type-specifier.  */

If we're deferring this case, how about deferring the diagnostic in general rather than duplicating it?
+         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.

@@ -17278,6 +17365,39 @@ tsubst (tree t, tree args, tsubst_flags_t complain, 
tree in_decl)
        if (f == error_mark_node)
          return error_mark_node;
+ /* We had [:X:]:: which was substituted into a NAMESPACE_DECL and not
+          a type as is expected here.  */
+       if (TREE_CODE (ctx) == NAMESPACE_DECL)

This comment reads like this is going to be an error, let's drop "as is expected here".

@@ -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.

@@ -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.

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?

+    case REFLECT_EXPR:
+      {
+       tree h = REFLECT_EXPR_HANDLE (t);
+       auto kind = static_cast<reflect_kind> (REFLECT_EXPR_KIND (t));

I'd expect the cast to be part of the REFLECT_EXPR_KIND macro, if it needs to be anywhere.

@@ -29318,6 +29567,27 @@ value_dependent_expression_p (tree expression)
            if (value_dependent_expression_p (op))
              return true;
          }
+       if (flag_reflection && !fn && CALL_EXPR_FN (expression))
+         {
+           fn = MAYBE_BASELINK_FUNCTIONS (CALL_EXPR_FN (expression));
+           if (fn && TREE_CODE (fn) != FUNCTION_DECL)
+             fn = NULL_TREE;
+         }
+       /* [meta.reflection.access.context]/8: An invocation of current that
+          appears at a program point P is value-dependent if eval-point(P)
+          is enclosed by a scope corresponding to a templated entity.  */
+       if (flag_reflection
+           && fn
+           && metafunction_p (fn)
+           && id_equal (DECL_NAME (fn), "current")
+           && DECL_CLASS_SCOPE_P (fn)
+           && TYPE_NAME (DECL_CONTEXT (fn))
+           && TREE_CODE (TYPE_NAME (DECL_CONTEXT (fn))) == TYPE_DECL
+           && DECL_NAME (TYPE_NAME (DECL_CONTEXT (fn)))
+           && id_equal (DECL_NAME (TYPE_NAME (DECL_CONTEXT (fn))),
+                        "access_context"))
+         return true;

The last four && could be just

&& id_equal (TYPE_IDENTIFIER (DECL_CONTEXT (fn)), "access_context")

+fold_builtin_is_string_literal (location_t loc, int nargs, tree *args)
+{
+  /* Unless users call the builtin directly, the following 3 checks should be
+     ensured from std::is_string_literal overloads.  */
+  if (nargs != 1)
+    {
+      error_at (loc, "%<__builtin_is_string_literal%> needs a single "
+               "argument");
+      return boolean_false_node;
+    }
+  tree arg = args[0];
+  if (error_operand_p (arg))
+    return boolean_false_node;
+  if (!TYPE_PTR_P (TREE_TYPE (arg))
+      || !TYPE_READONLY (TREE_TYPE (TREE_TYPE (arg)))
+      || TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (arg))))
+    {
+    arg_invalid:
+      error_at (loc, "%<__builtin_is_string_literal%> "
+                    "argument is not %<const char*%>, %<const wchar_t*%>, "
+                    "%<const char8_t*%>, %<const char16_t*%> or "
+                    "%<const char32_t*%>");
+      return boolean_false_node;
+    }
+  tree chart = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (arg)));
+  if (chart != char_type_node
+      && chart != wchar_type_node
+      && chart != char8_type_node
+      && chart != char16_type_node
+      && chart != char32_type_node)
+    goto arg_invalid;
+
+  STRIP_NOPS (arg);
+  while (TREE_CODE (arg) == POINTER_PLUS_EXPR)
+    {
+      if (TREE_CODE (TREE_OPERAND (arg, 1)) != INTEGER_CST)
+       return boolean_false_node;

Why check the offset?

+      arg = TREE_OPERAND (arg, 0);
+      STRIP_NOPS (arg);
+    }
+  if (TREE_CODE (arg) != ADDR_EXPR)
+    return boolean_false_node;
+  arg = TREE_OPERAND (arg, 0);
+  if (TREE_CODE (arg) == ARRAY_REF
+      && TREE_CODE (TREE_OPERAND (arg, 1)) == INTEGER_CST)

Likewise.

@@ -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?

@@ -3520,10 +3542,15 @@ finish_class_member_access_expr (cp_expr object, tree 
name, bool template_p,
             one copy of the data member that is shared by all the objects of
             the class.  So NAME can be unambiguously referred to even if
             there are multiple indirect base classes containing NAME.  */
-         const base_access ba = [scope, name] ()
+         const base_access ba = [scope, name, splice_p] ()
            {
              if (identifier_p (name))
                {
+                 /* [class.access.base]/5: A member m is accessible at the
+                    point R when designated in class N if
+                    -- m is designated by a splice-expression  */
+                 if (splice_p)
+                   return ba_unique;

It being accessible doesn't mean it's unique. But I assume this is already being reworked to avoid repeating the lookup.

+/* 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?

OK, that's everything in patch 1/9.

Jason

Reply via email to