Evaluation order oddity

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
}