When you write
1<<64
1 above is not int64 . This is a constant literal. From language specifications:
Constant expressions are always evaluated exactly; intermediate values ββand constants themselves can significantly increase accuracy more than is supported on any previous type in the language.
Thus, a constant literal is evaluated at compile time and can be very large because it is not a specific type of language implementation.
The following is an overflow error:
var i int64 i = 1<<65 - 1
Because now the expression of constant literals evaluates the value more than int64 can contain.
Read more about it here .
To find out why your sample code works for i = 65 , refer to the specification below from the Golang specs:
The correct operand in the shift expression must be an unsigned integer type or be an untyped constant that can be converted to an unsigned integer type. If the left operand of a variable shift expression is an untyped constant, it is first converted to the type that it will assume that if the shift expression has been replaced with its left operand one .
Part of the block above relates to your code. Consider the following code:
a := 66 var j uint64 = 1<<uint64(a) - 1
Here in the shift operator, the right operand is a mutable exrpession. Thus, the entire switching operation becomes not a constant expression of the shift. Thus, as described above, the left operand 1 converted to uint64 .
Now the shift is performed on uint64(1) , which can be shifted with << to as many places as you want. You can move it beyond 64 bits, and implementation will easily allow it. But in this case, the memory containing uint64(1) above will contain all zeros.
Please note that this behavior is not the same as overflow according to language specifications. Again, the language implementation allows so many shifts until the right operator is a constant expression. So, for example, this will work:
a := 6666 var j uint64 = 1<<uint64(a) - 1
Think of it this way. Previously 1 was untyped. It had arbitrary precision (implementation dependent) and the whole number (all bits) was returned. Now, since it is uint64 , only the first 64 bits are counted.
This still causes an overflow because the left operand 1 is untypes and may contain a large number of bits, returning a value too large for uint64 :
var j uint64 = 1<<uint64(66) - 1