compiler: emit CONVERT opcode for type assertions

Emit CONVERT for converting between different types. NeoVM behavior is
different from that of Go (e.g. assertions of `int` and `uint32` are
equivalent).
This commit is contained in:
Evgenii Stratonikov 2020-06-24 17:58:15 +03:00
parent f75d888f5f
commit 904b2136fc
3 changed files with 46 additions and 0 deletions

View file

@ -991,6 +991,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
// not the assertion type.
case *ast.TypeAssertExpr:
ast.Walk(c, n.X)
typ := toNeoType(c.typeOf(n.Type))
emit.Instruction(c.prog.BinWriter, opcode.CONVERT, []byte{byte(typ)})
return nil
}
return c

View file

@ -61,3 +61,14 @@ func TestConvert(t *testing.T) {
})
}
}
func TestTypeAssertion(t *testing.T) {
src := `package foo
func Main() int {
a := []byte{1}
var u interface{}
u = a
return u.(int)
}`
eval(t, src, big.NewInt(1))
}

View file

@ -3,6 +3,8 @@ package compiler
import (
"go/ast"
"go/types"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
func (c *codegen) typeAndValueOf(e ast.Expr) types.TypeAndValue {
@ -42,3 +44,34 @@ func isByteSlice(typ types.Type) bool {
t, ok := typ.Underlying().(*types.Slice)
return ok && isByte(t.Elem())
}
func toNeoType(typ types.Type) stackitem.Type {
if typ == nil {
return stackitem.AnyT
}
switch t := typ.Underlying().(type) {
case *types.Basic:
info := t.Info()
switch {
case info&types.IsInteger != 0:
return stackitem.IntegerT
case info&types.IsBoolean != 0:
return stackitem.BooleanT
case info&types.IsString != 0:
return stackitem.ByteArrayT
default:
return stackitem.AnyT
}
case *types.Map:
return stackitem.MapT
case *types.Struct:
return stackitem.StructT
case *types.Slice:
if isByte(t.Elem()) {
return stackitem.BufferT
}
return stackitem.ArrayT
default:
return stackitem.AnyT
}
}