go/pointer: support ssa.SliceToArrayPointer

Updates golang/go#47326

Change-Id: I6b9b59e82b1b93f7a328ba802ad473d4104d7577
Reviewed-on: https://go-review.googlesource.com/c/tools/+/339890
Run-TryBot: Tim King <taking@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Guodong Li <guodongli@google.com>
Trust: Robert Findley <rfindley@google.com>
diff --git a/go/pointer/gen.go b/go/pointer/gen.go
index 36e1aef..ef5108a 100644
--- a/go/pointer/gen.go
+++ b/go/pointer/gen.go
@@ -791,7 +791,7 @@
 // Some SSA instructions always have singletons points-to sets:
 // 	Alloc, Function, Global, MakeChan, MakeClosure,  MakeInterface,  MakeMap,  MakeSlice.
 // Others may be singletons depending on their operands:
-// 	FreeVar, Const, Convert, FieldAddr, IndexAddr, Slice.
+// 	FreeVar, Const, Convert, FieldAddr, IndexAddr, Slice, SliceToArrayPointer.
 //
 // Idempotent.  Objects are created as needed, possibly via recursion
 // down the SSA value graph, e.g IndexAddr(FieldAddr(Alloc))).
@@ -882,6 +882,11 @@
 		case *ssa.Slice:
 			obj = a.objectNode(cgn, v.X)
 
+		case *ssa.SliceToArrayPointer:
+			// Going from a []T to a *[k]T for some k.
+			// A slice []T is treated as if it were a *T pointer.
+			obj = a.objectNode(cgn, v.X)
+
 		case *ssa.Convert:
 			// TODO(adonovan): opt: handle these cases too:
 			// - unsafe.Pointer->*T conversion acts like Alloc
@@ -1030,6 +1035,12 @@
 	case *ssa.Slice:
 		a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
 
+	case *ssa.SliceToArrayPointer:
+		// Going from a []T to a *[k]T (for some k) is a single `dst = src` constraint.
+		// Both []T and *[k]T are modelled as an *IdArrayT where IdArrayT is the identity
+		// node for an array of type T, i.e `type IdArrayT struct{elem T}`.
+		a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
+
 	case *ssa.If, *ssa.Jump:
 		// no-op.
 
