Undefined behavior when constexpr-evaluating negative bitshift? - c++

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).

Related

Why is it not legal to access global non constant variable in constexpr non member function

int d = 1;
constexpr void add()
{
d++;
}
int main()
{
}
GCC 7.1 will report errors below. The error message is very clear. The problem is that I don't see any explicit explanations in constexpr to describe it's illegal.
Can someone explain which rules defined in spec this case violate?
main.cpp: In function 'constexpr void add()':
main.cpp:8:1: error: the value of 'd' is not usable in a constant expression
}
^
main.cpp:4:5: note: 'int d' is not const
int d = 1;
^
From cppreference:
A core constant expression is any expression that does not have any one of the following in any subexpression (ignoring unevaluated expressions such as the operand of sizeof or the right operand of builtin && when the left operand evaluates to false).
...
16) modification of an object, unless the object has non-volatile literal type and its lifetime began within the evaluation of the expression.
In your example, d's lifetime began before add was evaluated - so any modification of d inside add is illegal. The example on the reference is specifically for incrementing, but this holds for all modifications.
Edit: Not quoting from the standard because as far as I'm aware, you've got to buy it...
The comments are good from a refer-you-to-the-standard perspective, but here hopefully a more intuitive explanation.
A constexpr function must be reducible to a constant expression at compile time. Since you are interacting with a regular, non-const int in the function, the compiler cannot determine what d++ is under all circumstances. Consider the following case:
int d;
constexpr void add() {
d++;
}
void foo() {
int n;
std::cin >> n;
d = n;
add();
}
In this case, the value of d inside foo() is indeterminate at compile time, and therefore, the constant expression that you are hoping add() to leave you with cannot be determined. I hope that helps.

Is a function pointer odr-used if it is called

This question is sparked by a comment here
Consider the following code
template <typename T, typename C>
void g(T, C) {}
template <typename T, typename C>
struct G
{
static constexpr void (*m) (T, C) = &g;
};
void foo()
{
auto l = [](int){return 42;};
G<int, decltype(l)>::m(420, l);
}
This is legal in C++17 everywhere, G::m is defined within G via inlined variables and all that.
What's weird is in C++14 and C++11 gcc rejects this stating m is used but never defined, while clang accepts it. Live
Is m odr-used? Or is this a gcc bug?
This is not a GCC bug. This is a interpretation of the c++14.
One and only one definition of every non-inline function or variable
that is odr-used (see below) is required to appear in the entire
program (including any standard and user-defined libraries). The
compiler is not required to diagnose this violation, but the behavior
of the program that violates it is undefined.
The error is
source:7:29: error: 'constexpr void (* const G<int,
foo()::<lambda(int)> >::m)(int, foo()::<lambda(int)>)', declared using
local type 'foo()::<lambda(int)>', is used but never defined
[-fpermissive]
And it is true that the type foo()::<lambda(int)> is never defined (by the programmer, it is actually defined by the compiler) since every lambda has a unique type.
replacing line static constexpr void (*m) (T, C) = &g; by static inline constexpr void (*m) (T, C) = &g; makes the error go away.
Which is clearly an indication that m is odr-used if not tag as inline.
I believe this message is a way of warning you that
auto l = [](int){return 42;};
G<int, decltype(l)>::m(420, l);
G<int, decltype(l)>::m(420, [](int){return 42;});
will result in the following error (even with using -std=c++1z and GCC or CLANG)
source: In function 'void foo()': <source>:15:34: error: could not
convert 'g' from 'foo()::<lambda(int)>' to 'foo()::<lambda(int)>'
G<int, decltype(l)>::m(420, g);
because the lambda types are unique.
However GCC transforms the error into a warning if you use -fpermissive which is really a way of knowing that it is not a GCC bug but an over-protection meant to discourage certain practices.
One way of lifting any ambiguity without using -fpermissive is to do what GCC recommends and declare the prototype.
template <typename T, typename C>
void g(T, C) {}
template <typename T, typename C>
struct G
{
static constexpr void (*m) (T, C) = &g;
};
typedef int (*return_int)(int);
void foo()
{
auto l = [](int){return 42;};
G<int, return_int>::m(420, l);
G<int, return_int>::m(420, [](int){return 41;});
//Or even better if the aim is to use embedded template parameters
return_int ln = [](int){return 42;};
G<int, decltype(ln)>::m(420, ln);
}
This compile fines.
Last, why did the GCC team make this protection pop with C++17. I don't know maybe they receive complaints that the behavior was over protective and that cast errors of lambda types were sufficient. But seriously the error
error: could not
convert 'g' from 'foo()::<lambda(int)>' to 'foo()::<lambda(int)>'
Is a little bit off-putting the first time… When getting this error your mind wonders, until you come to the conclusion that decltype([](int){return 42;}) is different from decltype([](int){return 42;}) ! When you try this...
#include <random>
#include <iostream>
int main()
{
auto l = [](int) {return 42; };
std::cout << typeid(decltype(l)).name() << std::endl;
auto m = [](int) {return 42; };
std::cout << typeid(decltype(m)).name() << std::endl;
return 0;
}
which outputs (under visual studio)
class <lambda_37799c61f9e31cc7b5f51a1bd0a09621>
class <lambda_818eb0a43a553fc43d3adadd7480d71e>
TLDR m isn't odr-used, this is indeed a GCC bug.
odr-use
Intuitively, variables needs to be stored in memory somewhere. The only exception is when the value of the variable can be optimized out by the compiler and never used in another way. Odr-usage formalizes this idea: a variable requires a definition only if it is odr-used.
Odr-usage is defined by [basic.def.odr]
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion to x yields a constant expression that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion is applied to e, or e is a discarded-value expression.
In other words, x isn't odr-used if either
x doesn't appear as a potentially-evaluated expression (e.g. decltype(x)).
x isn't an object (e.g. a reference) and applying the lvalue-to-rvalue conversion to x yields a constant expression.
x is an object and applying the lvalue-to-rvalue conversion to x yields a constant expression. Furthermore, there is a "coupled" expression e that either has the lvalue-to-rvalue conversion applied to it or is discarded.
The "coupling" refers to the intuition that there is some enclosing expression closely related to x that can only be used in certain ways without needing x be stored in memory. This notion is formalized by the definition of potential results [basic.def.odr]
The set of potential results of an expression e is defined as follows:
If e is an id-expression, the set contains only e.
If e is a subscripting operation with an array operand, the set contains the potential results of that operand.
If e is a class member access expression, the set contains the potential results of the object expression.
If e is a pointer-to-member expression whose second operand is a constant expression, the set contains the potential results of the object expression.
If e has the form (e1), the set contains the potential results of e1.
If e is a glvalue conditional expression, the set is the union of the sets of potential results of the second and third operands.
If e is a comma expression, the set contains the potential results of the right operand.
Otherwise, the set is empty.
An expression ex that is within the potential results of e is "coupled" with e.
The question
Applying the definitions
m is potentially evaluated.
Applying the lvalue-to-rvalue conversion to m yields a constant expression.
m is an object.
m is an expression whose potential results includes m and has the lvalue-to-rvalue conversion applied to.
We thus conclude m isn't odr-used.

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.

Why do constant expressions have an exclusion for undefined behavior?

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.