compiler: set default values for complex struct fields

Set default value also for complex (struct, map) types.
Note: because VM does not distinguish empty array and nil array,
comparison 'a == nil' can't be handled properly without substantial
effort. This will be fixed in NEO3.
This commit is contained in:
Evgenii Stratonikov 2020-05-14 11:04:38 +03:00
parent 788304ec49
commit bf6aa02dcf
4 changed files with 54 additions and 3 deletions

View file

@ -44,8 +44,9 @@ func typeAndValueForField(fld *types.Var) (types.TypeAndValue, error) {
default:
return types.TypeAndValue{}, fmt.Errorf("could not initialize struct field %s to zero, type: %s", fld.Name(), t)
}
default:
return types.TypeAndValue{Type: t}, nil
}
return types.TypeAndValue{}, nil
}
// countGlobals counts the global variables in the program to add

View file

@ -533,6 +533,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.emitLoadConst(value)
} else if tv := c.typeInfo.Types[n]; tv.Value != nil {
c.emitLoadConst(tv)
} else if n.Name == "nil" {
c.emitDefault(new(types.Slice))
} else {
c.emitLoadLocal(n.Name)
}
@ -1230,11 +1232,32 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Int(c.prog.BinWriter, int64(i))
c.emitLoadConst(typeAndVal)
c.emitDefault(typeAndVal.Type)
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
}
}
func (c *codegen) emitDefault(typ types.Type) {
switch t := c.scTypeFromGo(typ); t {
case "Integer":
emit.Int(c.prog.BinWriter, 0)
case "Boolean":
emit.Bool(c.prog.BinWriter, false)
case "String":
emit.String(c.prog.BinWriter, "")
case "Map":
emit.Opcode(c.prog.BinWriter, opcode.NEWMAP)
case "Struct":
emit.Int(c.prog.BinWriter, int64(typ.(*types.Struct).NumFields()))
emit.Opcode(c.prog.BinWriter, opcode.NEWSTRUCT)
case "Array":
emit.Int(c.prog.BinWriter, 0)
emit.Opcode(c.prog.BinWriter, opcode.NEWARRAY)
case "ByteArray":
emit.Bytes(c.prog.BinWriter, []byte{})
}
}
func (c *codegen) convertToken(tok token.Token) {
switch tok {
case token.ADD_ASSIGN:

View file

@ -183,7 +183,11 @@ func (c *codegen) scReturnTypeFromScope(scope *funcScope) string {
}
func (c *codegen) scTypeFromExpr(typ ast.Expr) string {
switch t := c.typeInfo.Types[typ].Type.(type) {
return c.scTypeFromGo(c.typeInfo.Types[typ].Type)
}
func (c *codegen) scTypeFromGo(typ types.Type) string {
switch t := typ.(type) {
case *types.Basic:
info := t.Info()
switch {

View file

@ -338,6 +338,29 @@ var structTestCases = []testCase{
}`,
big.NewInt(2),
},
{
"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) {