Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 821
Bernard Avatar asked Oct 15 '25 15:10

Bernard


2 Answers

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.

like image 94
StoryTeller - Unslander Monica Avatar answered Oct 17 '25 04:10

StoryTeller - Unslander Monica


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

like image 27
MikeMB Avatar answered Oct 17 '25 06:10

MikeMB



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!