People have asked how I could chair SG21 and still object to the product that came out of it.
This paper is mainly about that. It is about my failures as SG21 chair, and why objecting now is not a contradiction of having chaired the group.
It is not a complete technical critique of C++26 contracts. Those critiques already exist. See P3835R0, P3573R0, and Bjarne Stroustrup et al., Contracts’ fundamental flaws. Appendix A is the C++20 record. Appendix B is the C++26 pace and product record. I will say a little about the product at the end, because the process failures and the product failures are related.
When I was asked to be chair, contracts had recently been withdrawn from the C++20 working draft.
The group had many people asserting a lack of good faith, and that the resulting product did not meet users’ needs.
So my focus in SG21 was establishing trust and a more transparent process. That seemed to me the job that needed doing.
Many of the contributors to SG21, including the co-chair Timur, wanted a rapid and demanding process to produce a result as quickly as possible.
I supported that process. I now regret that.
People complained throughout the SG21 work that the pace was too fast and that it made it impossible for many key contributors (such as Bjarne) to participate.
I heard those complaints. I did not change course.
In retrospect, supporting that pace was a failure of chairing. A process that excludes people who needed to be involved is not a success, even if it ships a paper on schedule.
I also viewed my role as administrative and not as a technical leader.
I did not want my role as chair to be compromised by taking sides on technical issues.
That is a reasonable instinct in ordinary circumstances. Here it was a mistake.
I did have private conversations with the paper authors to make them aware of my concerns. The paper authors gave lip-service to my concerns and then undermined them when the papers that were intended to address those concerns came up for discussion.
I limited my views to private discussions with the paper authors and with Timur as assistant chair until the paper was voted out of SG21 to EWG.
Many SG21 members were shocked when I made my concerns public at that point, even though I had gone out of my way to spend hours in calls with them to make my concerns clear.
I should have made those concerns clear to the whole of SG21 while the work was still in SG21. Waiting until the paper left the group was the wrong trade-off. It protected the appearance of a neutral chair at the cost of an honest technical process.
In retrospect, I failed in my role as SG21 chair in two related ways.
I supported a process that excluded many of the parties that needed to be involved.
I also allowed what I consider to be an unacceptable product to be forwarded without making my technical concerns clear to all of the participants in SG21.
Chairing a group does not require endorsing every paper it forwards. It does require that serious objections be visible to the people who are being asked to decide. I did not do that well enough.
Language design has moved toward expressing what is guaranteed, not what might be checked. That matters when something must always be checked, as opposed to something that is only optionally checked. C++26 contracts, as specified, do not give you that. A check may be ignored, and different translation units may disagree, so a feature of that form is the wrong tool for a check that must run.
P3846R1, written largely by the designers and supporters of P2900, says the same thing in its own words: guaranteed enforcement is a different need from what P2900 provides, and a portable in-code guarantee that a check is performed is not in C++26. People who need that are told to use ordinary control flow, vendor attributes, or wait for later work.
The mixed-mode / component-semantic problem described in P3835R0 is part of that: without a way to tie a consistent semantic to a component, the feature is not usable for the cases that matter most. Several large C++ organizations, including those associated with the contracts design, have made it clear that they cannot use them in the current form.
In the meantime, what users want from such a facility has evolved.
Hardened libraries, static analysis, and always-on checks in production
are what people ask for when they ask for safety. Optional, build-time
selectable assertions that may be compiled as ignore, and that
need not agree across translation units, are a poor fit for that.
P3878
shows that C++26 contracts are not a workable basis for standard-library
hardening.
There are also a number of library alternatives that are superior in terms of completeness and flexibility. See P4324R0 (Ville Voutilainen), which explores a more library-oriented approach with user-extensible control of semantics and behavior.
For more detail on the failures of the proposed C++26 contracts, see Contracts’ fundamental flaws (Stroustrup et al.).
I chaired SG21. I also object to C++26 contracts.
Those are not in conflict once you look at what I thought the chair’s job was, and at how I did it.
I focused on process and trust after the C++20 withdrawal. I backed a pace I now think was too fast. I kept my technical objections mostly private until the paper left SG21. That was a failure of chairing, and I am saying so.
The product that came out still does not meet the needs it is claimed to meet. Library approaches such as P4324R0 look more complete and more flexible. The technical case against shipping P2900 as a foundation is made elsewhere; this paper is mainly about how we got here, and about my part in that.
This appendix is not a full history. It is a short documentary record of why contracts were pulled from C++20, and why that episode shaped how I approached chairing SG21.
Contracts entered the C++20 working draft from P0542R5 (Dos Reis, García, Lakos, Meredith, Myers, Stroustrup), based on the earlier design in P0380. The usual assumption was that the committee had agreement on the goal and on the programming model. That assumption did not hold.
After adoption, a stream of papers documented unresolved design fights and technical problems: assumption / UB for unchecked predicates (P1290, P1490R0), competing redesigns of levels and semantics (P1429, P1607, P1486), side effects and predicate elision (P1670R0), evaluation in constant expressions (P1671R0), and mixed build / continuation questions that never settled into one story. Myers proposed removal as an escape hatch in P1426R0 (“Pull the Plug on Contracts?”). That poll failed in Kona (February 2019) while people still thought there was time to fix things.
By Cologne (July 2019), the situation was worse, not better. Major design changes were still being accepted in EWG. The original authors did not agree with each other. Details were still being designed on the fly. There was no serious implementation experience applying the then-proposed feature across the standard library. Stroustrup’s P1711R0 laid out the choices as minimal fix, status quo, or removal, and warned that late novel features under time pressure were a bad bet.
I coauthored P1823R0 (Josuttis, Voutilainen, Orr, Vandevoorde, Spicer, Di Bella), which proposed removal. The opening is blunt because the situation was blunt:
This is a proposal nobody likes and we are not happy to make it. But we have to suggest to remove Contracts from C++20, because the feature is not ready for standardization yet and to continue to force to have it in C++20 is taking the whole C++20 at risk.
P1823 also stated the process point that mattered then, and still matters:
Part of the train model must be an agreement that if there is not consensus that a feature is in acceptable form when the train is about to depart that the feature must be taken off the train and that it has to catch the next train.
P1823 was adopted. Contracts left C++20.
After removal, WG21 formed SG21 to try again with a more deliberate path: use cases first (P1995), a contracts working paper (P2521), and later an explicit C++26 MVP schedule that I coauthored with Timur as P2695R1. That plan was meant to prevent the C++20 failure mode of designing under the train. Looking back, the schedule itself became part of the next failure mode.
This appendix collects primary sources that speak to process: how fast SG21 and then EWG moved, how much of the design was still moving, and whether the MVP story answered the concerns or mostly deferred them.
P2695R1 made the schedule trade-off explicit: getting contracts into C++26 was “an ambitious goal,” and the plan used fixed meeting milestones to force design decisions by date. SG21 adopted that roadmap. I supported it. The intent was discipline after C++20. The effect, for people who could not live on the reflector and in the telecons, was exclusion.
By early 2025, a group of us put the product and process concerns in writing in P3573R0 (Hava, García, Regev, Dos Reis, Spicer, Stroustrup, van Winkel, Vandevoorde, Voutilainen). On the pace of change:
Instability of overall design: There were 10+ papers suggesting changes, 1,000+ reflector messages, and dozens of changes to P2900 in 2024. It is a full-time job to keep up. Nobody outside a small group of people knows what is really being proposed. This is not a solid basis for an international standard.
P3573 also said the MVP was incomplete and untried on the points that matter for real use: mixed checking across TUs, modules, virtual functions at scale, and more. The closing line was deliberate:
Remember what happened to the C++20 contract design.
Dos Reis said the same thing more narrowly in P3506R0 (“P2900 Is Still Not Ready for C++26”): field experience was still missing where the novelty actually is (pre and post), and the facility needed more work before C++26.
I raised a concrete MVP-scope objection in P3478R0: constification should not be in the MVP. Novel expression semantics inside contract predicates are surprising, change overload resolution, and buy very little. Shipping them as part of a “minimal” product is the opposite of minimal.
After adoption into the working draft, the objections did not go away. P3835R0 (Spicer, Voutilainen, García) describes the mixed-mode / component-semantic problem: without a way to tie a consistent semantic to a component, the feature is not usable for the cases that matter. Eric Fiselier said as much at the February 2025 meeting; the standard still does not provide that. P3829R0 (Chisnall, Spicer, Dos Reis, Voutilainen, García) argues that the P2900 shape does not belong in the language at all. P3878 (Voutilainen, Wakely, Spicer, and later Lavavej) shows that C++26 contracts are not a workable basis for standard-library hardening.
Other papers made the process / maturity case without needing my name on them. P3851R0 (García et al.) lists lack of deployment, user, implementation, and build-system experience — especially for mixed-mode builds — and recommends a TS or white paper rather than C++26. P3853R0 (Voutilainen) rejects the claim that shipping an MVP is how we get feedback: we do not have meaningful third-party deployment experience on function contract assertions, and shipping in an IS ends the experiment. P3889R0 (Achitz) asks whether P2900 is an MVP at all, given new build modes and extensive implementation-defined behavior. P3909R0 (Voutilainen) argues for a white paper even late, because the language feature is rigid where users need extensibility. P4043R0 (Neațu) asks whether contracts are ready to ship in C++26 and points at the Concepts precedent: major features have been pulled before when maturity was in doubt.
The defense of the current product is mainly P3846R1 (Doumler, Berne, et al.), with earlier context in P3591R0. Those papers say the objections are largely repeats, that SG21 spent years on use cases and design, that incremental standardization is the right model, and that removing P2900 from C++26 would help no one. Fair enough to cite that side. I do not find it persuasive on the points in P3573, P3835, and P3878: “we discussed it” is not the same as “the concern is resolved,” and “MVP / C++29 will fix it” is how serious accuracy problems get postponed past the point where the train can still be stopped.
Library-oriented alternatives such as P4324R0 are part of the same accuracy discussion: if the language feature cannot meet the needs, and a library approach can explore semantics and extension without freezing a weak core into the IS, that is relevant process information, not just product preference.