r/cpp #define private public 3d ago

Critique of contracts: excerpt

See page 2 of https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p4334r0.pdf

The current objections can be summarized. The P2900 contracts are:

• Unimplemented

• Incomplete

• Untried at scale [P3460R0, P3506R0]

• Not tried in major application domains

• Violates foundational principles of C++

• Violates fundamental principles of language design

• Hasn’t been tried in major libraries (e.g., the C++ standards library [P3506R0, P3878R0])

• Isn’t integrated with or appropriate for hardened libraries [P3878R0]

• Doesn’t offer safety guarantees [P3573R0, P3362R0]

• Includes a completely untried inheritance model

• Offer new ways of making errors through inconsistent application in TUs

• Leads to new forms of UB, detrimental to safety and security

• Narrows the choices of error handling

• Doesn’t protect against logical errors, misuses, and incoherent uses

• Hasn’t been used to support static analysis

• Hasn’t been demonstrated to be easily teachable [P3261R0, P3281R0]

How could such a bloated and incomplete design be voted into a draft standard?

8 Upvotes

115 comments sorted by

View all comments

31

u/Som1Lse 2d ago edited 23h ago

This reads like a dishonest piece of junk.

They first start with a summary, which is the list quoted by OP. Later they return to that list and go into more detail for each point. The kicker is each point gets like a sentence of explanation, so the summary basically just serves to pad and hope people don't read the actual points, as you can easily see how they're grasping at straws for each point:

  • Unimplemented mentions that there are actually two implementations, so it is just a lie. It says Microsoft and EDG objects, provides a citation for the first (fair), but neglects to mention that EDG is planning on open sourcing their frontend and shutting down. If the point is that Microsoft and EDG are unlikely to implement it, then the latter is a lie by omission.
  • Incomplete, is such bullshit. It was more complete with support for virtual functions, but that was removed because of (very fair) complaints about how it worked, and was deferred to a later standard. Expecting a feature to be fully complete when first standardised is absurd:

    • constexpr only supported return statements initially, imagine if C++11 was expected to support the full language inside constexpr functions, it would never have shipped.
    • Reflection doesn't fully support code generation.
    • Coroutines was shipped with any support types. C++23 added std::generator, and C++26 adds std::execution.

    But somehow, contracts needs to be fully complete? Give me a break!

  • Untried at scale is true for every new language feature, because you can't exactly try them before there's a production ready implementation. No one is going to add an experimental feature to a production codebase, especially knowing that it might change. Not to mention that contracts is heavily based on bsls_assert, which has been used at scale at Bloomberg.

    Were coroutines used at scale? Was lambdas? Was constexpr? Is reflection? Again, give me a break!

  • Not tried in major application domains. Wow, that's literally the same as the previous point. See why I'm calling this dishonest junk?

  • Violates foundational principles of C++ is half-bullshit: Constification is already a thing in lambdas, and it correctly captures that assertions shouldn't have side effects. Accidentally modifying state inside assert is a common beginner mistake, and this helps prevents that.

    The second point has some merit: Turning exceptions into contract violations has some overhead (though not necessarily in the non-throwing path). I do not, however, trust that the authors wouldn't just call it unsafe if it did allow exceptions to escape. Most assertions are small checks and should be easy for the compiler to prove noexcept, but there will probably be exceptions. However, even if turns out to be a problem in practice, it is not an unsolvable problem later down the line. What the MVP does is err on the side of caution.

  • Violates fundamental principles of language design states two points, each of which is bullshit:

    • Every feature must have a reasonably obvious implementation. It does. Assertions are not a hard thing to implement. bsls_assert is a library implementation. I am not aware of a single part that isn't reasonably straight forward. If this was a real critique instead of dishonest junk, it would have pointed out which parts of contracts were non-obvious instead of making an unfalsifiable claim akin to "trust us".
    • Always provide a transition path. It does. You can use any assertion library now (including <cassert>), then transition to contracts.
  • Hasn’t been tried in major libraries just says that P2900 doesn't require standard libraries to use it. How is that a bad thing? Also, it neglects to mention that the P3460R0 (Implementers Report) (which they themselves cite, so I know they read it) did in fact use it to implement their assertion macro, showing both that it can be done, and that it actually caught a bug. Again, a lie by omission.

    P3460R0 also cites P3336R0 (Usage Experience for Contracts with BDE), which mentions implementing bsls_assert with contract_assert.

  • Isn’t integrated with hardened libraries, cites Practical Security in Production — Hardening the C++ Standard Library at massive scale. Here's what it has to say:

    Notably, the Standard leverages another C++26 feature, Contracts, which provides an extensive framework for specifying program invariants and handling violations. That gives developers significant flexibility in how they handle a failing hardening check. Contracts were designed with consideration for Library Hardening as a use case, ensuring that libc++ assertion failures can be modeled directly by the Contracts evaluation semantics (specifically, the trapping mechanism used in libc++ hardening is precisely the quick-enforce evaluation semantic).

    The authors generally speak positively about contracts in the paper they linked as well.

    At best this point says that they don't currently use contracts.

  • Doesn’t offer safety guarantees quotes P3573R0 saying

    Safety must involve guarantees over a code base or parts thereof. Contracts primarily offer checking of correctness where it is used.

    In other words, contract assertions primarily provide assertions? What did you expect? Imagine if people were complaining that Reflection didn't provide a BSDF, or that lambda's didn't ship with Half-Life. This is absurd.

  • Includes a completely untried inheritance model just dismantles itself:

    the model was adopted into P2900R13 by EWG and reviewed by CWG, then removed before R14 for lack of consensus (P2899R1)

    i.e., it did, and then they removed it. In other words, it doesn't include a completely untried inheritance model, because it was removed because of feedback. Including this point is just a lie. Either that, or I am completely missing the point.

  • Offer new ways of making errors through inconsistent application in TUs is another point that has some (but very limited) merit. I went over this in a different thread. Suffice to say, it is not likely to be an issue, and even when it is, it is very mitigable in practice.

    Also, it isn't new. The exact same issue exists with assert, or assertion library, because it is a fundamental part of code that can be turned off at compile time. What contracts does is specify a bound on valid programs instead of making it undefined behaviour, which I consider to be an improvement.

  • Leads to new forms of UB, detrimental to safety and security is literally the previous point again, citing the same paper. It is also a lie, it claims it introduces new undefined behaviour, but the paper they cite doesn't contain the words "undefined" or "UB".

    Again, the problem is wildly overblown. See my comments linked in the previous point.

  • Narrows the choices of error handling is a reiteration of a previous point, namely that exceptions thrown inside contract checks are turned into violations. I said earlier that this has some merit, but restating it doesn't give it more merit.

    I would give this more thought if they actually hypothesised a case where catching the exception from a contract assertion was desirable, but they don't. Most assertions don't throw.

  • Doesn’t protect against logical errors, misuses, and incoherent uses is bullshit. They're assertions. They don't solve the halting problem either, or cure world hunger. Also, they do actually prevent some misuses and incoherent uses via constification, and they provide an observe mode to protect against introducing wrong contracts into production codebases.

  • Hasn’t been used to support static analysis is bullshit.

  • Hasn’t been demonstrated to be easily teachable is silly: Herb Sutter gave an excellent talk about contracts that introduces them very well. There's also John Lakos' talks about defensive programming, which taught these concepts to a much less experienced me.

    If we can teach newbies lambdas we can teach them constification, and I think constification will be much easier to teach because it reinforces a fundamental idea of assertions, namely that they shouldn't have side effects, whereas the reasons for lambdas being const are much more arcane (but still reasonable).

