While the C++26 variant of contracts doesn't provide the benefits of its static-analysis-centric ancestor from 2014, and therefore isn't as consequential a feature, it still provides value to some and is absolutely not important enough to be worth killing a standard. Concepts do not provide this value.
In any case, it's simply false that C++26 contracts are unnecessary because we have concepts, yet this claim is the thrust of your initial comment.
[–] jandrewrogers 4 days ago
[–] quotemstr 4 days ago
> People figured out how to do with C++20 better than what contracts promised.
What contracts used to promise is irrelevant. The contracts on the table modernized runtime assertions and are irrelevant to concepts, constexpr, and related facilities.
Is the contracts feature more useful than C's assert()?
If you want to argue that we're better off with the old macro, do so directly. Concepts, in this context, amount to irrelevant distraction.
[–] jandrewrogers 4 days ago
Why would anyone that already ditched C asserts use C++ contracts when it can’t express what the alternatives already can? This isn’t a new problem. People were creating more expressive assertion replacements than contracts offers a decade ago.
C++ contracts are nerfed. It is less expressive and capable than what anyone that cares about these things already uses. So who is expected to use it?
[–] rramadass 4 days ago
You have not understood the C++26 contracts proposal.
It introduces three new syntax viz. "Pre" and "Post" in function signature declaration and a separate "contract_assert" which you can use anywhere in the code path.
It is the "Pre"/"Post" that is useful here. Since it is part of function signature declaration, a static analyzer can easily make sure that all function calls satisfy the contract else you get warning/error. The compiler on its part can inject appropriate code at the function boundary points to do runtime checking etc.
You cannot do this with assert macros. If you wanted something like this earlier, you would have had to change the compiler to emit modified function prologue/epilogue and build your own contract framework which few people had the capability to do.
You can now see the immediate benefits particularly for standard library (and other) code hardening needs. Many UB circumstances can be easily flagged at compile time without too much complexity.
Finally, note that if you use compile-time switch "ignore" to turn-off everything, only the runtime checks are elided (so you do not lose any performance) but the static analyzer can still catch all possible inconsistencies at compile-time thus giving you the best of both worlds.
[–] William_BB 4 days ago
How realistic is the "all function calls" part? Maybe I just don't see the bigger picture, but I would expect most function calls arguments to only be known at runtime, which of course makes static analysis not possible?
[–] rramadass 3 days ago
Obviously "all function calls" phrase cannot be absolute, but most can be caught with the analyzer giving one of proved/disproved/unknown result. The reason all cannot be caught is because pre/post are logical predicates on sets of values and are not part of the typing system.
To understand that, you have to know how a static analyzer works and specifically two of its functions, "Data Flow Analysis" and "Symbolic Execution".
It is Symbolic Execution that does static checking without the need for the actual runtime values to be known. It basically "executes" the code mathematically, building logical predicates over "sets of values" (indicated by the type of the variable) and thence trying to prove/disprove the formula using a solver. Note that the program is being "run" abstractly in the mathematical world using quantifiers just like you do in a mathematical proof.
If you know Hoare triples, and Dijkstra's WP-Calculus you know how to do this mechanically.
So the analyzer does two things; a) in the function itself it assumes pre is true and tries to prove post and b) at the call site it makes sure the paths leading up to the invocation satisfy the pre/post args/result needs thus chaining everything through the codebase. Both are done using symbolic execution. If it cannot prove/disprove the assertions, it remains silent (i.e. "unknown" result above) and leaves it to runtime checks to catch the violations.
Some resources:
1) How Static Analysis works - https://pvs-studio.com/en/blog/posts/1048/
2) A Gentle Introduction to Symbolic Execution for Software Security - https://can-ozkan.medium.com/a-gentle-introduction-to-symbol...
3) Scaling Symbolic Execution to Large Software Systems - https://arxiv.org/abs/2408.01909
[–] William_BB 3 days ago
[–] rramadass 4 days ago
Do you have any information as to why they shifted the focus from static analysis to runtime checks? I only remember that it was based on some Microsoft annotation language or something.
[–] bluGill 4 days ago
Already the compiler is allowed to refuse to compile any code if it would guaranteed fail the contract. The next step is to see how far we can go with that static analysis and what those static analysis tools need to be better. However, this requires people having contracts in fairly widespread use so they can start trying their new ideas to see what happens when they hit the real world.
[–] rramadass 4 days ago
See my comment here - https://news.ycombinator.com/item?id=50361418
However, i also understand the reasoning behind this C++26 contracts proposition - https://news.ycombinator.com/item?id=50364593
While i understand the rationale here, i can also see the problems it might lead to in the future. Correctness concerns should be an all-or-nothing proposition, you cannot do it piecemeal. Hence whether you want to call it "profiles" or not, i would like a Eiffel/Ada-SPARK like system which gives me complete static compile-time analysis and verification along with separate run-time guarantees.
The contracts committee should go back to the drawing board and come up with a more comprehensive solution.
This is a real chance to finally realize the "Safe C++" dream! Ada did it and so can C++.
Contracts were sold with the idea of aiding compile-time verification, a noble purpose. The combination of concepts + constexpr allows you to do deeper compile-time verification today by other means than contracts can check. It isn’t obvious that this will ever not be the case. This means contracts is relegated to being another flavor of C asserts.
C++26 contracts are arguably unnecessary because we can achieve the same results other ways, sometimes with stronger properties. I’m a proponent of features that clean up the syntax but C++ contracts look like a nod to people that still use legacy C asserts. That’s fine but it also perpetuates an inadequate model of what assertions should be capable of expressing.
People figured out how to do with C++20 better than what contracts promised.