| Document #: | P4392R0 [Latest] [Status] |
| Date: | 2026-10-14 |
| Project: | Programming Language C++ |
| Audience: |
CWG SG23 Safety and Security |
| Reply-to: |
Jarrad J. Waterloo <descender76@gmail.com> |
The, mostly likely, biggest impediment to any safety proposal that
impacts the language is its being backwards compatible with existing
code. However, for new code blocks that should not be an impediment.
Consequently, new language features can and should be made as safe as
possible. It would be beneficial to the C++ community if the standard
put a moratorium on adding new language features where a programmer can
write new code blocks, statements and expressions that have the same
amount of undefined behavior as does the rest of the language. In other
words, new language features should be made as safe as possible i.e. new
language features should have less undefined behavior as the rest of
C++. This request herein may be called the
C++ Tourniquet or No MORE UB.
Two new major language features was added to C++26.
Reflection already exceeds the safety of existing code
because it is mostly consteval. This means it benefits from
not exhibiting “behavior explicitly specified as undefined”. [N5054]
|
3.66 [defns.undefined] [N5054] undefined behavior behavior for which this document imposes no requirements [Note 1 to entry: … Evaluation of a constant expression (7.7.2) never exhibits behavior explicitly specified as undefined in Clause 4 through Clause 15. — end note] |
Contracts unfortunately as a new language feature
suffers from the same degree of undefined behavior as the rest of the
language. Attempts to make it free from undefined behavior,
i.e. strict, failed. It failed for two seemingly rational
reasons.
conveyor [P3285R0] functions to the language would
take too long. We are ~15 years into adding constexpr
functions into the language and they are still not complete.
Consequently, the 88% [Favor88Percent] of the
C++ community that are in favor of contracts don’t want to
put contracts off for ~15 more years.pre, post and
contract_assert clauses would make contracts
unusable.Neither of which is entirely accurate. In order to understand why that is the case, we first need to review the history of these prior attempts.
Contracts always had an uphill battle when it came to safety. [P1995R1] 43%-56% of
programmers weighing in on the contract use cases thought that contracts
being free of undefined behavior was either a “must have” or a “nice to
have”. Looking solely at “must have” the percentages were 30%-40%, which
was more than enough to be a sustained concern throughout the
C++26 release cycle.
|
Contracts — Use Cases [P1995R1] … crit.noundef As a Critical Software Developer In order to Avoid undefined behavior I want to Have contract violation at run-time always have well-defined behavior Must Have: 9, Nice to Have: 4, Not Important: 17 … crit.noassume As a Critical Software Developer In order to Avoid unexpected or undefined behavior I want to Ensure checks will never be __assume’d/__builtin_assume’d by the compiler as if they were facts injected into the program (otherwise, if such an assumption ever failed, I would be running a different program that is not equivalent to the one I wrote; assumptions can expand the set of possible executions by injecting facts not otherwise knowable to the compiler) Must Have: 12, Nice to Have: 5, Not Important: 13 |
Throughout the C++26 release cycle there was sustained
objections concerning contracts on the grounds of too much undefined
behavior being permitted in the pre, post and
contract_assert contract assertions. During the years
2022-2024, there were at least 13 votes on this very issue. [P2899R0] Despite the
objections, 11 of the 13 votes resulted in “No consensus” and “Consensus
against” doing anything about undefined behavior.
What is truly interesting are the two votes that favored less
undefined behavior! The last vote kicked the bucket down the road to
C++29 or later and all but ensured that contracts will have
the same level of undefined behavior as the rest of the language. There
were 25 people voting. However an earlier vote had 58 people voting on
“contracts should expose less undefined behavior than regular C++ code
does”.
|
Contracts for C++ — Rationale [P2899R0] … 3.6.1 Undefined Behavior During Contract Checking …
…
|
That vote on “contracts should expose less undefined behavior than regular C++ code does” is similar in intent as another poll that resulted in a “Consensus against, but P2900 would be in danger of failure in plenary”.
|
EWG, Wrocław, 2024-11-19, Poll |
||||||||||
|
As suggested in P3362R0 / P2680 / P3285, the contracts proposal in P2900’s Minimal Viable Product shall be changed to incorporate stricter contracts in addition to regular contracts.
Result: Consensus against, but P2900 would be in danger of failure in plenary |
If we are all against unsafety, then why are we so divided and
against freeing contracts from as much undefined behavior as possible.
To the credit of the majority for contracts, the comments of the safety
minority was investigated in papers such as
Exploring strict contract predicates [P3499R0] and
Contract assertions versus static analysis and 'safety'
[P3376R0].
|
Exploring strict contract predicates [P3499R0]
While strict contracts as described above seem specifiable and implementable, it is worth highlighting that they are severely limited. Remember that in such strict predicates:
| ||||||||||||||||
|
Contract assertions versus static analysis and ‘safety’ [P3376R0] 1. The perception of safety |
It was concluded unanimously, SF/F, after that investigation to kick the undefined behavior ball down the road. However, that was premature. It would seem that the severe limitations was predicated on the unrealistic expectations of safety by both the majority of those for contracts and the minority opposed to contracts. This is highlighted by the two uncommented apositives, as described and in such.
|
Exploring strict contract predicates [P3499R0] While strict contracts as described above seem specifiable and implementable, it is worth highlighting that they are severely limited. Remember that in such strict predicates:
| |||||||||||||
|
Perfect is the enemy of good [PerfectEnemyGood] “Perfect is the enemy of good” is an aphorism that means insistence on perfection often prevents implementation of good improvements. The phrase suggests that achieving absolute perfection may be impossible, and that the pursuit of perfection should not prevent someone from recognizing or completing work that is imperfect yet still valuable. |
The “NO undefined behavior” does not allow one to make contracts stricter because that strict code may be called by less strict code and call less strict code. Since the UB can originate in less strict code, it can cause problems in strict code. This restricts improving any code in isolation and gradually as everything, throughout the core language, must be elevated in safety at once.
The “no WRITING undefined behavior” position is ok with undefined behavior originating in less strict code. It is OK to call less strict code and to be called by less strict code. What programmers is getting here is that if there is a problem, it is not in the strict code!
What does this “no WRITING undefined behavior” position actually mean by example? Consider an earlier example.
|
Contract assertions versus static analysis and ‘safety’ [P3376R0] 1. The perception of safety |
The “no WRITING undefined behavior” position does not consider this
to be a problem. While it is true that has_name has a use
after free. The real problem, the creation of the dangling reference
occurs outside of the contract assertion and it is in that other less
strict code where the fix must reside. The difference between the
compile time dangling reference/alias creation and the runtime use after
free parallels the compile time unitialized memory creation with the
runtime reading from uninitialized memory.
| More Compile Time | More Run Time | |
|---|---|---|
| Uninitialized |
Forced initialization Zero Initialization |
Uninitialized Read |
| Dangling | Dangling Alias Creation | Use After Free |
|
By permitting strict code to call less strict code and to be called by less strict code, at least 3 of the 4 severe limitations are resolved. The result is that new code can “expose less undefined behavior than regular C++ code does.
|
Exploring strict contract predicates [P3499R0] While strict contracts as described above seem specifiable and implementable, it is worth highlighting that they are severely limited. Remember that in such strict predicates:
| |