On Sun, Feb 08, 2026 at 09:13:07AM +0900, Jason Merrill wrote:
> On 2/8/26 7:19 AM, Marek Polacek wrote:
> > On Sat, Feb 07, 2026 at 06:24:21PM +0900, Jason Merrill wrote:
> > > On 2/6/26 4:44 AM, Marek Polacek wrote:
> > > > On Tue, Feb 03, 2026 at 12:13:41AM +0800, Jason Merrill wrote:
> > > > > On 2/2/26 2:43 AM, Marek Polacek wrote:
> > > > > > On Fri, Jan 30, 2026 at 10:55:43AM +0800, Jason Merrill wrote:
> > > > > > > On 1/30/26 8:36 AM, Marek Polacek wrote:
> > > > > > > > On Thu, Jan 29, 2026 at 07:32:03PM +0800, Jason Merrill wrote:
> > > > > > > > > On 1/28/26 11:49 PM, Marek Polacek wrote:
> > > > > > > > > > On Wed, Jan 28, 2026 at 11:21:53AM +0800, Jason Merrill 
> > > > > > > > > > wrote:
> > > > > > > > > > > On 1/28/26 10:50 AM, Marek Polacek wrote:
> > > > > > > > > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for 
> > > > > > > > > > > > trunk?
> > > > > > > > > > > > 
> > > > > > > > > > > > -- >8 --
> > > > > > > > > > > > This patch fixes the problem that when 
> > > > > > > > > > > > cp_parser_splice_specifier sees
> > > > > > > > > > > > :]<, it always thinks it's a template-id.  
> > > > > > > > > > > > Consequently, this should compile
> > > > > > > > > > > > but does not:
> > > > > > > > > > > > 
> > > > > > > > > > > >         int i;
> > > > > > > > > > > >         constexpr auto r = ^^i;
> > > > > > > > > > > >         bool b = [:r:] < 42;
> > > > > > > > > > > > 
> > > > > > > > > > > > because we think that a template argument list follows 
> > > > > > > > > > > > the splice.
> > > > > > > > > > > > Fixed by using the new 
> > > > > > > > > > > > cp_parser_next_token_starts_template_argument_list_p
> > > > > > > > > > > > which checks that we see the whole <...>.
> > > > > > > > > > > > 
> > > > > > > > > > > > But this turned out to be more complicated when I 
> > > > > > > > > > > > considered
> > > > > > > > > > > > splice-specialization-specifiers in a 
> > > > > > > > > > > > template-argument-list.  With this
> > > > > > > > > > > > patch we reject
> > > > > > > > > > > > 
> > > > > > > > > > > >         S<[:r:] < 43> s;
> > > > > > > > > > > > 
> > > > > > > > > > > > but I think that's fine given the footnote in 
> > > > > > > > > > > > [temp.names]: "A > that
> > > > > > > > > > > > encloses [...] the template-arguments of a subsequent 
> > > > > > > > > > > > template-id or
> > > > > > > > > > > > splice-specialization-specifier, is considered nested 
> > > > > > > > > > > > for the purpose
> > > > > > > > > > > > of this description."  (We can't just check 
> > > > > > > > > > > > !in_template_argument_list_p
> > > > > > > > > > > > because [:x:]<> can be valid in a template argument 
> > > > > > > > > > > > list.)
> > > > > > > > > > > 
> > > > > > > > > > > I think that's wrong under 
> > > > > > > > > > > https://eel.is/c++draft/temp.names#3.1 since the
> > > > > > > > > > > splice is not in a type-only context and is not preceded 
> > > > > > > > > > > by template or
> > > > > > > > > > > typename.
> > > > > > > > > > > 
> > > > > > > > > > > We should accept the above and not assume that the < 
> > > > > > > > > > > starts a
> > > > > > > > > > > template-argument-list.  It would still be helpful to 
> > > > > > > > > > > check whether it could
> > > > > > > > > > > do so for -Wmissing-template-keyword.
> > > > > > > > > > 
> > > > > > > > > > Ah, fixed (by checking template_p and typename_p).  We 
> > > > > > > > > > don't emit
> > > > > > > > > > a -Wmissing-template-keyword warning but currently we say:
> > > > > > > > > >        note: add 'template' to denote a template
> > > > > > > > > > although this may change if we downgrade the error to a 
> > > > > > > > > > pedwarn.
> > > > > > > > > > 
> > > > > > > > > > > > I think that 
> > > > > > > > > > > > cp_parser_skip_entire_template_parameter_list has a bug
> > > > > > > > > > > > because it doesn't correctly handle >> in a template 
> > > > > > > > > > > > argument list;
> > > > > > > > > > > > see the xfail in parse3.C.
> > > > > > > > > > > 
> > > > > > > > > > > Sounds right.
> > > > > > > > > > 
> > > > > > > > > > We have to make sure that in a template argument list we 
> > > > > > > > > > don't eat
> > > > > > > > > > both > because the second > isn't spurious.  I gave it a 
> > > > > > > > > > few minutes
> > > > > > > > > > but my quick fix didn't work, so I'm leaving it for later.
> > > > > > > > > > 
> > > > > > > > > > > > I also realized that my code to detect unparenthesized 
> > > > > > > > > > > > splice
> > > > > > > > > > > > expressions as template arguments is wrong.  
> > > > > > > > > > > > [expr.prim.splice] says that
> > > > > > > > > > > > 
> > > > > > > > > > > >         constexpr auto i = e<[:^^h:]>;
> > > > > > > > > > > > 
> > > > > > > > > > > > is ill-formed, but I think "S<[:r:] >= 1>" is fine.
> > > > > > > > > > > 
> > > > > > > > > > > Agreed, the normative citation is 
> > > > > > > > > > > https://eel.is/c++draft/temp#names-6
> > > > > > > > > > 
> > > > > > > > > > Cool, I've added that reference to a comment in the patch.
> > > > > > > > > > 
> > > > > > > > > > > > So I moved the
> > > > > > > > > > > > checking to cp_parser_template_argument while making 
> > > > > > > > > > > > sure that we
> > > > > > > > > > > > only complain when the splice-expression is the whole 
> > > > > > > > > > > > template-argument.
> > > > > > > > > > > 
> > > > > > > > > > > Sounds good.
> > > > > > > > > > 
> > > > > > > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> > > > > > > > > > 
> > > > > > > > > > -- >8 --
> > > > > > > > > > This patch fixes the problem that when 
> > > > > > > > > > cp_parser_splice_specifier sees
> > > > > > > > > > :]<, it always thinks it's a template-id.  Consequently, 
> > > > > > > > > > this should compile
> > > > > > > > > > but does not:
> > > > > > > > > > 
> > > > > > > > > >        int i;
> > > > > > > > > >        constexpr auto r = ^^i;
> > > > > > > > > >        bool b = [:r:] < 42;
> > > > > > > > > > 
> > > > > > > > > > because we think that a template argument list follows the 
> > > > > > > > > > splice.
> > > > > > > > > > Fixed by using the new 
> > > > > > > > > > cp_parser_next_token_starts_template_argument_list_p
> > > > > > > > > > which checks that we see the whole <...>.
> > > > > > > > > > 
> > > > > > > > > > But this turned out to be more complicated when I considered
> > > > > > > > > > splice-specialization-specifiers in a 
> > > > > > > > > > template-argument-list.  We should
> > > > > > > > > > accept the following given [temp.names]/3:
> > > > > > > > > > 
> > > > > > > > > >        S<[:r:] < 43> s;
> > > > > > > > > > 
> > > > > > > > > > To that effect, I'm checking !in_template_argument_list_p 
> > > > > > > > > > along with
> > > > > > > > > 
> > > > > > > > > I don't think that's valid either, unfortunately; even 
> > > > > > > > > outside of a template
> > > > > > > > > argument list
> > > > > > > > > 
> > > > > > > > >       [:r:] < 42 > 0
> > > > > > > > > 
> > > > > > > > > seems to be well-formed (though pathological) code.
> > > > > > > > 
> > > > > > > > Oh well.  How about if I parse the template-id tentatively, and 
> > > > > > > > only
> > > > > > > > consider the [:r:] < 42 > as a splice-specialization-specifier 
> > > > > > > > when
> > > > > > > > the parsing worked out?
> > > > > > > 
> > > > > > > This patch still breaks
> > > > > > 
> > > > > > Sorry :(
> > > > > > 
> > > > > > > using info = decltype(^^::);
> > > > > > > 
> > > > > > > template <info R>
> > > > > > > bool f()
> > > > > > > {
> > > > > > >      return [: R :]<42>0;
> > > > > > > }
> > > > > > > 
> > > > > > > int i;
> > > > > > > int main()
> > > > > > > {
> > > > > > >      f<^^i>();
> > > > > > > }
> > > > > > > 
> > > > > > > We must only consider it a splice-specialization-specifier as 
> > > > > > > specified by
> > > > > > > [temp.names]/3.1; otherwise we should only 
> > > > > > > -Wmissing-template-keyword.  And
> > > > > > > I think we should share that warning code with 
> > > > > > > cp_parser_id_expression.
> > > > > > > 
> > > > > > > Well, I suppose we could also treat it as a (ill-formed)
> > > > > > > splice-specialization-specifier if 'expr' is known to be a 
> > > > > > > template at that
> > > > > > > point, but not if it's dependent as above, and of course not if 
> > > > > > > it's a
> > > > > > > non-template.
> > > > > > 
> > > > > > I've tried to do the latter.
> > > > > 
> > > > > Ah, I wasn't thinking of them as alternatives; the latter was a 
> > > > > refinement
> > > > > of the former (an addition to [temp.names]/3.1).  That is, template_p 
> > > > > ||
> > > > > typename_p || TREE_CODE (expr) == TEMPLATE_DECL.
> > > > 
> > > > Done.
> > > > 
> > > > > I don't think using _next_token_starts_ here is appropriate for this 
> > > > > case;
> > > > > [temp.names] says if we have template or typename and see < it's a
> > > > > template-id regardless of what follows, and I expect we can give 
> > > > > better
> > > > > syntax errors if we go ahead with that.
> > > > 
> > > > I went back to cp_parser_nth_token_starts_template_argument_list_p.
> > > 
> > > Once again, I think that's wrong; see below.
> > 
> > Note that cp_parser_nth_token_starts_template_argument_list_p is
> > no the one that parses the whole <>, it only checks for a <.
> 
> Ah, my mistake.  Then the problem is that the new function does something
> very different from the existing function with a very similar name;
> typically _next_ and _nth_ only differ in the index parameter.

Yeah, the names were confusing.

> So using _nth_ as in v5 is right, and the new function needs a more distinct
> name, maybe _looks_like_ to clarify that it's a warning heuristic.  And then
> we could move the CPP_LAST_PUNCTUATOR comparison into that function so we
> don't need to pass the token back to the caller.
> 
> Or just drop the new function since it's now indepedent of the functional
> change.

I dropped it.  I may re-add it in the future if needed.  Thanks,

dg.exp passed thus far on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
This patch fixes the problem that when cp_parser_splice_specifier sees
:]<, it always thinks it's a template-id.  Consequently, this should compile
but does not:

  int i;
  constexpr auto r = ^^i;
  bool b = [:r:] < 42;

