Why do constant expressions have an exclusion for undefined behavior? - c++

I was researching what is allowed in a core constant expression*, which is covered in section 5.19 Constant expressions paragraph 2 of the draft C++ standard which says:
A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (3.2), but subexpressions of logical AND (5.14), logical OR (5.15), and conditional (5.16) operations that are not evaluated are not considered [ Note: An overloaded operator invokes a function.—end note ]:
and lists out the exclusions in the bullets that follows and includes (emphasis mine):
— an operation that would have undefined behavior [ Note: including, for example, signed integer overflow (Clause 5), certain pointer arithmetic (5.7), division by zero (5.6), or certain shift operations (5.8) —end note ];
Huh? Why do constant expressions need this clause to cover undefined behavior? Is there something special about constant expressions that requires undefined behavior to have a special carve out in the exclusions?
Does having this clause give us any advantages or tools we would not have without it?
For reference, this looks like the last revision of the proposal for Generalized Constant Expressions.

The wording is actually the subject of defect report #1313 which says:
The requirements for constant expressions do not currently, but should, exclude expressions that have undefined behavior, such as pointer arithmetic when the pointers do not point to elements of the same array.
The resolution being the current wording we have now, so this clearly was intended, so what tools does this give us?
Let's see what happens when we try to create a constexpr variable with an expression that contains undefined behavior, we will use clang for all the following examples. This code (see it live):
constexpr int x = std::numeric_limits<int>::max() + 1 ;
produces the following error:
error: constexpr variable 'x' must be initialized by a constant expression
constexpr int x = std::numeric_limits<int>::max() + 1 ;
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: value 2147483648 is outside the range of representable values of type 'int'
constexpr int x = std::numeric_limits<int>::max() + 1 ;
^
This code (see it live):
constexpr int x = 1 << 33 ; // Assuming 32-bit int
produces this error:
error: constexpr variable 'x' must be initialized by a constant expression
constexpr int x = 1 << 33 ; // Assuming 32-bit int
^ ~~~~~~~
note: shift count 33 >= width of type 'int' (32 bits)
constexpr int x = 1 << 33 ; // Assuming 32-bit int
^
and this code which has undefined behavior in a constexpr function:
constexpr const char *str = "Hello World" ;
constexpr char access( int index )
{
return str[index] ;
}
int main()
{
constexpr char ch = access( 20 ) ;
}
produces this error:
error: constexpr variable 'ch' must be initialized by a constant expression
constexpr char ch = access( 20 ) ;
^ ~~~~~~~~~~~~
note: cannot refer to element 20 of array of 12 elements in a constant expression
return str[index] ;
^
Well that is useful the compiler can detect undefined behavior in constexpr, or at least what clang believes is undefined. Note, gcc behaves the same except in the case of undefined behavior with right and left shift, gcc will usually produce a warning in these cases but still sees the expression as constant.
We can use this functionality via SFINAE to detect whether an addition expression would cause overflow, the following contrived example was inspired by dyp's clever answer here:
#include <iostream>
#include <limits>
template <typename T1, typename T2>
struct addIsDefined
{
template <T1 t1, T2 t2>
static constexpr bool isDefined()
{
return isDefinedHelper<t1,t2>(0) ;
}
template <T1 t1, T2 t2, decltype( t1 + t2 ) result = t1+t2>
static constexpr bool isDefinedHelper(int)
{
return true ;
}
template <T1 t1, T2 t2>
static constexpr bool isDefinedHelper(...)
{
return false ;
}
};
int main()
{
std::cout << std::boolalpha <<
addIsDefined<int,int>::isDefined<10,10>() << std::endl ;
std::cout << std::boolalpha <<
addIsDefined<int,int>::isDefined<std::numeric_limits<int>::max(),1>() << std::endl ;
std::cout << std::boolalpha <<
addIsDefined<unsigned int,unsigned int>::isDefined<std::numeric_limits<unsigned int>::max(),std::numeric_limits<unsigned int>::max()>() << std::endl ;
}
which results in (see it live):
true
false
true
It is not evident that the standard requires this behavior but apparently this comment by Howard Hinnant indicates it indeed is:
[...] and is also constexpr, meaning UB is caught at compile time
Update
Somehow I missed Issue 695 Compile-time calculation errors in constexpr functions which revolves over the wording of section 5 paragraph 4 which used to say (emphasis mine going forward):
If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined, unless such an expression appears where an integral constant expression is required (5.19 [expr.const]), in which case the program is ill-formed.
and goes on to say:
intended as an acceptable Standardese circumlocution for “evaluated at compile time,” a concept that is not directly defined by the Standard. It is not clear that this formulation adequately covers constexpr functions.
and a later note says:
[...]There is a tension between wanting to diagnose errors at compile time versus not diagnosing errors that will not actually occur at runtime.[...]The consensus of the CWG was that an expression like 1/0 should simply be considered non-constant; any diagnostic would result from the use of the expression in a context requiring a constant expression.
which if I am reading correctly confirms the intention was to be able to diagnose undefined behavior at compile time in the context requiring a constant expression.
We can not definitely say this was the intent but is does strongly suggest it was. The difference in how clang and gcc treat undefined shifts does leave some room for doubt.
I filed a gcc bug report: Right and left shift undefined behavior not an error in a constexpr. Although it seems like this is conforming, it does break SFINAE and we can see from my answer to Is it a conforming compiler extension to treat non-constexpr standard library functions as constexpr? that divergence in implementation observable to SFINAE users seems undesirable to the committee.

