compiler: support initializing struct fields from a variable

While initializing a struct, it is a top item on ALTSTACK.
This means that if we need to load a local variable,
DUPFROMALTSTACK won't longer push an array of locals on stack
but rather a currently initializing struct.

Closes #656.
This commit is contained in:
Evgenii Stratonikov 2020-02-11 11:10:51 +03:00
parent bcff9faac4
commit ae03560589
2 changed files with 76 additions and 5 deletions

View file

@ -872,7 +872,6 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
emit.Opcode(c.prog.BinWriter, opcode.NOP)
emit.Int(c.prog.BinWriter, int64(strct.NumFields()))
emit.Opcode(c.prog.BinWriter, opcode.NEWSTRUCT)
emit.Opcode(c.prog.BinWriter, opcode.TOALTSTACK)
// We need to locally store all the fields, even if they are not initialized.
// We will initialize all fields to their "zero" value.
@ -886,9 +885,14 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
fieldName := f.Key.(*ast.Ident).Name
if sField.Name() == fieldName {
ast.Walk(c, f.Value)
emit.Opcode(c.prog.BinWriter, opcode.DUP)
pos := indexOfStruct(strct, fieldName)
c.emitStoreLocal(pos)
emit.Int(c.prog.BinWriter, int64(pos))
ast.Walk(c, f.Value)
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
fieldAdded = true
break
}
@ -902,10 +906,12 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
c.prog.Err = err
return
}
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Int(c.prog.BinWriter, int64(i))
c.emitLoadConst(typeAndVal)
c.emitStoreLocal(i)
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
}
emit.Opcode(c.prog.BinWriter, opcode.FROMALTSTACK)
}
func (c *codegen) convertToken(tok token.Token) {

View file

@ -93,6 +93,71 @@ var structTestCases = []testCase{
`,
big.NewInt(12),
},
{
"initialize struct field from variable",
`
package foo
type token struct {
x int
y int
}
func Main() int {
x := 10
t := token {
x: x,
y: 4,
}
y := t.x + t.y
return y
}`,
big.NewInt(14),
},
{
"assign a variable to a struct field",
`
package foo
type token struct {
x int
y int
}
func Main() int {
ten := 10
t := token {
x: 2,
y: 4,
}
t.x = ten
y := t.y + t.x
return y
}`,
big.NewInt(14),
},
{
"assign a struct field to a struct field",
`
package foo
type token struct {
x int
y int
}
func Main() int {
t1 := token {
x: 2,
y: 4,
}
t2 := token {
x: 3,
y: 5,
}
t1.x = t2.y
y := t1.x + t2.x
return y
}`,
big.NewInt(8),
},
{
"initialize same struct twice",
`