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.

Reply via email to