I noticed the following: package main
func main() { x := float64(1 << 3) // fine (constant expression) v := uint(3) x = float64(1 << v) // invalid operation: 1 << uint(v) (shift of type float64) _ = x } (playground: https://play.golang.org/p/xMhEvyMw0wg) It's clear from the spec why the first one works, but I was a little surprised that the second one doesn't. As best I can tell, the spec discusses this at https://golang.org/ref/spec#Operators: The right operand in a shift expression must have unsigned integer type or be an untyped constant representable <https://golang.org/ref/spec#Representability> by a value of type uint. If the left operand of a non-constant shift expression is an untyped constant, it is first converted to the type it would assume if the shift expression were replaced by its left operand alone. var s uint = 33 var i = 1<<s // 1 has type int var j int32 = 1<<s // 1 has type int32; j == 0 var k = uint64(1<<s) // 1 has type uint64; k == 1<<33 var m int = 1.0<<s // 1.0 has type int; m == 0 if ints are 32bits in size var n = 1.0<<s == j // 1.0 has type int32; n == true var o = 1<<s == 2<<s // 1 and 2 have type int; o == true if ints are 32bits in size var p = 1<<s == 1<<33 // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int var u = 1.0<<s // illegal: 1.0 has type float64, cannot shift var u1 = 1.0<<s != 0 // illegal: 1.0 has type float64, cannot shift var u2 = 1<<s != 1.0 // illegal: 1 has type float64, cannot shift var v float32 = 1<<s // illegal: 1 has type float32, cannot shift var w int64 = 1.0<<33 // 1.0<<33 is a constant shift expression var x = a[1.0<<s] // 1.0 has type int; x == a[0] if ints are 32bits in size var a = make([]byte, 1.0<<s) // 1.0 has type int; len(a) == 0 if ints are 32bits in size I'm trying to understand the logic by which the 1 in float64(1 << v) is converted to a float64. The last sentence says "[...] it is first converted to the type it would assume if the shift expression were replaced by its left operand alone", but if we wrote float64(1) would we really say that the 1 is a float64? Clearly float64(1) is, as a whole, a typed float64 constant, but I don't see how we can infer a type for 1 itself in that context. The most similar of the above examples from the spec is this one: var v float32 = 1<<s // illegal: 1 has type float32, cannot shift This seems logical: the whole RHS must evaluate to something assignable to a float32 variable, so 1 is inferred to be a float32. But if we write var v = float32(1 << s) as in my example code then it doesn't really make sense to infer float32 as the type for 1. In fact, given that the code is trying to convert 1 << s *to* a float32, it seems better to infer that the type is *not* float32! I found a couple of related issues discussing this: https://github.com/golang/go/issues/13061#issuecomment-152297737 https://github.com/golang/go/issues/19963 So I have two questions: 1. Does the spec fully specify this behavior? (I think it ought to be clarified.) 2. Should the spec be changed to make this behavior more sensible? Regarding (2), my suggested change would be for an untyped constant on the LHS of a shift that is inside a conversion to be inferred to have its default type rather than the conversion type: s := uint(3) _ = float64(1 << s) // used to be an error, ok with my suggested change _ = int(1.0 << s) // used to be ok, now an error with my suggested change Given that it would break certain (very unusual) code, it would probably have to be a Go 2 change. Caleb -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.