c++: Parse splice-type-specifier in cp_parser_mem_initializer_id [PR124944]

The grammar has:
mem-initializer-id:
        class-or-decltype
        identifier
class-or-decltype:
        nested-name-specifier[opt] type-name
        nested-name-specifier template simple-template-id
        computed-type-specifier
computed-type-specifier:
        decltype-specifier
        pack-index-specifier
        splice-type-specifier
but we weren't parsing splice-type-specifier in there, just in
cp_parser_base_specifier.  So, the following patch defers
similarly to cp_parser_base_specifier the typename diagnostics
because we don't know whether typename [: will be valid or not
- it could be splice-scope-specifier and in that case typename
is not valid, or splice-type-specifier, in which case it is valid
but not required.  And calls cp_parser_splice_type_specifier too
when nested-name-specifier nor :: don't appear.

2026-04-21  Jakub Jelinek  <jakub@redhat.com>

	PR c++/124944
	* parser.cc (cp_parser_mem_initializer_id): Parse
	splice-type-specifier.

	* g++.dg/reflect/splice13.C: New test.
	* g++.dg/reflect/splice14.C: New test.

Reviewed-by: Jason Merrill <jason@redhat.com>
This commit is contained in:
Jakub Jelinek
2026-04-21 19:32:22 +02:00
committed by Jakub Jelinek
parent c5b8140b27
commit 80958ebae3
3 changed files with 57 additions and 5 deletions

View File

@@ -19980,14 +19980,17 @@ cp_parser_mem_initializer_id (cp_parser* parser)
bool template_p = false;
tree id;
cp_token *token = cp_lexer_peek_token (parser->lexer);
cp_token *typename_token = nullptr, *splice_token = nullptr;
/* `typename' is not allowed in this context ([temp.res]). */
/* `typename' is not allowed in this context ([temp.res]), unless
it is part of splice-type-specifier. Defer diagnostics for now
because it needs to be diagnosed if followed by splice-scope-specifier
but not when followed by splice-type-specifier. */
if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TYPENAME))
{
error_at (token->location,
"keyword %<typename%> not allowed in this context (a qualified "
"member initializer is implicitly a type)");
typename_token = cp_lexer_peek_token (parser->lexer);
if (cp_parser_next_tokens_start_splice_type_spec_p (parser, true))
splice_token = cp_lexer_peek_nth_token (parser->lexer, 2);
cp_lexer_consume_token (parser->lexer);
}
/* Look for the optional `::' operator. */
@@ -20014,6 +20017,12 @@ cp_parser_mem_initializer_id (cp_parser* parser)
/*type_p=*/true,
/*is_declaration=*/true)
!= NULL_TREE);
if (typename_token && cp_lexer_peek_token (parser->lexer) != splice_token)
/* Emit deferred diagnostics for invalid typename keyword if
cp_parser_nested_name_specifier_opt parsed splice-scope-specifier. */
error_at (typename_token->location,
"keyword %<typename%> not allowed in this context (a qualified "
"member initializer is implicitly a type)");
if (nested_name_specifier_p)
template_p = cp_parser_optional_template_keyword (parser);
/* If there is a `::' operator or a nested-name-specifier, then we
@@ -20026,6 +20035,14 @@ cp_parser_mem_initializer_id (cp_parser* parser)
/*check_dependency_p=*/true,
/*class_head_p=*/false,
/*is_declaration=*/true);
else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SPLICE))
{
/* Parse C++26 splice-type-specifier. */
id = cp_parser_splice_type_specifier (parser);
if (id == NULL_TREE)
return error_mark_node;
return id;
}
/* Otherwise, we could also be looking for an ordinary identifier. */
cp_parser_parse_tentatively (parser);
if (cp_lexer_next_token_is_decltype (parser->lexer))

View File

@@ -0,0 +1,23 @@
// PR c++/124944
// { dg-do compile { target c++26 } }
// { dg-additional-options "-freflection" }
struct A {};
constexpr auto x = ^^A;
struct B : [: x :] {
B () : typename [: x :] () {}
};
struct C : [: x :] {
C () : [: x :] () {}
};
namespace D {
struct E {};
}
constexpr auto y = ^^D;
struct F : [: y :]::E {
F () : [: y :]::E () {}
};

View File

@@ -0,0 +1,12 @@
// PR c++/124944
// { dg-do compile { target c++26 } }
// { dg-additional-options "-freflection" }
namespace D {
struct E {};
}
constexpr auto y = ^^D;
struct F : [: y :]::E {
F () : typename [: y :]::E () {} // { dg-error "keyword 'typename' not allowed in this context \\\(a qualified member initializer is implicitly a type\\\)" }
};