https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98882
--- Comment #2 from Jakub Jelinek <jakub at gcc dot gnu.org> --- It isn't just on empty files, but on any files that don't end with newline. I believe the /* Files always end in a newline. We rely on this for character peeking safety. */ gcc_assert (buffer->rlimit[-1] == '\n'); assert is just wrong. It isn't buffer->rlimit[-1] that should be '\n', it is actually buffer->rlimit[0] that should be end of line terminator, buffer->rlimit[-1] is the last character of the buffer, so whatever is in the buffer (and empty files have buffer->cur == buffer->rlimit and thus have even nothing there at all so rlimit[-1] access is invalid). Another problem is though that even buffer->rlimit[0] == '\n' is not guaranteed. libcpp/charset.c has: /* If the file is using old-school Mac line endings (\r only), terminate with another \r, not an \n, so that we do not mistake the \r\n sequence for a single DOS line ending and erroneously issue the "No newline at end of file" diagnostic. */ if (to.len && to.text[to.len - 1] == '\r') to.text[to.len] = '\r'; else to.text[to.len] = '\n'; So, I think we can assert that buffer->rlimit[0] == '\n' || buffer->rlimit[0] == '\r' and in the case '\r': /* MAC line ending, or Windows \r\n */ if (*pos == '\n') pos++; /* FALLTHROUGH */ and else if (*pos == '\r') { if (pos[1] == '\n') pos++; pos++; goto next_line; } goto dflt; and case '\r': if (*pos == '\n') pos++; (twice) cases unfortunately need to compare pos against limit.