diff --git a/go/types/builtins.go b/go/types/builtins.go index 2dbe9609..3ff50fe1 100644 --- a/go/types/builtins.go +++ b/go/types/builtins.go @@ -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) diff --git a/go/types/call.go b/go/types/call.go index 1b8f390b..aa3da728 100644 --- a/go/types/call.go +++ b/go/types/call.go @@ -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 { diff --git a/go/types/gcimporter.go b/go/types/gcimporter.go index 5c95fa64..ab249d08 100644 --- a/go/types/gcimporter.go +++ b/go/types/gcimporter.go @@ -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 . diff --git a/go/types/lookup.go b/go/types/lookup.go index aad20420..325bef3c 100644 --- a/go/types/lookup.go +++ b/go/types/lookup.go @@ -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 } } diff --git a/go/types/methodset.go b/go/types/methodset.go index 17ca1fab..8dc4b595 100644 --- a/go/types/methodset.go +++ b/go/types/methodset.go @@ -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] } diff --git a/go/types/objects.go b/go/types/objects.go index a1eb03fe..7c86271a 100644 --- a/go/types/objects.go +++ b/go/types/objects.go @@ -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 diff --git a/go/types/predicates.go b/go/types/predicates.go index d9a71357..84286acb 100644 --- a/go/types/predicates.go +++ b/go/types/predicates.go @@ -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 } diff --git a/go/types/scope.go b/go/types/scope.go index ed03219e..faafa1c1 100644 --- a/go/types/scope.go +++ b/go/types/scope.go @@ -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 } } diff --git a/go/types/typexpr.go b/go/types/typexpr.go index 345e80cb..b831727b 100644 --- a/go/types/typexpr.go +++ b/go/types/typexpr.go @@ -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) diff --git a/go/types/universe.go b/go/types/universe.go index a06198c8..75231c79 100644 --- a/go/types/universe.go +++ b/go/types/universe.go @@ -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 { diff --git a/ssa/create.go b/ssa/create.go index 70153914..af42c969 100644 --- a/ssa/create.go +++ b/ssa/create.go @@ -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)) diff --git a/ssa/promote.go b/ssa/promote.go index 292dc240..05dd719d 100644 --- a/ssa/promote.go +++ b/ssa/promote.go @@ -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())