C++26 Disapproved

37 points by BenjaminRi a day ago on lobsters | 32 comments

wareya | 19 hours ago

I've been looking around intensely for the past few days at the proposed text for contracts and many of the arguments that have been posted online about them, trying to sort out how I feel about the situation.

First of all, I like the idea of contracts and think that it's a good thing for languages to have something like them.

I think describing the current text for contracts that was going to go into C++26, in any way other than "contentious" and "not ready to ship" is blissfully hopeful and misguided. It's trying to be three or four different things at once, succeeds at only one of those three or four things, and that one thing is something that very few people are going to benefit from, but the resulting feature surface area looks a lot like "oh nice I can refactor my battery of asserts into a battery of contract statements" but does not actually work like that and is a huge footgun.

So C++26 getting disapproved was the thing that should have happened and should have been expected to happen.

Some of the anti-contract points are trivial and pointless, and could easily be resolved by saying something like "implementations should (but are not required to) emit a diagnostic when the linker tries to do X or Y" or whatever (just a random example, not me proposing anything), but nothing like that was actually added to all of the trivial pointless edge cases in the contract text, it feels like these minor problems are being swept under the rug instead of getting fixed. I have no idea why they're not just getting fixed. Committee internal politics? That wouldn't be good. It's probably not that but it's one of the few things that comes to mind as a possible explanation.

Some of the anti-contract arguments are just garbage and really should be ignored. I won't say which ones I think they are because it's not important.

But there are other anti-contract arguments that I think are legitimate and have not actually been addressed. In particular there are 3 where I'll give my opinions on what the contracts people should do:

  1. There needs to be a way to write contract assertions that cannot be turned off and will always be run. Every usable assert library has a way to do this. There is no valid reason not to include a way to do this.

  2. Contract batteries (i.e. sets of contract assertions that have sequence points before and after them) should be treated as wholes: the #1 #2 #2 #1 scenario described as "valid" in the proposed standard text (at least the latest public version of it) must be explicitly forbidden. Allowing re-evaluation like #1 #2 #1 #2 is fine. But allowing scattered or "shredded" evaluation (to make up a term for it) is such an enormous logic footgun that I can't come up with any real reason to allow it. The only arguments I can come up with to allow it revolve around weak compilers that barely do anything or are written like transpilers, but there are exactly zero such C++ compilers, so there's no reason to design around them. I understand that there are valid reasons for re-evaluating contract batteries, like evaluating them on both the caller and callee side, but I cannot come up with any reason for "shredding".

  3. "observe" mode needs to be explicitly outlined as a porting or debugging tool and heavily proscribed or even marked as invalid for production code somehow, or removed from the standard entirely, or become an annotation on individual contract assertions instead of being available as a contract mode. Merely recommending against its is not enough. It is too dangerous and causes too many weird semantic edge cases in contract evaluation (e.g. it makes the "shredding" discussed in 2 even more dangerous) for it to be given to developers as a mode for general contracts. If fixing it (e.g. making it an annotation) is a headache because of how contract handlers work and you don't want to do it properly, then it should be gutted.

I have a couple more problems with them (like the const-elevation thing, which seems to be a scar from "trying to be 3 different things at once" and not a real design need), but they're basically quibbles in comparison to these 3 points.

... And for C++26, give up on contracts. Pull them out. Try again next time and keep trying to build agreement and compromise with the NBs until then. In particular, doing point 1 will probably win over enough NBs to win out over the detractors, IMO.

hunger | 18 hours ago

Contracts were supposed to be the minimal prototype that can get consensus. Entirely bare bones, just supporting the absolutely critical use cases that are needed. That it is as complex as it turned out to be is a direct result of trying to get to a point where enough people can agree to it being useful enough to get into the standard.

There needs to be a way to write contract assertions that cannot be turned of

That goes entirely against the theory of contracts. A contract adds redundant checks into the code base to validate your implementation against. "Always on contracts" are part of the implementation itself and thus can not be a used to validate the implementation.

But allowing scattered or "shredded" evaluation (to make up a term for it) is such an enormous logic footgun that I can't come up with any real reason to allow it.

Apparently a MVP for contracts needs to support use cases like "only run every 1000th assert". This is C++: You need ways to keep your code fast, while keeping up the illusion that the entire thing is safe because you enabled contract checking.

"observe" mode needs to be explicitly outlined as a porting or debugging tool

Its only there because some groups insisted it is needed in the MVP. It is a debugging tool and has afaict never been advertized as anything else.

