Your first case "works" due to integer overflow.
Because you are using uint(bitSize)
, the expression is no longer a constant and is instead evaluated at runtime.
Breaking it down, this yields:
uint64(1)
: that's just 1
1 << 64
: (still a uint64 since 1
is a uint64): 2^64 == 0 in uint64.
0 - 1
: 2^64 - 1 (18446744073709551615
) due to unsigned integer wrap around.
If you change the expression to a constant (by replacing uint(bitSize)
with uint(64)
, it will fail at compile time.
In the second example, you are asking the compiler to specifically treat 1
as a uint64. It will try to compute the constant 1 << 64
and store it in a uint64, but this is not possible without overflowing.
Instead, you should let the compiler operate on untyped constants and only force the final result to be uint64 (which fits):
fmt.Println(uint64(1<<64 - 1))
This works because shifts on untyped integer constants are also untyped integer constants. The entire constant expression can be evaluated to 2^64 - 1
before trying to store it in a uint64. The spec on constant expressions has the following to say:
If the left operand of a constant shift expression is an untyped
constant, the result is an integer constant; otherwise it is a
constant of the same type as the left operand, which must be of
integer type.