because we think that a template argument list follows the splice.

Fixed by implementing [temp.names]/3 better: only attempt to parse
a template argument list if we saw template or typename.  As an
extension, also parse a template argument list if the splice yielded
a TEMPLATE_DECL -- in that case, chances are that the user simply
forgot to specify 'template'.  In that case we'll suggest adding
'template' in cp_parser_splice_expression.

We should accept the following given [temp.names]/3:

  S<[:r:] < 43> s;

and we should also accept:

  [:r:] < 42 > 0;

I also realized that my code to detect unparenthesized splice
expressions as template arguments is wrong.  [expr.prim.splice] says that

  constexpr auto i = e<[:^^h:]>;

is ill-formed, but "S<[:r:] >= 1>" is fine as per [temp.names]/6.  I moved
the checking to cp_parser_template_argument while making sure that we
only complain when the splice-expression is the whole template-argument.

This patch also fixes 123640.

        PR c++/123823
        PR c++/123640

gcc/cp/ChangeLog:

        * parser.cc (cp_parser_splice_specifier): New typename_p parameter.
        Use cp_parser_nth_token_starts_template_argument_list_p instead of
        checking CPP_LESS.  Check for typename/template/TEMPLATE_DECL before
        parsing a template-id.
        (cp_parser_splice_type_specifier): Adjust the call to
        cp_parser_splice_specifier.
        (cp_parser_splice_expression): Don't detect unparenthesized splice
        expressions here.  Adjust the call to cp_parser_splice_specifier.
        (cp_parser_splice_scope_specifier): Adjust the call to
        cp_parser_splice_specifier.
        (cp_parser_skip_entire_splice_expr): New, broken out of...
        (cp_parser_splice_spec_is_nns_p): ...this.
        (cp_parser_template_id): Call pop_deferring_access_checks.
        (cp_parser_template_argument): Detect unparenthesized splice
        expressions here.