wareya | 17 hours ago

Contracts were supposed to be the minimal prototype that can get consensus. Entirely bare bones, just supporting the absolutely critical use cases that are needed. That it is as complex as it turned out to be is a direct result of trying to get to a point where enough people can agree to it being useful enough to get into the standard.

I'm not sure about the "useful enough" part. There are many forms that a useful MVP could have taken; this isn't one of them, since it stopped being minimal and started having so many weird far-reaching semantics implications. "It's syntax sugar for a standard assert implementation to make it easier for static analyzers to find asserts" would have been an MVP. So would "you can write some constant/primitive expressions that a static semantic analyzer has an easy time validating, but you don't have all of C++ available to you in contract expressions, only stuff that's really easy for static analyzers". There are another few things contracts would have been MVPs.

The current text for C++26 contracts is trying to validate a lot of those use cases at the same time and ends up being a specific compromise that is not actually useful to any of them. It's not better for static analyzers than asserts, it's not better or easier asserts for assert users, it's not a good diagnostics or error telemetry tool because it's too hard to control what the compiler is doing because so much is implementation-defined, etc. A good-enough compromise might exist, but the current proposed text isn't one.

That goes entirely against the theory of contracts. A contract adds redundant checks into the code base to validate your implementation against. "Always on contracts" are part of the implementation itself and thus can not be a used to validate the implementation.

I'm sure this is the point to a lot of people, but it is definitely not a good prediction for what contracts are going to be used for in the wild once they ship. People are going to convert assert batteries to contract assertion batteries. 100%. They are going to do it. Insisting on a shape of contract feature area that specifically excludes this usecase for whatever ideological reason isn't helpful to anyone, it's just stubborn. As-is, you're going to have libraries going "only supports enforce and quick-enforce mode". And with the way the proposed standards text is currently written, implementations seem to be encouraged to support such behavior in programmers using contracts; there are a large number of tangents about "in such and such case an implementation may deem it inappropriate to provide XXX semantics" or whatever, for example. But leaving this particular thing as up to the implementation to enable is just a weird safety and fragmentation hazard for no real reason.

Apparently a MVP for contracts needs to support use cases like "only run every 1000th assert". This is C++: You need ways to keep your code fast, while keeping up the illusion that the entire thing is safe because you enabled contract checking.

Yeah I have no idea what they're thinking with "It is implementation-defined which evaluation semantic is used for any given evaluation of a contract assertion." -- it seems unnecessary, overdesigned, hard to reason about, and dangerous. But in this case I'm pointing at p2900r14 4.1.6.(9+b).2.18, where it says // valid sequence of evaluations: #1 #2 #2 #1.

Its only there because some groups insisted it is needed in the MVP. It is a debugging tool and has afaict never been advertized as anything else.

In that case the proposed text for it needs to outline this in parts of the text that are going to inserted into the standards document itself. It's rare for text outside of the standard to become normative or prescriptive; they aren't even usually used for disambiguation. A bunch of early ISO C standards discussion is totally obsolete despite not ever being retracted or whatever.

hunger | 15 hours ago

I'm not sure about the "useful enough" part. There are many forms that a useful MVP could have taken; this isn't one of them

Its the compromise that managed to get consensus in the plenary because of all the extra stuff that they added.

As-is, you're going to have libraries going "only supports enforce and quick-enforce mode".

Any feature will be misused, but I kind of doubt this will happen: Stopping the application is a big no-no for libraries with or without contracts. I am sure libraries will continue to do proper error handling withnor without contracts.

it seems unnecessary, overdesigned, hard to reason about, and dangerous.

It is a good example of design by committee :-)

I found this confusing because I didn't understand what "DIS" is. I wondered if it was related to the result "disapproved", but no, it seems to be "Draft International Standard".

nytpu | 20 hours ago

