mirror of
https://github.com/golang/go
synced 2024-11-18 02:54:47 -07:00
go.tools/go/types: replace ObjSet with improved Scope
- First step towards unified use of scopes. Will enable further simplifications. - Removed various ForEach iterators in favor of the existing accessor methods, for a thinner API. - Renamed outer/Outer to parent/Parent for scopes. - Removed check.lookup in favor of Scope.LookupParent. R=adonovan CC=golang-dev https://golang.org/cl/9862044
This commit is contained in:
parent
291b2c84b6
commit
3cad037e2f
@ -48,14 +48,6 @@ type checker struct {
|
||||
pos []token.Pos // stack of expr positions; debugging support, used if trace is set
|
||||
}
|
||||
|
||||
func (check *checker) openScope() {
|
||||
check.topScope = &Scope{Outer: check.topScope}
|
||||
}
|
||||
|
||||
func (check *checker) closeScope() {
|
||||
check.topScope = check.topScope.Outer
|
||||
}
|
||||
|
||||
func (check *checker) callIdent(id *ast.Ident, obj Object) {
|
||||
if f := check.ctxt.Ident; f != nil {
|
||||
f(id, obj)
|
||||
@ -68,18 +60,6 @@ func (check *checker) callImplicitObj(node ast.Node, obj Object) {
|
||||
}
|
||||
}
|
||||
|
||||
// lookup returns the Object denoted by ident by looking up the
|
||||
// identifier in the scope chain, starting with the top-most scope.
|
||||
// If no object is found, lookup returns nil.
|
||||
func (check *checker) lookup(ident *ast.Ident) Object {
|
||||
for scope := check.topScope; scope != nil; scope = scope.Outer {
|
||||
if obj := scope.Lookup(ident.Name); obj != nil {
|
||||
return obj
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type function struct {
|
||||
file *Scope // only valid if resolve is set
|
||||
obj *Func // for debugging/tracing only
|
||||
@ -104,7 +84,7 @@ type bailout struct{}
|
||||
func check(ctxt *Context, path string, fset *token.FileSet, files ...*ast.File) (pkg *Package, err error) {
|
||||
pkg = &Package{
|
||||
path: path,
|
||||
scope: &Scope{Outer: Universe},
|
||||
scope: NewScope(Universe),
|
||||
imports: make(map[string]*Package),
|
||||
}
|
||||
|
||||
|
@ -117,10 +117,11 @@ func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
|
||||
return
|
||||
}
|
||||
|
||||
func (check *checker) collectMethods(scope *Scope, list *ast.FieldList) (methods ObjSet) {
|
||||
func (check *checker) collectMethods(scope *Scope, list *ast.FieldList) *Scope {
|
||||
if list == nil {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
methods := 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
|
||||
@ -164,7 +165,7 @@ func (check *checker) collectMethods(scope *Scope, list *ast.FieldList) (methods
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
return methods
|
||||
}
|
||||
|
||||
func (check *checker) tag(t *ast.BasicLit) string {
|
||||
@ -1045,15 +1046,12 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
goto Error // error was reported before
|
||||
|
||||
case *ast.Ident:
|
||||
obj := check.lookup(e)
|
||||
obj := check.topScope.LookupParent(e.Name)
|
||||
check.callIdent(e, obj)
|
||||
if obj == nil {
|
||||
if e.Name == "_" {
|
||||
check.invalidOp(e.Pos(), "cannot use _ as value or type")
|
||||
check.errorf(e.Pos(), "cannot use _ as value or type")
|
||||
} else {
|
||||
// TODO(gri) anonymous function result parameters are
|
||||
// not declared - this causes trouble when
|
||||
// type-checking return statements
|
||||
check.errorf(e.Pos(), "undeclared name: %s", e.Name)
|
||||
}
|
||||
goto Error // error was reported before
|
||||
@ -1279,9 +1277,9 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
// can only appear in qualified identifiers which are mapped to
|
||||
// selector expressions.
|
||||
if ident, ok := e.X.(*ast.Ident); ok {
|
||||
if pkg, ok := check.lookup(ident).(*Package); ok {
|
||||
if pkg, ok := check.topScope.LookupParent(ident.Name).(*Package); ok {
|
||||
check.callIdent(ident, pkg)
|
||||
exp := pkg.scope.Lookup(sel)
|
||||
exp := pkg.scope.Lookup(nil, sel)
|
||||
if exp == nil {
|
||||
check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
|
||||
goto Error
|
||||
@ -1693,20 +1691,20 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
x.mode = typexpr
|
||||
|
||||
case *ast.StructType:
|
||||
scope := &Scope{Outer: check.topScope}
|
||||
scope := NewScope(check.topScope)
|
||||
fields, tags := check.collectFields(scope, e.Fields, cycleOk)
|
||||
x.mode = typexpr
|
||||
x.typ = &Struct{scope: scope, fields: fields, tags: tags}
|
||||
|
||||
case *ast.FuncType:
|
||||
scope := &Scope{Outer: check.topScope}
|
||||
scope := NewScope(check.topScope)
|
||||
params, isVariadic := check.collectParams(scope, e.Params, true)
|
||||
results, _ := check.collectParams(scope, e.Results, false)
|
||||
x.mode = typexpr
|
||||
x.typ = &Signature{scope: scope, recv: nil, params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic}
|
||||
|
||||
case *ast.InterfaceType:
|
||||
scope := &Scope{Outer: check.topScope}
|
||||
scope := NewScope(check.topScope)
|
||||
x.mode = typexpr
|
||||
x.typ = &Interface{methods: check.collectMethods(scope, e.Methods)}
|
||||
|
||||
|
@ -202,7 +202,7 @@ func declConst(pkg *Package, name string) *Const {
|
||||
// the constant may have been imported before - if it exists
|
||||
// already in the respective scope, return that constant
|
||||
scope := pkg.scope
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
if obj := scope.Lookup(nil, name); obj != nil {
|
||||
return obj.(*Const)
|
||||
}
|
||||
// otherwise create a new constant and insert it into the scope
|
||||
@ -213,7 +213,7 @@ func declConst(pkg *Package, name string) *Const {
|
||||
|
||||
func declTypeName(pkg *Package, name string) *TypeName {
|
||||
scope := pkg.scope
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
if obj := scope.Lookup(nil, name); obj != nil {
|
||||
return obj.(*TypeName)
|
||||
}
|
||||
obj := &TypeName{pkg: pkg, name: name}
|
||||
@ -226,7 +226,7 @@ func declTypeName(pkg *Package, name string) *TypeName {
|
||||
|
||||
func declVar(pkg *Package, name string) *Var {
|
||||
scope := pkg.scope
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
if obj := scope.Lookup(nil, name); obj != nil {
|
||||
return obj.(*Var)
|
||||
}
|
||||
obj := &Var{pkg: pkg, name: name}
|
||||
@ -236,7 +236,7 @@ func declVar(pkg *Package, name string) *Var {
|
||||
|
||||
func declFunc(pkg *Package, name string) *Func {
|
||||
scope := pkg.scope
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
if obj := scope.Lookup(nil, name); obj != nil {
|
||||
return obj.(*Func)
|
||||
}
|
||||
obj := &Func{pkg: pkg, name: name}
|
||||
@ -360,7 +360,7 @@ func (p *gcParser) getPkg(id, name string) *Package {
|
||||
}
|
||||
pkg := p.imports[id]
|
||||
if pkg == nil && name != "" {
|
||||
pkg = &Package{name: name, path: id, scope: new(Scope)}
|
||||
pkg = &Package{name: name, path: id, scope: NewScope(nil)}
|
||||
p.imports[id] = pkg
|
||||
}
|
||||
return pkg
|
||||
@ -385,7 +385,7 @@ func (p *gcParser) parseExportedName() (pkg *Package, name string) {
|
||||
//
|
||||
func (p *gcParser) parseBasicType() Type {
|
||||
id := p.expect(scanner.Ident)
|
||||
obj := Universe.Lookup(id)
|
||||
obj := Universe.Lookup(nil, id)
|
||||
if obj, ok := obj.(*TypeName); ok {
|
||||
return obj.typ
|
||||
}
|
||||
@ -580,7 +580,7 @@ func (p *gcParser) parseSignature() *Signature {
|
||||
// visible in the export data.
|
||||
//
|
||||
func (p *gcParser) parseInterfaceType() Type {
|
||||
var methods ObjSet
|
||||
var methods *Scope // lazily allocated
|
||||
|
||||
p.expectKeyword("interface")
|
||||
p.expect('{')
|
||||
@ -590,6 +590,9 @@ func (p *gcParser) parseInterfaceType() Type {
|
||||
}
|
||||
pkg, name := p.parseName(true)
|
||||
sig := p.parseSignature()
|
||||
if methods == nil {
|
||||
methods = NewScope(nil)
|
||||
}
|
||||
if alt := methods.Insert(&Func{token.NoPos, pkg, nil, name, sig, nil}); alt != nil {
|
||||
p.errorf("multiple methods named %s.%s", alt.Pkg().name, alt.Name())
|
||||
}
|
||||
@ -879,6 +882,9 @@ 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)
|
||||
}
|
||||
base.methods.Insert(&Func{token.NoPos, pkg, nil, name, sig, nil})
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ func TestGcImportedTypes(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
|
||||
obj := pkg.scope.Lookup(objName)
|
||||
obj := pkg.scope.Lookup(nil, objName)
|
||||
|
||||
// TODO(gri) should define an accessor on Object
|
||||
var kind ast.ObjKind
|
||||
|
@ -19,14 +19,14 @@ import (
|
||||
// All objects implement the Object interface.
|
||||
//
|
||||
type Object interface {
|
||||
Pkg() *Package // nil for objects in the Universe scope and labels
|
||||
Outer() *Scope // the scope in which this object is declared
|
||||
Pkg() *Package // nil for objects in the Universe scope and labels
|
||||
Parent() *Scope // the scope in which this object is declared
|
||||
Name() string
|
||||
Type() Type
|
||||
Pos() token.Pos // position of object identifier in declaration
|
||||
// TODO(gri) provide String method!
|
||||
|
||||
setOuter(*Scope)
|
||||
setParent(*Scope)
|
||||
}
|
||||
|
||||
// A Package represents the contents (objects) of a Go package.
|
||||
@ -34,7 +34,7 @@ type Package struct {
|
||||
pos token.Pos // position of package import path or local package identifier, if present
|
||||
name string
|
||||
path string // import path, "" for current (non-imported) package
|
||||
outer *Scope
|
||||
parent *Scope
|
||||
scope *Scope // package-level scope
|
||||
imports map[string]*Package // map of import paths to imported packages
|
||||
complete bool // if set, this package was imported completely
|
||||
@ -45,7 +45,7 @@ func NewPackage(path, name string) *Package {
|
||||
}
|
||||
|
||||
func (obj *Package) Pkg() *Package { return obj }
|
||||
func (obj *Package) Outer() *Scope { return obj.outer }
|
||||
func (obj *Package) Parent() *Scope { return obj.parent }
|
||||
func (obj *Package) Scope() *Scope { return obj.scope }
|
||||
func (obj *Package) Name() string { return obj.name }
|
||||
func (obj *Package) Type() Type { return Typ[Invalid] }
|
||||
@ -53,56 +53,56 @@ func (obj *Package) Pos() token.Pos { return obj.pos }
|
||||
func (obj *Package) Path() string { return obj.path }
|
||||
func (obj *Package) Imports() map[string]*Package { return obj.imports }
|
||||
func (obj *Package) Complete() bool { return obj.complete }
|
||||
func (obj *Package) setOuter(*Scope) { /* don't do anything - this is the package's scope */
|
||||
func (obj *Package) setParent(*Scope) { /* don't do anything - this is the package's scope */
|
||||
}
|
||||
|
||||
// A Const represents a declared constant.
|
||||
type Const struct {
|
||||
pos token.Pos // position of identifier in constant declaration
|
||||
pkg *Package
|
||||
outer *Scope
|
||||
name string
|
||||
typ Type
|
||||
val exact.Value
|
||||
pos token.Pos // position of identifier in constant declaration
|
||||
pkg *Package
|
||||
parent *Scope
|
||||
name string
|
||||
typ Type
|
||||
val exact.Value
|
||||
|
||||
visited bool // for initialization cycle detection
|
||||
}
|
||||
|
||||
func (obj *Const) Pkg() *Package { return obj.pkg }
|
||||
func (obj *Const) Outer() *Scope { return obj.outer }
|
||||
func (obj *Const) Name() string { return obj.name }
|
||||
func (obj *Const) Type() Type { return obj.typ }
|
||||
func (obj *Const) Pos() token.Pos { return obj.pos }
|
||||
func (obj *Const) Val() exact.Value { return obj.val }
|
||||
func (obj *Const) setOuter(s *Scope) { obj.outer = s }
|
||||
func (obj *Const) Pkg() *Package { return obj.pkg }
|
||||
func (obj *Const) Parent() *Scope { return obj.parent }
|
||||
func (obj *Const) Name() string { return obj.name }
|
||||
func (obj *Const) Type() Type { return obj.typ }
|
||||
func (obj *Const) Pos() token.Pos { return obj.pos }
|
||||
func (obj *Const) Val() exact.Value { return obj.val }
|
||||
func (obj *Const) setParent(s *Scope) { obj.parent = s }
|
||||
|
||||
// A TypeName represents a declared type.
|
||||
type TypeName struct {
|
||||
pos token.Pos // position of identifier in type declaration
|
||||
pkg *Package
|
||||
outer *Scope
|
||||
name string
|
||||
typ Type // *Named or *Basic
|
||||
pos token.Pos // position of identifier in type declaration
|
||||
pkg *Package
|
||||
parent *Scope
|
||||
name string
|
||||
typ Type // *Named or *Basic
|
||||
}
|
||||
|
||||
func NewTypeName(pkg *Package, name string, typ Type) *TypeName {
|
||||
return &TypeName{token.NoPos, pkg, nil, name, typ}
|
||||
}
|
||||
|
||||
func (obj *TypeName) Pkg() *Package { return obj.pkg }
|
||||
func (obj *TypeName) Outer() *Scope { return obj.outer }
|
||||
func (obj *TypeName) Name() string { return obj.name }
|
||||
func (obj *TypeName) Type() Type { return obj.typ }
|
||||
func (obj *TypeName) Pos() token.Pos { return obj.pos }
|
||||
func (obj *TypeName) setOuter(s *Scope) { obj.outer = s }
|
||||
func (obj *TypeName) Pkg() *Package { return obj.pkg }
|
||||
func (obj *TypeName) Parent() *Scope { return obj.parent }
|
||||
func (obj *TypeName) Name() string { return obj.name }
|
||||
func (obj *TypeName) Type() Type { return obj.typ }
|
||||
func (obj *TypeName) Pos() token.Pos { return obj.pos }
|
||||
func (obj *TypeName) setParent(s *Scope) { obj.parent = s }
|
||||
|
||||
// A Variable represents a declared variable (including function parameters and results).
|
||||
type Var struct {
|
||||
pos token.Pos // position of identifier in variable declaration
|
||||
pkg *Package // nil for parameters
|
||||
outer *Scope
|
||||
name string
|
||||
typ Type
|
||||
pos token.Pos // position of identifier in variable declaration
|
||||
pkg *Package // nil for parameters
|
||||
parent *Scope
|
||||
name string
|
||||
typ Type
|
||||
|
||||
visited bool // for initialization cycle detection
|
||||
}
|
||||
@ -111,41 +111,41 @@ func NewVar(pkg *Package, name string, typ Type) *Var {
|
||||
return &Var{token.NoPos, pkg, nil, name, typ, false}
|
||||
}
|
||||
|
||||
func (obj *Var) Pkg() *Package { return obj.pkg }
|
||||
func (obj *Var) Outer() *Scope { return obj.outer }
|
||||
func (obj *Var) Name() string { return obj.name }
|
||||
func (obj *Var) Type() Type { return obj.typ }
|
||||
func (obj *Var) Pos() token.Pos { return obj.pos }
|
||||
func (obj *Var) setOuter(s *Scope) { obj.outer = s }
|
||||
func (obj *Var) Pkg() *Package { return obj.pkg }
|
||||
func (obj *Var) Parent() *Scope { return obj.parent }
|
||||
func (obj *Var) Name() string { return obj.name }
|
||||
func (obj *Var) Type() Type { return obj.typ }
|
||||
func (obj *Var) Pos() token.Pos { return obj.pos }
|
||||
func (obj *Var) setParent(s *Scope) { obj.parent = s }
|
||||
|
||||
// A Func represents a declared function.
|
||||
type Func struct {
|
||||
pos token.Pos
|
||||
pkg *Package
|
||||
outer *Scope
|
||||
name string
|
||||
typ Type // *Signature or *Builtin
|
||||
pos token.Pos
|
||||
pkg *Package
|
||||
parent *Scope
|
||||
name string
|
||||
typ Type // *Signature or *Builtin
|
||||
|
||||
decl *ast.FuncDecl // TODO(gri) can we get rid of this field?
|
||||
}
|
||||
|
||||
func (obj *Func) Pkg() *Package { return obj.pkg }
|
||||
func (obj *Func) Outer() *Scope { return obj.outer }
|
||||
func (obj *Func) Name() string { return obj.name }
|
||||
func (obj *Func) Type() Type { return obj.typ }
|
||||
func (obj *Func) Pos() token.Pos { return obj.pos }
|
||||
func (obj *Func) setOuter(s *Scope) { obj.outer = s }
|
||||
func (obj *Func) Pkg() *Package { return obj.pkg }
|
||||
func (obj *Func) Parent() *Scope { return obj.parent }
|
||||
func (obj *Func) Name() string { return obj.name }
|
||||
func (obj *Func) Type() Type { return obj.typ }
|
||||
func (obj *Func) Pos() token.Pos { return obj.pos }
|
||||
func (obj *Func) setParent(s *Scope) { obj.parent = s }
|
||||
|
||||
// A Label represents a declared label.
|
||||
type Label struct {
|
||||
pos token.Pos
|
||||
outer *Scope
|
||||
name string
|
||||
pos token.Pos
|
||||
parent *Scope
|
||||
name string
|
||||
}
|
||||
|
||||
func (obj *Label) Pkg() *Package { return nil }
|
||||
func (obj *Label) Outer() *Scope { return obj.outer }
|
||||
func (obj *Label) Name() string { return obj.name }
|
||||
func (obj *Label) Type() Type { return nil }
|
||||
func (obj *Label) Pos() token.Pos { return obj.pos }
|
||||
func (obj *Label) setOuter(s *Scope) { obj.outer = s }
|
||||
func (obj *Label) Pkg() *Package { return nil }
|
||||
func (obj *Label) Parent() *Scope { return obj.parent }
|
||||
func (obj *Label) Name() string { return obj.name }
|
||||
func (obj *Label) Type() Type { return nil }
|
||||
func (obj *Label) Pos() token.Pos { return obj.pos }
|
||||
func (obj *Label) setParent(s *Scope) { obj.parent = s }
|
||||
|
@ -1,64 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
)
|
||||
|
||||
// An ObjSet maintains a set of objects identified by
|
||||
// their name and package that declares them.
|
||||
//
|
||||
type ObjSet struct {
|
||||
entries []Object // set entries in insertion order
|
||||
}
|
||||
|
||||
// Lookup returns the object with the given package and name
|
||||
// if it is found in ObjSet s, otherwise it returns nil.
|
||||
//
|
||||
func (s *ObjSet) Lookup(pkg *Package, name string) Object {
|
||||
for _, obj := range s.entries {
|
||||
// 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 obj.Name() == name && (ast.IsExported(name) || obj.Pkg().path == pkg.path) {
|
||||
return obj
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Insert attempts to insert an object obj into ObjSet s.
|
||||
// If s already contains an object from the same package
|
||||
// with the same name, Insert leaves s unchanged and returns
|
||||
// that object. Otherwise it inserts obj and returns nil.
|
||||
//
|
||||
func (s *ObjSet) Insert(obj Object) Object {
|
||||
pkg := obj.Pkg()
|
||||
name := obj.Name()
|
||||
assert(obj.Type() != nil)
|
||||
if alt := s.Lookup(pkg, name); alt != nil {
|
||||
return alt
|
||||
}
|
||||
s.entries = append(s.entries, obj)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Debugging support
|
||||
func (s *ObjSet) String() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "ObjSet %p {", s)
|
||||
if s != nil && len(s.entries) > 0 {
|
||||
fmt.Fprintln(&buf)
|
||||
for _, obj := range s.entries {
|
||||
fmt.Fprintf(&buf, "\t%s.%s\t%T\n", obj.Pkg().path, obj.Name(), obj)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(&buf, "}\n")
|
||||
return buf.String()
|
||||
}
|
@ -208,11 +208,16 @@ func identicalTypes(a, b *Tuple) bool {
|
||||
|
||||
// identicalMethods returns true if both object sets a and b have the
|
||||
// same length and corresponding methods have identical types.
|
||||
// TODO(gri) make this more efficient
|
||||
func identicalMethods(a, b ObjSet) bool {
|
||||
if len(a.entries) != len(b.entries) {
|
||||
// TODO(gri) make this more efficient (e.g., sort them on completion)
|
||||
func identicalMethods(a, b *Scope) bool {
|
||||
if a.NumEntries() != b.NumEntries() {
|
||||
return false
|
||||
}
|
||||
|
||||
if a.IsEmpty() {
|
||||
return true
|
||||
}
|
||||
|
||||
m := make(map[string]*Func)
|
||||
for _, obj := range a.entries {
|
||||
x := obj.(*Func)
|
||||
@ -227,6 +232,7 @@ func identicalMethods(a, b ObjSet) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -269,6 +275,11 @@ func missingMethod(typ Type, T *Interface) (method *Func, wrongType bool) {
|
||||
// TODO(gri): distinguish pointer and non-pointer receivers
|
||||
// an interface type implements T if it has no methods with conflicting signatures
|
||||
// Note: This is stronger than the current spec. Should the spec require this?
|
||||
if T.IsEmpty() {
|
||||
return
|
||||
}
|
||||
// T.methods.NumEntries() > 0
|
||||
|
||||
if ityp, _ := typ.Underlying().(*Interface); ityp != nil {
|
||||
for _, obj := range T.methods.entries {
|
||||
m := obj.(*Func)
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
func (check *checker) declare(scope *Scope, id *ast.Ident, obj Object) {
|
||||
if obj.Name() == "_" {
|
||||
// blank identifiers are not declared
|
||||
obj.setOuter(scope)
|
||||
obj.setParent(scope)
|
||||
} else if alt := scope.Insert(obj); alt != nil {
|
||||
check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
|
||||
if pos := alt.Pos(); pos.IsValid() {
|
||||
@ -32,7 +32,7 @@ func (check *checker) declareShort(scope *Scope, list []Object) {
|
||||
n := 0 // number of new objects
|
||||
for _, obj := range list {
|
||||
if obj.Name() == "_" {
|
||||
obj.setOuter(scope)
|
||||
obj.setParent(scope)
|
||||
continue // blank identifiers are not visible
|
||||
}
|
||||
if scope.Insert(obj) == nil {
|
||||
@ -86,7 +86,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
// the package identifier denotes the current package, but it is in no scope
|
||||
check.callIdent(file.Name, pkg)
|
||||
|
||||
fileScope = &Scope{Outer: pkg.scope}
|
||||
fileScope = NewScope(pkg.scope)
|
||||
scopes = append(scopes, fileScope)
|
||||
|
||||
for _, decl := range file.Decls {
|
||||
@ -125,7 +125,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
// add import to file scope
|
||||
if name == "." {
|
||||
// merge imported scope with file scope
|
||||
for _, obj := range imp.scope.Entries {
|
||||
for _, obj := range imp.scope.entries {
|
||||
// gcimported package scopes contain non-exported
|
||||
// objects such as types used in partially exported
|
||||
// objects - do not accept them
|
||||
@ -232,7 +232,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
obj := &Func{pos: d.Name.Pos(), pkg: pkg, name: d.Name.Name, decl: d}
|
||||
if obj.name == "init" {
|
||||
// init functions are not visible - don't declare them in package scope
|
||||
obj.outer = pkg.scope
|
||||
obj.parent = pkg.scope
|
||||
check.callIdent(d.Name, obj)
|
||||
} else {
|
||||
check.declare(pkg.scope, d.Name, obj)
|
||||
@ -247,8 +247,8 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
|
||||
// Phase 2: Objects in file scopes and package scopes must have different names.
|
||||
for _, scope := range scopes {
|
||||
for _, obj := range scope.Entries {
|
||||
if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
|
||||
for _, obj := range scope.entries {
|
||||
if alt := pkg.scope.Lookup(nil, obj.Name()); alt != nil {
|
||||
// TODO(gri) better error message
|
||||
check.errorf(alt.Pos(), "%s redeclared in this block by import of package %s", obj.Name(), obj.Pkg().Name())
|
||||
}
|
||||
@ -258,7 +258,6 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
// Phase 3: Associate methods with types.
|
||||
// We do this after all top-level type names have been collected.
|
||||
|
||||
check.topScope = pkg.scope
|
||||
for _, meth := range methods {
|
||||
m := meth.meth
|
||||
// The receiver type must be one of the following:
|
||||
@ -270,6 +269,8 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
typ = ptr.X
|
||||
}
|
||||
// determine receiver base type name
|
||||
// Note: We cannot simply call check.typ because this will require
|
||||
// check.objMap to be usable, which it isn't quite yet.
|
||||
ident, ok := typ.(*ast.Ident)
|
||||
if !ok {
|
||||
// Disabled for now since the parser reports this error.
|
||||
@ -278,7 +279,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
}
|
||||
// determine receiver base type object
|
||||
var tname *TypeName
|
||||
if obj := check.lookup(ident); obj != nil {
|
||||
if obj := pkg.scope.LookupParent(ident.Name); obj != nil {
|
||||
obj, ok := obj.(*TypeName)
|
||||
if !ok {
|
||||
check.errorf(ident.Pos(), "%s is not a type", ident.Name)
|
||||
@ -291,24 +292,29 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||
tname = obj
|
||||
} else {
|
||||
// identifier not declared/resolved
|
||||
check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
|
||||
if ident.Name == "_" {
|
||||
check.errorf(ident.Pos(), "cannot use _ as value or type")
|
||||
} else {
|
||||
check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
|
||||
}
|
||||
continue // ignore this method
|
||||
}
|
||||
// declare method in receiver base type scope
|
||||
scope := check.methods[tname]
|
||||
scope := check.methods[tname] // lazily allocated
|
||||
if scope == nil {
|
||||
scope = new(Scope)
|
||||
check.methods[tname] = scope
|
||||
}
|
||||
fun := &Func{pos: m.Name.Pos(), pkg: check.pkg, name: m.Name.Name, decl: m}
|
||||
check.declare(scope, m.Name, fun)
|
||||
// HACK(gri) change method outer scope to file scope containing the declaration
|
||||
fun.outer = meth.file // remember the file scope
|
||||
// HACK(gri) change method parent scope to file scope containing the declaration
|
||||
fun.parent = meth.file // remember the file scope
|
||||
}
|
||||
|
||||
// Phase 4) Typecheck all objects in objList but not function bodies.
|
||||
|
||||
check.objMap = objMap // indicate we are doing global declarations (objects may not have a type yet)
|
||||
check.topScope = pkg.scope
|
||||
for _, obj := range objList {
|
||||
if obj.Type() == nil {
|
||||
check.declareObject(obj, false)
|
||||
@ -469,39 +475,42 @@ func (check *checker) declareType(obj *TypeName, typ ast.Expr, cycleOk bool) {
|
||||
case *Struct:
|
||||
// struct fields must not conflict with methods
|
||||
for _, f := range t.fields {
|
||||
if m := scope.Lookup(f.Name); m != nil {
|
||||
if m := scope.Lookup(nil, f.Name); m != nil {
|
||||
check.errorf(m.Pos(), "type %s has both field and method named %s", obj.name, f.Name)
|
||||
// ok to continue
|
||||
}
|
||||
}
|
||||
case *Interface:
|
||||
// methods cannot be associated with an interface type
|
||||
for _, m := range scope.Entries {
|
||||
for _, m := range scope.entries {
|
||||
recv := m.(*Func).decl.Recv.List[0].Type
|
||||
check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.name, obj.name)
|
||||
// ok to continue
|
||||
}
|
||||
}
|
||||
// typecheck method signatures
|
||||
var methods ObjSet
|
||||
for _, obj := range scope.Entries {
|
||||
m := obj.(*Func)
|
||||
var methods *Scope // lazily allocated
|
||||
if !scope.IsEmpty() {
|
||||
methods = NewScope(nil)
|
||||
for _, obj := range scope.entries {
|
||||
m := obj.(*Func)
|
||||
|
||||
// set the correct file scope for checking this method type
|
||||
fileScope := m.outer
|
||||
assert(fileScope != nil)
|
||||
oldScope := check.topScope
|
||||
check.topScope = fileScope
|
||||
// set the correct file scope for checking this method type
|
||||
fileScope := m.parent
|
||||
assert(fileScope != nil)
|
||||
oldScope := check.topScope
|
||||
check.topScope = fileScope
|
||||
|
||||
sig := check.typ(m.decl.Type, cycleOk).(*Signature)
|
||||
params, _ := check.collectParams(sig.scope, m.decl.Recv, false)
|
||||
sig := check.typ(m.decl.Type, cycleOk).(*Signature)
|
||||
params, _ := check.collectParams(sig.scope, m.decl.Recv, false)
|
||||
|
||||
check.topScope = oldScope // reset topScope
|
||||
check.topScope = oldScope // reset topScope
|
||||
|
||||
sig.recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
|
||||
m.typ = sig
|
||||
assert(methods.Insert(obj) == nil)
|
||||
check.later(m, sig, m.decl.Body)
|
||||
sig.recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
|
||||
m.typ = sig
|
||||
assert(methods.Insert(obj) == nil)
|
||||
check.later(m, sig, m.decl.Body)
|
||||
}
|
||||
}
|
||||
named.methods = methods
|
||||
delete(check.methods, obj) // we don't need this scope anymore
|
||||
|
@ -31,7 +31,7 @@ func (check *checker) isTerminating(s ast.Stmt, label string) bool {
|
||||
// the predeclared panic() function is terminating
|
||||
if call, _ := s.X.(*ast.CallExpr); call != nil {
|
||||
if id, _ := call.Fun.(*ast.Ident); id != nil {
|
||||
if obj := check.lookup(id); obj != nil {
|
||||
if obj := check.topScope.LookupParent(id.Name); obj != nil {
|
||||
// TODO(gri) Predeclared functions should be modelled as objects
|
||||
// rather then ordinary functions that have a predeclared
|
||||
// function type. This would simplify code here and else-
|
||||
|
@ -7,71 +7,140 @@ package types
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
)
|
||||
|
||||
// A Scope maintains the set of named language entities declared
|
||||
// in the scope and a link to the immediately surrounding (outer)
|
||||
// scope.
|
||||
//
|
||||
// TODO(gri) Provide scopes with a name or other mechanism so that
|
||||
// objects can use that information for better printing.
|
||||
|
||||
// A Scope maintains a set of objects and a link to its containing (parent)
|
||||
// scope. Objects may be inserted and looked up by name, or by package path
|
||||
// and name. A nil *Scope acts like an empty scope for operations that do not
|
||||
// modify the scope or access a scope's parent scope.
|
||||
type Scope struct {
|
||||
Outer *Scope
|
||||
Entries []Object // scope entries in insertion order
|
||||
large map[string]Object // for fast lookup - only used for larger scopes
|
||||
parent *Scope
|
||||
entries []Object
|
||||
}
|
||||
|
||||
// Lookup returns the object with the given name if it is
|
||||
// found in scope s, otherwise it returns nil. Outer scopes
|
||||
// NewScope returns a new, empty scope.
|
||||
func NewScope(parent *Scope) *Scope {
|
||||
return &Scope{parent, nil}
|
||||
}
|
||||
|
||||
// Parent returns the scope's containing (parent) scope.
|
||||
func (s *Scope) Parent() *Scope {
|
||||
return s.parent
|
||||
}
|
||||
|
||||
// NumEntries() returns the number of scope entries.
|
||||
// If s == nil, the result is 0.
|
||||
func (s *Scope) NumEntries() int {
|
||||
if s == nil {
|
||||
return 0 // empty scope
|
||||
}
|
||||
return len(s.entries)
|
||||
}
|
||||
|
||||
// IsEmpty reports whether the scope is empty.
|
||||
// If s == nil, the result is true.
|
||||
func (s *Scope) IsEmpty() bool {
|
||||
return s == nil || len(s.entries) == 0
|
||||
}
|
||||
|
||||
// At returns the i'th scope entry for 0 <= i < NumEntries().
|
||||
func (s *Scope) At(i int) Object {
|
||||
return s.entries[i]
|
||||
}
|
||||
|
||||
// Index returns the index of the scope entry with the given package
|
||||
// (path) and name if such an entry exists in s; otherwise the result
|
||||
// is negative. A nil scope acts like an empty scope, and parent scopes
|
||||
// are ignored.
|
||||
//
|
||||
func (s *Scope) Lookup(name string) Object {
|
||||
if s.large != nil {
|
||||
return s.large[name]
|
||||
// If pkg != nil, both pkg.Path() and name are used to identify an
|
||||
// entry, per the Go rules for identifier equality. If pkg == nil,
|
||||
// only the name is used and the package path is ignored.
|
||||
func (s *Scope) Index(pkg *Package, name string) int {
|
||||
if s == nil {
|
||||
return -1 // empty scope
|
||||
}
|
||||
for _, obj := range s.Entries {
|
||||
if obj.Name() == name {
|
||||
return obj
|
||||
|
||||
// fast path: only the name must match
|
||||
if pkg == nil {
|
||||
for i, obj := range s.entries {
|
||||
if obj.Name() == name {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// slow path: both pkg path and name must match
|
||||
// TODO(gri) if packages were canonicalized, we could just compare the packages
|
||||
for i, obj := range s.entries {
|
||||
// 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 obj.Name() == name && (ast.IsExported(name) || obj.Pkg().path == pkg.path) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
// not found
|
||||
return -1
|
||||
|
||||
// TODO(gri) Optimize Lookup by also maintaining a map representation
|
||||
// for larger scopes.
|
||||
}
|
||||
|
||||
// Lookup returns the scope entry At(i) for i = Index(pkg, name), if i >= 0.
|
||||
// Otherwise it returns nil.
|
||||
func (s *Scope) Lookup(pkg *Package, name string) Object {
|
||||
if i := s.Index(pkg, name); i >= 0 {
|
||||
return s.At(i)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LookupParent follows the parent chain of scopes starting with s until it finds
|
||||
// a scope where Lookup(nil, name) returns a non-nil entry, and then returns that
|
||||
// entry. If no such scope exists, the result is nil.
|
||||
func (s *Scope) LookupParent(name string) Object {
|
||||
for s != nil {
|
||||
if i := s.Index(nil, name); i >= 0 {
|
||||
return s.At(i)
|
||||
}
|
||||
s = s.parent
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Insert attempts to insert an object obj into scope s.
|
||||
// If s already contains an object with the same name,
|
||||
// Insert leaves s unchanged and returns that object.
|
||||
// Otherwise it inserts obj, sets the object's scope to
|
||||
// s, and returns nil.
|
||||
// If s already contains an object with the same package path
|
||||
// and name, Insert leaves s unchanged and returns that object.
|
||||
// Otherwise it inserts obj, sets the object's scope to s, and
|
||||
// returns nil.
|
||||
//
|
||||
func (s *Scope) Insert(obj Object) Object {
|
||||
name := obj.Name()
|
||||
if alt := s.Lookup(name); alt != nil {
|
||||
if alt := s.Lookup(obj.Pkg(), obj.Name()); alt != nil {
|
||||
return alt
|
||||
}
|
||||
s.Entries = append(s.Entries, obj)
|
||||
obj.setOuter(s)
|
||||
|
||||
// If the scope size reaches a threshold, use a map for faster lookups.
|
||||
const threshold = 20
|
||||
if len(s.Entries) > threshold {
|
||||
if s.large == nil {
|
||||
m := make(map[string]Object, len(s.Entries))
|
||||
for _, obj := range s.Entries {
|
||||
m[obj.Name()] = obj
|
||||
}
|
||||
s.large = m
|
||||
}
|
||||
s.large[name] = obj
|
||||
}
|
||||
|
||||
s.entries = append(s.entries, obj)
|
||||
obj.setParent(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Debugging support
|
||||
// String returns a string representation of the scope, for debugging.
|
||||
func (s *Scope) String() string {
|
||||
if s == nil {
|
||||
return "scope {}"
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "scope %p {", s)
|
||||
if s != nil && len(s.Entries) > 0 {
|
||||
if s != nil && len(s.entries) > 0 {
|
||||
fmt.Fprintln(&buf)
|
||||
for _, obj := range s.Entries {
|
||||
for _, obj := range s.entries {
|
||||
fmt.Fprintf(&buf, "\t%s\t%T\n", obj.Name(), obj)
|
||||
}
|
||||
}
|
||||
|
@ -299,6 +299,14 @@ func (check *checker) multipleDefaults(list []ast.Stmt) {
|
||||
}
|
||||
}
|
||||
|
||||
func (check *checker) openScope() {
|
||||
check.topScope = NewScope(check.topScope)
|
||||
}
|
||||
|
||||
func (check *checker) closeScope() {
|
||||
check.topScope = check.topScope.Parent()
|
||||
}
|
||||
|
||||
// stmt typechecks statement s.
|
||||
func (check *checker) stmt(s ast.Stmt) {
|
||||
switch s := s.(type) {
|
||||
@ -328,7 +336,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
// but some builtins are excluded
|
||||
// (Caution: This evaluates e.Fun twice, once here and once
|
||||
// below as part of s.X. This has consequences for
|
||||
// check.register. Perhaps this can be avoided.)
|
||||
// check.callIdent. Perhaps this can be avoided.)
|
||||
check.expr(&x, e.Fun, nil, -1)
|
||||
if x.mode != invalid {
|
||||
if b, ok := x.typ.(*Builtin); ok && !b.isStatement {
|
||||
@ -397,7 +405,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
if ident, ok := x.(*ast.Ident); ok {
|
||||
// use the correct obj if the ident is redeclared
|
||||
obj = &Var{pos: ident.Pos(), pkg: check.pkg, name: ident.Name}
|
||||
if alt := check.topScope.Lookup(ident.Name); alt != nil {
|
||||
if alt := check.topScope.Lookup(nil, ident.Name); alt != nil {
|
||||
obj = alt
|
||||
}
|
||||
check.callIdent(ident, obj)
|
||||
@ -532,7 +540,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
||||
if tag == nil {
|
||||
// use fake true tag value and position it at the opening { of the switch
|
||||
ident := &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"}
|
||||
check.callIdent(ident, Universe.Lookup("true"))
|
||||
check.callIdent(ident, Universe.Lookup(nil, "true"))
|
||||
tag = ident
|
||||
}
|
||||
check.expr(&x, tag, nil, -1)
|
||||
|
5
go/types/testdata/decls2a.src
vendored
5
go/types/testdata/decls2a.src
vendored
@ -36,9 +36,8 @@ func (x *pi /* ERROR "not a type" */ ) m3() {}
|
||||
type _ struct { m int }
|
||||
type _ struct { m int }
|
||||
|
||||
// TODO(gri) blank idents not fully checked - disabled for now
|
||||
// func (_ /* ERROR "cannot use _" */) m() {}
|
||||
// func (_ /* ERROR "cannot use _" */) m() {}
|
||||
func (_ /* ERROR "cannot use _" */) m() {}
|
||||
func m(_ /* ERROR "cannot use _" */) {}
|
||||
|
||||
// Methods with receiver base type declared in another file.
|
||||
func (T3) m1() {}
|
||||
|
@ -162,11 +162,6 @@ func (s *Struct) Tag(i int) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
func (s *Struct) ForEachField(f func(*Field)) {
|
||||
for _, fld := range s.fields {
|
||||
f(fld)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Field) isMatch(pkg *Package, name string) bool {
|
||||
// spec:
|
||||
@ -226,16 +221,6 @@ func (t *Tuple) Len() int {
|
||||
// At returns the i'th variable of tuple t.
|
||||
func (t *Tuple) At(i int) *Var { return t.vars[i] }
|
||||
|
||||
// ForEach calls f with each variable of tuple t in index order.
|
||||
// TODO(gri): Do we keep ForEach or should we abandon it in favor or Len and At?
|
||||
func (t *Tuple) ForEach(f func(*Var)) {
|
||||
if t != nil {
|
||||
for _, x := range t.vars {
|
||||
f(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A Signature represents a (non-builtin) function type.
|
||||
type Signature struct {
|
||||
scope *Scope // function scope
|
||||
@ -313,27 +298,19 @@ func (b *Builtin) Name() string {
|
||||
|
||||
// An Interface represents an interface type.
|
||||
type Interface struct {
|
||||
methods ObjSet
|
||||
methods *Scope // may be nil
|
||||
}
|
||||
|
||||
// NumMethods returns the number of methods of interface t.
|
||||
func (t *Interface) NumMethods() int { return len(t.methods.entries) }
|
||||
func (t *Interface) NumMethods() int { return t.methods.NumEntries() }
|
||||
|
||||
// Method returns the i'th method of interface t for 0 <= i < t.NumMethods().
|
||||
func (t *Interface) Method(i int) *Func {
|
||||
return t.methods.entries[i].(*Func)
|
||||
return t.methods.At(i).(*Func)
|
||||
}
|
||||
|
||||
// IsEmpty() reports whether t is an empty interface.
|
||||
func (t *Interface) IsEmpty() bool { return len(t.methods.entries) == 0 }
|
||||
|
||||
// ForEachMethod calls f with each method of interface t in index order.
|
||||
// TODO(gri) Should we abandon this in favor of NumMethods and Method?
|
||||
func (t *Interface) ForEachMethod(f func(*Func)) {
|
||||
for _, obj := range t.methods.entries {
|
||||
f(obj.(*Func))
|
||||
}
|
||||
}
|
||||
func (t *Interface) IsEmpty() bool { return t.methods.IsEmpty() }
|
||||
|
||||
// A Map represents a map type.
|
||||
type Map struct {
|
||||
@ -372,11 +349,25 @@ 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
|
||||
methods ObjSet // directly associated methods (not the method set of this type)
|
||||
methods *Scope // directly associated methods (not the method set of this type); may be nil
|
||||
}
|
||||
|
||||
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
|
||||
func NewNamed(obj *TypeName, underlying Type, methods ObjSet) *Named {
|
||||
// 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 {
|
||||
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
|
||||
@ -388,19 +379,11 @@ func NewNamed(obj *TypeName, underlying Type, methods ObjSet) *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 len(t.methods.entries) }
|
||||
func (t *Named) NumMethods() int { return t.methods.NumEntries() }
|
||||
|
||||
// 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.entries[i].(*Func)
|
||||
}
|
||||
|
||||
// ForEachMethod calls f with each method associated with t in index order.
|
||||
// TODO(gri) Should we abandon this in favor of NumMethods and Method?
|
||||
func (t *Named) ForEachMethod(fn func(*Func)) {
|
||||
for _, obj := range t.methods.entries {
|
||||
fn(obj.(*Func))
|
||||
}
|
||||
return t.methods.At(i).(*Func)
|
||||
}
|
||||
|
||||
// Implementations for Type methods.
|
||||
|
@ -110,7 +110,7 @@ func TestTypes(t *testing.T) {
|
||||
t.Errorf("%s: %s", src, err)
|
||||
continue
|
||||
}
|
||||
typ := pkg.scope.Lookup("T").Type().Underlying()
|
||||
typ := pkg.scope.Lookup(nil, "T").Type().Underlying()
|
||||
str := typeString(typ)
|
||||
if str != test.str {
|
||||
t.Errorf("%s: got %s, want %s", test.src, str, test.str)
|
||||
|
@ -101,7 +101,7 @@ func init() {
|
||||
// error type
|
||||
{
|
||||
// Error has a nil package in its qualified name since it is in no package
|
||||
var methods ObjSet
|
||||
methods := NewScope(nil)
|
||||
sig := &Signature{results: NewTuple(&Var{name: "", typ: Typ[String]})}
|
||||
methods.Insert(&Func{token.NoPos, nil, nil, "Error", sig, nil})
|
||||
def(&TypeName{name: "error", typ: &Named{underlying: &Interface{methods: methods}}})
|
||||
@ -115,7 +115,7 @@ func init() {
|
||||
def(&Func{name: f.name, typ: f})
|
||||
}
|
||||
|
||||
universeIota = Universe.Lookup("iota").(*Const)
|
||||
universeIota = Universe.Lookup(nil, "iota").(*Const)
|
||||
}
|
||||
|
||||
// Objects with names containing blanks are internal and not entered into
|
||||
|
@ -169,8 +169,8 @@ func NewBuilder(context *Context) *Builder {
|
||||
}
|
||||
|
||||
// Create Values for built-in functions.
|
||||
for _, obj := range types.Universe.Entries {
|
||||
switch obj := obj.(type) {
|
||||
for i, n := 0, types.Universe.NumEntries(); i < n; i++ {
|
||||
switch obj := types.Universe.At(i).(type) {
|
||||
case *types.Func:
|
||||
v := &Builtin{obj}
|
||||
b.globals[obj] = v
|
||||
@ -1856,7 +1856,7 @@ func (b *Builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value
|
||||
} else {
|
||||
// length = len(x).
|
||||
var c Call
|
||||
c.Call.Func = b.globals[types.Universe.Lookup("len")]
|
||||
c.Call.Func = b.globals[types.Universe.Lookup(nil, "len")]
|
||||
c.Call.Args = []Value{x}
|
||||
c.setType(tInt)
|
||||
length = fn.emit(&c)
|
||||
@ -2324,9 +2324,10 @@ func (b *Builder) buildFunction(fn *Function) {
|
||||
if recv := fn.Signature.Recv(); recv != nil {
|
||||
fn.addParamObj(recv)
|
||||
}
|
||||
fn.Signature.Params().ForEach(func(p *types.Var) {
|
||||
fn.addParamObj(p)
|
||||
})
|
||||
params := fn.Signature.Params()
|
||||
for i, n := 0, params.Len(); i < n; i++ {
|
||||
fn.addParamObj(params.At(i))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -2571,8 +2572,9 @@ func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, fil
|
||||
// No code.
|
||||
// No position information.
|
||||
|
||||
for _, obj := range p.Types.Scope().Entries {
|
||||
b.memberFromObject(p, obj, nil)
|
||||
scope := p.Types.Scope()
|
||||
for i, n := 0, scope.NumEntries(); i < n; i++ {
|
||||
b.memberFromObject(p, scope.At(i), nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2619,9 +2621,9 @@ func (b *Builder) buildDecl(pkg *Package, decl ast.Decl) {
|
||||
continue
|
||||
}
|
||||
nt := pkg.ObjectOf(id).Type().(*types.Named)
|
||||
nt.ForEachMethod(func(m *types.Func) {
|
||||
b.buildFunction(b.Prog.concreteMethods[m])
|
||||
})
|
||||
for i, n := 0, nt.NumMethods(); i < n; i++ {
|
||||
b.buildFunction(b.Prog.concreteMethods[nt.Method(i)])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ var errorType = makeNamedType("error", &opaqueType{nil, "error"})
|
||||
|
||||
func makeNamedType(name string, underlying types.Type) *types.Named {
|
||||
obj := types.NewTypeName(reflectTypesPackage, name, nil)
|
||||
return types.NewNamed(obj, underlying, types.ObjSet{})
|
||||
return types.NewNamed(obj, underlying, nil)
|
||||
}
|
||||
|
||||
func makeReflectValue(t types.Type, v value) value {
|
||||
|
@ -147,17 +147,19 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
|
||||
t = t.Deref()
|
||||
|
||||
if nt, ok := t.(*types.Named); ok {
|
||||
nt.ForEachMethod(func(m *types.Func) {
|
||||
for i, n := 0, nt.NumMethods(); i < n; i++ {
|
||||
m := nt.Method(i)
|
||||
addCandidate(nextcands, MakeId(m.Name(), m.Pkg()), m, prog.concreteMethods[m], node)
|
||||
})
|
||||
}
|
||||
t = nt.Underlying()
|
||||
}
|
||||
|
||||
switch t := t.(type) {
|
||||
case *types.Interface:
|
||||
t.ForEachMethod(func(m *types.Func) {
|
||||
for i, n := 0, t.NumMethods(); i < n; i++ {
|
||||
m := t.Method(i)
|
||||
addCandidate(nextcands, MakeId(m.Name(), m.Pkg()), m, nil, node)
|
||||
})
|
||||
}
|
||||
|
||||
case *types.Struct:
|
||||
for i, n := 0, t.NumFields(); i < n; i++ {
|
||||
@ -443,13 +445,12 @@ func findPromotedField(st *types.Struct, id Id) (*anonFieldPath, int) {
|
||||
visited := make(map[types.Type]bool)
|
||||
|
||||
var list, next []*anonFieldPath
|
||||
i := 0
|
||||
st.ForEachField(func(f *types.Field) {
|
||||
for i, n := 0, st.NumFields(); i < n; i++ {
|
||||
f := st.Field(i)
|
||||
if f.IsAnonymous {
|
||||
list = append(list, &anonFieldPath{nil, i, f})
|
||||
}
|
||||
i++
|
||||
})
|
||||
}
|
||||
|
||||
// Search the current level if there is any work to do and collect
|
||||
// embedded types of the next lower level in the next list.
|
||||
@ -470,13 +471,12 @@ func findPromotedField(st *types.Struct, id Id) (*anonFieldPath, int) {
|
||||
return node, i
|
||||
}
|
||||
}
|
||||
i := 0
|
||||
typ.ForEachField(func(f *types.Field) {
|
||||
for i, n := 0, typ.NumFields(); i < n; i++ {
|
||||
f := typ.Field(i)
|
||||
if f.IsAnonymous {
|
||||
next = append(next, &anonFieldPath{node, i, f})
|
||||
}
|
||||
i++
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ func (info *TypeInfo) IsType(e ast.Expr) bool {
|
||||
func (info *TypeInfo) isPackageRef(sel *ast.SelectorExpr) types.Object {
|
||||
if id, ok := sel.X.(*ast.Ident); ok {
|
||||
if pkg, ok := info.ObjectOf(id).(*types.Package); ok {
|
||||
return pkg.Scope().Lookup(sel.Sel.Name)
|
||||
return pkg.Scope().Lookup(nil, sel.Sel.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
Loading…
Reference in New Issue
Block a user