go/ssa: emit Low expression before High in *ast.Slice
Order of evaluation is lexical left-to-right order. Emit the Low subexpression before High subexpression when building an *ast.Slice.
Fixes golang/go#52142
Change-Id: If56a1c9ac12485717d2783efdf791fe24582b306
Reviewed-on: https://go-review.googlesource.com/c/tools/+/398057
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Tim King <taking@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/go/ssa/builder.go b/go/ssa/builder.go
index 05aa458..9a691e9 100644
--- a/go/ssa/builder.go
+++ b/go/ssa/builder.go
@@ -686,12 +686,12 @@
default:
panic("unreachable")
}
- if e.High != nil {
- high = b.expr(fn, e.High)
- }
if e.Low != nil {
low = b.expr(fn, e.Low)
}
+ if e.High != nil {
+ high = b.expr(fn, e.High)
+ }
if e.Slice3 {
max = b.expr(fn, e.Max)
}
diff --git a/go/ssa/builder_test.go b/go/ssa/builder_test.go
index 107bc9d..84f0692 100644
--- a/go/ssa/builder_test.go
+++ b/go/ssa/builder_test.go
@@ -6,6 +6,7 @@
import (
"bytes"
+ "fmt"
"go/ast"
"go/build"
"go/importer"
@@ -627,3 +628,67 @@
})
}
}
+
+// TestOrderOfOperations ensures order of operations are as intended.
+func TestOrderOfOperations(t *testing.T) {
+ // Testing for the order of operations within an expression is done
+ // by collecting the sequence of direct function calls within a *Function.
+ // Callees are all external functions so they cannot be safely re-ordered by ssa.
+ const input = `
+package p
+
+func a() int
+func b() int
+func c() int
+
+func slice(s []int) []int { return s[a():b()] }
+func sliceMax(s []int) []int { return s[a():b():c()] }
+
+`
+
+ // Parse
+ var conf loader.Config
+ f, err := conf.ParseFile("<input>", input)
+ if err != nil {
+ t.Fatalf("parse: %v", err)
+ }
+ conf.CreateFromFiles("p", f)
+
+ // Load
+ lprog, err := conf.Load()
+ if err != nil {
+ t.Fatalf("Load: %v", err)
+ }
+
+ // Create and build SSA
+ prog := ssautil.CreateProgram(lprog, 0)
+ p := prog.Package(lprog.Package("p").Pkg)
+ p.Build()
+
+ for _, item := range []struct {
+ fn string
+ want string // sequence of calls within the function.
+ }{
+ {"sliceMax", "[a() b() c()]"},
+ {"slice", "[a() b()]"},
+ } {
+ fn := p.Func(item.fn)
+ want := item.want
+ t.Run(item.fn, func(t *testing.T) {
+ t.Parallel()
+
+ var calls []string
+ for _, b := range fn.Blocks {
+ for _, instr := range b.Instrs {
+ if call, ok := instr.(ssa.CallInstruction); ok {
+ calls = append(calls, call.String())
+ }
+ }
+ }
+ if got := fmt.Sprint(calls); got != want {
+ fn.WriteTo(os.Stderr)
+ t.Errorf("Expected sequence of function calls in %s was %s. got %s", fn, want, got)
+ }
+ })
+ }
+}