gcc/testsuite/ChangeLog:

        * g++.dg/reflect/crash6.C: Adjust expected diagnostic.
        * g++.dg/reflect/expr3.C: Likewise.  Test more cases.
        * g++.dg/reflect/splice4.C: Adjust expected diagnostic.
        * g++.dg/reflect/error12.C: New test.
        * g++.dg/reflect/parse1.C: New test.
        * g++.dg/reflect/parse2.C: New test.
        * g++.dg/reflect/parse3.C: New test.
        * g++.dg/reflect/parse4.C: New test.
        * g++.dg/reflect/parse5.C: New test.
        * g++.dg/reflect/parse6.C: New test.
---
 gcc/cp/parser.cc                       | 96 ++++++++++++++++++--------
 gcc/testsuite/g++.dg/reflect/crash6.C  |  2 +-
 gcc/testsuite/g++.dg/reflect/error12.C | 22 ++++++
 gcc/testsuite/g++.dg/reflect/expr3.C   | 10 ++-
 gcc/testsuite/g++.dg/reflect/parse1.C  | 43 ++++++++++++
 gcc/testsuite/g++.dg/reflect/parse2.C  | 39 +++++++++++
 gcc/testsuite/g++.dg/reflect/parse3.C  | 59 ++++++++++++++++
 gcc/testsuite/g++.dg/reflect/parse4.C  | 23 ++++++
 gcc/testsuite/g++.dg/reflect/parse5.C  | 12 ++++
 gcc/testsuite/g++.dg/reflect/parse6.C  | 16 +++++
 gcc/testsuite/g++.dg/reflect/splice4.C |  4 +-
 11 files changed, 292 insertions(+), 34 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/reflect/error12.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/parse1.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/parse2.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/parse3.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/parse4.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/parse5.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/parse6.C

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 5f1d028de0c..986605e0cdc 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -6125,11 +6125,13 @@ cp_parser_next_tokens_can_start_splice_scope_spec_p 
(cp_parser *parser)
       splice-specifier < template-argument-list[opt] >
 
    TEMPLATE_P is true if we've parsed the leading template keyword.