When we talk about undefined behavior, it is important to remember that the Standard leaves the behavior undefined for these cases. It does not prohibit implementations from making stronger guarantees. For example some implementations may guarantee that signed integer overflow wraps around, while others may guarantee saturation.
Requiring compilers to process constant expressions involving undefined behavior would limit the guarantees that an implementation could make, restricting them to producing some value without side effects (what the Standard calls indeterminate value). That excludes a lot of the extended guarantees found in the real world.
For example, some implementation or companion standard (i.e. POSIX) may define the behavior of integral division by zero to generate a signal. That's a side effect which would be lost if the expression were calculated at compile-time instead.
So, these expressions are rejected at compile-time to avoid loss of side effects in the execution environment.

There is another point to excluding undefined behavior from constant expressions: constant expressions should, by definition, be evaluated by the compiler at compile time. Allowing a constant expression to invoke undefined behavior would allow the compiler itself to show undefined behavior. And a compiler that formats your hard-drive because you compile some evil code is not something you want to have.

Related

Why is calling a constexpr function with a member array not a constant expression?

I have the following helper function:
template<typename T, std::size_t N>
constexpr std::size_t Length(const T(&)[N]) {
return N;
}
Which returns the length of a static array. In the past this always has worked but when I do this:
struct Foo
{
unsigned int temp1[3];
void Bar()
{
constexpr std::size_t t = Length(temp1); // Error here
}
};
I get an error when using MSVS 2017:
error C2131: expression did not evaluate to a constant
note: failure was caused by a read of a variable outside its lifetime
note: see usage of 'this'
I was hoping someone can shed light on what I'm doing wrong.
MSVC is correct. Length(temp1) is not a constant expression. From [expr.const]p2
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
this, except in a constexpr function or a constexpr constructor that is being evaluated as part of e;
temp1 evaluates this implicitly (because you are referring to this->temp1), and so you don't have a constant expression. gcc and clang accept it because they support VLAs as an extension (try compiling with -Werror=vla or -pedantic-errors).
Why isn't this allowed? Well, you could access the underlying elements and potentially modify them. This is completely fine if you are dealing with a constexpr array or an array that is being evaluated as a constant expression, but if you are not, then you cannot possibly have a constant expression as you will be manipulating values that are set at run time.
Length(decltype(temp1){})
seems to work.
Unfortunately, I cannot comment, but Mehrdad 's solution is wrong. The reason: it is
not technically undefined behavior but it is undefined behavior. During constexpr evaluation, the compiler must catch undefined behavior. Therefore, the code is ill-formed.
Your question's already been answered, but in terms of how to "fix" it, a quick-and-dirty way is to replace
Length(temp1)
with
Length(*(true ? NULL : &temp1))
which I think is technically undefined behavior but practically going to work fine for MSVC.
If you need a solution that works despite the UB, you can change Length to use a pointer:
template<typename T, std::size_t N>
constexpr std::size_t Length(const T(*)[N]) {
return N;
}
and then you can use Length(true ? NULL : &temp1).

