But I think a safe C that has similar guarantees as Rust is quite possible and we can gradually move there.
In almost all cases for preconditions, that's solvable by better practices that could be identified in a review. It's the same underlying problem as C, but dramatically more manageable.
And of course, any C is liable to be misused in C++ where the committee's current position is that the problem is unsolvable.
You can't fix this with minor changes in the language or improved tooling. You need a complete overhaul of how the standards and implementations deal with UB.
If there were actually an appetite to eliminate it, I suspect we could probably eliminate 70% of J.2 with a few straightforward changes. Many of these aren't completely amenable to static analysis, like signed/shift bounds, but they're workable at the language level. The members of the C++ committee I've talked to were skeptical about the actual chances here though.
The rest of annex J is at least partially addressed with existing efforts like checked C and Coral ("constraints"), so at least there's a path forward with tooling.
Currently, all of that work has to come with a little asterisk saying "except for undefined behavior". I can't constrain where UB might be in the system, or how it might violate the safety model. In short, if I do my job properly I shouldn't assume C/C++ semantics at all. Obviously that's not reasonable, so everyone goes around pretending this isn't an issue to their safety models and their regulatory filings and so on.
Obviously I think that's wrong, but there haven't been ways to address it in C and C++. It's possible in Rust.
But frankly, if you’re going through all that trouble, it’s probably worth considering a newer language that fixes those things today. I mentioned Rust as one example, not the end-all solution to all problems. Nim also plays on the same space and it’s quite nice. The broader point is that I believe it’d be easier to start writing new code in a different language that can easily call existing C code.
First, all those new languages also have various downsides, and trade-offs, which may make them a less than ideal choice for many applications. Second, there are an incredible amount of existing C code. We will not get rid of C in the next at least 50 years even if we wanted to (not that I want to). Incrementally improving that code is far more useful than adding other languages into the mix.