go.tools/go/type: hook up interface method receivers
Also: - Renamed Object.SameName -> Object.SameId - Exported Object.Id R=adonovan CC=golang-dev https://golang.org/cl/11567046
This commit is contained in:
parent
b0ae7702cb
commit
d722d82c52
|
|
@ -355,7 +355,10 @@ 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
|
||||
}
|
||||
|
||||
// TODO(gri) don't create a lookupResult if no Objects map exists
|
||||
check.recordObject(arg.Sel, lookupResult(base, obj, index, indirect))
|
||||
|
||||
offs := check.conf.offsetof(base, index)
|
||||
x.mode = constant
|
||||
x.val = exact.MakeInt64(offs)
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
goto Error
|
||||
}
|
||||
|
||||
// TODO(gri) don't create a lookupResult if no Objects map exists
|
||||
check.recordObject(e.Sel, lookupResult(x.typ, obj, index, indirect))
|
||||
|
||||
if x.mode == typexpr {
|
||||
|
|
@ -236,12 +237,9 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
}
|
||||
|
||||
// verify that m is in the method set of x.typ
|
||||
// (the receiver is nil if m is an interface method)
|
||||
if recv := m.typ.(*Signature).recv; recv != nil {
|
||||
if _, isPtr := deref(recv.typ); isPtr && !indirect {
|
||||
check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ)
|
||||
goto Error
|
||||
}
|
||||
if _, isPtr := deref(m.typ.(*Signature).recv.typ); isPtr && !indirect {
|
||||
check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ)
|
||||
goto Error
|
||||
}
|
||||
|
||||
// the receiver type becomes the type of the first function
|
||||
|
|
@ -268,17 +266,14 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
case *Func:
|
||||
// TODO(gri) This code appears elsewhere, too. Factor!
|
||||
// verify that obj is in the method set of x.typ (or &(x.typ) if x is addressable)
|
||||
// (the receiver is nil if obj is an interface method)
|
||||
//
|
||||
// spec: "A method call x.m() is valid if the method set of (the type of) x
|
||||
// contains m and the argument list can be assigned to the parameter
|
||||
// list of m. If x is addressable and &x's method set contains m, x.m()
|
||||
// is shorthand for (&x).m()".
|
||||
if recv := obj.typ.(*Signature).recv; recv != nil {
|
||||
if _, isPtr := deref(recv.typ); isPtr && !indirect && x.mode != variable {
|
||||
check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x)
|
||||
goto Error
|
||||
}
|
||||
if _, isPtr := deref(obj.typ.(*Signature).recv.typ); isPtr && !indirect && x.mode != variable {
|
||||
check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x)
|
||||
goto Error
|
||||
}
|
||||
|
||||
if debug {
|
||||
|
|
|
|||
|
|
@ -601,6 +601,7 @@ func (p *gcParser) parseInterfaceType() Type {
|
|||
var scope *Scope // lazily allocated (empty interfaces are not uncommon)
|
||||
var methods []*Func
|
||||
|
||||
typ := new(Interface)
|
||||
p.expectKeyword("interface")
|
||||
p.expect('{')
|
||||
for i := 0; p.tok != '}'; i++ {
|
||||
|
|
@ -609,6 +610,9 @@ func (p *gcParser) parseInterfaceType() Type {
|
|||
}
|
||||
pkg, name := p.parseName(true)
|
||||
sig := p.parseSignature()
|
||||
// TODO(gri) Ideally, we should use a named type here instead of
|
||||
// typ, for less verbose printing of interface method signatures.
|
||||
sig.recv = NewVar(token.NoPos, pkg, "", typ)
|
||||
m := NewFunc(token.NoPos, pkg, name, sig)
|
||||
if scope == nil {
|
||||
scope = NewScope(nil)
|
||||
|
|
@ -619,8 +623,9 @@ func (p *gcParser) parseInterfaceType() Type {
|
|||
methods = append(methods, m)
|
||||
}
|
||||
p.expect('}')
|
||||
typ.methods = methods
|
||||
|
||||
return NewInterface(methods)
|
||||
return typ
|
||||
}
|
||||
|
||||
// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ func lookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
|
|||
case *Struct:
|
||||
// look for a matching field and collect embedded types
|
||||
for i, f := range t.fields {
|
||||
if f.SameName(pkg, name) {
|
||||
if f.sameId(pkg, name) {
|
||||
assert(f.typ != nil)
|
||||
index = concat(e.index, i)
|
||||
if obj != nil || e.multiples {
|
||||
|
|
@ -318,7 +318,7 @@ func fieldIndex(fields []*Var, pkg *Package, name string) int {
|
|||
return -1 // blank identifiers are never found
|
||||
}
|
||||
for i, f := range fields {
|
||||
if f.SameName(pkg, name) {
|
||||
if f.sameId(pkg, name) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
|
@ -329,7 +329,7 @@ func fieldIndex(fields []*Var, pkg *Package, name string) int {
|
|||
func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
|
||||
assert(name != "_")
|
||||
for i, m := range methods {
|
||||
if m.SameName(pkg, name) {
|
||||
if m.sameId(pkg, name) {
|
||||
return i, m
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ func (s *MethodSet) String() string {
|
|||
var buf bytes.Buffer
|
||||
fmt.Fprintln(&buf, "MethodSet {")
|
||||
for _, m := range s.list {
|
||||
fmt.Fprintf(&buf, "\t%s -> %s\n", m.uniqueName(), m.Func)
|
||||
fmt.Fprintf(&buf, "\t%s -> %s\n", m.Id(), m.Func)
|
||||
}
|
||||
fmt.Fprintln(&buf, "}")
|
||||
return buf.String()
|
||||
|
|
@ -95,14 +95,14 @@ func (s *MethodSet) Lookup(pkg *Package, name string) *Method {
|
|||
return nil
|
||||
}
|
||||
|
||||
key := (&object{pkg: pkg, name: name}).uniqueName()
|
||||
key := Id(pkg, name)
|
||||
i := sort.Search(len(s.list), func(i int) bool {
|
||||
m := s.list[i]
|
||||
return m.uniqueName() >= key
|
||||
return m.Id() >= key
|
||||
})
|
||||
if i < len(s.list) {
|
||||
m := s.list[i]
|
||||
if m.uniqueName() == key {
|
||||
if m.Id() == key {
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
|
@ -268,7 +268,7 @@ func (s fieldSet) add(f *Var, multiples bool) fieldSet {
|
|||
if s == nil {
|
||||
s = make(fieldSet)
|
||||
}
|
||||
key := f.uniqueName()
|
||||
key := f.Id()
|
||||
// if f is not in the set, add it
|
||||
if !multiples {
|
||||
if _, found := s[key]; !found {
|
||||
|
|
@ -296,7 +296,7 @@ func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool)
|
|||
s = make(methodSet)
|
||||
}
|
||||
for i, f := range list {
|
||||
key := f.uniqueName()
|
||||
key := f.Id()
|
||||
// if f is not in the set, add it
|
||||
if !multiples {
|
||||
if _, found := s[key]; !found && (indirect || !ptrRecv(f)) {
|
||||
|
|
@ -320,5 +320,5 @@ func ptrRecv(f *Func) bool {
|
|||
type byUniqueName []*Method
|
||||
|
||||
func (a byUniqueName) Len() int { return len(a) }
|
||||
func (a byUniqueName) Less(i, j int) bool { return a[i].uniqueName() < a[j].uniqueName() }
|
||||
func (a byUniqueName) Less(i, j int) bool { return a[i].Id() < a[j].Id() }
|
||||
func (a byUniqueName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
|
|
|||
|
|
@ -26,16 +26,33 @@ type Object interface {
|
|||
Name() string // package local object name
|
||||
Type() Type // object type
|
||||
IsExported() bool // reports whether the name starts with a capital letter
|
||||
|
||||
// SameName reports whether the object's name is the same as some
|
||||
// other qualified name, per the rules of Uniqueness of identifiers.
|
||||
SameName(pkg *Package, name string) bool
|
||||
Id() string // object id (see Id below)
|
||||
|
||||
// String returns a human-readable string of the object.
|
||||
String() string
|
||||
|
||||
// setParent sets the parent scope of the object.
|
||||
setParent(*Scope)
|
||||
|
||||
// sameId reports whether obj.Id() and Id(pkg, name) are the same.
|
||||
sameId(pkg *Package, name string) bool
|
||||
}
|
||||
|
||||
// Id returns name if it is exported, otherwise it
|
||||
// returns the name qualified with the package path.
|
||||
func Id(pkg *Package, name string) string {
|
||||
if ast.IsExported(name) {
|
||||
return name
|
||||
}
|
||||
// unexported names need the package path for differentiation
|
||||
path := ""
|
||||
if pkg != nil {
|
||||
path = pkg.path
|
||||
if path == "" {
|
||||
path = "?"
|
||||
}
|
||||
}
|
||||
return path + "." + name
|
||||
}
|
||||
|
||||
// An object implements the common parts of an Object.
|
||||
|
|
@ -53,8 +70,27 @@ func (obj *object) Pkg() *Package { return obj.pkg }
|
|||
func (obj *object) Name() string { return obj.name }
|
||||
func (obj *object) Type() Type { return obj.typ }
|
||||
func (obj *object) IsExported() bool { return ast.IsExported(obj.name) }
|
||||
func (obj *object) Id() string { return Id(obj.pkg, obj.name) }
|
||||
|
||||
func (obj *object) SameName(pkg *Package, name string) bool {
|
||||
func (obj *object) toString(kind string, typ Type) string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
buf.WriteString(kind)
|
||||
buf.WriteByte(' ')
|
||||
if obj.pkg != nil {
|
||||
buf.WriteString(obj.pkg.name)
|
||||
buf.WriteByte('.')
|
||||
}
|
||||
buf.WriteString(obj.name)
|
||||
buf.WriteByte(' ')
|
||||
writeType(&buf, typ)
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (obj *object) setParent(parent *Scope) { obj.parent = parent }
|
||||
|
||||
func (obj *object) sameId(pkg *Package, name string) bool {
|
||||
// spec:
|
||||
// "Two identifiers are different if they are spelled differently,
|
||||
// or if they appear in different packages and are not exported.
|
||||
|
|
@ -76,39 +112,6 @@ func (obj *object) SameName(pkg *Package, name string) bool {
|
|||
return pkg.path == obj.pkg.path
|
||||
}
|
||||
|
||||
func (obj *object) uniqueName() string {
|
||||
if obj.IsExported() {
|
||||
return obj.name
|
||||
}
|
||||
// unexported names need the package path for differentiation
|
||||
path := ""
|
||||
if obj.pkg != nil {
|
||||
path = obj.pkg.path
|
||||
if path == "" {
|
||||
path = "?"
|
||||
}
|
||||
}
|
||||
return path + "." + obj.name
|
||||
}
|
||||
|
||||
func (obj *object) toString(kind string, typ Type) string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
buf.WriteString(kind)
|
||||
buf.WriteByte(' ')
|
||||
if obj.pkg != nil {
|
||||
buf.WriteString(obj.pkg.name)
|
||||
buf.WriteByte('.')
|
||||
}
|
||||
buf.WriteString(obj.name)
|
||||
buf.WriteByte(' ')
|
||||
writeType(&buf, typ)
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (obj *object) setParent(parent *Scope) { obj.parent = parent }
|
||||
|
||||
// A Package represents the contents (objects) of a Go package.
|
||||
type Package struct {
|
||||
object
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ func IsIdentical(x, y Type) bool {
|
|||
g := y.fields[i]
|
||||
if f.anonymous != g.anonymous ||
|
||||
x.Tag(i) != y.Tag(i) ||
|
||||
!f.SameName(g.pkg, g.name) ||
|
||||
!f.sameId(g.pkg, g.name) ||
|
||||
!IsIdentical(f.typ, g.typ) {
|
||||
return false
|
||||
}
|
||||
|
|
@ -216,13 +216,13 @@ func identicalMethods(a, b []*Func) bool {
|
|||
|
||||
m := make(map[string]*Func)
|
||||
for _, x := range a {
|
||||
key := x.uniqueName()
|
||||
key := x.Id()
|
||||
assert(m[key] == nil) // method list must not have duplicate entries
|
||||
m[key] = x
|
||||
}
|
||||
|
||||
for _, y := range b {
|
||||
key := y.uniqueName()
|
||||
key := y.Id()
|
||||
if x := m[key]; x == nil || !IsIdentical(x.typ, y.typ) {
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ func (s *Scope) Lookup(pkg *Package, name string) Object {
|
|||
|
||||
// slow path: both pkg path and name must match
|
||||
for _, obj := range s.entries {
|
||||
if obj.SameName(pkg, name) {
|
||||
if obj.sameId(pkg, name) {
|
||||
return obj
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,11 +232,13 @@ func (check *checker) typ0(e ast.Expr, def *Named, cycleOk bool) Type {
|
|||
|
||||
case *ast.InterfaceType:
|
||||
typ := new(Interface)
|
||||
var recv Type = typ
|
||||
if def != nil {
|
||||
def.underlying = typ
|
||||
recv = def // use named receiver type if available
|
||||
}
|
||||
|
||||
typ.methods = check.collectMethods(e.Methods, cycleOk)
|
||||
typ.methods = check.collectMethods(recv, e.Methods, cycleOk)
|
||||
return typ
|
||||
|
||||
case *ast.MapType:
|
||||
|
|
@ -334,7 +336,7 @@ func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
|
|||
return
|
||||
}
|
||||
|
||||
func (check *checker) collectMethods(list *ast.FieldList, cycleOk bool) (methods []*Func) {
|
||||
func (check *checker) collectMethods(recv Type, list *ast.FieldList, cycleOk bool) (methods []*Func) {
|
||||
if list == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -347,11 +349,12 @@ func (check *checker) collectMethods(list *ast.FieldList, cycleOk bool) (methods
|
|||
if len(f.Names) > 0 {
|
||||
// methods (the parser ensures that there's only one
|
||||
// and we don't care if a constructed AST has more)
|
||||
sig, ok := typ.(*Signature)
|
||||
if !ok {
|
||||
sig, _ := typ.(*Signature)
|
||||
if sig == nil {
|
||||
check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ)
|
||||
continue
|
||||
}
|
||||
sig.recv = NewVar(token.NoPos, check.pkg, "", recv)
|
||||
for _, name := range f.Names {
|
||||
m := NewFunc(name.Pos(), check.pkg, name.Name, sig)
|
||||
check.declare(scope, name, m)
|
||||
|
|
|
|||
|
|
@ -100,9 +100,12 @@ func init() {
|
|||
// error type
|
||||
{
|
||||
// Error has a nil package in its qualified name since it is in no package
|
||||
sig := &Signature{results: NewTuple(NewVar(token.NoPos, nil, "", Typ[String]))}
|
||||
methods := []*Func{NewFunc(token.NoPos, nil, "Error", sig)}
|
||||
def(NewTypeName(token.NoPos, nil, "error", &Named{underlying: NewInterface(methods), complete: true}))
|
||||
res := NewVar(token.NoPos, nil, "", Typ[String])
|
||||
sig := &Signature{results: NewTuple(res)}
|
||||
err := NewFunc(token.NoPos, nil, "Error", sig)
|
||||
typ := &Named{underlying: &Interface{methods: []*Func{err}}, complete: true}
|
||||
sig.recv = NewVar(token.NoPos, nil, "", typ)
|
||||
def(NewTypeName(token.NoPos, nil, "error", typ))
|
||||
}
|
||||
|
||||
for _, c := range predeclaredConstants {
|
||||
|
|
|
|||
|
|
@ -119,6 +119,10 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
|
|||
pkg.Members[name] = fn
|
||||
} else {
|
||||
// Method declaration.
|
||||
// TODO(adonovan) Move this test elsewhere.
|
||||
if _, ok := recv.Type().Underlying().(*types.Interface); ok {
|
||||
return // ignore interface methods
|
||||
}
|
||||
_, method := namedTypeMethodIndex(
|
||||
deref(recv.Type()).(*types.Named),
|
||||
makeId(name, pkg.Object))
|
||||
|
|
|
|||
|
|
@ -114,12 +114,16 @@ func promotionWrapper(prog *Program, typ types.Type, obj *types.Method) *Functio
|
|||
|
||||
promotedRecv := obj.Func.Type().(*types.Signature).Recv()
|
||||
|
||||
// TODO(gri): fix: promotedRecv should always be non-nil but
|
||||
// for now it's nil for interface methods that were promoted.
|
||||
promotedRecvString := "INTERFACE?"
|
||||
if promotedRecv != nil {
|
||||
// TODO(adonovan): Interface method receivers used to be nil, but
|
||||
// aren't anymore. Nil them again so this code works for now. Fix.
|
||||
var promotedRecvString string
|
||||
if _, ok := promotedRecv.Type().Underlying().(*types.Interface); ok {
|
||||
promotedRecv = nil
|
||||
promotedRecvString = "INTERFACE?"
|
||||
} else {
|
||||
promotedRecvString = promotedRecv.String()
|
||||
}
|
||||
|
||||
// TODO(adonovan): include implicit field path in description.
|
||||
description := fmt.Sprintf("promotion wrapper for (%s).%s",
|
||||
promotedRecvString, obj.Func.Name())
|
||||
|
|
|
|||
Loading…
Reference in New Issue