+   TYPENAME_P is true if we've parsed the leading typename keyword or are
+   in a type-only context.
    TARGS_P is set to true if there is a splice-specialization-specifier.  */
 
 static cp_expr
 cp_parser_splice_specifier (cp_parser *parser, bool template_p = false,
-                           bool *targs_p = nullptr)
+                           bool typename_p = false, bool *targs_p = nullptr)
 {
   /* Get the location of the '[:'.  */
   location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
@@ -6169,8 +6171,18 @@ cp_parser_splice_specifier (cp_parser *parser, bool 
template_p = false,
   /* Get the reflected operand.  */
   expr = splice (expr);
 
-  /* If the next token is a '<', it's a splice-specialization-specifier.  */
-  if (cp_lexer_next_token_is (parser->lexer, CPP_LESS))
+  /* If the next token is a <, it could be a splice-specialization-specifier.
+     But we need to handle "[:r:] < 42" where the < doesn't start a template
+     argument list.  [temp.names]/3: A < is interpreted as the delimiter of
+     a template-argument-list if either
+     -- it follows a splice-specifier that either
+       -- appears in a type-only context or
+       -- is preceded by template or typename.  */
+  if (cp_parser_nth_token_starts_template_argument_list_p (parser, 1)
+      /* As a courtesy to the user, if there is a < after a template
+        name, parse the construct as an s-s-s and warn about the missing
+        'template'; it can't be anything else.  */
+      && (template_p || typename_p || TREE_CODE (expr) == TEMPLATE_DECL))
     {
       /* For member access splice-specialization-specifier, try to wrap
         non-dependent splice for function template into a BASELINK so
@@ -6222,7 +6234,8 @@ cp_parser_splice_type_specifier (cp_parser *parser)
   if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TYPENAME))
     cp_lexer_consume_token (parser->lexer);
 
-  cp_expr expr = cp_parser_splice_specifier (parser);
+  cp_expr expr = cp_parser_splice_specifier (parser, /*template_p=*/false,
+                                            /*typename_p=*/true);
   const location_t loc = (expr != error_mark_node
                          ? expr.get_location () : input_location);
   tree type = expr.get_value ();
@@ -6271,7 +6284,8 @@ cp_parser_splice_expression (cp_parser *parser, bool 
template_p,
   parser->object_scope = NULL_TREE;
   parser->qualifying_scope = NULL_TREE;
 
-  cp_expr expr = cp_parser_splice_specifier (parser, template_p, &targs_p);
+  cp_expr expr = cp_parser_splice_specifier (parser, template_p,
+                                            /*typename_p=*/false, &targs_p);
 
   /* And don't leave the scopes set, either.  */
   parser->scope = NULL_TREE;
@@ -6351,14 +6365,6 @@ cp_parser_splice_expression (cp_parser *parser, bool 
template_p,
       return error_mark_node;
     }
 
-  if (parser->in_template_argument_list_p
-      && !parser->greater_than_is_operator_p)
-    {
-      error_at (loc, "unparenthesized splice expression cannot be used as "
-               "a template argument");
-      return error_mark_node;
-    }
-
   /* Make sure this splice-expression produces an expression.  */
   if (!check_splice_expr (loc, expr.get_start (), t, address_p,
                          member_access_p, /*complain=*/true))
@@ -6432,7 +6438,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, typename_p,
+                                             &targs_p);
   const location_t loc = scope.get_location ();
   if (TREE_CODE (scope) == TYPE_DECL)
     scope = TREE_TYPE (scope);
@@ -6476,28 +6483,23 @@ cp_parser_splice_scope_specifier (cp_parser *parser, 
bool typename_p,
   return scope;
 }
 
-/* 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.  */
+/* Skip the whole splice-expression: the optional typename/template,
+   [:...:], and also the <...>, if present.  Return true if we skipped
+   successfully.  */
 
 static bool
-cp_parser_splice_spec_is_nns_p (cp_parser *parser)
+cp_parser_skip_entire_splice_expr (cp_parser *parser)
 {
-  /* ??? 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);
-
   if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TYPENAME)
       || cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE))
     cp_lexer_consume_token (parser->lexer);
 
-  bool ok = false;
+  if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_SPLICE))
+    return false;
+
   size_t n = cp_parser_skip_balanced_tokens (parser, 1);
   if (n != 1)
     {
-      ok = true;
       /* Consume tokens up to the ':]' (including).  */
       for (n = n - 1; n; --n)
        cp_lexer_consume_token (parser->lexer);
@@ -6505,11 +6507,30 @@ cp_parser_splice_spec_is_nns_p (cp_parser *parser)
       /* Consume the whole '<....>', if present.  */
       if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)
          && !cp_parser_skip_entire_template_parameter_list (parser))
-       ok = false;
+       return false;
 
-      ok = ok && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE);
+      return true;
     }
 
