go/ssa: allow conversion from slice to array pointer

Fixes golang/go#46987

Change-Id: Ic3b4410c3ea9f62d3cdce579a1152884167be16b
Reviewed-on: https://go-review.googlesource.com/c/tools/+/332049
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
diff --git a/go/ssa/builder_go117_test.go b/go/ssa/builder_go117_test.go
new file mode 100644
index 0000000..93316c1
--- /dev/null
+++ b/go/ssa/builder_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.
+
+//go:build go1.17
+// +build go1.17
+
+package ssa_test
+
+import (
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"testing"
+
+	"golang.org/x/tools/go/ssa"
+	"golang.org/x/tools/go/ssa/ssautil"
+)
+
+func TestSliceToArrayPtr(t *testing.T) {
+	src := `package p
+
+func f() {
+	var s []byte
+	_ = (*[4]byte)(s)
+}
+`
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "p.go", src, parser.ParseComments)
+	if err != nil {
+		t.Fatal(err)
+	}
+	files := []*ast.File{f}
+
+	pkg := types.NewPackage("p", "")
+	conf := &types.Config{}
+	if _, _, err := ssautil.BuildPackage(conf, fset, pkg, files, ssa.SanityCheckFunctions); err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+}
diff --git a/go/ssa/emit.go b/go/ssa/emit.go
index 13fe2aa..df9ca4f 100644
--- a/go/ssa/emit.go
+++ b/go/ssa/emit.go
@@ -168,7 +168,7 @@
 // emitConv emits to f code to convert Value val to exactly type typ,
 // and returns the converted value.  Implicit conversions are required
 // by language assignability rules in assignments, parameter passing,
-// etc.  Conversions cannot fail dynamically.
+// etc.
 //
 func emitConv(f *Function, val Value, typ types.Type) Value {
 	t_src := val.Type()
@@ -228,6 +228,16 @@
 		// e.g. string -> []byte/[]rune.
 	}
 
+	// Conversion from slice to array pointer?
+	if slice, ok := ut_src.(*types.Slice); ok {
+		if ptr, ok := ut_dst.(*types.Pointer); ok {
+			if arr, ok := ptr.Elem().(*types.Array); ok && types.Identical(slice.Elem(), arr.Elem()) {
+				c := &Convert{X: val}
+				c.setType(ut_dst)
+				return f.emit(c)
+			}
+		}
+	}
 	// A representation-changing conversion?
 	// At least one of {ut_src,ut_dst} must be *Basic.
 	// (The other may be []byte or []rune.)
diff --git a/go/ssa/sanity.go b/go/ssa/sanity.go
index 0a7abc5..16df7e4 100644
--- a/go/ssa/sanity.go
+++ b/go/ssa/sanity.go
@@ -133,6 +133,13 @@
 	case *ChangeInterface:
 	case *ChangeType:
 	case *Convert:
+		if _, ok := instr.X.Type().Underlying().(*types.Slice); ok {
+			if ptr, ok := instr.Type().Underlying().(*types.Pointer); ok {
+				if _, ok := ptr.Elem().(*types.Array); ok {
+					break
+				}
+			}
+		}
 		if _, ok := instr.X.Type().Underlying().(*types.Basic); !ok {
 			if _, ok := instr.Type().Underlying().(*types.Basic); !ok {
 				s.errorf("convert %s -> %s: at least one type must be basic", instr.X.Type(), instr.Type())
diff --git a/go/ssa/ssa.go b/go/ssa/ssa.go
index 4dfdafd..c604272 100644
--- a/go/ssa/ssa.go
+++ b/go/ssa/ssa.go
@@ -615,10 +615,9 @@
 //    - between pointers and unsafe.Pointer.
 //    - between unsafe.Pointer and uintptr.
 //    - from (Unicode) integer to (UTF-8) string.
+//    - from slice to array pointer.
 // A conversion may imply a type name change also.
 //
-// This operation cannot fail dynamically.
-//
 // Conversions of untyped string/number/bool constants to a specific
 // representation are eliminated during SSA construction.
 //