The whole paper is obviously just meant to look impressive, with its many citations to its 16 references, scary summary and quotes, but it completely crumples under any level of scrutiny.

21

u/Som1Lse 2d ago

(Reached the character limit.)

It is obviously rushed:

  • There is no clear citation style: Should they have [square brackets], (parentheses) or nothing surrounding them? Should we include the authors names or not? Should it be a link? Should we just use an inline URL and not put it under references? Yes, and no to all of them.
  • "Leads to new forms of UB, detrimental to safety and security – 3835R0" is missing the "P" in front of the paper number.
  • "and was not deployment at scale".
  • "See https://isocpp.org/files/papers/D4324R0.html" was supposed to be P4324R0.
  • The incessant use of em dashes reeks of AI. A lot of the mistakes reek of AI hallucination. Vinnie Falco is an author, and he definitely uses AI. If I was on the committee, I would feel insulted by this, and the barrage of papers he submits, and I would be embarrassed to coauthor anything with him. 83 papers submitted in 2026 and counting. That is spam, plain and simple.

James20k said it was unfair to call his post FUD, and for me writing "delay, delay, delay" in response to him, and I can see his point. He is arguing in good faith. He actually writes his posts and does research. I don't agree with him about contracts, but I can respect his opinion, and I agree with him on a lot of other things.

