mirror of https://github.com/golang/go.git
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:
parent
7f68387a4a
commit
412ee174ef
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue