The idea that you can just stop writing code with undefined behavior is absurd.
Dynamic linking vs. static linking is irrelevant.
If this is true, then this should also apply to Rust or other proven languages: your program will still run in a dynamic environment, and probably calling into all kind of OS and third party libraries. Unless... you write your own (unsafe) wrappers around libraries, trying to catch, as much as possible, errors that can filter into the Rust (or whatever) code. But still, someone has to write these wrappers. If it's not you, you have to trust on somebody else's work, and this is a runtime check that will slow your program down.
Windows has different requirements for invoking foreign code vs. Linux for example.
The foreign code may have requirements on how it should be called, but it is not C's undefined behavior to invoke an OS system call incorrectly because the OS is not regulated by the C standard.
You can formally prove that a program has no undefined behavior, and it can still have lots of errors when actually run.
a.c
b.c c.c All three translation units do not have undefined behavior, so no compiler should report it.At the time of compilation of a.c, foo does not have a definition. If it is linked with b.c's object file, the program would have undefined behavior. But if it is linked with c.c's object file, there is no undefined behavior. Object files can't tell who they're going to be linked with.
And when b.c is compiled, foo only exhibits undefined behavior if somebody else calls it with an invalid pointer. It's not b.c's problem.
Further, a.c and b.c might be compiled with different C compilers(very common), so you can't rely on any kind of caching of type information or state-tracking across translation units.
When the linker is run, the undefined behavior is instantiated to create an incorrect binary, but linkers aren't regulated by the C standard so it wouldn't be appropriate to include a UB checker in one.
To do what you're saying, you'd need a language designed for whole-program analysis. Rust and Haskell do this, and they both require you to have source code for all your dependencies so they can recompile them each time. It'd be quite the design change for C, and not something compiler writers can "fix" by themselves.