+  return false;
+}
+
+/* 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)
+{
+  /* ??? 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);
+
+  const bool ok = (cp_parser_skip_entire_splice_expr (parser)
+                  && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE));
+
   /* Roll back the tokens we skipped.  */
   cp_lexer_rollback_tokens (parser->lexer);
 
@@ -21270,6 +21291,7 @@ cp_parser_template_id (cp_parser *parser,
            error_at (token->location, "%qT is not a template", templ);
          else
            error_at (token->location, "%qE is not a template", templ);
+         pop_deferring_access_checks ();
          return error_mark_node;
        }
       else
@@ -21950,9 +21972,27 @@ cp_parser_template_argument (cp_parser* parser)
          && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
        return cp_parser_braced_list (parser);
 
+      /* [temp.names]/6: The constant-expression of a template-argument
+        shall not be an unparenthesized splice-expression.  */
+      cp_token *next = nullptr;
+      if (flag_reflection)
+       {
+         saved_token_sentinel toks (parser->lexer, STS_ROLLBACK);
+         if (cp_parser_skip_entire_splice_expr (parser))
+           next = cp_lexer_peek_token (parser->lexer);
+       }
+
       /* With C++17 generalized non-type template arguments we need to handle
         lvalue constant expressions, too.  */
       argument = cp_parser_assignment_expression (parser);
+      if (UNLIKELY (cp_lexer_peek_token (parser->lexer) == next)
+         && argument != error_mark_node)
+       {
+         loc = cp_lexer_peek_token (parser->lexer)->location;
+         error_at (loc, "unparenthesized splice expression cannot be used "
+                   "as a template argument");
+         return error_mark_node;
+       }
     }
 
   if (!maybe_type_id)
