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.
First of all, even if the compiler can't solve the Collatz Conjecture and the Halting Problem, it is undefined behavior if those expressions don't evaluate to
true.- [dcl.attr.assume]
The compiler might not be able to do anything with an assumption, but that doesn't matter to the C++ standard. However, since some assumptions are obviously too complex to diagnose, the standard has this paragraph:
- [expr.const] p5.33
What this means is:
[[assume( (std::exit(0), B) )]]does not disqualifyEfrom being a core constant expression, regardless whetherBistrueorfalse[[assume(extremely_complicated(x))]]disqualifiesdivfrom being a core constant expressionECompilers can implement this very simply, by either
Efrom being a core constant expression, orfalse, don't disqualifyE, otherwise raise an error1) This cannot change observed behavior, such as by blowing an implementation-defined limit. An evaluation can take place, but it needs to behave as if it was a hypothetical evaluation.