Date: Sat, 26 Oct 2013 14:09:12 -0400

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

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

Received on 2013-10-26 20:08:57