Well, since the Rust borrow check is basically a static analysis that's always on and is required to pass for the compiler to emit code, we've put a lot of thought into how to make it as programmer friendly as possible. The final trick that seemed to got it to fall into place was restricting data to only one mutable reference at all times—this was a restriction that's easy enough to understand and can be readily explained through error messages and tutorials. There's still a learning curve, of course, but I think we've got the system down to a reasonable level.
If a program does anything important, if it handles secrets, if it enforces invariants ("the robots arm must not be out of the safe area"), etc. then other aspects of the programs logic may be just as safety critical as memory safety. Frama-C can prove other things about program saftey: this calculation cannot overflow, this error state can never be reached, this operation will complete within X steps... even if the results of non-safety don't result in anything to do with memory errors.
Some of the programmers expectations can be extracted from the program— signed integers won't overflow, loops (unless otherwise annotated) will always terminate, pointers will not be extended outside of their objects, a pointer to an integer won't be called as a function pointer, behavior will not depend on the order function arguments are evaluated in, etc. Some of the expectations must be expressed as assertions.
Once as many of the programmers expectations are understood the analysis needs to know the invariants that allow them to be true. Some of these can be extracted, some must be asserted.
The gap that exists today is that a lot that could be extracted (e.g. by another programmer reading in a context free way, with high probability of success) can't be soundly extracted because there is a lot of technically valid behavior which is just usually unlikely to be what anyone intended. This means that anyone attempting to apply tools like frama-c has to spend a lot of time making the implicit assumptions explicit with assertions. Thats why I was saying that something like making singed overflow defined may be a step backwards because it takes a whole class of "you couldn't possibly have intended this behavior" back to "maybe you meant this".
Having a borrow check is important, because otherwise you're left with iterator invalidation and numerous other memory safety holes.
When its unable to prove a range the feedback you get from the solvers is inscrutable enough that its very hard to figure out what additional data it would need to satisfy the analysis. (Sort of like parsing C++ compiler template related errors)
I'd hoped that language features like the typestate stuff that used to be in Rust would someday make the work required to use sound analysis tools in production code smaller. I'm not sure if much thought has been given to what kinds of accommodations languages could give to ease static analysis while still being programmer friendly.
It seems that newer languages have actually moved away from analysis friendliness in some respects, however. E.g. in C a signed overflow is always a bug so if analysis can prove one is possible you have something to fix. Several modern languages have defined signed operations to wrap and so that obvious safety test is no longer available. (You could define in your own code that it should never wrap, effectively writing in a subset of the language, but as soon as you call into third party code you never know if an overflow was intended and safe or not— not without extensive analysis)