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:
Robert Griesemer 2013-07-19 16:26:32 -07:00
parent b0ae7702cb
commit d722d82c52
12 changed files with 96 additions and 76 deletions

View File

@ -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)

View File

@ -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 {

View File

@ -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 .

View File

@ -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
}
}

View File

@ -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] }

View File

@ -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

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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)

View File

@ -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 {

View File

@ -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))

View File

@ -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())