At Canonical we’re increasingly invested in gccgo
. While testing various packages built with gccgo
we ran across test failures which we traced to an innocent looking piece of code.
package main
import "fmt"
type T struct {
i int
}
func (t *T) readInt32() int32 {
t.i += 4
return 42 // not important
}
func main() {
var d = T{i:200}
end := d.i - 4 + int(d.readInt32())
fmt.Println(end)
}
So, knowing that the Go spec defines the order of evaluation of functions calls and assignments as left to right, let’s try to predict what this code will print.
200 - 4 + 42 = 238
So, is 238 the correct answer ?
% go run odd.go
242
Wow, that is odd. Let’s see what gccgo
thinks the answer is
% go run -compiler gccgo odd.go
238
Well, at least it got the right answer, so is gc
at fault or are the arguments actually evaluated in the opposite direction ?
It turns out that both compilers are correct because the evaluation order of this sort of expression is not specified. In fact, in the spec, this case is called out in an example which is strongly reminiscent of the original code.
a := 1
f := func() int { a = 2; return 3 }
x := []int{a, f()} // x may be [1, 3] or [2, 3]: evaluation order between a and f() is not specified
The correct solution to this problem is to break the expression into two lines, removing the ambiguity about when the addition to d.i
is visible.
func main() {
var d = T{i:200}
v := int(d.readInt32())
end := v + d.i - 4
fmt.Println(end) // prints 242
}