all: add SliceToArrayPointer instruction

CL 332049 added support for slice to array pointer, which introduced in
go1.17, by using Convert instruction. However, the conversion can fail
dynamically, while all current conversion instructions can not. That
leads to inconsistent with other parts of SSA IR.

@timothy-king suggested to add new instruction for this conversion, this
CL implements that.

Fixes golang/go#46987

Change-Id: I1a00d51e257d2b3eabc2c37e3a09b78754193a78
Reviewed-on: https://go-review.googlesource.com/c/tools/+/333749
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Trust: Bryan C. Mills <bcmills@google.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: Tim King <taking@google.com>
This commit is contained in:
Cuong Manh Le 2021-07-10 10:37:28 +07:00
parent 7f68387a4a
commit 412ee174ef
11 changed files with 166 additions and 57 deletions

View File

@ -579,6 +579,8 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
y.pos = e.Lparen
case *MakeInterface:
y.pos = e.Lparen
case *SliceToArrayPointer:
y.pos = e.Lparen
}
}
return y

View File

@ -49,3 +49,34 @@ func TestBuildPackageGo117(t *testing.T) {
})
}
}
func TestBuildPackageFailuresGo117(t *testing.T) {
tests := []struct {
name string
src string
importer types.Importer
}{
{"slice to array pointer - source is not a slice", "package p; var s [4]byte; var _ = (*[4]byte)(s)", nil},
{"slice to array pointer - dest is not a pointer", "package p; var s []byte; var _ = ([4]byte)(s)", nil},
{"slice to array pointer - dest pointer elem is not an array", "package p; var s []byte; var _ = (*byte)(s)", nil},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "p.go", tc.src, parser.ParseComments)
if err != nil {
t.Error(err)
}
files := []*ast.File{f}
pkg := types.NewPackage("p", "")
conf := &types.Config{Importer: tc.importer}
if _, _, err := ssautil.BuildPackage(conf, fset, pkg, files, ssa.SanityCheckFunctions); err == nil {
t.Error("want error, but got nil")
}
})
}
}

View File

@ -50,50 +50,51 @@
// Instruction interfaces. The following table shows for each
// concrete type which of these interfaces it implements.
//
// Value? Instruction? Member?
// *Alloc ✔ ✔
// *BinOp ✔ ✔
// *Builtin ✔
// *Call ✔ ✔
// *ChangeInterface ✔ ✔
// *ChangeType ✔ ✔
// *Const ✔
// *Convert ✔ ✔
// *DebugRef ✔
// *Defer ✔
// *Extract ✔ ✔
// *Field ✔ ✔
// *FieldAddr ✔ ✔
// *FreeVar ✔
// *Function ✔ ✔ (func)
// *Global ✔ ✔ (var)
// *Go ✔
// *If ✔
// *Index ✔ ✔
// *IndexAddr ✔ ✔
// *Jump ✔
// *Lookup ✔ ✔
// *MakeChan ✔ ✔
// *MakeClosure ✔ ✔
// *MakeInterface ✔ ✔
// *MakeMap ✔ ✔
// *MakeSlice ✔ ✔
// *MapUpdate ✔
// *NamedConst ✔ (const)
// *Next ✔ ✔
// *Panic ✔
// *Parameter ✔
// *Phi ✔ ✔
// *Range ✔ ✔
// *Return ✔
// *RunDefers ✔
// *Select ✔ ✔
// *Send ✔
// *Slice ✔ ✔
// *Store ✔
// *Type ✔ (type)
// *TypeAssert ✔ ✔
// *UnOp ✔ ✔
// Value? Instruction? Member?
// *Alloc ✔ ✔
// *BinOp ✔ ✔
// *Builtin ✔
// *Call ✔ ✔
// *ChangeInterface ✔ ✔
// *ChangeType ✔ ✔
// *Const ✔
// *Convert ✔ ✔
// *SliceToArrayPointer ✔ ✔
// *DebugRef ✔
// *Defer ✔
// *Extract ✔ ✔
// *Field ✔ ✔
// *FieldAddr ✔ ✔
// *FreeVar ✔
// *Function ✔ ✔ (func)
// *Global ✔ ✔ (var)
// *Go ✔
// *If ✔
// *Index ✔ ✔
// *IndexAddr ✔ ✔
// *Jump ✔
// *Lookup ✔ ✔
// *MakeChan ✔ ✔
// *MakeClosure ✔ ✔
// *MakeInterface ✔ ✔
// *MakeMap ✔ ✔
// *MakeSlice ✔ ✔
// *MapUpdate ✔
// *NamedConst ✔ (const)
// *Next ✔ ✔
// *Panic ✔
// *Parameter ✔
// *Phi ✔ ✔
// *Range ✔ ✔
// *Return ✔
// *RunDefers ✔
// *Select ✔ ✔
// *Send ✔
// *Slice ✔ ✔
// *Store ✔
// *Type ✔ (type)
// *TypeAssert ✔ ✔
// *UnOp ✔ ✔
//
// Other key types in this package include: Program, Package, Function
// and BasicBlock.

