In C++23, the [[assume(conditonal-expression)]]
attribute makes it so that if conditional-expression doesn't evaluate to true
, 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
As commenters have pointed out, this is not a required optimization; it's just what GCC happens to do with the information that anything but y == 1
would be UB.
It would be valid for compilers to completely ignore all assumptions.
But what about constant expressions?
Compilers are required to diagnose all undefined behavior in constant expressions1), but is this reasonable? For example:
constexpr bool extremely_complicated(int x) {
bool result;
// 10,000 lines of math ...
return result;
}
constexpr int div(int x, int y) {
// This should result in a compiler error when the compiler is unable to prove
// what extremely_complicated(x) returns.
// extremely_complicated(x) is not evaluated, so it needs to be able to
// prove the result without evaluating the function.
[[assume(extremely_complicated(x))]];
return x / y;
}
constexpr int quotient = div(4, 2);
Is it still a problem even if the compiler has no way of proving whether an assumption would evaluate to true
? It obviously can't solve the halting problem.
How exactly do assumptions and constant expressions interact? [dcl.attr.assume] doesn't have any wording on this.
1) Note: extremely_complicated(x)
is not a constant expression, but it's located in an assumption whose failure would result in UB within a constant evaluation of div(4, 2)
, which is a constant expression. It is generally said that UB in a constant expression needs to be diagnosed.
There is a specific exception for
assume
in the final C++23 draft.[expr.const]/5.8
(with references deciphered)So a compiler doesn't need to judge the veracity of assumptions in order to judge the constantness of expressions. If you write
then
g()
may or may not be a core constant expression (it is unspecified whether[[assume(E)]];
disqualifies an expression for being constant ifE
both is allowed in theconstexpr
context and doesn't returntrue
). If you further writethere are two cases. If the implementation has decided
g()
is not a core constant expression (as it is free to do so), it is required to give a diagnostic. If the implementation has decidedg()
is a core constant expression, the program has undefined behavior.So you see that compilers have been given an out. A false assumption in a
constexpr
context can be undefined behavior and not a diagnostic at the implementation's choosing. An implementation could just choose to never check the assumptions in constant expressions. If an assumption then turns out to be false, the program appearing to compile and run successfully (if incorrectly) is just a manifestation of the resulting undefined behavior.