On 2/8/26 10:40 AM, Marek Polacek wrote:
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?

OK, thanks.

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


Reply via email to