Yeah, I was thinking maybe they were expecting people had downloaded draft C or C++ standards before from the working group; but nope, those are solely labelled "working draft" and "international standard" on separate pages, no instance of "DIS" or "Draft International Standard" (on every one of my C drafts at least, didn't look at C++)

The final drafts that go through the ISO process (DIS and FDIS) must be kept secret within the committee and national bodies. There are some documents and subdirectories on https://www.open-std.org that are password protected because of this requirement.

nytpu | 20 hours ago

Hmm, interesting. Although contributes even more to wondering why they didn't spell it out like "draft international standard (DIS)" the first time they used the acronym, since it's unlikely to be something that a reader not already on a standards body would know.

Yeah there’s a lot of discussion amongst insiders going on here. I’m amused that even the link to the explanation of ISO’s procedures doesn’t explain DIS or FDIS or NB, heh.

gulbanana | 19 hours ago

Seems like a norms vs rules thing. The author sees this vote as a breach of trust by sore losers who wouldn't accept that the majority is against them, but the actual ISO rules are that you need a larger majority than is willing to override this kind of NB veto...

wareya | 19 hours ago

And the rules are set up that way exactly for situations like these.

thasso | a day ago

I am curious why this went to a vote at all. This sounds like the kind of situation that should have been resolved before voting on the standard.

From the post:

In the next step, WG21 voted on the C++26 draft in March 2026, which included Contracts. Here, too, a large majority was achieved, though not without dissenting votes. And that brings us to the next phase: DIS. At this stage, there were enough “no” votes that a two-thirds majority was not reached.

What happened in the interim?

aw1621107 | 23 hours ago

I think NB deliberations are technically supposed to be confidential so I'm skeptical that we'll see an official accounting of what happened.

If you're willing to believe comments on /r/cpp, including from people who are privy to those internal deliberations, then I think one can attribute this outcome to some combination of hope (contracts had a pretty significant margin in favor in WG21, so perhaps that'd be enough to make it past the NB ballot), vote distribution (i.e., each NB gets one vote and some NBs are larger than others, so the right distributions of support/oppose in WG21 can still result in the NB vote failing even if you assume no malfeasance), peculiarities of how NB votes are done (IIRC the US NB vote is determined at least in part by people who aren't deeply involved in WG21) and active lobbying (or sabotage, depending on who you ask).

Pomax | 11 hours ago

It sounds like the process worked exactly as intended: just because no one voted down a DIS before doesn't mean that shouldn't ever happen. When the water is as muddy as it is now, saying no is a signal to clear the water. No matter how "good" it might seem to those who want it, if you wrote a book instead of a proposal, or you walked a marathon instead of a mile, or you did the entire year's worth of homework on day one, you went too far. Same for adding an entire JIT compiler to CPython without an adequate PEP. Same for spending ten years discussing collection literals in Java instead of coming up with something people actually asked for.

When something brings this much confusion, the right thing to do is say "no, this needs to go back to the actual first draft phase, because this went way too far without anyone calling it back". That's doing right by the entire community by reaching for the rules you almost never need: they're there exactly for these situations. And now you keep talking: the vote says "no, in the current form", it does not say "no, now stop asking".

hunger | a day ago

C++ committee members are by definition part of the national bodies that rejected the C++ committees proposal. There are of course people in the NBs that do not care for C++ at all, but those typically follow the people they trustbthat were in the relvant meetings or abstain. That's why hardly any prior art exists here: Who votes against the draft they approved themselves earlier?

Its a spectacular mess the C++ committee has created for itself here.

david_chisnall | a day ago

Who votes against the draft they approved themselves earlier?

That's not really how it works. Individual papers are approved one at a time by votes in sessions in the in-person meetings. This already creates a bias towards people who have the funding to travel to these. Because they're in-person meetings and the votes are live, there's a reasonable chance that people either haven't had time to read the entire proposal (especially something as big as P2900) or they are voting based on presentations or trust for other people. I've not been to one of these meetings but other folks have written about the peer pressure they experienced to vote the 'right' way. The final vote based on the DIS is a vote on the combination of all of the things that were voted in individually and it's also a vote where national bodies have polls that run for several weeks and give people a chance to read a lot.

Contrast this with another standards group I'm involved in: ETSI. In an ETSI process, the draft is stored in git. Desired changes are raised as issues. There's online discussion that either reaches consensus or is flagged for discussion at the meetings. Concrete changes are raised as PRs. These either reach consensus in review or are flagged for discussion. The discussions in meetings are not where you make decisions, they're where you explain and discuss things. This gives a much more thoughtful decision-making process.

I’m most familiar with the IETF so this drama makes me wonder how something similar might play out in that context.

The IETF generally doesn’t do big feature releases of major standards, so there’s no way to (eg) cancel three years of work on TLS because of controversy over one part of it. When the IETF does do big standard revisions (eg RFC 9110 and friends for HTTP, or RFC 9293 for TCP) it’s a very conservative consolidation of firmly established features, and newer stuff remains in separate RFCs. This makes it easier for the IETF to revise specifications, but they end up spread piecemeal across dozens of documents and it’s often unclear which RFCs are core and which are ignored. Sucks to be an implementer who doesn’t have easy access to advice from old hands.

There aren’t any votes in the IETF. To get a spec through the process the authors have to be persistent enough (typically over a period of a few years) and responsive enough to feedback from the working group that they can persuade the WG chairs that the WG participants think the draft is worthy. Experience from an implementation helps but isn’t required. There’s generally a bias in favour of publication, modulo the effort required to push the document through the process. If a document is controversial then there can be ongoing flamewars about it for literally years, but that isn’t necessarily enough to prevent publication, especially if prominent vendors have draft implementations that interoperate.

Decisions are formally made on the working group mailing lists where discussions happen. IETF meetings act as checkpoints where WG chairs review the progress of active documents and determine what needs to happen next. They also help to grease the wheels by getting people into the same room (or bar or restaurant) which can unstick the online discussions. (Sounds like ETSI is similar in this respect.)

I doubt the IETF’s piecemeal strategy would work so well for a programming language, where users normally expect it to be a coherent whole. But ISO does have “technical specifications” for extensions to a language (defer in C is being published as a TS). And there are a bunch of informal de facto small extensions to C that aren’t standardized because there’s no venue that can support that kind of work. And because responsibility for libc is split between ISO’s standard library and POSIX, we get silly things like pthreads being renamed in order to import it from one standard into the other. Similarly, the C committee tends to re-do existing extensions instead of adopting them as-is. So a more open and flexible process would be better, but probably not IETF-level anarchy.

0x2ba22e11 | 15 hours ago

I don't know if this part of the culture changed later but early IETF did seem to occasionally publish RFCs with imperfectly thought out parts and then publish another a few years later which says nobody implemented those parts and you shouldn't either.

That does still happen, but with a caveat that in recent years it’s less common for an RFC to be a mixture of good and bad parts. A lot of that is because the good patterns and bad antipatterns of protocol design have become clearer since the 1990s, and part of the (informal) IETF process is teaching those design patterns as part of the design review of internet drafts. That sounds like rainbows and unicorns, but in practice the learning experience is more like being flamed to a crisp by burnt-out greybeards who have no patience for your shit, go away and do your homework newbie.

0x2ba22e11 | 4 hours ago

Thank you

hunger | a day ago

The draft sent off to the NBs was polled in the plenary. So there is a picture of what the majority of experts in the committee (present at the day) think.

Whether or not some of those experts are actually doing a good job is a different story.

olliej | a day ago

[edit: I fixed some grammar and apostrophes]

No, they aren't - I'm not sure why you believe that but it's not true. The basic model is that each wg21 member is a representative on a TAG or similar for each country's NB. The TAG makes a recommendation to their NB, and then each NB can choose to accept (or not) that recommendation - independently to anything that the TAG recommends. The votes inside each NB may not even be available to that country's own TAG.

I also cannot comment on the actual voting at any given part of this, but claiming that the members of WG21 are the voters at the NB level is absolutely false.

But you're not wrong about the mess.

hunger | a day ago

Oh, come on. That's just a level of indirection you are describing there. Its the same group of people either voting or advising the rest on what to vote for.

olliej | 14 hours ago

I'm literally in these groups, and the ISO NB votes are not individual members of WG21 - in fact the majority of members have nothing to do with C++. The NB votes are made by organizations or companies in the organization that does the NB vote in each country.

For example, in the US NB for JTC1 (the ISO section covering IT) is INCITS, which has 18 voting members: https://www.incits.org/committees/eb/ .

The voting rules for INCITS are that approval requires a 2/3rds majority of those organizations that vote and a majority of the members that do not explicitly abstain. E.g. non-voting members are equivalent to "opposed" unless explicitly abstaining from the vote. This is covered in section 5.2.2 of the INCITS policies and procedures: https://www.incits.org/dotAsset/5c620240-4714-4ec8-b3bb-dee03b5bc6dd.pdf

But I think the problem here is that you're thinking that the NB is a "C++" NB, which is part of why you are thinking "the NBs WG21 members are the ones voting".

The NB is not just C++. It is responsible for voting on a vast array of different ISO standards. For example here are the the iso standards that the INCITS executive board votes on: https://standards.incits.org/browse/us-tag-listing - C++ is working group 21 under subcommittee 22, next to COBOL, Ada, Prolog, "Programming Language Vulnerabilities" (wat?), "Linux" (?????), Fortran, and C.

So again, the NB votes are not just a bunch C++ committee members: the INCITS EB page has links to each member and you'll see that a bunch of them have no programming language involvement at all.

Hopefully once you see that the voting members of a NB like INCITS are not engineers, they are not all C++ or WG21 members, and a bunch of the INCITS members have nothing to do with programming languages at all, it will become clearer that this is not a "WG21 members voted against C++26".

Historically the NB vote has been a formality, but this time the WG21 DIS was rejected, and who knows what happens next.

And before you ask: no I cannot talk about anything that happened in any committees, subcommittees, technical committees, advisory groups, or any of the other myriad names ISO has for "group of experts talking about about stuff".

hunger | 14 hours ago

I am not asking, no worry. I am aware that ISO members must protect the secret handshakes from the unwashed masses :-)