diff --git a/gcc/testsuite/g++.dg/reflect/crash6.C 
b/gcc/testsuite/g++.dg/reflect/crash6.C
index 13ce48afcba..a455a61cd65 100644
--- a/gcc/testsuite/g++.dg/reflect/crash6.C
+++ b/gcc/testsuite/g++.dg/reflect/crash6.C
@@ -11,7 +11,7 @@ void
 f ()
 {
   [:R:] r; // { dg-error "expected" }
-  [:R:]<int> r; // { dg-error "reflection .\\\[: R :\\\]<int>. not usable in a 
splice expression" }
+  [:R:]<int> r; // { dg-error "expected" }
 }
 
 void
diff --git a/gcc/testsuite/g++.dg/reflect/error12.C 
b/gcc/testsuite/g++.dg/reflect/error12.C
new file mode 100644
index 00000000000..7eb787f010d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/error12.C
@@ -0,0 +1,22 @@
+// PR c++/123823
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+constexpr int i = 42;
+constexpr auto r = ^^i;
+constexpr bool b0 = template [:r:] < 0 > 0;  // { dg-error "not a template" }
+constexpr bool b1 = template [:r:] < 0 < 0;  // { dg-error "not a 
template|expected" }
+constexpr bool b2 = template [:r:] < 43;     // { dg-error "not a 
template|expected" }
+constexpr bool b3 = template [:r:] <= 43;    // { dg-error "template splice" }
+constexpr bool b4 = template [:r:] > 41;     // { dg-error "template splice" }
+constexpr bool b5 = template [:r:] >= 41;    // { dg-error "template splice" }
+constexpr bool b6 = template [:r:] == 42;    // { dg-error "template splice" }
+constexpr bool b7 = template [:r:] != 41;    // { dg-error "template splice" }
+
+template<bool> struct S { };
+S<template [:r:] < 43> s1;    // { dg-error "not a template|invalid" }
+S<template [:r:] <= 43> s2;   // { dg-error "template splice|invalid" }
+S<(template [:r:] > 41)> s3;  // { dg-error "template splice|invalid" }
+S<template [:r:] >= 41> s4;   // { dg-error "template splice|invalid" }
+S<template [:r:] == 42> s5;   // { dg-error "template splice|invalid" }
+S<template[:r:] != 41> s6;    // { dg-error "template splice|invalid" }
diff --git a/gcc/testsuite/g++.dg/reflect/expr3.C 
b/gcc/testsuite/g++.dg/reflect/expr3.C
index 27295f1875a..de7cffe1a56 100644
--- a/gcc/testsuite/g++.dg/reflect/expr3.C
+++ b/gcc/testsuite/g++.dg/reflect/expr3.C
@@ -31,15 +31,19 @@ g ()
   int i6 = template [: ^^foo :](42);
   int i7 = [: ^^foo<int> :](42);
   int i8 = template [: ^^foo<int> :](42);   // { dg-error "reflection 
.foo<int>. not usable in a template splice" }
-  int i9 = [: ^^foo :]<int>(42);           // { dg-error "reflection 
.foo<int>. not usable in a splice expression with template arguments" }
+  int i9 = [: ^^foo :]<int>(42);           // { dg-error "reflection .foo. not 
usable in a splice expression|expected" }
   int i10 = template [: ^^foo :]<int>(42);
   int i11 = template [: ^^bar :]<int>(42);  // { dg-error "no matching 
function for call" }
   int i12 = [: ^^two :]<int>;              // { dg-error "reflection 
.two<int>. not usable in a splice expression with template arguments" }
   int i13 = template [: ^^two :]<int>;
 
   [: ^^ST :]<int> c1;  // { dg-error "reflection .ST<int>. not usable in a 
splice expression with template arguments" }
-  [: ^^S :]<int> c2;   // { dg-error "not a template|reflection not usable in 
a splice expression with template arguments" }
-  [: ^^bar :]<int>();  // { dg-error "reflection .bar<int>. not usable in a 
splice expression with template arguments" }
+  typename [: ^^ST :]<int> c2;
+  template [: ^^ST :]<int> c3;  // { dg-error "expected a reflection of an 
expression" }
+  [: ^^S :]<int> c4;   // { dg-error "expected a reflection of an 
expression|expected primary-expression" }
+  template [: ^^S :]<int> c5;   // { dg-error ".S. is not a template" }
+  typename [: ^^S :]<int> c6;   // { dg-error ".S. is not a template|expected" 
}
+  [: ^^bar :]<int>();  // { dg-error "expected" }
 
   auto x1 = [: ^^ST :]<int>{};   // { dg-error "reflection .ST<int>. not 
usable in a splice expression with template arguments" }
   auto x2 = template [: ^^ST :]<int>{};        // { dg-error "expected a 
