https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127788
Bug ID: 127788
Summary: [C++17] GCC incorrectly applies typename from
declarator to those after it in a using-declaration
list
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Keywords: rejects-valid
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: gabravier at gmail dot com
Target Milestone: ---
namespace N { struct T {}; }
void h();
using typename N::T, ::h;
template<class B> struct D : B {
using typename B::type, B::value;
int f() { return value; }
};
This code is, as far as I can tell, perfectly valid C++17. Clang is fine with
it. Trunk GCC (with -std=c++17) is not:
<source>:3:24: error: expected nested-name-specifier before 'h'
3 | using typename N::T, ::h;
| ^
<source>: In member function 'int D<B>::f()':
<source>:7:25: error: expected primary-expression before ';' token
[-Wtemplate-body]
7 | int f() { return value; }
| ^
Reproduces identically on every version of GCC down to GCC 7 (GCC 6 and before
do not appear to implement using-declaration lists with more than one
declarator).
The faulty code looks to me to be here:
/* Parse a using-declaration, or, if ACCESS_DECLARATION_P is true, an
access declaration.
using-declaration:
using typename [opt] :: [opt] nested-name-specifier unqualified-id ;
using :: unqualified-id ;
access-declaration:
qualified-id ;
*/
static bool
cp_parser_using_declaration (cp_parser* parser,
bool access_declaration_p)
{
cp_token *token;
bool typename_p = false;
bool global_scope_p;
tree identifier;
tree qscope;
int oldcount = errorcount;
cp_token *diag_token = NULL;
if (access_declaration_p)
{
diag_token = cp_lexer_peek_token (parser->lexer);
cp_parser_parse_tentatively (parser);
}
else
{
/* Look for the `using' keyword. */
cp_parser_require_keyword (parser, RID_USING, RT_USING);
again:
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
/* See if it's `typename'. */
if (token->keyword == RID_TYPENAME)
{
/* Remember that we've seen it. */
typename_p = true;
/* Consume the `typename' token. */
cp_lexer_consume_token (parser->lexer);
}
}
...where typename_p is never reset to false, despite the C++ standard dictating
that it only applies to the declarator it appears in. I am fairly certain
adding `typename_p = false;` right after the `again:` label would fix this
issue (I have not actually tested this).
(also, AIUI, that comment above the function looks outdated to me)