In C++23, the [[assume(expression)]]
attribute makes it so that if expression is false
, the behavior is undefined.
For example:
int div(int x, int y) {
[[assume(y == 1)]];
return x / y;
}
This compiles to the same code as if y
was always 1
.
div(int, int):
mov eax, edi
ret
However, what happens if there is another level of undefined behavior?
int div(int x, int y) {
[[assume(x / 0 == 1)]];
return x / y;
}
Now there is UB inside the assumption, but the assumption is not evaluated. What does this mean? Is it just nonsense or can the compiler do anything with this assumption?
From [dcl.attr.assume]:
So the undefined behavior of the expression does not immediately imply undefined behavior of the program with the given inputs.
However it continues:
Either the evaluation of an expression that would have undefined behavior is not an evaluation of the expression that evaluates to
true
and the behavior of the program would be undefined per the "Otherwise" sentence as well, or alternatively, if you consider it unspecified whether or not "If the converted expression would evaluate to true at the point where the assumption appears" is satisfied if the evaluation would have undefined behavior, then it would still not be specified whether or not the "Otherwise" branch is taken and so there will again be undefined behavior overall, since undefined behavior for one choice of unspecified behavior implies undefined behavior overall.This is specifically addressed in the proposal P1774 in chapter 4.3. as a subtle difference between the decision to make behavior undefined if the assumption does not evaluate to
true
vs. making the behavior undefined if the assumption evaluates tofalse
.This way it is for example possible to write assumptions like
x + y == z
without the compiler having to consider the special case of signed overflow, which might make the assumption unusable for optimization.