View File

@ -232,7 +232,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
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 := &SliceToArrayPointer{X: val}
c.setType(ut_dst)
return f.emit(c)
}

View File

@ -210,6 +210,9 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
case *ssa.Convert:
fr.env[instr] = conv(instr.Type(), instr.X.Type(), fr.get(instr.X))
case *ssa.SliceToArrayPointer:
fr.env[instr] = sliceToArrayPointer(instr.Type(), instr.X.Type(), fr.get(instr.X))
case *ssa.MakeInterface:
fr.env[instr] = iface{t: instr.X.Type(), v: fr.get(instr.X)}

View File

@ -0,0 +1,12 @@
// 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 interp_test
func init() {
testdataTests = append(testdataTests, "slice2arrayptr.go")
}

View File

@ -1357,6 +1357,29 @@ func conv(t_dst, t_src types.Type, x value) value {
panic(fmt.Sprintf("unsupported conversion: %s -> %s, dynamic type %T", t_src, t_dst, x))
}
// sliceToArrayPointer converts the value x of type slice to type t_dst
// a pointer to array and returns the result.
func sliceToArrayPointer(t_dst, t_src types.Type, x value) value {
utSrc := t_src.Underlying()
utDst := t_dst.Underlying()
if _, ok := utSrc.(*types.Slice); ok {
if utSrc, ok := utDst.(*types.Pointer); ok {
if arr, ok := utSrc.Elem().(*types.Array); ok {
x := x.([]value)
a := make(array, arr.Len())
for i := range a {
a[i] = x[i]
}
v := value(a)
return &v
}
}
}
panic(fmt.Sprintf("unsupported conversion: %s -> %s, dynamic type %T", t_src, t_dst, x))
}
// checkInterface checks that the method set of x implements the
// interface itype.
// On success it returns "", on failure, an error message.

View File

@ -0,0 +1,23 @@
// 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.
package main
// Test for slice to array pointer conversion introduced in go1.17
import "fmt"
var s = []byte{1, 2, 3, 4}
var a = (*[4]byte)(s)
func main() {
for i := range s {
if a[i] != s[i] {
panic(fmt.Sprintf("value mismatched: %v - %v\n", a[i], s[i]))
}
if (*a)[i] != s[i] {
panic(fmt.Sprintf("value mismatched: %v - %v\n", (*a)[i], s[i]))
}
}
}

View File

@ -159,10 +159,11 @@ func printConv(prefix string, v, x Value) string {
relName(x, v.(Instruction)))
}
func (v *ChangeType) String() string { return printConv("changetype", v, v.X) }
func (v *Convert) String() string { return printConv("convert", v, v.X) }
func (v *ChangeInterface) String() string { return printConv("change interface", v, v.X) }
func (v *MakeInterface) String() string { return printConv("make", v, v.X) }
func (v *ChangeType) String() string { return printConv("changetype", v, v.X) }
func (v *Convert) String() string { return printConv("convert", v, v.X) }
func (v *ChangeInterface) String() string { return printConv("change interface", v, v.X) }
func (v *SliceToArrayPointer) String() string { return printConv("slice to array pointer", v, v.X) }
func (v *MakeInterface) String() string { return printConv("make", v, v.X) }
func (v *MakeClosure) String() string {
var b bytes.Buffer

View File

@ -132,14 +132,8 @@ func (s *sanity) checkInstr(idx int, instr Instruction) {
case *Call:
case *ChangeInterface:
case *ChangeType:
case *SliceToArrayPointer:
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())

View File

@ -615,9 +615,10 @@ type ChangeType struct {
// - 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.
//
@ -649,6 +650,20 @@ type ChangeInterface struct {
X Value
}
// The SliceToArrayPointer instruction yields the conversion of slice X to
// array pointer.
//
// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
// from an explicit conversion in the source.
//
// Example printed form:
// t1 = slice to array pointer *[4]byte <- []byte (t0)
//
type SliceToArrayPointer struct {
register
X Value
}
// MakeInterface constructs an instance of an interface type from a
// value of a concrete type.
//
@ -1566,6 +1581,10 @@ func (v *Convert) Operands(rands []*Value) []*Value {
return append(rands, &v.X)
}
func (v *SliceToArrayPointer) Operands(rands []*Value) []*Value {
return append(rands, &v.X)
}
func (s *DebugRef) Operands(rands []*Value) []*Value {
return append(rands, &s.X)
}