Undefined behavior when constexpr-evaluating negative bitshift?

Consider the following snippet of code:
int main(){
constexpr int x = -1;
if(x >= 0){
constexpr int y = 1<<x;
}
}
GCC 7 (and probably other versions of GCC) refuses to compile this and says:
error: right operand of shift expression '(1 << -1)' is negative [-fpermissive]
I can guess where this may have come from: the constexpr declaration on y makes GCC evaluate y at compile time, where it might be negative. Removing the constexpr fixes the error.
However, is this undefined behavior by the standard? The condition is always false, so the value of y will never be used.
In my actual code, x is a template parameter, which may or may not be negative.
GCC complains because your definition of y is explicitly an ill-formed constexpr declaration. The initialzier violates [expr.const]/2, which specifies:
An expression e is a core constant expression unless the
evaluation of e, following the rules of the abstract machine, would
evaluate one of the following expressions:
an operation that would have undefined behavior as specified in Clauses [intro] through [cpp] of this International Standard [ Note:
including, for example, signed integer overflow (Clause [expr]),
certain pointer arithmetic ([expr.add]), division by zero, or
certain shift operations  — end note ] ;
So you can't use 1<<x to initialize y. It doesn't matter that the branch will never be executed and can be eliminated. GCC is still obligated to verify it's semantically correct.
Just as StoryTeller explained, this is the expected behavior, as left shifting by a negative number is undefined behavior and an expression resulting in UB can't be used in a core constant expression (the fact that you don't try to access the result of that expression during runtime doesnT' change the fact that you require the compiler to evaluate it during compiletime).
If your branch actually depends on a template parameter you can work around this by using if constexpr:
template<int x>
constexpr int foo() {
if constexpr (x >= 0) {
constexpr int y = 1 << x;
return y;
}
return 0;
}
Edit: As the answers to StoryTeller's question explain, this ONLY works inside a template and only if the conditional depends on the template parameter (more detailed explanation in the answers).

Does constexpr imply noexcept?

