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)

Reply via email to