reflection of an expression" }
diff --git a/gcc/testsuite/g++.dg/reflect/parse1.C 
b/gcc/testsuite/g++.dg/reflect/parse1.C
new file mode 100644
index 00000000000..e66721465cf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/parse1.C
@@ -0,0 +1,43 @@
+// PR c++/123823
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+constexpr int i = 42;
+constexpr auto r = ^^i;
+static_assert ([:r:] < 43);
+static_assert ([:r:] <= 43);
+static_assert ([:r:] > 41);
+static_assert ([:r:] >= 41);
+static_assert ([:r:] == 42);
+static_assert ([:r:] != 41);
+
+static_assert (43 > [:r:]);
+static_assert (43 >= [:r:]);
+static_assert (41 < [:r:]);
+static_assert (41 <= [:r:]);
+static_assert (42 == [:r:]);
+static_assert (41 != [:r:]);
+
+static_assert ([:r:] < 86 >> 1);
+static_assert ([:r:] < 43 > 0);
+static_assert (!([:r:] < 42 > 0));
+
+template<bool>
+struct S;
+template<>
+struct S<true> { };
+
+S<[:r:] < 43> s1;
+S<[:r:] <= 43> s2;
+// [temp.names]/4 -> need the ().
+S<([:r:] > 41)> s3;
+S<[:r:] >= 41> s4;
+S<[:r:] == 42> s5;
+S<[:r:] != 41> s6;
+
+S<(43 > [:r:])> s7;
+S<43 >= [:r:]> s8;
+S<41 < [:r:]> s9;
+S<41 <= [:r:]> s10;
+S<42 == [:r:]> s11;
+S<41 != [:r:]> s12;
diff --git a/gcc/testsuite/g++.dg/reflect/parse2.C 
b/gcc/testsuite/g++.dg/reflect/parse2.C
new file mode 100644
index 00000000000..504f87d92cc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/parse2.C
@@ -0,0 +1,39 @@
+// PR c++/123640
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+template<class T>
+constexpr std::size_t field_count {
+  std::meta::nonstatic_data_members_of(^^T, 
std::meta::access_context::unchecked()).size()
+};
+
+template<std::size_t index, class T>
+constexpr std::meta::info field_at {
+  std::meta::nonstatic_data_members_of(^^T, 
std::meta::access_context::unchecked())[index]
+};
+
+struct S {
+  int a, b, c;
+  constexpr bool operator<(this auto&&l, auto&&r) noexcept
+  {
+    using T = std::remove_cvref_t<decltype (l)>;
+    static constexpr auto N{field_count<T>};
+    if constexpr (N == 0)
+      return false;
+    else
+      {
+       template for (constexpr auto i : std::make_index_sequence<N - 1>{}) // 
{ dg-bogus "constant" "" { xfail *-*-* } }
+         if (l.[:field_at<i,T>:] != r.[:field_at<i,T>:]) [[likely]]
+           return l.[:field_at<i,T>:] < r.[:field_at<i,T>:];
+       return l.[:field_at<N - 1, T>:] < r.[:field_at<N - 1, T>:];
+      }
+  }
+};
+
+int
+main ()
+{
+  return S{1,2,3} < S{1,3,2};
+}
diff --git a/gcc/testsuite/g++.dg/reflect/parse3.C 
b/gcc/testsuite/g++.dg/reflect/parse3.C
new file mode 100644
index 00000000000..c114a45475b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/parse3.C
@@ -0,0 +1,59 @@
+// PR c++/123823
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+constexpr int i = 0;
+constexpr auto r = ^^i;
+
+template<auto U, auto>
+constexpr int S2 = [:U:];
+
+constexpr auto a1 = S2<[:^^r:],  // { dg-error "unparenthesized splice" }
+                       [:^^r:]>; // { dg-error "unparenthesized 
splice|invalid" }
+constexpr auto a2 = S2<([:^^r:]),
+                       [:^^r:]>; // { dg-error "unparenthesized 
splice|invalid" }
+constexpr auto a3 = S2<[:^^r:],  // { dg-error "unparenthesized splice" }
+                       ([:^^r:])>; // { dg-error "invalid" }
+constexpr auto a4 = S2<([:^^r:]),
+                       ([:^^r:])>;
+
+template<int>
+struct S { };
+
+template<typename>
+struct R { };
+
+template<typename T>
+constexpr int fn (T) { return 42; }
+
+constexpr int foo (int) { return 42; };
+
+S<[: ^^foo :](0)> s0;
+S<template [: ^^fn :](1)> s1;
+S<template [: ^^fn :](1) < 43> s2;
+S<(template [: ^^fn :](1) > 43)> s3;
+
+template<int N>
+constexpr auto var = N;
+S<[: ^^var<1> :]> s4; // { dg-error "unparenthesized splice|invalid" }
+S<([: ^^var<1> :])> s5;
+
+template<typename T>
+struct C {
+  static constexpr T t{};
+};
+
+template<typename T>
+void
+f ()
+{
+  S<template [: ^^C :]<T>::t>();
+  R<typename [: ^^C :]<int> >();
+  R<typename [: ^^C :]<int>>();
+}
+
+void
+g ()
+{
+  f<int> ();
+}
diff --git a/gcc/testsuite/g++.dg/reflect/parse4.C 
b/gcc/testsuite/g++.dg/reflect/parse4.C
new file mode 100644
index 00000000000..d4ac04f660c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/parse4.C
@@ -0,0 +1,23 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+// From [temp.names].
+
+using size_t = decltype(sizeof(int));
+using info = decltype(^^void);
+
+struct X {
+  template<size_t> X* alloc();
+  template<size_t> static X* adjust();
+};
+template<class T> void f(T* p) {
+  T* p1 = p->alloc<200>();              // { dg-error "expected" }
+  // { dg-warning "expected .template. keyword before dependent template name" 
"" { target *-*-* } .-1 }
+  T* p2 = p->template alloc<200>();     // OK, < starts template argument list
+  T::adjust<100>();                     // { dg-error "expected" }
+  // { dg-warning "expected .template. keyword before dependent template name" 
"" { target *-*-* } .-1 }
+  T::template adjust<100>();            // OK, < starts template argument list
+
+  static constexpr info r = ^^T::adjust;
+  T* p3 = [:r:]<200>();                 // { dg-error "expected" }
+  T* p4 = template [:r:]<200>();        // OK, < starts template argument list
+}
diff --git a/gcc/testsuite/g++.dg/reflect/parse5.C 
b/gcc/testsuite/g++.dg/reflect/parse5.C
new file mode 100644
index 00000000000..e5386b1958c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/parse5.C
@@ -0,0 +1,12 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+// From [temp.names].
+
+using info = decltype(^^void);
+
+template<int> struct S { };
+constexpr int k = 5;
+constexpr info r = ^^k;
+S<[:r:]> s1;                        // { dg-error "unparenthesized 
splice|invalid" }
+S<([:r:])> s2;                      // OK
+S<[:r:] + 1> s3;                    // OK
diff --git a/gcc/testsuite/g++.dg/reflect/parse6.C 
b/gcc/testsuite/g++.dg/reflect/parse6.C
new file mode 100644
index 00000000000..ca9250f1710
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/parse6.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+using info = decltype(^^::);
+
+template <info R>
+bool f()
+{
+   return [: R :]<42>0;
+}
+
+int i;
+int main()
+{
+   f<^^i>();
+}
diff --git a/gcc/testsuite/g++.dg/reflect/splice4.C 
b/gcc/testsuite/g++.dg/reflect/splice4.C
index 29567d2df35..01ce764d665 100644
--- a/gcc/testsuite/g++.dg/reflect/splice4.C
+++ b/gcc/testsuite/g++.dg/reflect/splice4.C
@@ -12,6 +12,6 @@ namespace M
 static_assert (template [: ^^M::foo :] <42> () == 42);
 static_assert (template [: members_of (^^M, 
std::meta::access_context::unchecked ())[0] :] <43> () == 43);
 int a = [: ^^M::foo :] <44> ();
-// { dg-error "reflection 'M::foo<44>' not usable in a splice expression with 
template arguments" "" { target *-*-* } .-1 }
+// { dg-error "reflection 'M::foo' not usable in a splice expression|expected" 
"" { target *-*-* } .-1 }
 int b = [: members_of (^^M, std::meta::access_context::unchecked ())[0] :] 
<45> ();
-// { dg-error "reflection 'M::foo<45>' not usable in a splice expression with 
template arguments" "" { target *-*-* } .-1 }
+// { dg-error "reflection 'M::foo' not usable in a splice expression|expected" 
"" { target *-*-* } .-1 }

base-commit: be597e8c7423998609efd733bf465912ca84d0d3
-- 
2.53.0


Reply via email to