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

            Bug ID: 127857
           Summary: [GCC C++ contracts] a contract condition that spans a
                    whitespace-only line: ICE in `char_span::subspan`
                    (file-cache.h:52) from `get_source_text_between`
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: 0599jiangyc at gmail dot com
  Target Milestone: ---

**Repo:** gcc-mirror/gcc · **Commit:** `d373724d1` (master, 2026-09-17) ·
**Build:** `--enable-languages=c,c++ --disable-bootstrap
--enable-checking=yes,extra,rtl` (x86_64-pc-linux-gnu, Ubuntu 22.04)


## What happens

`build_comment` (cp/contracts.cc) asks `get_source_text_between` (input.cc) for
the source text of
the contract predicate. For a multi-line range, the loop over the lines
"combines spaces at the
beginning of later lines":

```c++
          for (off = 0; off < line.length(); ++off)
            if (line[off] != ' ' && line[off] != '\t')
              break;
          if (off > 0)
            {
              obstack_1grow (&buf_obstack, ' ');
              line = line.subspan (off, line.length() - off);
            }
```

When a middle line consists only of blanks, `off == line.length ()`, and
`subspan (off, 0)` trips `gcc_assert (offset < (int)m_n_elts)`. (The first-line
branch has the
same shape: `line.length () < off` lets `off == line.length ()` through to
`subspan`.) The
contract itself is valid; the source text is only used for the violation
message.

```
gcc-contract-condition-whitespace-only-line.cc:3:5: internal compiler error: in
subspan, at file-cache.h:52
    3 |   > 0);
      |     ^
0x3046dcb internal_error(char const*, ...)
        gcc/diagnostic-global-context.cc:787
0xdd9a1e fancy_abort(char const*, int, char const*)
        gcc/diagnostics/context.cc:1813
0xddca85 diagnostics::char_span::subspan(int, int) const
        gcc/diagnostics/file-cache.h:52
0xddca85 get_source_text_between(diagnostics::file_cache&, unsigned long,
unsigned long)
        gcc/input.cc:181
0xe9226b build_comment
        gcc/cp/contracts.cc:2061
0xe9469e grok_contract(tree_node*, tree_node*, tree_node*, cp_expr, unsigned
long)
        gcc/cp/contracts.cc:2136
0xff25d3 cp_parser_function_contract_specifier
        gcc/cp/parser.cc:34112
0xff25d3 cp_parser_function_contract_specifier_seq
        gcc/cp/parser.cc:34156
0xfed726 cp_parser_direct_declarator
        gcc/cp/parser.cc:26926
```

## Reproducer

`gcc-contract-condition-whitespace-only-line.cc` (line 2 is two spaces):

```c++
void f (int x) pre (x

  > 0);
```

```bash
g++ -fsyntax-only -std=c++26 gcc-contract-condition-whitespace-only-line.cc
```

Also ICEs with `-std=c++20 -fcontracts`. An empty line 2 (no blanks) is fine.
Inside a class the same predicate ICEs through
`cp_parser_late_contract_condition` → `update_late_contract` → `build_comment`.

## Expected

The condition text `x > 0` (or a fallback to `expr_to_string`); never an
assertion. Checking `off < line.length ()` before both `subspan` calls, or
skipping a blank-only middle line the way an empty one is skipped, would do.

## Notes

Found by fusion: in a member contract `pre (px->m ())` from a g++.dg contracts
test, the fusion substituted the macro `O_NOATIME` (defined 156 lines earlier
by the other seed) for `px`. The range then starts at the macro's *spelling*
location — `expand_location_to_spelling_point` — and runs from the `#define`
line to the contract, across blank-only lines. So a second trigger, with no
whitespace inside the condition itself, is a condition that begins with an
object-like macro defined above a whitespace-only line:

```c++
#define M 1

int f (int x) pre (M
 > 0);
```

(line 2 is two spaces; verified to ICE the same way). Using the expansion point
rather than the spelling point would be the more fundamental fix for the
garbled comment text. Neither seed alone crashes. 3/3 reproductions,
empty-input control clean. `gcc/input.cc` is unchanged upstream since the
checkout; the two `contracts.cc` commits since (PR127281, PR127295) do not
touch `build_comment`.

Reply via email to