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.

Reply via email to