So random people can go to the WG21 meetings and participate in the polls? I understood you needed to be delegated by a NB into WG21 to be allowed to participate, with a few exceptions that get invited on a per-person basis. Is that incorrect?

olliej | 10 hours ago

Essentially anyone can participate in wg21, but you can only vote in wg21 if you are a registered member of iso, which means you have to be representative of some country, though that is mostly a formality.

After WG21 (or whatever ISO working group) makes a decision, that then goes on to the various national technical committees/TAGs ("technical advisory group" I think). The TAG consists of the members of the ISO WG that are representatives of that country - e.g. US representatives can only be in the TAG for the US NB, french representatives can only be in the TAG for the French NB, etc.

The TAG then makes its recommendation to the voting members of the NB - for incits that is the "executive board", but I don't know if that's ISO terminology or just INCITS. The voting members do not necessarily have anything at all to do with the various standards that come up, so normally it is simply a formality in which they accept the recommendation of their TAG, which would normally just be to recommend that they accept what WG21 (or whichever working group) recommends.

That clearly did not happen, but while I can't talk about anything I am aware of, I also do not know anything about what happened in other NBs - only what the voting outcomes were. The goal of this separation is to ensure independence of the various NBs.

But the fundamental point is that the NB votes are not made by members of WG21 (or necessarily any of the WGs for which they vote). I guess in principle a WG member could be a representative of one of the voting members, but I don't know if that happens or if so how common - and of course every representative that is a member of one WG is unlikely to be on the WG for a bunch of other things they're expected to vote on \o/

