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)
+			}
+		})
+	}
+}