diff --git a/_test/fun6.go b/_test/fun6.go new file mode 100644 index 00000000..eb62c575 --- /dev/null +++ b/_test/fun6.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "sync" +) + +func NewPool() Pool { return Pool{} } + +type Pool struct { + p *sync.Pool +} + +var _pool = NewPool() + +func main() { + fmt.Println(_pool) +} + +// Output: +// {} diff --git a/_test/method25.go b/_test/method25.go new file mode 100644 index 00000000..91665bbf --- /dev/null +++ b/_test/method25.go @@ -0,0 +1,33 @@ +package main + +import ( + "fmt" + "sync" +) + +func (p Pool) Get() *Buffer { return &Buffer{} } + +func NewPool() Pool { return Pool{} } + +type Buffer struct { + bs []byte + pool Pool +} + +type Pool struct { + p *sync.Pool +} + +var ( + _pool = NewPool() + Get = _pool.Get +) + +func main() { + fmt.Println(_pool) + fmt.Println(Get()) +} + +// Output: +// {} +// &{[] {}} diff --git a/_test/method26.go b/_test/method26.go new file mode 100644 index 00000000..492f19d4 --- /dev/null +++ b/_test/method26.go @@ -0,0 +1,18 @@ +package main + +func NewT(name string) *T { return &T{name} } + +var C = NewT("test") + +func (t *T) f() { println(t == C) } + +type T struct { + Name string +} + +func main() { + C.f() +} + +// Output: +// true diff --git a/_test/struct26.go b/_test/struct26.go new file mode 100644 index 00000000..612f1906 --- /dev/null +++ b/_test/struct26.go @@ -0,0 +1,20 @@ +package main + +import "fmt" + +func newT2() *T2 { return &T2{} } + +type T2 struct { + T1 +} + +type T1 struct { + bs []byte +} + +func main() { + fmt.Println(newT2()) +} + +// Output: +// &{{[]}} diff --git a/_test/struct27.go b/_test/struct27.go new file mode 100644 index 00000000..1e04a551 --- /dev/null +++ b/_test/struct27.go @@ -0,0 +1,18 @@ +package main + +import "fmt" + +func (f *Foo) Boo() { fmt.Println(f.name, "Boo") } + +type Foo struct { + name string + fun func(f *Foo) +} + +func main() { + t := &Foo{name: "foo"} + t.Boo() +} + +// Output: +// foo Boo diff --git a/_test/struct28.go b/_test/struct28.go new file mode 100644 index 00000000..7f3ffaa7 --- /dev/null +++ b/_test/struct28.go @@ -0,0 +1,19 @@ +package main + +import "fmt" + +type T1 struct { + T2 +} + +type T2 struct { + *T1 +} + +func main() { + t := T1{} + fmt.Println(t) +} + +// Output: +// {{}} diff --git a/interp/cfg.go b/interp/cfg.go index fe0fafea..9e6f8878 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -355,6 +355,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) { if atyp != nil { dest.typ = atyp } else { + if src.typ, err = nodeType(interp, sc, src); err != nil { + return + } dest.typ = src.typ } if dest.typ.sizedef { diff --git a/interp/gta.go b/interp/gta.go index 3ecf0730..2a526596 100644 --- a/interp/gta.go +++ b/interp/gta.go @@ -79,41 +79,36 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) { if n.typ, err = nodeType(interp, sc, n.child[2]); err != nil { return false } - if !isMethod(n) { - sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1} - } - if len(n.child[0].child) > 0 { - // function is a method, add it to the related type + if isMethod(n) { + // Add a method symbol in the receiver type name space var rcvrtype *itype - var typeName string n.ident = n.child[1].ident rcvr := n.child[0].child[0] - if len(rcvr.child) < 2 { - // Receiver var name is skipped in method declaration (fix that in AST ?) - typeName = rcvr.child[0].ident - } else { - typeName = rcvr.child[1].ident - } + rtn := rcvr.lastChild() + typeName := rtn.ident if typeName == "" { // The receiver is a pointer, retrieve typeName from indirection - typeName = rcvr.lastChild().child[0].ident + typeName = rtn.child[0].ident elementType := sc.getType(typeName) if elementType == nil { // Add type if necessary, so method can be registered - sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, pkgPath: rpath}} + sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, pkgPath: rpath, incomplete: true, node: rtn.child[0], scope: sc}} elementType = sc.sym[typeName].typ } - rcvrtype = &itype{cat: ptrT, val: elementType} + rcvrtype = &itype{cat: ptrT, val: elementType, incomplete: elementType.incomplete, node: rtn, scope: sc} elementType.method = append(elementType.method, n) } else { rcvrtype = sc.getType(typeName) if rcvrtype == nil { // Add type if necessary, so method can be registered - sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, pkgPath: rpath}} + sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, pkgPath: rpath, incomplete: true, node: rtn, scope: sc}} rcvrtype = sc.sym[typeName].typ } } rcvrtype.method = append(rcvrtype.method, n) + } else { + // Add a function symbol in the package name space + sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1} } return false @@ -165,6 +160,9 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) { n.typ.method = append(n.typ.method, sc.sym[typeName].typ.method...) } sc.sym[typeName].typ = n.typ + if n.typ.incomplete { + revisit = append(revisit, n) + } return false } return true diff --git a/interp/run.go b/interp/run.go index 776aa11e..b00d1f3e 100644 --- a/interp/run.go +++ b/interp/run.go @@ -198,12 +198,12 @@ func convert(n *node) { } } -func isRecursiveStruct(t *itype) bool { - if t.cat == structT && t.rtype.Kind() == reflect.Interface { +func isRecursiveStruct(t *itype, rtype reflect.Type) bool { + if t.cat == structT && rtype.Kind() == reflect.Interface { return true } if t.cat == ptrT { - return isRecursiveStruct(t.val) + return isRecursiveStruct(t.val, t.rtype.Elem()) } return false } @@ -230,7 +230,7 @@ func assign(n *node) { case src.kind == basicLit && src.val == nil: t := dest.typ.TypeOf() svalue[i] = func(*frame) reflect.Value { return reflect.New(t).Elem() } - case isRecursiveStruct(dest.typ): + case isRecursiveStruct(dest.typ, dest.typ.rtype): svalue[i] = genValueInterfacePtr(src) default: svalue[i] = genValue(src) @@ -1087,7 +1087,7 @@ func getPtrIndexSeq(n *node) { index := n.val.([]int) tnext := getExec(n.tnext) var value func(*frame) reflect.Value - if isRecursiveStruct(n.child[0].typ) { + if isRecursiveStruct(n.child[0].typ, n.child[0].typ.rtype) { v := genValue(n.child[0]) value = func(f *frame) reflect.Value { return v(f).Elem().Elem() } } else { @@ -1727,7 +1727,7 @@ func _append(n *node) { values := make([]func(*frame) reflect.Value, l) for i, arg := range args { switch { - case isRecursiveStruct(n.typ.val): + case isRecursiveStruct(n.typ.val, n.typ.val.rtype): values[i] = genValueInterfacePtr(arg) case arg.typ.untyped: values[i] = genValueAs(arg, n.child[1].typ.TypeOf().Elem()) @@ -1747,7 +1747,7 @@ func _append(n *node) { } else { var value0 func(*frame) reflect.Value switch { - case isRecursiveStruct(n.typ.val): + case isRecursiveStruct(n.typ.val, n.typ.val.rtype): value0 = genValueInterfacePtr(n.child[2]) case n.child[2].typ.untyped: value0 = genValueAs(n.child[2], n.child[1].typ.TypeOf().Elem()) diff --git a/interp/scope.go b/interp/scope.go index 002c04b9..d025fe02 100644 --- a/interp/scope.go +++ b/interp/scope.go @@ -44,17 +44,16 @@ func (k sKind) String() string { // A symbol represents an interpreter object such as type, constant, var, func, // label, builtin or binary object. Symbols are defined within a scope. type symbol struct { - kind sKind - typ *itype // Type of value - node *node // Node value if index is negative - from []*node // list of nodes jumping to node if kind is label, or nil - recv *receiver // receiver node value, if sym refers to a method - index int // index of value in frame or -1 - rval reflect.Value // default value (used for constants) - path string // package path if typ.cat is SrcPkgT or BinPkgT - builtin bltnGenerator // Builtin function or nil - global bool // true if symbol is defined in global space - recursive bool // true if symbol is a recursive type definition + kind sKind + typ *itype // Type of value + node *node // Node value if index is negative + from []*node // list of nodes jumping to node if kind is label, or nil + recv *receiver // receiver node value, if sym refers to a method + index int // index of value in frame or -1 + rval reflect.Value // default value (used for constants) + path string // package path if typ.cat is SrcPkgT or BinPkgT + builtin bltnGenerator // Builtin function or nil + global bool // true if symbol is defined in global space // TODO: implement constant checking //constant bool // true if symbol value is constant } diff --git a/interp/type.go b/interp/type.go index 4a86d866..934e05d3 100644 --- a/interp/type.go +++ b/interp/type.go @@ -130,6 +130,16 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { var t = &itype{node: n, scope: sc} + if n.anc.kind == typeSpec { + name := n.anc.child[0].ident + if sym := sc.sym[name]; sym != nil { + // recover previously declared methods + t.method = sym.typ.method + t.pkgPath = sym.typ.pkgPath + t.name = name + } + } + switch n.kind { case addressExpr, starExpr: t.cat = ptrT @@ -314,11 +324,6 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { case identExpr: if sym, _, found := sc.lookup(n.ident); found { t = sym.typ - if sym.recursive && t.incomplete { - t.incomplete = false - t.rtype = reflect.TypeOf((*interface{})(nil)).Elem() - sym.typ = t - } if t.incomplete && t.node != n { m := t.method if t, err = nodeType(interp, sc, t.node); err != nil { @@ -416,7 +421,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { var sym *symbol if sname := structName(n); sname != "" { if sym, _, found = sc.lookup(sname); found && sym.kind == typeSym { - sym.recursive = true + sym.typ = t } } for _, c := range n.child[0].child { @@ -460,7 +465,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { } if t.cat == nilT && !t.incomplete { - err = n.cfgErrorf("use of untyped nil") + err = n.cfgErrorf("use of untyped nil %s", t.name) } return t, err @@ -517,15 +522,23 @@ func init() { func (t *itype) finalize() (*itype, error) { var err cfgError if t.incomplete { + sym, _, found := t.scope.lookup(t.name) + if found && !sym.typ.incomplete { + sym.typ.method = append(sym.typ.method, t.method...) + return sym.typ, nil + } m := t.method if t, err = nodeType(t.node.interp, t.scope, t.node); err != nil { return nil, err } if t.incomplete { - return nil, t.node.cfgErrorf("incomplete type") + return nil, t.node.cfgErrorf("incomplete type %s", t.name) } t.method = m t.node.typ = t + if sym != nil { + sym.typ = t + } } return t, err } @@ -691,7 +704,7 @@ func exportName(s string) string { } // TypeOf returns the reflection type of dynamic interpreter type t. -func (t *itype) TypeOf() reflect.Type { +func (t *itype) Type(name string) reflect.Type { if t.rtype != nil { return t.rtype } @@ -702,26 +715,29 @@ func (t *itype) TypeOf() reflect.Type { panic(err) } } + if name == "" { + name = t.name + } switch t.cat { case arrayT: if t.size > 0 { - t.rtype = reflect.ArrayOf(t.size, t.val.TypeOf()) + t.rtype = reflect.ArrayOf(t.size, t.val.Type(name)) } else { - t.rtype = reflect.SliceOf(t.val.TypeOf()) + t.rtype = reflect.SliceOf(t.val.Type(name)) } case chanT: - t.rtype = reflect.ChanOf(reflect.BothDir, t.val.TypeOf()) + t.rtype = reflect.ChanOf(reflect.BothDir, t.val.Type(name)) case errorT: t.rtype = reflect.TypeOf(new(error)).Elem() case funcT: in := make([]reflect.Type, len(t.arg)) out := make([]reflect.Type, len(t.ret)) for i, v := range t.arg { - in[i] = v.TypeOf() + in[i] = v.Type(name) } for i, v := range t.ret { - out[i] = v.TypeOf() + out[i] = v.Type(name) } t.rtype = reflect.FuncOf(in, out, false) case interfaceT: @@ -729,18 +745,14 @@ func (t *itype) TypeOf() reflect.Type { case mapT: t.rtype = reflect.MapOf(t.key.TypeOf(), t.val.TypeOf()) case ptrT: - // FIXME: the following should probably be done in nodeType() instead - if t.val.cat == nilT { - t.val.incomplete = true - if s, _, ok := t.scope.lookup(t.val.name); ok { - t.val = s.typ - } + if t.val != nil && name != "" && t.val.name == name { + t.val.rtype = reflect.TypeOf(new(interface{})).Elem() } - t.rtype = reflect.PtrTo(t.val.TypeOf()) + t.rtype = reflect.PtrTo(t.val.Type(name)) case structT: var fields []reflect.StructField for _, f := range t.field { - field := reflect.StructField{Name: exportName(f.name), Type: f.typ.TypeOf(), Tag: reflect.StructTag(f.tag)} + field := reflect.StructField{Name: exportName(f.name), Type: f.typ.Type(name), Tag: reflect.StructTag(f.tag)} fields = append(fields, field) } t.rtype = reflect.StructOf(fields) @@ -752,6 +764,10 @@ func (t *itype) TypeOf() reflect.Type { return t.rtype } +func (t *itype) TypeOf() reflect.Type { + return t.Type(t.name) +} + func (t *itype) frameType() (r reflect.Type) { var err error if t, err = t.finalize(); err != nil { @@ -764,29 +780,11 @@ func (t *itype) frameType() (r reflect.Type) { } else { r = reflect.SliceOf(t.val.frameType()) } - //case ChanT: - // r = reflect.ChanOf(reflect.BothDir, t.val.frameType()) - //case ErrorT: - // r = reflect.TypeOf(new(error)).Elem() case funcT: r = reflect.TypeOf((*node)(nil)) case interfaceT: r = reflect.TypeOf((*valueInterface)(nil)).Elem() - //case MapT: - // r = reflect.MapOf(t.key.frameType(), t.val.frameType()) - //case PtrT: - // r = reflect.PtrTo(t.val.frameType()) - //case StructT: - // var fields []reflect.StructField - // for _, f := range t.field { - // field := reflect.StructField{Name: exportName(f.name), Type: f.typ.frameType()} - // fields = append(fields, field) - // } - // r = reflect.StructOf(fields) default: - // if z, _ := t.zero(); z.IsValid() { - // r = z.Type() - // } r = t.TypeOf() } return r