compiler: support creating pointers to struct

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgenii Stratonikov 2020-08-02 14:42:29 +03:00
parent 00671deb8f
commit eb047b12a7
2 changed files with 52 additions and 5 deletions

View file

@ -438,7 +438,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.emitStoreVar(t.X.(*ast.Ident).Name, t.Sel.Name) c.emitStoreVar(t.X.(*ast.Ident).Name, t.Sel.Name)
return nil return nil
} }
strct, ok := typ.Underlying().(*types.Struct) strct, ok := c.getStruct(typ)
if !ok { if !ok {
c.prog.Err = fmt.Errorf("nested selector assigns not supported yet") c.prog.Err = fmt.Errorf("nested selector assigns not supported yet")
return nil return nil
@ -623,7 +623,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
case *ast.CompositeLit: case *ast.CompositeLit:
switch typ := c.typeOf(n).Underlying().(type) { switch typ := c.typeOf(n).Underlying().(type) {
case *types.Struct: case *types.Struct:
c.convertStruct(n) c.convertStruct(n, false)
case *types.Map: case *types.Map:
c.convertMap(n) c.convertMap(n)
default: default:
@ -835,7 +835,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
} }
return nil return nil
} }
strct, ok := typ.Underlying().(*types.Struct) strct, ok := c.getStruct(typ)
if !ok { if !ok {
c.prog.Err = fmt.Errorf("selectors are supported only on structs") c.prog.Err = fmt.Errorf("selectors are supported only on structs")
return nil return nil
@ -846,6 +846,19 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
return nil return nil
case *ast.UnaryExpr: case *ast.UnaryExpr:
if n.Op == token.AND {
// We support only taking address from struct literals.
// For identifiers we can't support "taking address" in a general way
// because both struct and array are reference types.
lit, ok := n.X.(*ast.CompositeLit)
if ok {
c.convertStruct(lit, true)
return nil
}
c.prog.Err = fmt.Errorf("'&' can be used only with struct literals")
return nil
}
ast.Walk(c, n.X) ast.Walk(c, n.X)
// From https://golang.org/ref/spec#Operators // From https://golang.org/ref/spec#Operators
// there can be only following unary operators // there can be only following unary operators
@ -1325,7 +1338,19 @@ func (c *codegen) convertMap(lit *ast.CompositeLit) {
} }
} }
func (c *codegen) convertStruct(lit *ast.CompositeLit) { func (c *codegen) getStruct(typ types.Type) (*types.Struct, bool) {
switch t := typ.Underlying().(type) {
case *types.Struct:
return t, true
case *types.Pointer:
strct, ok := t.Elem().Underlying().(*types.Struct)
return strct, ok
default:
return nil, false
}
}
func (c *codegen) convertStruct(lit *ast.CompositeLit, ptr bool) {
// Create a new structScope to initialize and store // Create a new structScope to initialize and store
// the positions of its variables. // the positions of its variables.
strct, ok := c.typeOf(lit).Underlying().(*types.Struct) strct, ok := c.typeOf(lit).Underlying().(*types.Struct)
@ -1336,7 +1361,11 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
emit.Opcode(c.prog.BinWriter, opcode.NOP) emit.Opcode(c.prog.BinWriter, opcode.NOP)
emit.Int(c.prog.BinWriter, int64(strct.NumFields())) emit.Int(c.prog.BinWriter, int64(strct.NumFields()))
if ptr {
emit.Opcode(c.prog.BinWriter, opcode.NEWARRAY)
} else {
emit.Opcode(c.prog.BinWriter, opcode.NEWSTRUCT) emit.Opcode(c.prog.BinWriter, opcode.NEWSTRUCT)
}
keyedLit := len(lit.Elts) > 0 keyedLit := len(lit.Elts) > 0
if keyedLit { if keyedLit {

View file

@ -0,0 +1,18 @@
package compiler_test
import (
"math/big"
"testing"
)
func TestAddressOfLiteral(t *testing.T) {
src := `package foo
type Foo struct { A int }
func Main() int {
f := &Foo{}
setA(f, 3)
return f.A
}
func setA(s *Foo, a int) { s.A = a }`
eval(t, src, big.NewInt(3))
}