[ub] ub due to left operand of shift

Howard Hinnant howard.hinnant at gmail.com
Sat Oct 26 20:09:12 CEST 2013


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



More information about the ub mailing list