Does constexpr specifier imply noexcept specifier for a function? Answer to the similar question says "yes" concerning inline specifier, but Eric Niebler's article makes me wonder about possible answer to the current one. On my mind answer can depends on context of a using a constexpr function: is it constant expression context or run-time context, i.e. are all the parameters of the function known at compile time or not.
I expected that the answer is "yes", but simple check shows that it is not the case.
constexpr
bool f(int) noexcept
{
return true;
}
constexpr
bool g(int)
{
return true;
}
static_assert(noexcept(f(1)));
static_assert(noexcept(g(2))); // comment this line to check runtime behaviour
#include <cassert>
#include <cstdlib>
int
main(int argc, char * [])
{
assert(noexcept(f(argc)));
assert(noexcept(g(argc)));
return EXIT_SUCCESS;
}
No this can not be the case, because not every inovocation of constexpr function has to be able to be evaluated as subexpression of a core constant expression. We only need one argument value that allows for this. So a constexpr function can contain a throw statement as long as we have a argument value which does not invoke that branch.
This is covered in the draft C++14 standard section 7.1.5 The constexpr specifier [dcl.constexpr] which tells us what is allowed in a constexpr function:
The definition of a constexpr function shall satisfy the following constraints:
it shall not be virtual (10.3);
its return type shall be a literal type;
each of its parameter types shall be a literal type;
its function-body shall be = delete, = default, or a compound-statement that does not contain
an asm-definition,
a goto statement,
a try-block, or
a definition of a variable of non-literal type or of static or thread storage duration or for which
no initialization is performed.
which as we can see does not forbid throw and in fact forbids very little since the Relaxing constraints on constexpr functions proposal became part of C++14.
Below we see the rule that says a constexpr function is well-formed if at least one argument value exists such that it can be evaluated as a subexpression of a core constant expression:
For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting
constexpr constructor, if no argument values exist such that an invocation of the function or constructor
could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no
diagnostic required.
and below this paragraph we have the following example, which shows a perfect example for this case:
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
So we would expect the output for the following example:
#include <iostream>
constexpr int f(bool b) { return b ? throw 0 : 0; }
int main() {
std::cout << noexcept( f(1) ) << "\n"
<< noexcept( f(0) ) << "\n" ;
}
to be (see it live with gcc):
0
1
Visual Studio via webcompiler also produces the same result. As hvd noted, clang has a bug as documented by the bug report noexcept should check whether the expression is a constant expression.
Defect Report 1129
Defect report 1129: Default nothrow for constexpr functions asks the same question:
A constexpr function is not permitted to return via an exception. This should be recognised, and a function declared constexpr without an explicit exception specification should be treated as if declared noexcept(true) rather than the usual noexcept(false). For a function template declared constexpr without an explicit exception specification, it should be considered noexcept(true) if and only if the constexpr keyword is respected on a given instantiation.
and the response was:
The premise is not correct: an exception is forbidden only when a constexpr function is invoked in a context that requires a constant expression. Used as an ordinary function, it can throw.
and it modified 5.3.7 [expr.unary.noexcept] paragraph 3 bullet 1 (addition noted with emphasis):
a potentially evaluated call80 to a function, member function, function pointer, or member function pointer that does not have a non-throwing exception-specification (15.4 [except.spec]), unless the call is a constant expression (5.20 [expr.const]),
It is said of noexcept that:
The result is false if the expression contains [...] call to any type of function that does not have non-throwing exception specification, unless it is a constant expression.
Also, about constexpr, it is true that:
the noexcept operator always returns true for a constant expression
Under no circumstances does it seem to imply that the constexpr specifier forces a noexcept specifier for the surrounded expression, as someone showed in the comments with a counterexample and you also verified.
Anyway, from the documentation, there is the following interesting note about the relationship between noexcept and constexpr:
Because the noexcept operator always returns true for a constant expression, it can be used to check if a particular invocation of a constexpr function takes the constant expression branch
EDIT: example with GCC
Thanks to #hvd for his interesting comment/example with GCC about my last quote.
constexpr int f(int i) {
return i == 0 ? i : f(i - 1);
}
int main() {
noexcept(f(512));
return noexcept(f(0)) == noexcept(f(0));
}
The code above returns 0, with a warning that the statement noexcept(f(512)) has no effect.
Commenting out that statement that supposedly has no effect, the return value changes to 1.
EDIT: known bug on clang
Again, thanks to #hvd also for this link, that is about a well known bug in clang regarding the code mentioned in the question.
Quote from the bug report:
Quoth the book of C++, [expr.unary.noexcept]p3:
"The result of the noexcept operator is false if in a potentially-evaluated context the expression would contain a potentially-evaluated call to a function, member function, function pointer, or member function pointer that does not have a non-throwing exception-specification (15.4), unless the call is a constant expression (5.19)".
We do not implement that last phrase.
You are allowed to throw an exception in a constexpr function. It is designed as such so that the implementer can indicate an error to the compiler. Consider that you have the following function:
constexpr int fast_sqrt(int x) {
return (x < 0) ? throw invalid_domain() : fast_sqrt_impl(x);
}
In this case if x is negative we need to stop compilation immediately and indicate the problem to the user via compiler error. This follows the idea that compiler errors are better than runtime errors (fail fast).
C++ standard says this in (5.20):
A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the
abstract machine (1.9), would evaluate one of the following expressions:
— a throw-expression (5.17)
No, in general it doesn't.
A constexpr function can be invoked in a non-constepr context in which it is allowed to throw an exception (except of course if you manually specify it to be noexcept(true)).
However, as part of a constant expression (such as in your example), it should behave as if specified to be noexcept(true) (of course, if the evaluation of the expression would result in an exception being thrown this can't result in a call to std::terminate as the program isn't running yet, but instead leads to a compile time error).
As I would have expected, your example doesn't trigger the static assertion with MSVC and g++. I'm not sure, whether this is a bug in clang or I'm missunderstanding something in the standard.