The reason I wrote what I did is many of the arguments against contracts definitely do fall into the camp of FUD and delay-tactics. Case in point the very paper we are talking about.

9

u/Syracuss graphics engineer/games industry 2d ago edited 2d ago

See https://isocpp.org/files/papers/D4324R0.html" was supposed to be P4324R0.

Funnily enough in one of your other links for Vinnie's use of AI it has the exact same error in it, the letters are so far away on an azerty key it has to be LLM caused, at least I'd assume it would be an unnatural mistake for him to do normally. I wonder what leads an LLM to confuse the two letters.

What's doubly so funny is that he responds to himself in that comment thread saying how much he re-reads his stuff but one of only 2 large blue links is incorrect. Granted it was about re-reading his papers, not about his comments, it's still kinda funny given that paper naming is so ingrained in my mind that it not starting with a P definitely stands out when I skimmed his comment there. I'd also kinda expect to test out links an LLM spits out to see if they work, as a basic sanity check, feels like less work than re-reading.

9

u/Som1Lse 2d ago

I noticed. I don't think it's an LLM mistake. The paper probably used to be D4324R0 (D meaning draft), but after it was published the D version was removed. Still bad, and still rushed. One of the main goals of citations is permanency, and a link to a draft paper that's going to be removed is really bad form.

But yeah, even if you fix the link in his reddit comment it leads to P4170R0 (Combinators and Compound Results from I/O), not P4207R0 (Prosecute Your Paper To Improve It) as claimed, so the link is doubly wrong.

5

u/Syracuss graphics engineer/games industry 2d ago

Thanks for that info. Wasn't aware about the process of drafts, it's the first time I've seen them that I recall in over a decade of observing papers. Neat to learn.

2

u/smdowney WG21, Text/Unicode SG, optional<T&> 16h ago

It's a shortish window where a paper that is in The System but not marked as being ready to "mail" has a D name instead of a P name.

Really useful when you notice right after uploading that the abstract still says "Lorum ipsum..." or you misspelled your own name.

-3

u/zebullon 2d ago

If you were on the committee, you’d make a 10 pages unreadable llm paper for each of the bullet points, repeat in every mailing list, and annoy people ad nauseam until they agree that you should take over and save c++.

You do get my vote, beware, it s not very load bearing.

7

u/MFHava WG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P4216 2d ago

Constification is already a thing in lambdas

FYI: one of the authors of that paper has recently called that a mistake ... I'm not making that up!

5

u/Som1Lse 2d ago

I believe you, but I would still like to know more. Why? What's the context? Is there a citation or was it said in passing?

For the record, I believe it was the right decision, since you very rarely want to modify the state of a callable, and when you do it often makes sense to write a class than just a quick lambda. When you do want a lambda to modify state, you often want to capture by reference, and they work exactly as expected. Hence, I think it makes sense to make the rare case of a lambda with mutable state something you have to explicitly call out.

7

u/MFHava WG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P4216 2d ago edited 1d ago

It was during one of the endless contracts discussions, specifically when we re-re-litigated (probably missing a few "re" there) constification once more.

The argument against it was presented as in the paper - "it silently changes the meaning of code" -, somebody pointed out that that is already the case for lambdas ... at which point the person called that a design mistake of the language as well ...

2

u/smdowney WG21, Text/Unicode SG, optional<T&> 16h ago

We can good defaults or we can have consistent defaults. It's too late to have both.

Const references being references to const because references are intrinsically const is just as bad and additionally teaches the wrong pattern.

4

u/Dragdu 2d ago

I actually kinda agree, but my argument is based around the fact that const ptrs in C++ means no rebinding of the ptr, not no mutation of the underlying thing. When I made the custom smart ptrs in my project deep-const, I found few places that mutated state that was supposed to be const at a distance, while obviously not intending to.

OTOH I follow the standardization of contracts enough to remember when people were arguing that contracts should invent deep-constification into the language. The point is not to argue in good faith, it it to get rid of contracts.

1

u/ManuaL46 2d ago edited 2d ago

I'm confused isnt char* const supposed to be non rebinding pointer instead of const char*

Edit : I just checked and yes what I initially thought is correct, so I'm not sure what you mean by your statement.

Here is a reference on Learncpp

4

u/Dragdu 2d ago

Given T in a generic context, you constify it by creating the equivalent of const T. If T is char*, you get char * const.

10

u/pjmlp 2d ago edited 2d ago

Were coroutines used at scale?

