C++ Logo

SG12

Advanced search

Subject: Re: [ub] ub due to left operand of shift
From: Howard Hinnant (howard.hinnant_at_[hidden])
Date: 2013-10-26 13:09:12


Consider:

int
stest()
{
    return -1 << 0;
}

With clang++ using -O3, the assembly is:

__Z5stestv: ## @_Z5stestv
        .cfi_startproc
## BB#0: ## %entry
        pushq %rbp
Ltmp2:
        .cfi_def_cfa_offset 16
Ltmp3:
        .cfi_offset %rbp, -16
        movq %rsp, %rbp
Ltmp4:
        .cfi_def_cfa_register %rbp
        movl $-1, %eax
        popq %rbp
        ret
        .cfi_endproc

Which looks pretty good to me (just returns -1).

But:

constexpr
int
stest()
{
    return -1 << 0;
}

Yields:

test.cpp:103:1: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
stest()
^
test.cpp:105:15: note: left shift of negative value -1
    return -1 << 0;
              ^
1 error generated.

What danger, or what non-portable behavior has been prevented here?

Might we reword [expr.shift]/p2 in terms of shifting 1 bits off of the most significant end, instead of in terms of E1*2^E2?

The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled. <del>If E1 has an unsigned type, the value of the result is E1 × 2^E2, reduced modulo one more than the maximum value representable in the result type. Otherwise, if E1 has a signed type and non-negative value, and E1 × 2^E2 is representable in the corresponding unsigned type of the result type, then that value, converted to the result type, is the resulting value; otherwise<del><ins>If E1 has signed type and the result would have fewer non-zero bits than E1</ins>, the behavior is undefined.

Indeed, the conflation of bit shifts with multiplying / dividing by 2 has been a never-ending source of bugs over the decades. Let's just say no to that.

Howard


SG12 list run by herb.sutter at gmail.com