I'm sure there's a good reason why this is hard, but I'm a little surprised that this isn't caught by static analysis. Sure enough, I can't get MSVC Code Analysis to complain about the example with different inline functions.
I'm sure there's a good reason why this is hard, but I'm a little surprised that this isn't caught by static analysis. Sure enough, I can't get MSVC Code Analysis to complain about the example with different inline functions.
However a bunch of IFNDR in C++ covers cases where Rice's Theorem applies, so your static analysis cannot help.
Rice says that if you language has non-trivial semantic requirements (not just syntactic ones) that's Undecidable, which means there cannot be any algorithm which always says whether any program does or does not satify the requirement.
C++ is reluctant to lose any programs which might potentially satisfy its requirements, so instead it chooses to say that they must compile unless the compiler can prove they do not, which by Rice we know cannot be 100% effective. In practice, why even bother? Many compilers don't put much work into trying, it might help users but it's not a compliance requirement so it's often not a priority.
Rust's choice here (thus avoiding IFNDR) is to reject programs unless it can show that they meet the requirements. Even if you can convince a PhD team that your program satisfies the requirements, that won't help, rustc needs to be convinced or it won't compile. Unlike C++ the incentives here are aligned, that's what Rust's "Non-lexical lifetimes" change was about - the compiler got smarter, allowing previously unacceptable Rust to compile.
I wonder if a C++ compiler that does that would be feasible. A lot of this check avoidance is due to the expense of compilation when the language was created (and, back then, C++ itself was sometimes considered prohibitively expensive). The Rust compiler is slow because it’s doing a lot of work (the same reasoning applied comparing C++ to C - and C++ started as a transpiler compiling C++ to C first.
This is commonly assumed but the evidence suggests otherwise. The borrowck for example is not a large fraction of the compiler runtime.
So if you've just assumed this is correct, rather than you've collected evidence, I suggest your foundational argument needs re-evaluating.
As to the idea that you can assess C++ instead, not really, you need invasive language changes, Sean Baxter's "Circle" compiler provides a whole bunch of experimental features including his take on borrow checking, but you can't meaningfully check existing C++, it just gets rejected, you have to write your new checkable C++ instead.
That was more or less where I was going. Existing code is mostly hopeless, so, maybe, a dialect that makes more of the developer’s intentions explicit would provide enough for more checking. I don’t have evidence that Rust’s checking is why it’s slow, but I assumed it was a good guess.
I don't think a C dialect is a good idea. You want a blank slate, on which you can begin with your foundational concerns (e.g. constant time evaluation) rather than starting from C. There may be practical reasons to prefer how C spells things, the way Rust looks like a semi-colon language even though it's mostly an ML, but the core language isn't helpful.
Static analyzers tend to be structurally unable to detect ODR violations - either they only analyze one translation unit at a time, or their cross-module analysis itself relies on the ODR being obeyed.
Note that in case of shared libraries (DLLs, .so), the OS loader/linker would have to perform that analysis.