compiler: fix initialization of struct fields, fix #1164

Make `s{}` initializers work. Deduplicate code a bit along the way. The test
added follows bf6aa02dcf test.
This commit is contained in:
Roman Khimov 2020-07-09 12:26:16 +03:00
parent f32920bf39
commit eee8ac1655
2 changed files with 46 additions and 32 deletions

View file

@ -1310,41 +1310,32 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
// We will initialize all fields to their "zero" value.
for i := 0; i < strct.NumFields(); i++ {
sField := strct.Field(i)
fieldAdded := false
if !keyedLit {
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Int(c.prog.BinWriter, int64(i))
ast.Walk(c, lit.Elts[i])
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
continue
}
// Fields initialized by the program.
for _, field := range lit.Elts {
f := field.(*ast.KeyValueExpr)
fieldName := f.Key.(*ast.Ident).Name
if sField.Name() == fieldName {
emit.Opcode(c.prog.BinWriter, opcode.DUP)
pos := indexOfStruct(strct, fieldName)
emit.Int(c.prog.BinWriter, int64(pos))
ast.Walk(c, f.Value)
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
fieldAdded = true
break
}
}
if fieldAdded {
continue
}
var initialized bool
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Int(c.prog.BinWriter, int64(i))
c.emitDefault(sField.Type())
if !keyedLit {
if len(lit.Elts) > i {
ast.Walk(c, lit.Elts[i])
initialized = true
}
} else {
// Fields initialized by the program.
for _, field := range lit.Elts {
f := field.(*ast.KeyValueExpr)
fieldName := f.Key.(*ast.Ident).Name
if sField.Name() == fieldName {
ast.Walk(c, f.Value)
initialized = true
break
}
}
}
if !initialized {
c.emitDefault(sField.Type())
}
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
}
}

View file

@ -405,6 +405,29 @@ var structTestCases = []testCase{
}`,
big.NewInt(12),
},
{
"uninitialized struct fields",
`package foo
type Foo struct {
i int
m map[string]int
b []byte
a []int
s struct { ii int }
}
func NewFoo() Foo { return Foo{} }
func Main() int {
foo := NewFoo()
if foo.i != 0 { return 1 }
if len(foo.m) != 0 { return 1 }
if len(foo.b) != 0 { return 1 }
if len(foo.a) != 0 { return 1 }
s := foo.s
if s.ii != 0 { return 1 }
return 2
}`,
big.NewInt(2),
},
}
func TestStructs(t *testing.T) {