1
0
mirror of https://github.com/golang/go synced 2024-11-18 16:04:44 -07:00

go.tools/go/types: use []*Func instead of *Scope to hold methods

R=golang-dev, adonovan
CC=golang-dev
https://golang.org/cl/10243043
This commit is contained in:
Robert Griesemer 2013-06-13 11:11:53 -07:00
parent 5efab5e9c0
commit 9ce6fcb502
9 changed files with 106 additions and 101 deletions

View File

@ -13,7 +13,7 @@ import (
"go/token"
)
// TODO(gri) eventually assert and unimplemented should disappear.
// TODO(gri) eventually assert should disappear.
func assert(p bool) {
if !p {
panic("assertion failed")
@ -289,15 +289,12 @@ func writeType(buf *bytes.Buffer, typ Type) {
case *Interface:
buf.WriteString("interface{")
if t.methods != nil {
for i, obj := range t.methods.entries {
if i > 0 {
buf.WriteString("; ")
}
m := obj.(*Func)
buf.WriteString(m.name)
writeSignature(buf, m.typ.(*Signature))
for i, m := range t.methods {
if i > 0 {
buf.WriteString("; ")
}
buf.WriteString(m.name)
writeSignature(buf, m.typ.(*Signature))
}
buf.WriteByte('}')

View File

@ -19,7 +19,6 @@ import (
// - simplify invalid handling: maybe just use Typ[Invalid] as marker, get rid of invalid Mode for values?
// - rethink error handling: should all callers check if x.mode == valid after making a call?
// - at the moment, iota is passed around almost everywhere - in many places we know it cannot be used
// - use "" or "_" consistently for anonymous identifiers? (e.g. reeceivers that have no name)
// - consider storing error messages in invalid operands for better error messages/debugging output
// TODO(gri) Test issues
@ -117,11 +116,11 @@ func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
return
}
func (check *checker) collectMethods(scope *Scope, list *ast.FieldList) *Scope {
func (check *checker) collectMethods(list *ast.FieldList) (methods []*Func) {
if list == nil {
return nil
}
methods := NewScope(nil)
scope := NewScope(nil)
for _, f := range list.List {
typ := check.typ(f.Type, len(f.Names) > 0) // cycles are not ok for embedded interfaces
// the parser ensures that f.Tag is nil and we don't
@ -135,31 +134,17 @@ func (check *checker) collectMethods(scope *Scope, list *ast.FieldList) *Scope {
continue
}
for _, name := range f.Names {
// TODO(gri) provide correct declaration info and scope
// TODO(gri) with unified scopes (Scope, ObjSet) this can become
// just a normal declaration
obj := NewFunc(name.Pos(), check.pkg, name.Name, sig)
if alt := methods.Insert(obj); alt != nil {
check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
if pos := alt.Pos(); pos.IsValid() {
check.errorf(pos, "previous declaration of %s", obj.Name())
}
obj = nil // for callIdent, below
}
check.callIdent(name, obj)
m := NewFunc(name.Pos(), check.pkg, name.Name, sig)
check.declare(scope, name, m)
methods = append(methods, m)
}
} else {
// embedded interface
utyp := typ.Underlying()
if ityp, ok := utyp.(*Interface); ok {
if ityp.methods != nil {
for _, obj := range ityp.methods.entries {
if alt := methods.Insert(obj); alt != nil {
check.errorf(list.Pos(), "multiple methods named %s", obj.Name())
obj = nil // for callImplicit, below
}
check.callImplicitObj(f, obj)
}
for _, m := range ityp.methods {
check.declare(scope, nil, m)
methods = append(methods, m)
}
} else if utyp != Typ[Invalid] {
// if utyp is invalid, don't complain (the root cause was reported before)
@ -167,7 +152,7 @@ func (check *checker) collectMethods(scope *Scope, list *ast.FieldList) *Scope {
}
}
}
return methods
return
}
func (check *checker) tag(t *ast.BasicLit) string {
@ -182,11 +167,13 @@ func (check *checker) tag(t *ast.BasicLit) string {
return ""
}
func (check *checker) collectFields(scope *Scope, list *ast.FieldList, cycleOk bool) (fields []*Field, tags []string) {
func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field, tags []string) {
if list == nil {
return
}
scope := NewScope(nil)
var typ Type // current field typ
var tag string // current field tag
add := func(field *ast.Field, ident *ast.Ident, name string, anonymous bool, pos token.Pos) {
@ -1702,10 +1689,8 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
x.mode = typexpr
case *ast.StructType:
scope := NewScope(check.topScope)
fields, tags := check.collectFields(scope, e.Fields, cycleOk)
x.mode = typexpr
x.typ = &Struct{fields: fields, tags: tags}
x.typ = NewStruct(check.collectFields(e.Fields, cycleOk))
case *ast.FuncType:
scope := NewScope(check.topScope)
@ -1715,9 +1700,8 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
x.typ = &Signature{scope: scope, recv: nil, params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic}
case *ast.InterfaceType:
scope := NewScope(check.topScope)
x.mode = typexpr
x.typ = &Interface{methods: check.collectMethods(scope, e.Methods)}
x.typ = NewInterface(check.collectMethods(e.Methods))
case *ast.MapType:
x.mode = typexpr

View File

@ -427,9 +427,6 @@ func (p *gcParser) parseMapType() Type {
// found in the p.imports map; we cannot create a real package in that case
// because we don't have a package name.
//
// TODO(gri): consider changing QualifiedIdents to (path, name) pairs to
// simplify this code.
//
func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) {
switch p.tok {
case scanner.Ident:
@ -584,7 +581,8 @@ func (p *gcParser) parseSignature() *Signature {
// visible in the export data.
//
func (p *gcParser) parseInterfaceType() Type {
var methods *Scope // lazily allocated
var scope *Scope // lazily allocated (empty interfaces are not uncommon)
var methods []*Func
p.expectKeyword("interface")
p.expect('{')
@ -594,16 +592,18 @@ func (p *gcParser) parseInterfaceType() Type {
}
pkg, name := p.parseName(true)
sig := p.parseSignature()
if methods == nil {
methods = NewScope(nil)
m := NewFunc(token.NoPos, pkg, name, sig)
if scope == nil {
scope = NewScope(nil)
}
if alt := methods.Insert(NewFunc(token.NoPos, pkg, name, sig)); alt != nil {
if alt := scope.Insert(m); alt != nil {
p.errorf("multiple methods named %s.%s", alt.Pkg().name, alt.Name())
}
methods = append(methods, m)
}
p.expect('}')
return &Interface{methods: methods}
return NewInterface(methods)
}
// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
@ -886,10 +886,10 @@ func (p *gcParser) parseMethodDecl() {
// add method to type unless type was imported before
// and method exists already
if base.methods == nil {
base.methods = NewScope(nil)
// TODO(gri) This is a quadratic algorithm - ok for now because method counts are small.
if lookupMethod(base.methods, pkg, name) == nil {
base.methods = append(base.methods, NewFunc(token.NoPos, pkg, name, sig))
}
base.methods.Insert(NewFunc(token.NoPos, pkg, name, sig))
}
// FuncDecl = "func" ExportedName Func .

View File

@ -220,6 +220,22 @@ type embeddedType struct {
multiples bool // if set, typ is embedded multiple times at the same level
}
func lookupMethod(methods []*Func, pkg *Package, name string) *Func {
if name == "_" {
return nil // blank identifiers are never found
}
for _, m := range methods {
// spec:
// "Two identifiers are different if they are spelled differently,
// or if they appear in different packages and are not exported.
// Otherwise, they are the same."
if m.name == name && (ast.IsExported(name) || m.pkg.path == pkg.path) {
return m
}
}
return nil
}
// lookupFieldBreadthFirst searches all types in list for a single entry (field
// or method) of the given name from the given package. If such a field is found,
// the result describes the field mode and type; otherwise the result mode is invalid.
@ -265,8 +281,7 @@ func lookupFieldBreadthFirst(list []embeddedType, pkg *Package, name string) (re
visited[typ] = true
// look for a matching attached method
if obj := typ.methods.Lookup(pkg, name); obj != nil {
m := obj.(*Func)
if m := lookupMethod(typ.methods, pkg, name); m != nil {
assert(m.typ != nil)
if !potentialMatch(e.multiples, value, m) {
return // name collision
@ -311,8 +326,7 @@ func lookupFieldBreadthFirst(list []embeddedType, pkg *Package, name string) (re
case *Interface:
// look for a matching method
if obj := t.methods.Lookup(pkg, name); obj != nil {
m := obj.(*Func)
if m := lookupMethod(t.methods, pkg, name); m != nil {
assert(m.typ != nil)
if !potentialMatch(e.multiples, value, m) {
return // name collision
@ -358,11 +372,14 @@ func findType(list []embeddedType, typ *Named) *embeddedType {
}
func lookupField(typ Type, pkg *Package, name string) lookupResult {
if name == "_" {
return lookupResult{mode: invalid} // empty fields/methods are never found
}
typ = typ.Deref()
if t, ok := typ.(*Named); ok {
if obj := t.methods.Lookup(pkg, name); obj != nil {
m := obj.(*Func)
if m := lookupMethod(t.methods, pkg, name); m != nil {
assert(m.typ != nil)
return lookupResult{value, m, nil}
}
@ -392,8 +409,7 @@ func lookupField(typ Type, pkg *Package, name string) lookupResult {
}
case *Interface:
if obj := t.methods.Lookup(pkg, name); obj != nil {
m := obj.(*Func)
if m := lookupMethod(t.methods, pkg, name); m != nil {
assert(m.typ != nil)
return lookupResult{value, m, nil}
}

View File

@ -221,28 +221,22 @@ func qname(f *Func) string {
return f.pkg.path + "." + f.name
}
// identicalMethods returns true if both object sets a and b have the
// same length and corresponding methods have identical types.
// identicalMethods returns true if both slices a and b have the
// same length and corresponding entries have identical types.
// TODO(gri) make this more efficient (e.g., sort them on completion)
func identicalMethods(a, b *Scope) bool {
if a.NumEntries() != b.NumEntries() {
func identicalMethods(a, b []*Func) bool {
if len(a) != len(b) {
return false
}
if a.IsEmpty() {
return true
}
m := make(map[string]*Func)
for _, obj := range a.entries {
x := obj.(*Func)
for _, x := range a {
k := qname(x)
assert(m[k] == nil) // method list must not have duplicate entries
m[k] = x
}
for _, obj := range b.entries {
y := obj.(*Func)
for _, y := range b {
k := qname(y)
if x := m[k]; x == nil || !IsIdentical(x.typ, y.typ) {
return false
@ -297,8 +291,7 @@ func missingMethod(typ Type, T *Interface) (method *Func, wrongType bool) {
// T.methods.NumEntries() > 0
if ityp, _ := typ.Underlying().(*Interface); ityp != nil {
for _, obj := range T.methods.entries {
m := obj.(*Func)
for _, m := range T.methods {
res := lookupField(ityp, m.pkg, m.name) // TODO(gri) no need to go via lookupField
if res.mode != invalid && !IsIdentical(res.obj.Type(), m.typ) {
return m, true
@ -308,8 +301,7 @@ func missingMethod(typ Type, T *Interface) (method *Func, wrongType bool) {
}
// a concrete type implements T if it implements all methods of T.
for _, obj := range T.methods.entries {
m := obj.(*Func)
for _, m := range T.methods {
res := lookupField(typ, m.pkg, m.name)
if res.mode == invalid {
return m, false

View File

@ -498,9 +498,8 @@ func (check *checker) declareType(obj *TypeName, typ ast.Expr, cycleOk bool) {
}
}
// typecheck method signatures
var methods *Scope // lazily allocated
var methods []*Func
if !scope.IsEmpty() {
methods = NewScope(nil)
for _, obj := range scope.entries {
m := obj.(*Func)
@ -517,7 +516,7 @@ func (check *checker) declareType(obj *TypeName, typ ast.Expr, cycleOk bool) {
sig.recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
m.typ = sig
assert(methods.Insert(obj) == nil)
methods = append(methods, m)
check.later(m, sig, m.decl.Body)
}
}

View File

@ -185,3 +185,26 @@ type (
Last int
)
// interfaces may have blank methods
type BlankI interface {
_()
_(int)
_() int
_(int) int
}
// non-interface types may have blank methods
type BlankT struct{}
func (BlankT) _() {}
func (BlankT) _(int) {}
func (BlankT) _() int { return 0 }
func (BlankT) _(int) int { return 0}
// no type can ever satisfy an interface with a _ method
func _() {
var i BlankI
var x BlankT
i = x /* ERROR "cannot assign" */
}

View File

@ -6,8 +6,6 @@ package types
import "go/ast"
// TODO(gri) Separate struct fields below into transient (used during type checking only)
// and permanent fields.
// TODO(gri) Revisit factory functions - make sure they have all relevant parameters.
// A Type represents a type of Go.
@ -162,6 +160,9 @@ func (s *Struct) Tag(i int) string {
// Index returns the index for the field in s with matching package and name.
// TODO(gri) should this be exported?
func (s *Struct) index(pkg *Package, name string) int {
if name == "_" {
return -1 // blank identifiers are never found
}
for i, f := range s.fields {
// spec:
// "Two identifiers are different if they are spelled differently,
@ -288,20 +289,24 @@ func (b *Builtin) Name() string {
// An Interface represents an interface type.
type Interface struct {
// TODO(gri) Change back to a sorted slice of methods.
methods *Scope // may be nil
methods []*Func
}
// NewInterface returns a new interface for the given methods.
func NewInterface(methods []*Func) *Interface {
return &Interface{methods}
}
// NumMethods returns the number of methods of interface t.
func (t *Interface) NumMethods() int { return t.methods.NumEntries() }
func (t *Interface) NumMethods() int { return len(t.methods) }
// Method returns the i'th method of interface t for 0 <= i < t.NumMethods().
func (t *Interface) Method(i int) *Func {
return t.methods.At(i).(*Func)
return t.methods[i]
}
// IsEmpty() reports whether t is an empty interface.
func (t *Interface) IsEmpty() bool { return t.methods.IsEmpty() }
func (t *Interface) IsEmpty() bool { return len(t.methods) == 0 }
// A Map represents a map type.
type Map struct {
@ -340,26 +345,16 @@ func (c *Chan) Elem() Type { return c.elt }
type Named struct {
obj *TypeName // corresponding declared object
underlying Type // nil if not fully declared yet; never a *Named
// TODO(gri): change back to a sorted slice of methods
methods *Scope // directly associated methods (not the method set of this type); may be nil
methods []*Func // methods declared for this type (not the method set of this type)
}
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
// The underlying type must exist and not be a *Named, and the methods scope entries must be *Func
// objects if the scope is not empty.
func NewNamed(obj *TypeName, underlying Type, methods *Scope) *Named {
func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
if _, ok := underlying.(*Named); ok {
panic("types.NewNamed: underlying type must not be *Named")
}
if methods != nil {
for _, obj := range methods.entries {
if _, ok := obj.(*Func); !ok {
panic("types.NewNamed: methods must be *Func objects")
}
}
}
typ := &Named{obj, underlying, methods}
if obj.typ == nil {
obj.typ = typ
@ -371,11 +366,11 @@ func NewNamed(obj *TypeName, underlying Type, methods *Scope) *Named {
func (t *Named) Obj() *TypeName { return t.obj }
// NumMethods returns the number of methods directly associated with named type t.
func (t *Named) NumMethods() int { return t.methods.NumEntries() }
func (t *Named) NumMethods() int { return len(t.methods) }
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
func (t *Named) Method(i int) *Func {
return t.methods.At(i).(*Func)
return t.methods[i]
}
// Implementations for Type methods.

View File

@ -101,10 +101,9 @@ func init() {
// error type
{
// Error has a nil package in its qualified name since it is in no package
methods := NewScope(nil)
sig := &Signature{results: NewTuple(NewVar(token.NoPos, nil, "", Typ[String]))}
methods.Insert(NewFunc(token.NoPos, nil, "Error", sig))
def(NewTypeName(token.NoPos, nil, "error", &Named{underlying: &Interface{methods: methods}}))
methods := []*Func{NewFunc(token.NoPos, nil, "Error", sig)}
def(NewTypeName(token.NoPos, nil, "error", &Named{underlying: NewInterface(methods)}))
}
for _, c := range predeclaredConstants {