diff --git a/go/pointer/pointer_go117_test.go b/go/pointer/pointer_go117_test.go
new file mode 100644
index 0000000..7546a06
--- /dev/null
+++ b/go/pointer/pointer_go117_test.go
@@ -0,0 +1,41 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// No testdata on Android.
+
+//go:build !android && go1.17
+// +build !android,go1.17
+
+package pointer_test
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"testing"
+)
+
+func TestSliceToArrayPointer(t *testing.T) {
+	// Based on TestInput. Keep this up to date with that.
+	filename := "testdata/arrays_go117.go"
+
+	if testing.Short() {
+		t.Skip("skipping in short mode; this test requires tons of memory; https://golang.org/issue/14113")
+	}
+
+	wd, err := os.Getwd()
+	if err != nil {
+		t.Fatalf("os.Getwd: %s", err)
+	}
+	fmt.Fprintf(os.Stderr, "Entering directory `%s'\n", wd)
+
+	content, err := ioutil.ReadFile(filename)
+	if err != nil {
+		t.Fatalf("couldn't read file '%s': %s", filename, err)
+	}
+
+	if !doOneInput(string(content), filename) {
+		t.Fail()
+	}
+}
diff --git a/go/pointer/testdata/arrays_go117.go b/go/pointer/testdata/arrays_go117.go
new file mode 100644
index 0000000..7a66f67
--- /dev/null
+++ b/go/pointer/testdata/arrays_go117.go
@@ -0,0 +1,173 @@
+//go:build ignore
+// +build ignore
+
+package main
+
+// Forked from arrays.go. Requires go1.17 to parse slice to array casts.
+// TODO(taking): Merge back into arrays.go once we can assume go1.17.
+
+var unknown bool // defeat dead-code elimination
+
+var a, b int
+
+func array1() {
+	sliceA := make([]*int, 10) // @line a1make
+	sliceA[0] = &a
+
+	var sliceB []*int
+	sliceB = append(sliceB, &b) // @line a1append
+
+	print(sliceA)    // @pointsto makeslice@a1make:16
+	print(sliceA[0]) // @pointsto main.a
+
+	print(sliceB)      // @pointsto append@a1append:17
+	print(sliceB[100]) // @pointsto main.b
+}
+
+func array2() {
+	sliceA := make([]*int, 10) // @line a2make
+	sliceA[0] = &a
+
+	sliceB := sliceA[:]
+
+	print(sliceA)    // @pointsto makeslice@a2make:16
+	print(sliceA[0]) // @pointsto main.a
+
+	print(sliceB)    // @pointsto makeslice@a2make:16
+	print(sliceB[0]) // @pointsto main.a
+}
+
+func array3() {
+	a := []interface{}{"", 1}
+	b := []interface{}{true, func() {}}
+	print(a[0]) // @types string | int
+	print(b[0]) // @types bool | func()
+}
+
+// Test of append, copy, slice.
+func array4() {
+	var s2 struct { // @line a4L0
+		a [3]int
+		b struct{ c, d int }
+	}
+	var sl1 = make([]*int, 10) // @line a4make
+	var someint int            // @line a4L1
+	sl1[1] = &someint
+	sl2 := append(sl1, &s2.a[1]) // @line a4append1
+	print(sl1)                   // @pointsto makeslice@a4make:16
+	print(sl2)                   // @pointsto append@a4append1:15 | makeslice@a4make:16
+	print(sl1[0])                // @pointsto someint@a4L1:6 | s2.a[*]@a4L0:6
+	print(sl2[0])                // @pointsto someint@a4L1:6 | s2.a[*]@a4L0:6
+
+	// In z=append(x,y) we should observe flow from y[*] to x[*].
+	var sl3 = make([]*int, 10) // @line a4L2
+	_ = append(sl3, &s2.a[1])
+	print(sl3)    // @pointsto makeslice@a4L2:16
+	print(sl3[0]) // @pointsto s2.a[*]@a4L0:6
+
+	var sl4 = []*int{&a} // @line a4L3
+	sl4a := append(sl4)  // @line a4L4
+	print(sl4a)          // @pointsto slicelit@a4L3:18 | append@a4L4:16
+	print(&sl4a[0])      // @pointsto slicelit[*]@a4L3:18 | append[*]@a4L4:16
+	print(sl4a[0])       // @pointsto main.a
+
+	var sl5 = []*int{&b} // @line a4L5
+	copy(sl5, sl4)
+	print(sl5)     // @pointsto slicelit@a4L5:18
+	print(&sl5[0]) // @pointsto slicelit[*]@a4L5:18
+	print(sl5[0])  // @pointsto main.b | main.a
+
+	var sl6 = sl5[:0]
+	print(sl6)     // @pointsto slicelit@a4L5:18
+	print(&sl6[0]) // @pointsto slicelit[*]@a4L5:18
+	print(sl6[0])  // @pointsto main.b | main.a
+}
+
+func array5() {
+	var arr [2]*int
+	arr[0] = &a
+	arr[1] = &b
+
+	var n int
+	print(arr[n]) // @pointsto main.a | main.b
+}
+
+func array6() {
+	var n int
+
+	sl0 := []*int{&a}
+	ap0 := (*[1]*int)(sl0)
+	ar0 := *ap0
+
+	print(ap0[n]) // @pointsto main.a
+	print(sl0[n]) // @pointsto main.a
+	print(ar0[n]) // @pointsto main.a
+
+	sl1 := []*int{&a}
+	ap1 := (*[1]*int)(sl1)
+	ar1 := *ap1
+
+	ar1[0] = &b
+	print(ap1[n]) // @pointsto main.a
+	print(sl1[n]) // @pointsto main.a
+	print(ar1[n]) // @pointsto main.a | main.b
+
+	sl2 := []*int{&a}
+	ap2 := (*[1]*int)(sl2)
+	ar2 := *ap2
+
+	ap2[0] = &b
+	print(ap2[n]) // @pointsto main.a | main.b
+	print(sl2[n]) // @pointsto main.a | main.b
+	print(ar2[n]) // @pointsto main.a | main.b
+
+	sl3 := []*int{&b, nil}
+	ap3 := (*[1]*int)(sl3)
+	ar3 := *ap3
+
+	print(sl3[n]) // @pointsto main.b
+	print(ap3[n]) // @pointsto main.b
+	print(ar3[n]) // @pointsto main.b
+}
+
+func array7() {
+	var n int
+
+	sl0 := []*int{nil, nil, nil}
+	ap0 := (*[2]*int)(sl0)
+	ap1 := (*[1]*int)(sl0[2:])
+
+	ap1[0] = &a
+
+	print(sl0[n]) // @pointsto main.a
+	print(ap0[n]) // @pointsto main.a
+	print(ap1[n]) // @pointsto main.a
+}
+
+func array8() {
+	var n int
+
+	sl1 := make([]*int, 1, 1)
+	sl2 := make([]*int, 1, 1)
+	pa1 := (*[1]*int)(sl1)
+	pa2 := (*[1]*int)(sl2)
+	sl1[0] = &a
+	sl2[0] = &b
+	print(pa1[n]) // @pointsto main.a
+	print(pa2[n]) // @pointsto main.b
+
+	pa2 = pa1
+	print(pa1[n]) // @pointsto main.a
+	print(pa2[n]) // @pointsto main.a
+}
+
+func main() {
+	array1()
+	array2()
+	array3()
+	array4()
+	array5()
+	array6()
+	array7()
+	array8()
+}