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. The !template_p && targs_p diagnostic in
cp_parser_splice_expression could be downgraded to a pedwarn as you
suggested elsewhere.
How does this look now?
dg.exp passed 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;
and we should also accept:
[:r:] < 42 > 0;
In both of these, we have tokens that form a valid template argument list
but the splice isn't actually a splice-specialization-specifiers. In
order to handle this, I'm parsing the putative template-id tentatively,
and only consider the splice as an s-s-s when the parsing has gone OK.
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.
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): Use
cp_parser_next_token_starts_template_argument_list_p instead of
checking CPP_LESS. Parse the template-id tentatively. New typename_p
parameter.
(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_id_expression): Use
cp_parser_next_token_starts_template_argument_list_p.
(cp_parser_template_id): Check _uncommitted_to_tentative_parse_p
before giving hard errors. Call cp_parser_simulate_error and
pop_deferring_access_checks.
(cp_parser_template_argument): Detect unparenthesized splice
expressions here.
(cp_parser_next_token_starts_template_argument_list_p): New.
gcc/testsuite/ChangeLog:
* g++.dg/reflect/crash6.C: Adjust expected diagnostic.
* g++.dg/reflect/expr3.C: Likewise. Test more cases.
* 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 | 190 +++++++++++++++++--------
gcc/testsuite/g++.dg/reflect/crash6.C | 2 +-
gcc/testsuite/g++.dg/reflect/error12.C | 22 +++
gcc/testsuite/g++.dg/reflect/expr3.C | 6 +-
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 | 17 +++
10 files changed, 354 insertions(+), 59 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 4d988c27cb8..84519df1451 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -3136,6 +3136,8 @@ static bool cp_parser_next_token_ends_template_argument_p
(cp_parser *);
static bool cp_parser_nth_token_starts_template_argument_list_p
(cp_parser *, size_t);
+static bool cp_parser_next_token_starts_template_argument_list_p
+ (cp_parser *, cp_token ** = nullptr);
static enum tag_types cp_parser_token_is_class_key
(cp_token *);
static enum tag_types cp_parser_token_is_type_parameter_key
@@ -6125,11 +6127,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,9 +6173,28 @@ 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_next_token_starts_template_argument_list_p (parser))
+ {
+ /* We have what looks like a valid template argument list. But in
+ constexpr int i = 42;
+ [:^^i:] < 42 > 0;
+ these < > do not form a template argument list; they are operators.
+ So parse tentatively when there was no typename/template, and only
+ consider these tokens as a valid splice-specialization-specifier
+ when a template-id has been parsed successfully. Fail the parse if
+ it's dependent thuogh, because then we can't say that the user just
+ forgot to specify the template keyword. */
+ const bool tentatively_p = !template_p && !typename_p;
+ if (tentatively_p)
+ cp_parser_parse_tentatively (parser);
+ tree e = expr;
/* For member access splice-specialization-specifier, try to wrap
non-dependent splice for function template into a BASELINK so
that cp_parser_template_id can handle it. */
@@ -6185,23 +6208,30 @@ cp_parser_splice_specifier (cp_parser *parser, bool
template_p = false,
tree access_path = lookup_base (object_type, scope, ba_unique,
NULL, tf_warning_or_error);
if (access_path == error_mark_node)
- expr = error_mark_node;
+ e = error_mark_node;
else
- expr
- = build_baselink (access_path, TYPE_BINFO (object_type),
- expr,
- IDENTIFIER_CONV_OP_P (OVL_NAME (expr))
+ e = build_baselink (access_path, TYPE_BINFO (object_type),
+ expr, IDENTIFIER_CONV_OP_P (OVL_NAME (expr))
? TREE_TYPE (OVL_NAME (expr)) : NULL_TREE);
}
}
/* Let cp_parser_template_id parse the template arguments. */
- expr = cp_parser_template_id (parser, template_p,
- /*check_dependency_p=*/true,
- /*tag_type=*/none_type,
- /*is_declaration=*/false,
- expr);
- if (targs_p)
- *targs_p = true;
+ e = cp_parser_template_id (parser, template_p,
+ /*check_dependency_p=*/true,
+ /*tag_type=*/none_type,
+ /*is_declaration=*/false, e);
+ /* If we parsed a valid [:R:]<args>, return it, unless it is
+ dependent. */
+ if (tentatively_p
+ && !cp_parser_error_occurred (parser)
+ && dependent_splice_p (e))
+ cp_parser_simulate_error (parser);
+ if (!tentatively_p || cp_parser_parse_definitely (parser))
+ {
+ if (targs_p)
+ *targs_p = true;
+ expr = e;
+ }
}
return cp_expr (expr, make_location (caret_loc, start_loc, finish_loc));
@@ -6222,7 +6252,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 +6302,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 +6383,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 +6456,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 +6501,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 +6525,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);
@@ -7388,6 +7427,7 @@ cp_parser_id_expression (cp_parser *parser,
optional_p);
}
+ cp_token *next;
if (id && TREE_CODE (id) == IDENTIFIER_NODE
&& warn_missing_template_keyword
&& !template_keyword_p
@@ -7400,19 +7440,13 @@ cp_parser_id_expression (cp_parser *parser,
/* Don't confuse an ill-formed constructor declarator for a missing
template keyword in a return type. */
&& !(declarator_p && constructor_name_p (id, scope))
- && cp_parser_nth_token_starts_template_argument_list_p (parser, 1)
&& warning_enabled_at (token->location,
- OPT_Wmissing_template_keyword))
- {
- saved_token_sentinel toks (parser->lexer, STS_ROLLBACK);
- if (cp_parser_skip_entire_template_parameter_list (parser)
- /* An operator after the > suggests that the > ends a
- template-id; a name or literal suggests that the > is an
- operator. */
- && (cp_lexer_peek_token (parser->lexer)->type
- <= CPP_LAST_PUNCTUATOR))
- missing_template_diag (token->location);
- }
+ OPT_Wmissing_template_keyword)
+ && cp_parser_next_token_starts_template_argument_list_p (parser, &next)
+ /* An operator after the > suggests that the > ends a template-id;
+ a name or literal suggests that the > is an operator. */
+ && (next->type <= CPP_LAST_PUNCTUATOR))
+ missing_template_diag (token->location);
return id;
}
@@ -21266,10 +21300,15 @@ cp_parser_template_id (cp_parser *parser,
{
/* This means there was a splice-specifier. Maybe the user
used the wrong reflection, so complain. */
- if (TYPE_P (templ))
- error_at (token->location, "%qT is not a template", templ);
- else
- error_at (token->location, "%qE is not a template", templ);
+ if (!cp_parser_uncommitted_to_tentative_parse_p (parser))
+ {
+ if (TYPE_P (templ))
+ error_at (token->location, "%qT is not a template", templ);
+ else
+ error_at (token->location, "%qE is not a template", templ);
+ }
+ cp_parser_simulate_error (parser);
+ pop_deferring_access_checks ();
return error_mark_node;
}
else
@@ -21950,9 +21989,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)
@@ -37987,6 +38044,25 @@ cp_parser_nth_token_starts_template_argument_list_p
(cp_parser * parser,
return false;
}
+/* Return true if the next token is a "<" and we can successfully find the
+ final ">"; i.e., the next tokens seem like a valid template-argument-list.
+ If NEXT is non-null, set it to the token following the "<...>". */
+
+static bool
+cp_parser_next_token_starts_template_argument_list_p (cp_parser *parser,
+ cp_token **next)
+{
+ saved_token_sentinel toks (parser->lexer, STS_ROLLBACK);
+ if (cp_parser_nth_token_starts_template_argument_list_p (parser, 1)
+ && cp_parser_skip_entire_template_parameter_list (parser))
+ {
+ if (next)
+ *next = cp_lexer_peek_token (parser->lexer);
+ return true;
+ }
+ return false;
+}
+
/* Returns the kind of tag indicated by TOKEN, if it is a class-key,
or none_type otherwise. */
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..dbfd28477e1
--- /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 "template splice" }
+constexpr bool b2 = template [:r:] < 43; // { dg-error "template splice" }
+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 "template splice|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..d6b1d901d23 100644
--- a/gcc/testsuite/g++.dg/reflect/expr3.C
+++ b/gcc/testsuite/g++.dg/reflect/expr3.C
@@ -38,7 +38,11 @@ g ()
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" }
+ 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 "expected" }
[: ^^bar :]<int>(); // { dg-error "reflection .bar<int>. not usable in a
splice expression with template arguments" }
auto x1 = [: ^^ST :]<int>{}; // { dg-error "reflection .ST<int>. not
usable in a splice expression with template arguments" }
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..b2bb1d12bb3
--- /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>>(); // { dg-bogus "invalid" "" { xfail *-*-* } }
+}
+
+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..70da8807434
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/parse6.C
@@ -0,0 +1,17 @@
+// { 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>();
+}
base-commit: 36fd961ba739f0bb3c4ec664c569f77635036ff8
--
2.52.0