What type does an untyped constant take in a non-constant shift? What does this print?
Question 39HardGo 1.22 to 1.25
var s uint = 33
var i = 1 << s // 1 takes type int
var j int32 = 1 << s // 1 takes type int32: the bit is shifted out, j == 0
var k = uint64(1 << s) // 1 takes type uint64
var m int = 1.0 << s // 1.0 takes type int, so this is legal
// var u = 1.0 << s // compile error: shifted operand 1.0 (type float64) must be integer
// var v float32 = 1 << s // compile error: shifted operand 1 (type float32) must be integer
fmt.Println(i, j, k, m)
Output (64-bit platforms):
8589934592 0 8589934592 8589934592
This is one of the strangest rules in the spec. When the shift count is not a constant, the left operand, if it is an untyped constant, takes the type it would have if the shift were replaced by the left operand alone. So in var j int32 = 1 << s the constant 1 becomes int32, and shifting an int32 by 33 gives 0. There is no overflow error, because the shift happens at run time. In var u = 1.0 << s the constant would default to float64, and floats can't be shifted, so it doesn't compile. But var m int = 1.0 << s is fine, because the context makes 1.0 an int.
Related rules:
- With a constant shift count,
1 << 33is evaluated exactly at compile time, and only the final value must fit its type. - Since Go 1.13 the shift count can be a signed integer. A negative count panics at run time. A negative constant count is a compile error.
- Shifting by the type's width or more is legal and gives 0 (or -1 for right shifts of negative signed values). It is not undefined behavior as in C.
- A real-world trap:
x := float64(1 << n)with a variablendoes not compile, because the conversion makes1afloat64. Writefloat64(1 << n)asfloat64(int(1) << n), or usemath.Ldexp(1, int(n)). In general, give the constant an explicit type so readers don't need to know this rule.
More on Language Fundamentals & Types
- Q37How do struct field alignment and padding work? What are the sizes of these structs?
- Q38What is the difference between a method value and a method expression? What does this print?