diff --git a/go/types/builtins.go b/go/types/builtins.go index a1c706c3..2dbe9609 100644 --- a/go/types/builtins.go +++ b/go/types/builtins.go @@ -355,6 +355,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) { check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base) goto Error } + check.recordObject(arg.Sel, lookupResult(base, obj, index, indirect)) offs := check.conf.offsetof(base, index) x.mode = constant x.val = exact.MakeInt64(offs) diff --git a/go/types/call.go b/go/types/call.go index 8d81d705..1b8f390b 100644 --- a/go/types/call.go +++ b/go/types/call.go @@ -225,7 +225,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) { goto Error } - check.recordObject(e.Sel, obj) + check.recordObject(e.Sel, lookupResult(x.typ, obj, index, indirect)) if x.mode == typexpr { // method expression diff --git a/go/types/lookup.go b/go/types/lookup.go index bd523dd0..aad20420 100644 --- a/go/types/lookup.go +++ b/go/types/lookup.go @@ -11,6 +11,26 @@ package types // types always have only one representation (even when imported // indirectly via different packages.) +// TODO(gri) Move Field to objects.go? + +// A Field represents a struct field x.f and corresponding path. +type Field struct { + *Var + selectorPath +} + +func lookupResult(typ Type, obj Object, index []int, indirect bool) Object { + switch obj := obj.(type) { + case nil: + return nil + case *Var: + return &Field{obj, selectorPath{typ, index, indirect}} + case *Func: + return &Method{obj, selectorPath{typ, index, indirect}} + } + panic("unreachable") +} + // LookupFieldOrMethod looks up a field or method with given package and name // in typ and returns the corresponding *Var or *Func, an index sequence, // and a bool indicating if there were any pointer indirections on the path @@ -249,7 +269,7 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) { // A concrete type implements T if it implements all methods of T. for _, m := range T.methods { - obj, _, indirect := LookupFieldOrMethod(typ, m.pkg, m.name) + obj, _, indirect := lookupFieldOrMethod(typ, m.pkg, m.name) if obj == nil { return m, false } diff --git a/go/types/methodset.go b/go/types/methodset.go index d4226fff..17ca1fab 100644 --- a/go/types/methodset.go +++ b/go/types/methodset.go @@ -13,34 +13,55 @@ import ( "sync" ) -// TODO(gri) Move Method and accessors to objects.go. +// TODO(gri) Move Method to objects.go? // TODO(gri) Method.Type() returns the wrong receiver type. -// A Method represents a concrete or abstract (interface) -// method of a method set. +// A Method represents a concrete or abstract (interface) method x.m +// and corresponding path. The method belongs to the method set of x. type Method struct { *Func + selectorPath +} + +// Type returns the promoted signature type of m (i.e., the receiver +// type is Recv()). +func (m *Method) Type() Type { + sig := *m.Func.typ.(*Signature) + recv := *sig.recv + recv.typ = m.recv + sig.recv = &recv + return &sig +} + +// A selectorPath describes the path from a value x to one of its fields +// or methods f for a selector expression x.f. A path may include implicit +// field selections (e.g., x.f may be x.a.b.c.f). +type selectorPath struct { recv Type index []int indirect bool } -// Recv returns the receiver type for m, which is the type -// for which the method set containing m was computed. For -// interface methods, the receiver type is the type of the -// interface. -func (m *Method) Recv() Type { return m.recv } +// Recv returns the type of x for the path p for x.f. +func (p *selectorPath) Recv() Type { return p.recv } -// Index describes the path to the concrete (possibly embedded) -// function implementing this method. See LookupFieldOrMethod -// for details. -func (m *Method) Index() []int { return m.index } +// Index describes the path from x to the concrete (possibly embedded) field +// or method f for the path p for x.f. +// +// The last index entry is the field or method index of the type declaring f; +// either: +// +// 1) the list of declared methods of a named type; or +// 2) the list of methods of an interface type; or +// 3) the list of fields of a struct type. +// +// The earlier index entries are the indices of the embedded fields implicitly +// traversed to get from (the type of) x to f, starting at embedding depth 0. +func (p *selectorPath) Index() []int { return p.index } -// Indirect reports whether any pointer indirections was -// required to get from a value of m's receiver type to -// the receiver type of the concrete function implementing m. -// For interface methods, Indirect is undefined. -func (m *Method) Indirect() bool { return m.indirect } +// Indirect reports whether any pointer indirection was required to get from +// a value x to f for the path p for x.f. +func (p *selectorPath) Indirect() bool { return p.indirect } // A MethodSet is an ordered set of concrete or abstract (interface) methods. // The zero value for a MethodSet is a ready-to-use empty method set. @@ -279,7 +300,7 @@ func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool) // if f is not in the set, add it if !multiples { if _, found := s[key]; !found && (indirect || !ptrRecv(f)) { - s[key] = &Method{Func: f, index: concat(index, i), indirect: indirect} + s[key] = &Method{f, selectorPath{index: concat(index, i), indirect: indirect}} continue } }