go.tools/go/types: faster scopes, cleanups around method lookups

R=adonovan
CC=golang-dev
https://golang.org/cl/11787043
This commit is contained in:
Robert Griesemer 2013-07-24 20:02:54 -07:00
parent 6dbbc59ae0
commit c98ff05fdd
10 changed files with 55 additions and 72 deletions

View File

@ -288,11 +288,11 @@ func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) {
check.initVars(vars, rhs, true) check.initVars(vars, rhs, true)
// declare variables // declare variables
n := scope.NumEntries() n := scope.Len()
for _, obj := range vars { for _, obj := range vars {
scope.Insert(obj) scope.Insert(obj)
} }
if n == scope.NumEntries() { if n == scope.Len() {
check.errorf(vars[0].Pos(), "no new variables on left side of :=") check.errorf(vars[0].Pos(), "no new variables on left side of :=")
} }
} }

View File

@ -237,7 +237,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
} }
// verify that m is in the method set of x.typ // verify that m is in the method set of x.typ
if _, isPtr := deref(m.typ.(*Signature).recv.typ); isPtr && !indirect { if !indirect && ptrRecv(m) {
check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ) check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ)
goto Error goto Error
} }
@ -271,7 +271,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
// contains m and the argument list can be assigned to the parameter // 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() // list of m. If x is addressable and &x's method set contains m, x.m()
// is shorthand for (&x).m()". // is shorthand for (&x).m()".
if _, isPtr := deref(obj.typ.(*Signature).recv.typ); isPtr && !indirect && x.mode != variable { if !indirect && x.mode != variable && ptrRecv(obj) {
check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x) check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x)
goto Error goto Error
} }
@ -281,7 +281,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
typ := x.typ typ := x.typ
if x.mode == variable { if x.mode == variable {
// If typ is not an (unnamed) pointer, use *typ instead, // If typ is not an (unnamed) pointer, use *typ instead,
// because the the method set of *typ includes the methods // because the method set of *typ includes the methods
// of typ. // of typ.
// Variables are addressable, so we can always take their // Variables are addressable, so we can always take their
// address. // address.

View File

@ -98,7 +98,7 @@ func lookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
return // blank fields/methods are never found return // blank fields/methods are never found
} }
// Start with typ as single entry at lowest depth. // Start with typ as single entry at shallowest depth.
// If typ is not a named type, insert a nil type instead. // If typ is not a named type, insert a nil type instead.
typ, isPtr := deref(typ) typ, isPtr := deref(typ)
t, _ := typ.(*Named) t, _ := typ.(*Named)
@ -280,12 +280,9 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) {
} }
// verify that f is in the method set of typ // verify that f is in the method set of typ
// (the receiver is nil if f is an interface method) if !indirect && ptrRecv(f) {
if recv := f.typ.(*Signature).recv; recv != nil {
if _, isPtr := deref(recv.typ); isPtr && !indirect {
return m, false return m, false
} }
}
if !IsIdentical(obj.Type(), m.typ) { if !IsIdentical(obj.Type(), m.typ) {
return m, true return m, true

View File

@ -143,7 +143,7 @@ func NewMethodSet(T Type) *MethodSet {
// method set up to the current depth, allocated lazily // method set up to the current depth, allocated lazily
var base methodSet var base methodSet
// Start with typ as single entry at lowest depth. // Start with typ as single entry at shallowest depth.
// If typ is not a named type, insert a nil type instead. // If typ is not a named type, insert a nil type instead.
typ, isPtr := deref(T) typ, isPtr := deref(T)
t, _ := typ.(*Named) t, _ := typ.(*Named)
@ -258,7 +258,7 @@ func NewMethodSet(T Type) *MethodSet {
// A fieldSet is a set of fields and name collisions. // A fieldSet is a set of fields and name collisions.
// A collision indicates that multiple fields with the // A collision indicates that multiple fields with the
// same unique name appeared. // same unique id appeared.
type fieldSet map[string]*Var // a nil entry indicates a name collision type fieldSet map[string]*Var // a nil entry indicates a name collision
// Add adds field f to the field set s. // Add adds field f to the field set s.
@ -282,7 +282,7 @@ func (s fieldSet) add(f *Var, multiples bool) fieldSet {
// A methodSet is a set of methods and name collisions. // A methodSet is a set of methods and name collisions.
// A collision indicates that multiple methods with the // A collision indicates that multiple methods with the
// same unique name appeared. // same unique id appeared.
type methodSet map[string]*Method // a nil entry indicates a name collision type methodSet map[string]*Method // a nil entry indicates a name collision
// Add adds all functions in list to the method set s. // Add adds all functions in list to the method set s.

View File

@ -13,7 +13,7 @@ package types
// An objset is a set of objects identified by their unique id. // An objset is a set of objects identified by their unique id.
// The zero value for objset is a ready-to-use empty objset. // The zero value for objset is a ready-to-use empty objset.
type objset struct { type objset struct {
objmap map[string]Object // allocated lazily elems map[string]Object // allocated lazily
} }
// insert attempts to insert an object obj into objset s. // insert attempts to insert an object obj into objset s.
@ -27,12 +27,12 @@ func (s *objset) insert(obj Object) Object {
return nil return nil
} }
id := Id(obj.Pkg(), name) id := Id(obj.Pkg(), name)
if alt := s.objmap[id]; alt != nil { if alt := s.elems[id]; alt != nil {
return alt return alt
} }
if s.objmap == nil { if s.elems == nil {
s.objmap = make(map[string]Object) s.elems = make(map[string]Object)
} }
s.objmap[id] = obj s.elems[id] = obj
return nil return nil
} }

View File

@ -196,7 +196,7 @@ func (check *checker) resolveFiles(files []*ast.File) {
// add import to file scope // add import to file scope
if name == "." { if name == "." {
// merge imported scope with file scope // merge imported scope with file scope
for _, obj := range imp.scope.entries { for _, obj := range imp.scope.elems {
// gcimported package scopes contain non-exported // gcimported package scopes contain non-exported
// objects such as types used in partially exported // objects such as types used in partially exported
// objects - do not accept them // objects - do not accept them
@ -320,7 +320,7 @@ func (check *checker) resolveFiles(files []*ast.File) {
// Phase 2: Verify that objects in package and file scopes have different names. // Phase 2: Verify that objects in package and file scopes have different names.
for _, scope := range scopes { for _, scope := range scopes {
for _, obj := range scope.entries { for _, obj := range scope.elems {
if alt := pkg.scope.Lookup(obj.Name()); alt != nil { if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
check.errorf(alt.Pos(), "%s already declared in this file through import of package %s", obj.Name(), obj.Pkg().Name()) check.errorf(alt.Pos(), "%s already declared in this file through import of package %s", obj.Name(), obj.Pkg().Name())
} }

View File

@ -11,6 +11,7 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"io" "io"
"sort"
"strings" "strings"
) )
@ -25,20 +26,18 @@ type Scope struct {
parent *Scope parent *Scope
children []*Scope children []*Scope
node ast.Node node ast.Node
elems map[string]Object // lazily allocated
entries []Object
objmap map[string]Object // lazily allocated for large scopes
} }
// NewScope returns a new, empty scope contained in the given parent // NewScope returns a new, empty scope contained in the given parent
// scope, if any. // scope, if any.
func NewScope(parent *Scope) *Scope { func NewScope(parent *Scope) *Scope {
scope := &Scope{parent: parent} s := &Scope{parent: parent}
// don't add children to Universe scope! // don't add children to Universe scope!
if parent != nil && parent != Universe { if parent != nil && parent != Universe {
parent.children = append(parent.children, scope) parent.children = append(parent.children, s)
} }
return scope return s
} }
// Parent returns the scope's containing (parent) scope. // Parent returns the scope's containing (parent) scope.
@ -62,11 +61,20 @@ func (s *Scope) Parent() *Scope { return s.parent }
// (universe and package scopes). // (universe and package scopes).
func (s *Scope) Node() ast.Node { return s.node } func (s *Scope) Node() ast.Node { return s.node }
// NumEntries() returns the number of scope entries. // Len() returns the number of scope elements.
func (s *Scope) NumEntries() int { return len(s.entries) } func (s *Scope) Len() int { return len(s.elems) }
// At returns the i'th scope entry for 0 <= i < NumEntries(). // Names returns the scope's element names in sorted order.
func (s *Scope) At(i int) Object { return s.entries[i] } func (s *Scope) Names() []string {
names := make([]string, len(s.elems))
i := 0
for name := range s.elems {
names[i] = name
i++
}
sort.Strings(names)
return names
}
// NumChildren() returns the number of scopes nested in s. // NumChildren() returns the number of scopes nested in s.
func (s *Scope) NumChildren() int { return len(s.children) } func (s *Scope) NumChildren() int { return len(s.children) }
@ -77,15 +85,7 @@ func (s *Scope) Child(i int) *Scope { return s.children[i] }
// Lookup returns the object in scope s with the given name if such an // Lookup returns the object in scope s with the given name if such an
// object exists; otherwise the result is nil. // object exists; otherwise the result is nil.
func (s *Scope) Lookup(name string) Object { func (s *Scope) Lookup(name string) Object {
if s.objmap != nil { return s.elems[name]
return s.objmap[name]
}
for _, obj := range s.entries {
if obj.Name() == name {
return obj
}
}
return nil
} }
// LookupParent follows the parent chain of scopes starting with s until // LookupParent follows the parent chain of scopes starting with s until
@ -93,7 +93,7 @@ func (s *Scope) Lookup(name string) Object {
// returns that object. If no such scope exists, the result is nil. // returns that object. If no such scope exists, the result is nil.
func (s *Scope) LookupParent(name string) Object { func (s *Scope) LookupParent(name string) Object {
for ; s != nil; s = s.parent { for ; s != nil; s = s.parent {
if obj := s.Lookup(name); obj != nil { if obj := s.elems[name]; obj != nil {
return obj return obj
} }
} }
@ -110,7 +110,6 @@ func (s *Scope) LookupParent(name string) Object {
// not inserted, but have their parent field set to s. // not inserted, but have their parent field set to s.
func (s *Scope) Insert(obj Object) Object { func (s *Scope) Insert(obj Object) Object {
name := obj.Name() name := obj.Name()
// spec: "The blank identifier, represented by the underscore // spec: "The blank identifier, represented by the underscore
// character _, may be used in a declaration like any other // character _, may be used in a declaration like any other
// identifier but the declaration does not introduce a new // identifier but the declaration does not introduce a new
@ -119,48 +118,35 @@ func (s *Scope) Insert(obj Object) Object {
obj.setParent(s) obj.setParent(s)
return nil return nil
} }
if alt := s.elems[name]; alt != nil {
if alt := s.Lookup(name); alt != nil {
return alt return alt
} }
if s.elems == nil {
// populate parallel objmap for larger scopes s.elems = make(map[string]Object)
// TODO(gri) what is the right threshold? should we only use a map?
if len(s.entries) == 32 {
m := make(map[string]Object)
for _, obj := range s.entries {
m[obj.Name()] = obj
}
s.objmap = m
}
// add object
s.entries = append(s.entries, obj)
if s.objmap != nil {
s.objmap[name] = obj
} }
s.elems[name] = obj
obj.setParent(s) obj.setParent(s)
return nil return nil
} }
// WriteTo writes a string representation of the scope to w. // WriteTo writes a string representation of the scope to w,
// with the scope elements sorted by name.
// The level of indentation is controlled by n >= 0, with // The level of indentation is controlled by n >= 0, with
// n == 0 for no indentation. // n == 0 for no indentation.
// If recurse is set, it also prints nested (children) scopes. // If recurse is set, it also writes nested (children) scopes.
func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) { func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) {
const ind = ". " const ind = ". "
indn := strings.Repeat(ind, n) indn := strings.Repeat(ind, n)
if len(s.entries) == 0 { if len(s.elems) == 0 {
fmt.Fprintf(w, "%sscope %p {}\n", indn, s) fmt.Fprintf(w, "%sscope %p {}\n", indn, s)
return return
} }
fmt.Fprintf(w, "%sscope %p {\n", indn, s) fmt.Fprintf(w, "%sscope %p {\n", indn, s)
indn1 := indn + ind indn1 := indn + ind
for _, obj := range s.entries { for _, name := range s.Names() {
fmt.Fprintf(w, "%s%s\n", indn1, obj) fmt.Fprintf(w, "%s%s\n", indn1, s.elems[name])
} }
if recurse { if recurse {

View File

@ -81,7 +81,7 @@ func (check *checker) stmt(s ast.Stmt) {
case *ast.LabeledStmt: case *ast.LabeledStmt:
scope := check.funcSig.labels scope := check.funcSig.labels
if scope == nil { if scope == nil {
scope = new(Scope) // no label scope chain scope = NewScope(nil) // no label scope chain
check.funcSig.labels = scope check.funcSig.labels = scope
} }
label := s.Label label := s.Label

View File

@ -86,8 +86,8 @@ var predeclaredFunctions = [...]*Builtin{
} }
func init() { func init() {
Universe = new(Scope) Universe = NewScope(nil)
Unsafe = NewPackage(token.NoPos, "unsafe", "unsafe", NewScope(nil), nil, true) Unsafe = NewPackage(token.NoPos, "unsafe", "unsafe", NewScope(Universe), nil, true)
// predeclared types // predeclared types
for _, t := range Typ { for _, t := range Typ {

View File

@ -48,8 +48,8 @@ func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
} }
// Create Values for built-in functions. // Create Values for built-in functions.
for i, n := 0, types.Universe.NumEntries(); i < n; i++ { for _, name := range types.Universe.Names() {
if obj, ok := types.Universe.At(i).(*types.Func); ok { if obj, ok := types.Universe.Lookup(name).(*types.Func); ok {
prog.builtins[obj] = &Builtin{obj} prog.builtins[obj] = &Builtin{obj}
} }
} }
@ -237,8 +237,8 @@ func (prog *Program) CreatePackage(info *importer.PackageInfo) *Package {
// No code. // No code.
// No position information. // No position information.
scope := p.Object.Scope() scope := p.Object.Scope()
for i, n := 0, scope.NumEntries(); i < n; i++ { for _, name := range scope.Names() {
obj := scope.At(i) obj := scope.Lookup(name)
if obj, ok := obj.(*types.TypeName); ok { if obj, ok := obj.(*types.TypeName); ok {
// TODO(adonovan): are the set of Func // TODO(adonovan): are the set of Func
// objects passed to memberFromObject // objects passed to memberFromObject