https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127782

            Bug ID: 127782
           Summary: [C++20] Empty init-statement in range-for (`for (;
                    decl : range-init)`) is incorrectly rejected
           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: ---

int a[1];
void f() { for (; int x : a); }

My reading of [stmt.ranged]/[stmt.pre] is that this is a valid range-based for
with an empty init-statement (from expression-statement -> `expression(opt)
;`). Clang agrees. GCC does not:

<source>: In function 'void f()':
<source>:2:25: error: found ':' in nested-name-specifier, expected '::'
    2 | void f() { for (; int x : a); }
      |                         ^
      |                         ::
<source>:2:23: error: 'x' has not been declared
    2 | void f() { for (; int x : a); }
      |                       ^
<source>:2:28: error: qualified-id in declaration before ')' token
    2 | void f() { for (; int x : a); }
      |                            ^
<source>:2:28: error: expected ';' before ')' token
    2 | void f() { for (; int x : a); }
      |                            ^
      |                            ;

This function seems to think it should be able to parse it:

/* Parses a for-statement or range-for-statement until the closing ')',
   not included. */

static tree
cp_parser_for (cp_parser *parser, bool ivdep, tree unroll, bool novector)
{
  tree init, scope, decl;
  bool is_range_for;

  /* Begin the for-statement.  */
  scope = begin_for_scope (&init);

  /* Maybe parse the optional init-statement in a range-based for loop.  */
  if (cp_parser_range_based_for_with_init_p (parser)
      /* Checked for diagnostic purposes only.  */
      && cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
    {
      tree dummy;
      cp_parser_init_statement (parser, &dummy);
      if (cxx_dialect < cxx20)
        {
          pedwarn (cp_lexer_peek_token (parser->lexer)->location,
                   OPT_Wc__20_extensions,
                   "range-based %<for%> loops with initializer only "
                   "available with %<-std=c++20%> or %<-std=gnu++20%>");
          decl = error_mark_node;
        }
    }

  /* Parse the initialization.  */
  is_range_for = cp_parser_init_statement (parser, &decl);

  if (is_range_for)
    return cp_parser_range_for (parser, scope, init, decl, ivdep, unroll,
                                novector, false);
  else
    return cp_parser_c_for (parser, scope, init, ivdep, unroll, novector);
}

...but the "for diagnostics purpose only" cp_lexer_next_token_is_not certainly
seems to do more to the code's behaviour than mere diagnostics x) (I believe if
it were simply removed this would just fix this, although I have not tested it)

P.S: the following

void f() {
  template for (; auto x : {1, 2, 3}) { (void)x; }
}

also has the same issue, I believe for the exact same reason (an identical
conditional) in cp_parser_expansion_statement.

Reply via email to