constexpr function and hardcoded arguments

So generally constexpr functions are functions, that are executed in compile time, when arguments passed to it are also constexpr so following:
constexpr int function(int x, int y){
return x+y;
}
with arguments declared as follows:
constexpr int x = 5;
constexpr int y = 6;
will be executed in compile time, but with following declaration of arguments:
int x=5;
int y=6;
It will not. I wonder what would happen if we call this function in a following way:
function(5,6);
From technical point of view 5 and 6 are rvalues but there is no way (I guess), that they can be casted to constexpr (if we can say generally about casting to constexpr), so in my opinion it will be executed in a runtime. However there is no practical reason to execute it in a run time as both x and y are known during compilation time.
So my question is How is it in real life? Will this function be executed in run-time or compile time
constexpr int fun(int x, int y) { return x+y; }
fun(5,6) // << constant expression?
tl;dr
5 and 6 are constant expressions. Thus fun(5,6) also is a constant expression and will be evaluated at compile time where this is mandatory (non-type templates for instance).
stuff...
I had a quick look into the standard and I hope I didn't miss any important points.
We already know from #42's answer:
According to N4527 int is a valid paramter type for a constexpr function since it is a literal type (since it is a scalar type which is by §3.9/10 of the same document a literal type). Therefore, fun is a valid constexpr function.
It provides code that puts fun(5,6) into a context where a constant expression is required and it seems to be accepted by certain compilers.
Now the question is whether this is valid, standard-conformant behaviour.
§5.20 from N4527 says:
A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:
here comes a large list of things that prevent expressions from being core constant expression
That list does not contain "constexpr function with constant expression arguments" which are therefore core constant expressions (unless they are undefined when used).
Thus, if 5 and 6 are constant expressions, then fun(5,6) is a constant expression if fun is a valid constexpr function and is defined before using it. The given function satisfies the required constraints in §7.1.5/3 and is a valid constexpr function.
Both 5 and 6 are integer literals of type int as per §2.13.2
1) An integer literal is a sequence of digits that has no period or exponent part, with optional separating single quotes that are ignored when determining its value. [...]
2) The type of an integer literal is the first of the corresponding list in Table 5 in which its value can be represented.
Suffix: none, Decimal literal: int, long int, long long int
Now looking at §5.20 again we see: both are constant expressions.
According to the draft standard N4527 7.1.5/3 The constexpr specifier [dcl.constexpr] (Emphasis mine):
The definition of a constexpr function shall satisfy the following
constraints:
(3.1) — it shall not be virtual (10.3);
(3.2) — its return type shall be a literal type;
(3.3) — each of its parameter types shall be a literal type;
...
Thus, calling function(5,6); satisfies the definition of a constexpr function and execution will take place at compile time.
Moreover, you can test it by yourself by using std::integral_constant:
#include <iostream>
#include <type_traits>
constexpr int fun(int x, int y) {
return x + y;
}
int main() {
std::cout << std::integral_constant<int, fun(5, 6)>::value << std::endl;
}
LIVE DEMO
If input parameters in fun are not constexpr compilation will fail.

Is constexpr a "hint" (like inline) or "a binding request" to the compiler?

