From 137b16580ca4c3145d0f2816ed7eca1df1afb569 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Mon, 3 Feb 2020 16:54:04 +0100 Subject: [PATCH] fix: handle binary methods where receiver is implicit --- _test/method29.go | 16 +++++++++++++++ _test/method30.go | 20 +++++++++++++++++++ interp/cfg.go | 8 ++++++-- interp/gta.go | 3 +++ interp/run.go | 5 +++-- interp/type.go | 51 +++++++++++++++++++++++++++++++---------------- 6 files changed, 82 insertions(+), 21 deletions(-) create mode 100644 _test/method29.go create mode 100644 _test/method30.go diff --git a/_test/method29.go b/_test/method29.go new file mode 100644 index 000000000..e267f76cc --- /dev/null +++ b/_test/method29.go @@ -0,0 +1,16 @@ +package main + +import ( + "context" + "net" +) + +var lookupHost = net.DefaultResolver.LookupHost + +func main() { + res, err := lookupHost(context.Background(), "localhost") + println(len(res) > 0, err == nil) +} + +// Output: +// true true diff --git a/_test/method30.go b/_test/method30.go new file mode 100644 index 000000000..0e9ee829c --- /dev/null +++ b/_test/method30.go @@ -0,0 +1,20 @@ +package main + +type T struct { + Name string +} + +func (t *T) foo(a string) string { + return t.Name + a +} + +var g = &T{"global"} + +var f = g.foo + +func main() { + println(f("-x")) +} + +// Output: +// global-x diff --git a/interp/cfg.go b/interp/cfg.go index 6b239ba86..fe9c35b47 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -410,7 +410,11 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) { if src.typ, err = nodeType(interp, sc, src); err != nil { return } - dest.typ = src.typ + if src.typ.isBinMethod { + dest.typ = &itype{cat: valueT, rtype: src.typ.methodCallType()} + } else { + dest.typ = src.typ + } } if dest.typ.sizedef { dest.typ.size = arrayTypeLen(src) @@ -1097,7 +1101,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) { n.val = method.Index n.gen = getIndexBinMethod n.recv = &receiver{node: n.child[0]} - n.typ = &itype{cat: valueT, rtype: method.Type} + n.typ = &itype{cat: valueT, rtype: method.Type, isBinMethod: true} case n.typ.rtype.Kind() == reflect.Ptr: if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok { n.typ = &itype{cat: valueT, rtype: field.Type} diff --git a/interp/gta.go b/interp/gta.go index b85e3cfea..d20328105 100644 --- a/interp/gta.go +++ b/interp/gta.go @@ -60,6 +60,9 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) { err = n.cfgErrorf("use of untyped nil") return false } + if typ.isBinMethod { + typ = &itype{cat: valueT, rtype: typ.methodCallType(), isBinMethod: true} + } sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val} if n.anc.kind == constDecl { sc.sym[dest.ident].kind = constSym diff --git a/interp/run.go b/interp/run.go index 940d58897..5baec21c9 100644 --- a/interp/run.go +++ b/interp/run.go @@ -828,7 +828,9 @@ func callBin(n *node) { // method signature obtained from reflect.Type include receiver as 1st arg, except for interface types rcvrOffset := 0 if recv := n.child[0].recv; recv != nil && recv.node.typ.TypeOf().Kind() != reflect.Interface { - rcvrOffset = 1 + if funcType.NumIn() > len(child) { + rcvrOffset = 1 + } } for i, c := range child { @@ -867,7 +869,6 @@ func callBin(n *node) { case interfaceT: values = append(values, genValueInterfaceValue(c)) default: - //values = append(values, genValue(c)) values = append(values, genInterfaceWrapper(c, defType)) } } diff --git a/interp/type.go b/interp/type.go index 74ada6ede..179db7c7a 100644 --- a/interp/type.go +++ b/interp/type.go @@ -103,22 +103,23 @@ type structField struct { // itype defines the internal representation of types in the interpreter type itype struct { - cat tcat // Type category - field []structField // Array of struct fields if structT or interfaceT - key *itype // Type of key element if MapT or nil - val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT - arg []*itype // Argument types if funcT or nil - ret []*itype // Return types if funcT or nil - method []*node // Associated methods or nil - name string // name of type within its package for a defined type - path string // for a defined type, the package import path - size int // Size of array if ArrayT - rtype reflect.Type // Reflection type if ValueT, or nil - incomplete bool // true if type must be parsed again (out of order declarations) - untyped bool // true for a literal value (string or number) - sizedef bool // true if array size is computed from type definition - node *node // root AST node of type definition - scope *scope // type declaration scope (in case of re-parse incomplete type) + cat tcat // Type category + field []structField // Array of struct fields if structT or interfaceT + key *itype // Type of key element if MapT or nil + val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT + arg []*itype // Argument types if funcT or nil + ret []*itype // Return types if funcT or nil + method []*node // Associated methods or nil + name string // name of type within its package for a defined type + path string // for a defined type, the package import path + size int // Size of array if ArrayT + rtype reflect.Type // Reflection type if ValueT, or nil + incomplete bool // true if type must be parsed again (out of order declarations) + untyped bool // true for a literal value (string or number) + sizedef bool // true if array size is computed from type definition + isBinMethod bool // true if the type refers to a bin method function + node *node // root AST node of type definition + scope *scope // type declaration scope (in case of re-parse incomplete type) } // nodeType returns a type definition for the corresponding AST subtree @@ -482,7 +483,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) { if m, _ := lt.lookupMethod(name); m != nil { t, err = nodeType(interp, sc, m.child[2]) } else if bm, _, _, ok := lt.lookupBinMethod(name); ok { - t = &itype{cat: valueT, rtype: bm.Type} + t = &itype{cat: valueT, rtype: bm.Type, isBinMethod: true} } else if ti := lt.lookupField(name); len(ti) > 0 { t = lt.fieldSeq(ti) } else if bs, _, ok := lt.lookupBinField(name); ok { @@ -809,6 +810,22 @@ func (t *itype) lookupBinField(name string) (s reflect.StructField, index []int, return s, index, ok } +// methodCallType returns a method function type without the receiver defined. +// The input type must be a method function type with the receiver as the first input argument. +func (t *itype) methodCallType() reflect.Type { + it := []reflect.Type{} + ni := t.rtype.NumIn() + for i := 1; i < ni; i++ { + it = append(it, t.rtype.In(i)) + } + ot := []reflect.Type{} + no := t.rtype.NumOut() + for i := 0; i < no; i++ { + ot = append(ot, t.rtype.Out(i)) + } + return reflect.FuncOf(it, ot, t.rtype.IsVariadic()) +} + // getMethod returns a pointer to the method definition func (t *itype) getMethod(name string) *node { for _, m := range t.method {