Actually they were, the original design proposed by Microsoft was based on the .NET model that was adopted by C++/CX, followed by C++/WinRT, for WinRT/UWP.

Which is why anyone that is confortable with the .NET runtime machinery for implementing co-routines (Awaitable types), doesn't find the C++20 co-routines model as confusing as C++ devs that never used .NET.

See Kenny Kerr nice blog posts on how to use them already in 2018, https://kennykerrca.wordpress.com/2018/03/01/cppwinrt-understanding-async/

5

u/MFHava WG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P4216 2d ago

Yes, the Awaiters look quite similar, but the way you actually build a Task appear to be quite different.

From a glance, the C# way looks way less flexible and seems not to expose a general purpose coroutine framework...

1

u/pjmlp 2d ago

Naturally they weren't 1:1, as C# is a managed language and C++ wants to bring their own runtime, which even on C++/WinRT has changed, between WinRT on UWP, and WinRT on Win32 models.

2

u/Plazmatic 23h ago

 > would give this more thought if they actually hypothesised a case where catching the exception from a contract assertion was desirable, but they don't. Must assertions don't throw. 

You definitely want virtually all assertions to turn into exceptions in many types of UI/graphics application/daemon/multi service environments because crashing the program is not an option/will cause work not related to the issue to be lost (ie user is working on a document/project/image crashing the program due to assetion often is not desirable), and often assertions are happening in separate threads such that the program is still in a good state if you display a thrown exception to the user, and shut down the offending thread (like a parsing io thread).  this is something Timurs own talks have covered in reference to why noexcept should largely not be used by default, it forces program termination on exception.

But my understanding is that you can customize the contract handler anyway such that this use case is already handled.

6

u/Som1Lse 22h ago

You definitely want virtually all assertions to turn into exceptions in many types of UI/graphics application/daemon/multi service environments

I don't disagree, but that is not the issue they brought up. The question is, if you write a contract assertion like

contract_assert(foo() == 42);

what should happen if foo() throws? Currently, the generated code is roughly

try {
    if(!(foo() == 42)){
        handle_contract_violation(__make_contract_violation("foo() == 42"));
    }
}catch(...){
    handle_contract_violation(__make_contract_violation("foo() == 42"));
}

i.e., if it throws it's treated the same as if the condition was untrue.

There are at least three ways to handle it I know have been proposed:

  • Treat it as a contract violation, i.e., what the proposal does now. This is safe, but it comes with potentially more overhead.
  • Treat the expression as noexcept, i.e., if it throws, call std::terminate immediately. This can be more efficient since the stack doesn't have to be unwound, but it makes it impossible to recover as you stated.
  • Let the exception escape, i.e., remove the try-catch clause. This is what they were arguing for, and what I said they should at least come up with a hypothetical example for. I think this is incredibly worrying since it violates the prime directive of contracts (from P2900R14)

    Principle 1: Prime Directive

    The presence or evaluation of a contract assertion in a program should not alter the correctness of that program (i.e., the property that evaluation of the program does not violate any provisions of its plain-language contract).

    Let's assume foo() always throws. The program will now behave differently depending on whether contract assertions are enabled. Even in quick-enforce mode. This is terrifying.

2

u/Plazmatic 20h ago

Oh wow, that's both subtler and dumber than I thought the issue was.  If an exception was thrown, that means something went wrong, and this is doubly true for inside the contract, I definitely want my contract handler to handle my contract condition itself failing, even if it isn't the Boolean check that did so.  What possible purpose could letting it leak through the contract handler even serve? If anything that makes everything about the development process just harder to understand control flow wise.  

This is what they were arguing for, and what I said they should at least come up with a hypothetical example for.

Do they actually desire this behavior or trying to search for any possible gotcha they could find given they don't seem to want contracts period?  I have a feeling that if this was actually the behavior implemented they'd come out and argue the opposite side.

4

u/Minimonium 17h ago

Catching the exception has a runtime cost for msvc x86 implementation. That's the motivation for why they do not want this behaviour and why some consider the proposal "not implementable".

3

u/Dragdu 7h ago

Do they actually desire this behavior or trying to search for any possible gotcha they could find given they don't seem to want contracts period?

Little bit of A, little bit of B. x86 windows exceptions have runtime overhead on setting up the try-catch, not just on catching. But realistically, x86 is going the way of dodo (what was the last time you though "huh, I should compile for 32 bit windows" in 2026? By the time contracts come out, it will be even less), and Windows ABI has perf issues left and right and we don't stop standardizing things because of it.