hunger | 3 hours ago

So the people that were polled in WG21 on whether to send the draft to the national bodies were those that their NBs sent there. The delegated people then meet in a TAG and recommend to their NB how they should vote.

That does sound very much like the same people to me... with a few extra layers of fluff in the process that is expected from any multi-national organization.

olliej | 2 hours ago

That does sound very much like you couldn't be bothered reading my replies, and instead said something that is demonstrably untrue.

The national bodies do not send anyone. Any ISO member represents a country, and can then vote as they fit. They can go to the TAG and vote there. The overwhelming majority have no direct connection to any of the voting members of any national body. US members of ISO are generally members through ANSI, nothing to do with INCITS, which is the US national body that covers WG21. There are 18 companies that vote in that national body. The majority of those companies do not have any representatives in WG21. Any company can pay INCITS to have a voting position on the NB, it costs as little as $11k a year to be one of the voting members of the executive board (https://www.incits.org/dotAsset/c9027f72-5ff0-4686-a8f3-24c85417e630.pdf). The NB votes are one vote per member company.

You've clearly decided that you know how this works, which tells me quite clearly you've never worked on any standard at all, so why are you so hell bent on ignoring answers that explain why your opinion is wrong?

You're clearly unwilling to do anything other than dismiss detailed answers to your questions, so why bother asking them in the first place?

aw1621107 | a day ago

Who votes against the draft they approved themselves earlier?

Votes are counted differently in the two cases. To a first approximation WG21 votes are 1 person 1 vote, while the NB ballot is 1 country 1 vote, so even if you assume the same set of people are involved in the two votes the right distribution of naysayers can result in diverging outcomes.

icefox | 15 hours ago

Starting to feel like C++ kinda deserves a fork. Having a "progressive version" and "conservative version" that don't have to argue with each other might be good for both.

cceckman | 12 hours ago

That was (is?) the idea behind https://en.wikipedia.org/wiki/Carbon_(programming_language) : "how can we have nice things without having to work through the ISO process, and working with the existing C++ codebase?"

People are still working on Carbon, fyi. It's slow moving (which is expected, it's an incredibly ambitious goal).