diff --git a/pkg/compiler/analysis.go b/pkg/compiler/analysis.go index 6c052e5af..7b3ff55c9 100644 --- a/pkg/compiler/analysis.go +++ b/pkg/compiler/analysis.go @@ -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 diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index c53282399..d1e36f75c 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -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: diff --git a/pkg/compiler/debug.go b/pkg/compiler/debug.go index 0d607eaae..91d0bbfbb 100644 --- a/pkg/compiler/debug.go +++ b/pkg/compiler/debug.go @@ -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 { diff --git a/pkg/compiler/struct_test.go b/pkg/compiler/struct_test.go index d945ab775..fad15b423 100644 --- a/pkg/compiler/struct_test.go +++ b/pkg/compiler/struct_test.go @@ -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) {