[1] https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.h...
[1] https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.h...
It does show potential, and I hope it improves in future. Be nice to have a libre version of coverity one day.
Edit: Output from compiling nbdkit upstream with -fanalyzer: https://paste.centos.org/view/8381f926
I've been meaning to formally prove one of our internal "mini libraries" using Frama-C. If we did that then no one would be able to complain about bugs in it :-)
Compiling with clang is much, much slower but it finds interesting errors sometimes. I only had to ifdef out one code section that it couldn’t understand (something about indexing an array with a constexpr function returning an enum class from a template parameter).
It depends on the project, but this one and another project I made work are both 100k lines of C++ and took half a day to setup. It was worth it imo.
It might have something to do with a heavy usage of templates in a few files, I don't know.
Clang's autovectorization is better in a few functions I disassembled but otherwise the generated code doesn't benchmark any faster, it's better in some places, worse in others.
But TCC is 10x faster than both of them.
In GCC it's a compiler flag. In LLVM it's a convoluted process automated by either an irritating perl/python script (The python one isn't included by default for some reason despite being far more common these days) or AN ENTIRE WEB SERVER TO OUTPUT TEXT (Which thankfully isn't included by default).
I'm sure you could set it up in make but since my projects aren't large scale enough to really need it I can't be bothered, even if it's one of those things you'll probably only need to write once and can just copy and paste ad-infinitum.
-% scan-build10 make
scan-build: Using '/usr/local/llvm10/bin/clang-10' for static analysis
...
scan-build: No bugs found.
What could ever have driven them to such a long and convoluted setup process? clang --analyze --analyzer-output text ...
Will print the entire analysis tree in the same format as regular diagnostics.Hopefully in a future version the kinks are ironed out and we can just use the flag without any hassle. It's like if we needed to still manually link our files with ld before compiling them instead of clang auto doing it.
Basically this replicates what clang-tidy did
Biggest advantage I see is it's integrated into the compiler and so sees the same things the compiler does.
Having gcc do this out of the box helps people port their experience/skills with static analysis to other companies.
We already have clang-tidy and I like it too but it's nice to have a fall-back to compare when one produces a strange result. And a bit of competition is always good to have between such projects. And on most big projects it's not like you can just change the build system to use another compiler.
Also I found some interesting cases which valgrind didn't see because it was in an unreachable branch.
You could do something like that at runtime though, but then you have Valgrind, basically.
This is a good thing, because it makes lifetimes and ownership explicit and visible in the code. It serves the similar purpose as type annotations in function signatures.
> Or having multiple mutable pointers/reference to the same object
Sure you can have that with `unsafe`. And this is a good thing, because multiple mutable pointers to the same object is at best bad coding practice that leads to unsafe code, and you should avoid that in any language, including the ones with GC. Working with codebases where there are multiple distant things mutating shared stuff is a terrible experience.
If a C/C++ version of "borrow checker" could mark such (anti)patterns at least as warnings, that would bring a lot of value.
However if I sounded like I wanted to belittle Rust I really expressed myself poorly, I love the language and hope it'll eventually become the new C++. If anything I was attempting to make the opposite point: you can't fix C/C++'s flaws with a smarter compiler. We're not two releases of GCC away from having safe C without having to change anything.
The parent was talking about the borrow checker so I only was talking about safe Rust code. Obviously if you consider that the entire C/C++ codebase is in a big unsafe {} block it'll work... because it won't do anything at all.
It's important to understand that unsafe doesn't turn off the borrow checker or disable any other of Rust's safety checks: if you use a reference in unsafe code, it will still be checked. The unsafe keyword only gives you access to these four features that are then not checked by the compiler for memory safety.
I understand the benefits and the risks of them, and understand how Rust prevents both, but I dont yet understand why it's bad practice, and am interested to learn why.
If N unrelated (or loosely related) things can mutate the same object, then you get a O(x^N) explosion of potential mutation orders and in order to understand that, you need to understand all the (sometimes complex) time-relationships between these N objects. This gets even much worse when some of these objects are also pointed from M other objects...
On the flip side, in case of using a simple unique_ptr (or a similar concept), this trivially reduces to a single sequence of modifications.
There are compiler optimisations you can do if compiler knows about aliasing, but that’s not so much a software authorship problem. There are some curly problems with passing aliased mutable pointers to a function written for non-aliased inputs, like memcpy and I imagine quite a lot of other code.
But common to all of these things is that it’s pretty hard to figure out if the programmer is the one who has to track the aliasing. In hashmap pointer invalidation, your code might work perfectly for years until some input comes along and finally triggers a shift or a reallocation at the exact right time. (I know this — I recently had to write a lot of C and these are the kinds of issues you get even after you implement some of Rust’s std APIs in C.)
No, it is additional burden. If it was possible to do it without annotations, you bet we would do it!
C codebases then follow certain defensive programming customs to avoid reading uninitialized or out of bounds memory, at the cost of some performance. This is the right trade-off in C but, funnily enough, the more restrictive borrow checker has the opposite effect as you can give out inmutable and mutable references with wanton abandon because they get checked for unsafe behavior. It's the same difference as a a gun where the best practice is to keep it unchambered at all times to avoid the risk of a misfire, and a more modern gun with a safety: it's one more thing to think about but it actually smoothes the operation.
The restriction of immutability spares the programmer from worrying about whether unknown parts of the codebase are going to decide to mutate an object.
JavaScript's single-thread restriction (not counting web-workers) closes the door on all manner of nasty concurrent-programming problems that can arise in languages that promote overuse of threads. (Last I checked, NetBeans uses over 20 threads.)
Back to the example at hand, C has no restrictions, but that hobbles the programmer when it comes to reasoning about the way memory is handled in a program. It's completely free-form. Rust takes a more restrictive approach, and even enables automated reasoning. (Disclaimer: I don't know much about Rust.)
Rust supports MaybeUninit<> for the former example, and unsafe raw pointers for the latter. It needs unsafe because these patterns are not safe in the general case and absent an actual proof of correctness embedded in the source code, a static analysis pass can only deal with the general case.
I've expressed myself poorly in my original comment and apparently it looks like I was criticizing Rust but I wasn't. I was just pointing out that safety didn't come "for free" by toggling a compiler flag, you have to change the way you code some things. If C and C++ were to become safe languages, code would need to be rewritten using things like MaybeUninit, split_at_mut, RefCell etc...
"Update on C++ Core Guidelines Lifetime Analysis. Gábor Horváth. CoreHard Spring 2019"
What about with a constrained (not necessarily general purpose) AI with the expertise of Scott Meyers, Andrei Alexandrescu, Herb Sutter and Alexander Stepanov?
https://herbsutter.com/2018/09/20/lifetime-profile-v1-0-post...
"Update on C++ Core Guidelines Lifetime Analysis. Gábor Horváth. CoreHard Spring 2019"
https://www.youtube.com/watch?v=EeEjgT4OJ3E
While it might never be Rust like due to language semantics, it is way better than not having anything.