On Sat, Dec 29, 2018 at 11:12 PM Caleb Spare <cesp...@gmail.com> wrote: > > 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 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.
First let me say that there have been many hours of discussions on the behavior of type inference for shift operations, and I don't think anybody has any interest in reopening that discussion. I believe that the spec does fully specify the current behavior. You even quote it: "it is first converted to the type it would assume if the shift expression were replaced by its left operand alone." Changing the shift expression, we get `x = float64(1)`. float64(1) is a constant expression. The 1 is an untyped constant. An untyped constant is converted to the type required by the expression. In this case the expression requires a float64. So the 1 is a float64. Then we reinsert the shift, and discover an attempt to shift a floating point constant, which is an error. You suggest that in `float64(1)` the 1 doesn't really have a type, so it should get the default type. But that would mean that `int64(1.2)` would be the same as `int64(1)`, whereas today it is an error. That said, this language change is still perhaps worth considering not because of shifts, which are fairly uninteresting, but for other reasons. See https://golang.org/issue/6923. Ian -- 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.