Is constexpr an indicator for the compiler or does it mandate a behaviour ?
The example at hand is the following :
template<typename T>
std::size_t constexpr getID() { return typeid(T).hash_code(); }
hash_code is a runtime constant, yet this snippet would compile even though a compile time evaluation is requested with constexpr. Only after the return value is used where a compile time constant is expected, would we get noticed that this is not usable as a constexpr function.
So is constexpr a "hint" (much like the inline keyword) or "a binding request" to the compiler ?
Is constexpr a “hint” (like inline) or “a binding request” to the compiler?
It is neither. Forget about when it is evaluated. Everything (with a few minor exceptions, notably involving volatile) is evaluated whenever the compiler deems it necessary to produce the behaviour of the C++ abstract machine. There isn't much else to say about when things are evaluated.
The compiler is free to produce code that evaluates what would be constant expressions at runtime if that doesn't produce a different behaviour. It is free to produce code that evaluates things not marked constexpr at compile-time if it has the smarts.
If not about compile-time vs runtime, what is constexpr about, then?
constexpr allows things to be treated as constant expressions. Anything marked constexpr must have the possibility of producing a constant expression in some way.
In the case of functions, they can be able to produce constant expressions with some arguments but not others. But as long as there is some set of arguments that can result in a constant expression, a function can be marked constexpr. If such a set of arguments is used in a function call, that expression is a constant expression. Does that mean it is evaluated at compile-time? See above. It's evaluated when the compiler deems appropriate. The only thing it means is that you can use it in a context requiring a constant expression.
For variables, either they are constant expressions or not. They have no arguments, so if constexpr they always have to be initialised with constant expressions.
TL;DR: constexpr is about tagging things as being usable in constant expressions, not about deciding when to evaluate them.
With that out of the way, it appears your function template is ill-formed. There is no set of arguments that could result in a constant expression. The standard doesn't require a diagnostic for this, though.
From the C++11 Wiki page:
If a constexpr function or constructor is called with arguments which
aren't constant expressions, the call behaves as if the function were
not constexpr, and the resulting value is not a constant expression.
Likewise, if the expression in the return statement of a constexpr
function does not evaluate to a constant expression for a particular
invocation, the result is not a constant expression.
The constexpr specifier thus expresses the possibility to evaluate something at compile time and is subject to some restrictions when used.
For your particular snippet it seems to me that the C++11 constraint:
exactly one return statement that contains only literal values,
constexpr variables and functions
is not fulfilled, as hash_code is defined to be:
size_t hash_code() const;
In this case the standard draft n3242 says:
For a constexpr function, if no function argument values exist such
that the function invocation substitution would produce a constant
expression (5.19), the program is ill-formed; no diagnostic required.
I believe your example fits here.
constexpr functions can be used to evaluate compile time constants. So it is possible to use it like:
constexpr int func(int a) { return a+2; }
char x[func(10)];
If func is called during runtime, the compiler can evaluate this expression before if possible. But that is not a must but normally done if the input is also const.
It is also important to have constexpr constructors. This is the only chance to get non POD classes constexpr objects.
class Point
{
private:
int x;
int y;
public:
constexpr Point( int _x, int _y) : x(_x), y(_y) {}
constexpr int GetX() const { return x; }
};
constexpr Point p{1,2};
int main()
{
char i[p.GetX()];
return 0;
}
The complete answer to your question has two aspects:
A constexpr function can only be evaluated at compile-time when all
arguments can be evaluated at compile-time. It can still be used as
a normal function which is evaluated at runtime.
An constexpr variable must be initialized with a value evaluated at compile-time.
The compiler has to raise an error if it cannot do this.
You could assign the hash code to a constexpr variable and then get a compiler output:
#include <typeinfo>
#include <array>
template<typename T>
std::size_t constexpr getID() {
return []() {constexpr size_t id = typeid(T).hash_code(); return id;}(); }
int main() {
// both statement generate compiler errors
//std::array<int, typeid(int).hash_code()> a;
//constexpr size_t y = typeid(int).hash_code();
size_t x = getID<int>();
}