Skip to content

Commit

Permalink
fix: handle binary methods where receiver is implicit
Browse files Browse the repository at this point in the history
  • Loading branch information
mvertes authored Feb 3, 2020
1 parent b057ada commit 137b165
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 21 deletions.
16 changes: 16 additions & 0 deletions _test/method29.go
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions _test/method30.go
Original file line number Diff line number Diff line change
@@ -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
8 changes: 6 additions & 2 deletions interp/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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}
Expand Down
3 changes: 3 additions & 0 deletions interp/gta.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions interp/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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))
}
}
Expand Down
51 changes: 34 additions & 17 deletions interp/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 137b165

Please sign in to comment.