]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: Moving from *ast.Objects to types.Objects (step 2).
authorRobert Griesemer <gri@golang.org>
Sun, 13 Jan 2013 18:33:08 +0000 (10:33 -0800)
committerRobert Griesemer <gri@golang.org>
Sun, 13 Jan 2013 18:33:08 +0000 (10:33 -0800)
Completely removed *ast.Objects from being exposed by the
types API. *ast.Objects are still required internally for
resolution, but now the door is open for an internal-only
rewrite of identifier resolution entirely at type-check
time. Once that is done, ASTs can be type-checked whether
they have been created via the go/parser or otherwise,
and type-checking does not require *ast.Object or scope
invariants to be maintained externally.

R=adonovan
CC=golang-dev
https://golang.org/cl/7096048

19 files changed:
src/pkg/exp/gotype/gotype.go
src/pkg/go/types/api.go
src/pkg/go/types/builtins.go
src/pkg/go/types/check.go
src/pkg/go/types/check_test.go
src/pkg/go/types/errors.go
src/pkg/go/types/expr.go
src/pkg/go/types/gcimporter.go
src/pkg/go/types/objects.go
src/pkg/go/types/operand.go
src/pkg/go/types/predicates.go
src/pkg/go/types/resolve.go
src/pkg/go/types/resolver_test.go
src/pkg/go/types/scope.go [new file with mode: 0644]
src/pkg/go/types/stmt.go
src/pkg/go/types/testdata/decls2a.src
src/pkg/go/types/types.go
src/pkg/go/types/types_test.go
src/pkg/go/types/universe.go

index d1de18a411331f79a6e631c054287e51ccadd709..bb3237c37cb63252a0d1a4704cb9c74a932df52d 100644 (file)
@@ -163,7 +163,7 @@ func processFiles(filenames []string, allFiles bool) {
 }
 
 func processPackage(fset *token.FileSet, files []*ast.File) {
-       _, _, err := types.Check(fset, files)
+       _, err := types.Check(fset, files)
        if err != nil {
                report(err)
        }
index c1d762e33c7384985f5091dfdd73064e713b77af..b7b5b90b626bef8fca0aabb023e032f710977a70 100644 (file)
@@ -74,11 +74,11 @@ var Default = Context{
 //          we have the scope moved from *ast.Scope to *Scope, only *Package
 //          will be returned.
 //
-func (ctxt *Context) Check(fset *token.FileSet, files []*ast.File) (*ast.Package, *Package, error) {
+func (ctxt *Context) Check(fset *token.FileSet, files []*ast.File) (*Package, error) {
        return check(ctxt, fset, files)
 }
 
 // Check is shorthand for Default.Check.
-func Check(fset *token.FileSet, files []*ast.File) (*ast.Package, *Package, error) {
+func Check(fset *token.FileSet, files []*ast.File) (*Package, error) {
        return Default.Check(fset, files)
 }
index 2ae6f566627f2d3536c4c528a7768810c8c79632..aabb2e66a34b8ad23b5eb83d623252f46f7fdbdf 100644 (file)
@@ -305,7 +305,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
 
        case _Recover:
                x.mode = value
-               x.typ = emptyInterface
+               x.typ = new(Interface)
 
        case _Alignof:
                x.mode = constant
index bf28ca12da642536a9a40dd62ece2af37c907f3d..07f0e861e4b658009046e8e2b70e11e57844c93b 100644 (file)
@@ -21,17 +21,50 @@ type checker struct {
        files []*ast.File
 
        // lazily initialized
-       pkg      *Package
-       pkgscope *ast.Scope
-       firsterr error
-       initspec map[*ast.ValueSpec]*ast.ValueSpec // "inherited" type and initialization expressions for constant declarations
-       funclist []function                        // list of functions/methods with correct signatures and non-empty bodies
-       funcsig  *Signature                        // signature of currently typechecked function
-       pos      []token.Pos                       // stack of expr positions; debugging support, used if trace is set
+       pkg       *Package                          // current package
+       firsterr  error                             // first error encountered
+       idents    map[*ast.Ident]Object             // maps identifiers to their unique object
+       objects   map[*ast.Object]Object            // maps *ast.Objects to their unique object
+       initspecs map[*ast.ValueSpec]*ast.ValueSpec // "inherited" type and initialization expressions for constant declarations
+       methods   map[*TypeName]*Scope              // maps type names to associated methods
+       funclist  []function                        // list of functions/methods with correct signatures and non-empty bodies
+       funcsig   *Signature                        // signature of currently typechecked function
+       pos       []token.Pos                       // stack of expr positions; debugging support, used if trace is set
+}
+
+// lookup returns the unique Object denoted by the identifier.
+// For identifiers without assigned *ast.Object, it uses the
+// checker.idents map; for identifiers with an *ast.Object it
+// uses the checker.objects map.
+//
+// TODO(gri) Once identifier resolution is done entirely by
+//           the typechecker, only the idents map is needed.
+//
+func (check *checker) lookup(ident *ast.Ident) Object {
+       astObj := ident.Obj
+       obj := check.idents[ident]
+
+       if obj != nil {
+               assert(astObj == nil || check.objects[astObj] == nil || check.objects[astObj] == obj)
+               return obj
+       }
+
+       if astObj == nil {
+               return nil
+       }
+
+       obj = check.objects[astObj]
+       if obj == nil {
+               obj = newObj(astObj)
+               check.idents[ident] = obj
+               check.objects[astObj] = obj
+       }
+
+       return obj
 }
 
 type function struct {
-       obj  *ast.Object // for debugging/tracing only
+       obj  *Func // for debugging/tracing only
        sig  *Signature
        body *ast.BlockStmt
 }
@@ -40,32 +73,20 @@ type function struct {
 // that need to be processed after all package-level declarations
 // are typechecked.
 //
-func (check *checker) later(obj *ast.Object, sig *Signature, body *ast.BlockStmt) {
+func (check *checker) later(f *Func, sig *Signature, body *ast.BlockStmt) {
        // functions implemented elsewhere (say in assembly) have no body
        if body != nil {
-               check.funclist = append(check.funclist, function{obj, sig, body})
+               check.funclist = append(check.funclist, function{f, sig, body})
        }
 }
 
-// declare declares an object of the given kind and name (ident) in scope;
-// decl is the corresponding declaration in the AST. An error is reported
-// if the object was declared before.
-//
-// TODO(gri) This is very similar to the declare function in go/parser; it
-// is only used to associate methods with their respective receiver base types.
-// In a future version, it might be simpler and cleaner to do all the resolution
-// in the type-checking phase. It would simplify the parser, AST, and also
-// reduce some amount of code duplication.
-//
-func (check *checker) declare(scope *ast.Scope, kind ast.ObjKind, ident *ast.Ident, decl ast.Decl) {
-       assert(ident.Obj == nil) // identifier already declared or resolved
-       obj := ast.NewObj(kind, ident.Name)
-       obj.Decl = decl
-       ident.Obj = obj
+func (check *checker) declareIdent(scope *Scope, ident *ast.Ident, obj Object) {
+       assert(check.lookup(ident) == nil) // identifier already declared or resolved
+       check.idents[ident] = obj
        if ident.Name != "_" {
                if alt := scope.Insert(obj); alt != nil {
                        prevDecl := ""
-                       if pos := alt.Pos(); pos.IsValid() {
+                       if pos := alt.GetPos(); pos.IsValid() {
                                prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos))
                        }
                        check.errorf(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
@@ -73,7 +94,7 @@ func (check *checker) declare(scope *ast.Scope, kind ast.ObjKind, ident *ast.Ide
        }
 }
 
-func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident, typ ast.Expr, rhs []ast.Expr, iota int) {
+func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spec *ast.ValueSpec, iota int) {
        if len(lhs) == 0 {
                check.invalidAST(pos, "missing lhs in declaration")
                return
@@ -81,38 +102,53 @@ func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident
 
        // determine type for all of lhs, if any
        // (but only set it for the object we typecheck!)
-       var t Type
-       if typ != nil {
-               t = check.typ(typ, false)
+       var typ Type
+       if spec.Type != nil {
+               typ = check.typ(spec.Type, false)
        }
 
        // len(lhs) > 0
+       rhs := spec.Values
        if len(lhs) == len(rhs) {
                // check only lhs and rhs corresponding to obj
                var l, r ast.Expr
                for i, name := range lhs {
-                       if name.Obj == obj {
+                       if check.lookup(name) == obj {
                                l = lhs[i]
                                r = rhs[i]
                                break
                        }
                }
                assert(l != nil)
-               obj.Type = t
+               switch obj := obj.(type) {
+               case *Const:
+                       obj.Type = typ
+               case *Var:
+                       obj.Type = typ
+               default:
+                       unreachable()
+               }
                check.assign1to1(l, r, nil, true, iota)
                return
        }
 
        // there must be a type or initialization expressions
-       if t == nil && len(rhs) == 0 {
+       if typ == nil && len(rhs) == 0 {
                check.invalidAST(pos, "missing type or initialization expression")
-               t = Typ[Invalid]
+               typ = Typ[Invalid]
        }
 
        // if we have a type, mark all of lhs
-       if t != nil {
+       if typ != nil {
                for _, name := range lhs {
-                       name.Obj.Type = t
+                       switch obj := check.lookup(name).(type) {
+                       case *Const:
+                               obj.Type = typ
+                       case *Var:
+                               obj.Type = typ
+                       default:
+                               unreachable()
+                       }
                }
        }
 
@@ -127,82 +163,97 @@ func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident
        }
 }
 
-// object typechecks an object by assigning it a type; obj.Type must be nil.
-// Callers must check obj.Type before calling object; this eliminates a call
-// for each identifier that has been typechecked already, a common scenario.
+// object typechecks an object by assigning it a type.
 //
-func (check *checker) object(obj *ast.Object, cycleOk bool) {
-       assert(obj.Type == nil)
-
-       switch obj.Kind {
-       case ast.Bad, ast.Pkg:
+func (check *checker) object(obj Object, cycleOk bool) {
+       switch obj := obj.(type) {
+       case *Package:
                // nothing to do
-
-       case ast.Con, ast.Var:
-               // The obj.Data field for constants and variables is initialized
-               // to the respective (hypothetical, for variables) iota value by
-               // the parser. The object's fields can be in one of the following
-               // states:
-               // Type != nil  =>  the constant value is Data
-               // Type == nil  =>  the object is not typechecked yet, and Data can be:
-               // Data is int  =>  Data is the value of iota for this declaration
-               // Data == nil  =>  the object's expression is being evaluated
-               if obj.Data == nil {
-                       check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name)
+       case *Const:
+               if obj.Type != nil {
+                       return // already checked
+               }
+               // The obj.Val field for constants is initialized to its respective
+               // iota value by the parser.
+               // The object's fields can be in one of the following states:
+               // Type != nil  =>  the constant value is Val
+               // Type == nil  =>  the constant is not typechecked yet, and Val can be:
+               // Val  is int  =>  Val is the value of iota for this declaration
+               // Val  == nil  =>  the object's expression is being evaluated
+               if obj.Val == nil {
+                       check.errorf(obj.GetPos(), "illegal cycle in initialization of %s", obj.Name)
                        obj.Type = Typ[Invalid]
                        return
                }
-               spec := obj.Decl.(*ast.ValueSpec)
-               iota := obj.Data.(int)
-               obj.Data = nil
+               spec := obj.spec
+               iota := obj.Val.(int)
+               obj.Val = nil // mark obj as "visited" for cycle detection
                // determine spec for type and initialization expressions
                init := spec
-               if len(init.Values) == 0 && obj.Kind == ast.Con {
-                       init = check.initspec[spec]
+               if len(init.Values) == 0 {
+                       init = check.initspecs[spec]
                }
-               check.valueSpec(spec.Pos(), obj, spec.Names, init.Type, init.Values, iota)
+               check.valueSpec(spec.Pos(), obj, spec.Names, init, iota)
 
-       case ast.Typ:
-               typ := &NamedType{AstObj: obj}
+       case *Var:
+               if obj.Type != nil {
+                       return // already checked
+               }
+               if obj.visited {
+                       check.errorf(obj.GetPos(), "illegal cycle in initialization of %s", obj.Name)
+                       obj.Type = Typ[Invalid]
+                       return
+               }
+               spec := obj.decl.(*ast.ValueSpec)
+               obj.visited = true
+               check.valueSpec(spec.Pos(), obj, spec.Names, spec, 0)
+
+       case *TypeName:
+               if obj.Type != nil {
+                       return // already checked
+               }
+               typ := &NamedType{Obj: obj}
                obj.Type = typ // "mark" object so recursion terminates
-               typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk))
+               typ.Underlying = underlying(check.typ(obj.spec.Type, cycleOk))
                // typecheck associated method signatures
-               if obj.Data != nil {
-                       scope := obj.Data.(*ast.Scope)
+               if scope := check.methods[obj]; scope != nil {
                        switch t := typ.Underlying.(type) {
                        case *Struct:
                                // struct fields must not conflict with methods
                                for _, f := range t.Fields {
                                        if m := scope.Lookup(f.Name); m != nil {
-                                               check.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, f.Name)
+                                               check.errorf(m.GetPos(), "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.Objects {
-                                       recv := m.Decl.(*ast.FuncDecl).Recv.List[0].Type
+                               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 []*Method
-                       for _, obj := range scope.Objects {
-                               mdecl := obj.Decl.(*ast.FuncDecl)
-                               sig := check.typ(mdecl.Type, cycleOk).(*Signature)
-                               params, _ := check.collectParams(mdecl.Recv, false)
+                       for _, obj := range scope.Entries {
+                               m := obj.(*Func)
+                               sig := check.typ(m.decl.Type, cycleOk).(*Signature)
+                               params, _ := check.collectParams(m.decl.Recv, false)
                                sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
-                               obj.Type = sig
-                               methods = append(methods, &Method{QualifiedName{nil, obj.Name}, sig})
-                               check.later(obj, sig, mdecl.Body)
+                               m.Type = sig
+                               methods = append(methods, &Method{QualifiedName{nil, m.Name}, sig})
+                               check.later(m, sig, m.decl.Body)
                        }
                        typ.Methods = methods
-                       obj.Data = nil // don't use obj.Data later, accidentally
+                       delete(check.methods, obj) // we don't need this scope anymore
                }
 
-       case ast.Fun:
-               fdecl := obj.Decl.(*ast.FuncDecl)
+       case *Func:
+               if obj.Type != nil {
+                       return // already checked
+               }
+               fdecl := obj.decl
                // methods are typechecked when their receivers are typechecked
                if fdecl.Recv == nil {
                        sig := check.typ(fdecl.Type, cycleOk).(*Signature)
@@ -215,12 +266,12 @@ func (check *checker) object(obj *ast.Object, cycleOk bool) {
                }
 
        default:
-               panic("unreachable")
+               unreachable()
        }
 }
 
 // assocInitvals associates "inherited" initialization expressions
-// with the corresponding *ast.ValueSpec in the check.initspec map
+// with the corresponding *ast.ValueSpec in the check.initspecs map
 // for constant declarations without explicit initialization expressions.
 //
 func (check *checker) assocInitvals(decl *ast.GenDecl) {
@@ -230,7 +281,7 @@ func (check *checker) assocInitvals(decl *ast.GenDecl) {
                        if len(s.Values) > 0 {
                                last = s
                        } else {
-                               check.initspec[s] = last
+                               check.initspecs[s] = last
                        }
                }
        }
@@ -251,48 +302,36 @@ func (check *checker) assocMethod(meth *ast.FuncDecl) {
        if ptr, ok := typ.(*ast.StarExpr); ok {
                typ = ptr.X
        }
+       // determine receiver base type name
+       ident, ok := typ.(*ast.Ident)
+       if !ok {
+               // not an identifier - parser reported error already
+               return // ignore this method
+       }
        // determine receiver base type object
-       var obj *ast.Object
-       if ident, ok := typ.(*ast.Ident); ok && ident.Obj != nil {
-               obj = ident.Obj
-               if obj.Kind != ast.Typ {
+       var tname *TypeName
+       if obj := check.lookup(ident); obj != nil {
+               obj, ok := obj.(*TypeName)
+               if !ok {
                        check.errorf(ident.Pos(), "%s is not a type", ident.Name)
                        return // ignore this method
                }
-               // TODO(gri) determine if obj was defined in this package
-               /*
-                       if check.notLocal(obj) {
-                               check.errorf(ident.Pos(), "cannot define methods on non-local type %s", ident.Name)
-                               return // ignore this method
-                       }
-               */
+               if obj.spec == nil {
+                       check.errorf(ident.Pos(), "cannot define method on non-local type %s", ident.Name)
+                       return // ignore this method
+               }
+               tname = obj
        } else {
-               // If it's not an identifier or the identifier wasn't declared/resolved,
-               // the parser/resolver already reported an error. Nothing to do here.
+               // identifier not declared/resolved - parser reported error already
                return // ignore this method
        }
        // declare method in receiver base type scope
-       var scope *ast.Scope
-       if obj.Data != nil {
-               scope = obj.Data.(*ast.Scope)
-       } else {
-               scope = ast.NewScope(nil)
-               obj.Data = scope
-       }
-       check.declare(scope, ast.Fun, meth.Name, meth)
-}
-
-func (check *checker) assocInitvalsOrMethod(decl ast.Decl) {
-       switch d := decl.(type) {
-       case *ast.GenDecl:
-               if d.Tok == token.CONST {
-                       check.assocInitvals(d)
-               }
-       case *ast.FuncDecl:
-               if d.Recv != nil {
-                       check.assocMethod(d)
-               }
+       scope := check.methods[tname]
+       if scope == nil {
+               scope = new(Scope)
+               check.methods[tname] = scope
        }
+       check.declareIdent(scope, meth.Name, &Func{Name: meth.Name.Name, decl: meth})
 }
 
 func (check *checker) decl(decl ast.Decl) {
@@ -303,17 +342,13 @@ func (check *checker) decl(decl ast.Decl) {
                for _, spec := range d.Specs {
                        switch s := spec.(type) {
                        case *ast.ImportSpec:
-                               // nothing to do (handled by ast.NewPackage)
+                               // nothing to do (handled by check.resolve)
                        case *ast.ValueSpec:
                                for _, name := range s.Names {
-                                       if obj := name.Obj; obj.Type == nil {
-                                               check.object(obj, false)
-                                       }
+                                       check.object(check.lookup(name), false)
                                }
                        case *ast.TypeSpec:
-                               if obj := s.Name.Obj; obj.Type == nil {
-                                       check.object(obj, false)
-                               }
+                               check.object(check.lookup(s.Name), false)
                        default:
                                check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
                        }
@@ -323,42 +358,33 @@ func (check *checker) decl(decl ast.Decl) {
                if d.Recv != nil {
                        return
                }
-               obj := d.Name.Obj
+               obj := check.lookup(d.Name)
                // Initialization functions don't have an object associated with them
                // since they are not in any scope. Create a dummy object for them.
                if d.Name.Name == "init" {
                        assert(obj == nil) // all other functions should have an object
-                       obj = ast.NewObj(ast.Fun, d.Name.Name)
-                       obj.Decl = d
-                       d.Name.Obj = obj
-               }
-               if obj.Type == nil {
-                       check.object(obj, false)
+                       obj = &Func{Name: d.Name.Name, decl: d}
+                       check.idents[d.Name] = obj
                }
+               check.object(obj, false)
        default:
                check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
        }
 }
 
-// iterate calls f for each package-level declaration.
-func (check *checker) iterate(f func(*checker, ast.Decl)) {
-       for _, file := range check.files {
-               for _, decl := range file.Decls {
-                       f(check, decl)
-               }
-       }
-}
-
 // A bailout panic is raised to indicate early termination.
 type bailout struct{}
 
-func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (astpkg *ast.Package, pkg *Package, err error) {
+func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package, err error) {
        // initialize checker
        check := checker{
-               ctxt:     ctxt,
-               fset:     fset,
-               files:    files,
-               initspec: make(map[*ast.ValueSpec]*ast.ValueSpec),
+               ctxt:      ctxt,
+               fset:      fset,
+               files:     files,
+               idents:    make(map[*ast.Ident]Object),
+               objects:   make(map[*ast.Object]Object),
+               initspecs: make(map[*ast.ValueSpec]*ast.ValueSpec),
+               methods:   make(map[*TypeName]*Scope),
        }
 
        // handle panics
@@ -369,7 +395,7 @@ func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (astpkg *ast.P
                        err = check.firsterr
                default:
                        // unexpected panic: don't crash clients
-                       // panic(p) // enable for debugging
+                       panic(p) // enable for debugging
                        // TODO(gri) add a test case for this scenario
                        err = fmt.Errorf("types internal error: %v", p)
                }
@@ -392,20 +418,20 @@ func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (astpkg *ast.P
                        return pkg, err
                }
        }
-       astpkg, pkg = check.resolve(imp)
-
-       // Imported packages and all types refer to types.Objects,
-       // the current package files' AST uses ast.Objects.
-       // Use an ast.Scope for the current package scope.
+       pkg, methods := check.resolve(imp)
        check.pkg = pkg
-       check.pkgscope = astpkg.Scope
 
-       // determine missing constant initialization expressions
-       // and associate methods with types
-       check.iterate((*checker).assocInitvalsOrMethod)
+       // associate methods with types
+       for _, m := range methods {
+               check.assocMethod(m)
+       }
 
        // typecheck all declarations
-       check.iterate((*checker).decl)
+       for _, f := range check.files {
+               for _, d := range f.Decls {
+                       check.decl(d)
+               }
+       }
 
        // typecheck all function/method bodies
        // (funclist may grow when checking statements - do not use range clause!)
index 285c130596a8e1004df6dbaf78cffc8fdd95b5f2..46c00c866302645d83b02f5573dacbd7c3ad3997 100644 (file)
@@ -234,8 +234,8 @@ func TestCheck(t *testing.T) {
        // Declare builtins for testing.
        // Not done in an init func to avoid an init race with
        // the construction of the Universe var.
-       def(ast.Fun, "assert", &builtin{aType, _Assert, "assert", 1, false, true})
-       def(ast.Fun, "trace", &builtin{aType, _Trace, "trace", 0, true, true})
+       def(&Func{Name: "assert", Type: &builtin{_Assert, "assert", 1, false, true}})
+       def(&Func{Name: "trace", Type: &builtin{_Trace, "trace", 0, true, true}})
 
        // For easy debugging w/o changing the testing code,
        // if there is a local test file, only test that file.
index b2a66e4dd207c3d02730a8dba894116d97842b18..3fe0b29690523a0f8e2dec4f8095baef0dbd6392 100644 (file)
@@ -20,11 +20,6 @@ func assert(p bool) {
        }
 }
 
-func unimplemented() {
-       // enable for debugging
-       // panic("unimplemented")
-}
-
 func unreachable() {
        panic("unreachable")
 }
@@ -311,14 +306,9 @@ func writeType(buf *bytes.Buffer, typ Type) {
                writeType(buf, t.Elt)
 
        case *NamedType:
-               var s string
-               switch {
-               case t.Obj != nil:
+               s := "<NamedType w/o object>"
+               if t.Obj != nil {
                        s = t.Obj.GetName()
-               case t.AstObj != nil:
-                       s = t.AstObj.Name
-               default:
-                       s = "<NamedType w/o object>"
                }
                buf.WriteString(s)
 
index c6fa84dda7fca976c098827bda864cb91743f662..9f4cece20ac9cb969e9a8fa872cbd6c2a1b94f84 100644 (file)
@@ -27,7 +27,7 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param
        if list == nil {
                return
        }
-       var last *ast.Object
+       var last *Var
        for i, field := range list.List {
                ftype := field.Type
                if t, _ := ftype.(*ast.Ellipsis); t != nil {
@@ -45,24 +45,24 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param
                if len(field.Names) > 0 {
                        // named parameter
                        for _, name := range field.Names {
-                               obj := name.Obj
-                               obj.Type = typ
-                               last = obj
-                               params = append(params, &Var{Name: obj.Name, Type: typ})
+                               par := check.lookup(name).(*Var)
+                               par.Type = typ
+                               last = par
+                               copy := *par
+                               params = append(params, &copy)
                        }
                } else {
                        // anonymous parameter
-                       obj := ast.NewObj(ast.Var, "")
-                       obj.Type = typ
-                       last = obj
-                       params = append(params, &Var{Name: obj.Name, Type: typ})
+                       par := &Var{Type: typ}
+                       last = nil // not accessible inside function
+                       params = append(params, par)
                }
        }
        // For a variadic function, change the last parameter's object type
        // from T to []T (this is the type used inside the function), but
        // keep the params list unchanged (this is the externally visible type).
-       if isVariadic {
-               last.Type = &Slice{Elt: last.Type.(Type)}
+       if isVariadic && last != nil {
+               last.Type = &Slice{Elt: last.Type}
        }
        return
 }
@@ -145,16 +145,7 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [
                        case *Basic:
                                fields = append(fields, &Field{QualifiedName{nil, t.Name}, typ, tag, true})
                        case *NamedType:
-                               var name string
-                               switch {
-                               case t.Obj != nil:
-                                       name = t.Obj.GetName()
-                               case t.AstObj != nil:
-                                       name = t.AstObj.Name
-                               default:
-                                       unreachable()
-                               }
-                               fields = append(fields, &Field{QualifiedName{nil, name}, typ, tag, true})
+                               fields = append(fields, &Field{QualifiedName{nil, t.Obj.GetName()}, typ, tag, true})
                        default:
                                if typ != Typ[Invalid] {
                                        check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ)
@@ -519,8 +510,9 @@ func (check *checker) index(index ast.Expr, length int64, iota int) int64 {
 // For details, see comment in go/parser/parser.go, method parseElement.
 func (check *checker) compositeLitKey(key ast.Expr) {
        if ident, ok := key.(*ast.Ident); ok && ident.Obj == nil {
-               ident.Obj = check.pkgscope.Lookup(ident.Name)
-               if ident.Obj == nil {
+               if obj := check.pkg.Scope.Lookup(ident.Name); obj != nil {
+                       check.idents[ident] = obj
+               } else {
                        check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
                }
        }
@@ -594,7 +586,7 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand,
        var z operand
        z.mode = variable
        z.expr = nil // TODO(gri) can we do better here? (for good error messages)
-       z.typ = par.Type.(Type)
+       z.typ = par.Type
 
        if arg != nil {
                check.expr(x, arg, z.typ, -1)
@@ -666,21 +658,17 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                        check.invalidOp(e.Pos(), "cannot use _ as value or type")
                        goto Error
                }
-               obj := e.Obj
+               obj := check.lookup(e)
                if obj == nil {
                        goto Error // error was reported before
                }
-               if obj.Type == nil {
-                       check.object(obj, cycleOk)
-               }
-               switch obj.Kind {
-               case ast.Bad:
-                       goto Error // error was reported before
-               case ast.Pkg:
+               check.object(obj, cycleOk)
+               switch obj := obj.(type) {
+               case *Package:
                        check.errorf(e.Pos(), "use of package %s not in selector", obj.Name)
                        goto Error
-               case ast.Con:
-                       if obj.Data == nil {
+               case *Const:
+                       if obj.Val == nil {
                                goto Error // cycle detected
                        }
                        x.mode = constant
@@ -691,24 +679,24 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                                }
                                x.val = int64(iota)
                        } else {
-                               x.val = obj.Data
+                               x.val = obj.Val
                        }
-               case ast.Typ:
+               case *TypeName:
                        x.mode = typexpr
-                       if !cycleOk && underlying(obj.Type.(Type)) == nil {
-                               check.errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name)
+                       if !cycleOk && underlying(obj.Type) == nil {
+                               check.errorf(obj.spec.Pos(), "illegal cycle in declaration of %s", obj.Name)
                                x.expr = e
                                x.typ = Typ[Invalid]
                                return // don't goto Error - need x.mode == typexpr
                        }
-               case ast.Var:
+               case *Var:
                        x.mode = variable
-               case ast.Fun:
+               case *Func:
                        x.mode = value
                default:
                        unreachable()
                }
-               x.typ = obj.Type.(Type)
+               x.typ = obj.GetType()
 
        case *ast.Ellipsis:
                // ellipses are handled explicitly where they are legal
@@ -877,8 +865,8 @@ 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 obj := ident.Obj; obj != nil && obj.Kind == ast.Pkg {
-                               exp := obj.Data.(*Package).Scope.Lookup(sel)
+                       if pkg, ok := check.lookup(ident).(*Package); ok {
+                               exp := pkg.Scope.Lookup(sel)
                                if exp == nil {
                                        check.errorf(e.Sel.Pos(), "cannot refer to unexported %s", sel)
                                        goto Error
@@ -1148,7 +1136,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                                        for i, obj := range t.Values {
                                                x.mode = value
                                                x.expr = nil // TODO(gri) can we do better here? (for good error messages)
-                                               x.typ = obj.Type.(Type)
+                                               x.typ = obj.Type
                                                check.argument(sig, i, nil, x, passSlice && i+1 == n)
                                        }
                                } else {
@@ -1182,7 +1170,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                                x.mode = novalue
                        case 1:
                                x.mode = value
-                               x.typ = sig.Results[0].Type.(Type)
+                               x.typ = sig.Results[0].Type
                        default:
                                x.mode = value
                                x.typ = &Result{Values: sig.Results}
index 7af014acdaa160894c2e3987725baa095effc7f4..31ff68ce6fcda4b84a4ecbb32b0136e4d6e2f0b8 100644 (file)
@@ -347,10 +347,11 @@ func (p *gcParser) parseExportedName() (*Package, string) {
 func (p *gcParser) parseBasicType() Type {
        id := p.expect(scanner.Ident)
        obj := Universe.Lookup(id)
-       if obj == nil || obj.Kind != ast.Typ {
-               p.errorf("not a basic type: %s", id)
+       if obj, ok := obj.(*TypeName); ok {
+               return obj.Type
        }
-       return obj.Type.(Type)
+       p.errorf("not a basic type: %s", id)
+       return nil
 }
 
 // ArrayType = "[" int_lit "]" Type .
index 39b5b06ed552376845179a7d44cc85fb2a3c00df..6f4b5cfc31638db8f4710fc4466094f477bb9c6f 100644 (file)
@@ -4,51 +4,65 @@
 
 package types
 
+import (
+       "go/ast"
+       "go/token"
+)
+
 // An Object describes a named language entity such as a package,
 // constant, type, variable, function (incl. methods), or label.
 // All objects implement the Object interface.
 //
 type Object interface {
-       anObject()
        GetName() string
+       GetType() Type
+       GetPos() token.Pos
+
+       anObject()
 }
 
 // A Package represents the contents (objects) of a Go package.
 type Package struct {
-       implementsObject
        Name    string
        Path    string              // import path, "" for current (non-imported) package
-       Scope   *Scope              // nil for current (non-imported) package for now
-       Imports map[string]*Package // map of import paths to packages
+       Scope   *Scope              // package-level scope
+       Imports map[string]*Package // map of import paths to imported packages
+
+       spec *ast.ImportSpec
 }
 
 // A Const represents a declared constant.
 type Const struct {
-       implementsObject
        Name string
        Type Type
        Val  interface{}
+
+       spec *ast.ValueSpec
 }
 
 // A TypeName represents a declared type.
 type TypeName struct {
-       implementsObject
        Name string
        Type Type // *NamedType or *Basic
+
+       spec *ast.TypeSpec
 }
 
 // A Variable represents a declared variable (including function parameters and results).
 type Var struct {
-       implementsObject
        Name string
        Type Type
+
+       visited bool // for initialization cycle detection
+       decl    interface{}
 }
 
 // A Func represents a declared function.
 type Func struct {
-       implementsObject
        Name string
        Type Type // *Signature or *Builtin
+
+       decl *ast.FuncDecl
 }
 
 func (obj *Package) GetName() string  { return obj.Name }
@@ -57,64 +71,84 @@ func (obj *TypeName) GetName() string { return obj.Name }
 func (obj *Var) GetName() string      { return obj.Name }
 func (obj *Func) GetName() string     { return obj.Name }
 
-func (obj *Package) GetType() Type  { return nil }
+func (obj *Package) GetType() Type  { return Typ[Invalid] }
 func (obj *Const) GetType() Type    { return obj.Type }
 func (obj *TypeName) GetType() Type { return obj.Type }
 func (obj *Var) GetType() Type      { return obj.Type }
 func (obj *Func) GetType() Type     { return obj.Type }
 
-// All concrete objects embed implementsObject which
-// ensures that they all implement the Object interface.
-type implementsObject struct{}
-
-func (*implementsObject) anObject() {}
-
-// A Scope maintains the set of named language entities declared
-// in the scope and a link to the immediately surrounding (outer)
-// scope.
-//
-type Scope struct {
-       Outer *Scope
-       Elems []Object          // scope entries in insertion order
-       large map[string]Object // for fast lookup - only used for larger scopes
-}
-
-// Lookup returns the object with the given name if it is
-// found in scope s, otherwise it returns nil. Outer scopes
-// are ignored.
-//
-func (s *Scope) Lookup(name string) Object {
-       if s.large != nil {
-               return s.large[name]
+func (obj *Package) GetPos() token.Pos { return obj.spec.Pos() }
+func (obj *Const) GetPos() token.Pos {
+       for _, n := range obj.spec.Names {
+               if n.Name == obj.Name {
+                       return n.Pos()
+               }
        }
-       for _, obj := range s.Elems {
-               if obj.GetName() == name {
-                       return obj
+       return token.NoPos
+}
+func (obj *TypeName) GetPos() token.Pos { return obj.spec.Pos() }
+func (obj *Var) GetPos() token.Pos {
+       switch d := obj.decl.(type) {
+       case *ast.Field:
+               for _, n := range d.Names {
+                       if n.Name == obj.Name {
+                               return n.Pos()
+                       }
+               }
+       case *ast.ValueSpec:
+               for _, n := range d.Names {
+                       if n.Name == obj.Name {
+                               return n.Pos()
+                       }
+               }
+       case *ast.AssignStmt:
+               for _, x := range d.Lhs {
+                       if ident, isIdent := x.(*ast.Ident); isIdent && ident.Name == obj.Name {
+                               return ident.Pos()
+                       }
                }
        }
-       return nil
+       return token.NoPos
 }
+func (obj *Func) GetPos() token.Pos { return obj.decl.Name.Pos() }
+
+func (*Package) anObject()  {}
+func (*Const) anObject()    {}
+func (*TypeName) anObject() {}
+func (*Var) anObject()      {}
+func (*Func) anObject()     {}
 
-// 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 and returns nil.
+// newObj returns a new Object for a given *ast.Object.
+// It does not canonicalize them (it always returns a new one).
+// For canonicalization, see check.lookup.
 //
-func (s *Scope) Insert(obj Object) Object {
-       name := obj.GetName()
-       if alt := s.Lookup(name); alt != nil {
-               return alt
-       }
-       s.Elems = append(s.Elems, obj)
-       if len(s.Elems) > 20 {
-               if s.large == nil {
-                       m := make(map[string]Object, len(s.Elems))
-                       for _, obj := range s.Elems {
-                               m[obj.GetName()] = obj
-                       }
-                       s.large = m
+// TODO(gri) Once we do identifier resolution completely in
+//           in the typechecker, this functionality can go.
+//
+func newObj(astObj *ast.Object) Object {
+       name := astObj.Name
+       typ, _ := astObj.Type.(Type)
+       switch astObj.Kind {
+       case ast.Bad:
+               // ignore
+       case ast.Pkg:
+               unreachable()
+       case ast.Con:
+               return &Const{Name: name, Type: typ, Val: astObj.Data, spec: astObj.Decl.(*ast.ValueSpec)}
+       case ast.Typ:
+               return &TypeName{Name: name, Type: typ, spec: astObj.Decl.(*ast.TypeSpec)}
+       case ast.Var:
+               switch astObj.Decl.(type) {
+               case *ast.Field, *ast.ValueSpec, *ast.AssignStmt: // these are ok
+               default:
+                       unreachable()
                }
-               s.large[name] = obj
+               return &Var{Name: name, Type: typ, decl: astObj.Decl}
+       case ast.Fun:
+               return &Func{Name: name, Type: typ, decl: astObj.Decl.(*ast.FuncDecl)}
+       case ast.Lbl:
+               unreachable() // for now
        }
+       unreachable()
        return nil
 }
index 1c8f35291eb812c54052d8873026fd823f53530e..ee6ae0c5228ea72b35313f6496ac1bb84b73b042 100644 (file)
@@ -265,9 +265,6 @@ func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res looku
                        visited[typ] = true
 
                        // look for a matching attached method
-                       if typ.AstObj != nil {
-                               assert(typ.AstObj.Data == nil) // methods must have been moved to typ.Methods
-                       }
                        for _, m := range typ.Methods {
                                if name.IsSame(m.QualifiedName) {
                                        assert(m.Type != nil)
@@ -355,9 +352,6 @@ func lookupField(typ Type, name QualifiedName) (operandMode, Type) {
        typ = deref(typ)
 
        if t, ok := typ.(*NamedType); ok {
-               if t.AstObj != nil {
-                       assert(t.AstObj.Data == nil) // methods must have been moved to t.Methods
-               }
                for _, m := range t.Methods {
                        if name.IsSame(m.QualifiedName) {
                                assert(m.Type != nil)
index e8ffb3647783cde824a7a8d0b3dd67376f814279..21781ea9792ec6a34ff9b0f0142cf884022ce590 100644 (file)
@@ -182,14 +182,7 @@ func isIdentical(x, y Type) bool {
                // Two named types are identical if their type names originate
                // in the same type declaration.
                if y, ok := y.(*NamedType); ok {
-                       switch {
-                       case x.Obj != nil:
-                               return x.Obj == y.Obj
-                       case x.AstObj != nil:
-                               return x.AstObj == y.AstObj
-                       default:
-                               unreachable()
-                       }
+                       return x.Obj == y.Obj
                }
        }
 
index 031be28fd9c40689098778b636aea5f5674b06ac..ef486c27ca20c8ae827a23d19715e53b15ee0dc1 100644 (file)
@@ -7,40 +7,39 @@ package types
 import (
        "fmt"
        "go/ast"
+       "go/token"
        "strconv"
 )
 
-func (check *checker) declareObj(scope, altScope *ast.Scope, obj *ast.Object) {
+func (check *checker) declareObj(scope, altScope *Scope, obj Object) {
        alt := scope.Insert(obj)
        if alt == nil && altScope != nil {
                // see if there is a conflicting declaration in altScope
-               alt = altScope.Lookup(obj.Name)
+               alt = altScope.Lookup(obj.GetName())
        }
        if alt != nil {
                prevDecl := ""
-               if pos := alt.Pos(); pos.IsValid() {
+               if pos := alt.GetPos(); pos.IsValid() {
                        prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos))
                }
-               check.errorf(obj.Pos(), fmt.Sprintf("%s redeclared in this block%s", obj.Name, prevDecl))
+               check.errorf(obj.GetPos(), fmt.Sprintf("%s redeclared in this block%s", obj.GetName(), prevDecl))
        }
 }
 
-func resolve(scope *ast.Scope, ident *ast.Ident) bool {
+func (check *checker) resolveIdent(scope *Scope, ident *ast.Ident) bool {
        for ; scope != nil; scope = scope.Outer {
                if obj := scope.Lookup(ident.Name); obj != nil {
-                       ident.Obj = obj
+                       check.idents[ident] = obj
                        return true
                }
        }
-       // handle universe scope lookups
        return false
 }
 
-// TODO(gri) eventually resolve should only return *Package.
-func (check *checker) resolve(importer Importer) (*ast.Package, *Package) {
+func (check *checker) resolve(importer Importer) (pkg *Package, methods []*ast.FuncDecl) {
        // complete package scope
        pkgName := ""
-       pkgScope := ast.NewScope(Universe)
+       pkgScope := &Scope{Outer: Universe}
 
        i := 0
        for _, file := range check.files {
@@ -57,9 +56,49 @@ func (check *checker) resolve(importer Importer) (*ast.Package, *Package) {
                check.files[i] = file
                i++
 
-               // collect top-level file objects in package scope
-               for _, obj := range file.Scope.Objects {
-                       check.declareObj(pkgScope, nil, obj)
+               // insert top-level file objects in package scope
+               // (the parser took care of declaration errors)
+               for _, decl := range file.Decls {
+                       switch d := decl.(type) {
+                       case *ast.BadDecl:
+                               // ignore
+                       case *ast.GenDecl:
+                               if d.Tok == token.CONST {
+                                       check.assocInitvals(d)
+                               }
+                               for _, spec := range d.Specs {
+                                       switch s := spec.(type) {
+                                       case *ast.ImportSpec:
+                                               // handled separately below
+                                       case *ast.ValueSpec:
+                                               for _, name := range s.Names {
+                                                       if name.Name == "_" {
+                                                               continue
+                                                       }
+                                                       pkgScope.Insert(check.lookup(name))
+                                               }
+                                       case *ast.TypeSpec:
+                                               if s.Name.Name == "_" {
+                                                       continue
+                                               }
+                                               pkgScope.Insert(check.lookup(s.Name))
+                                       default:
+                                               check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
+                                       }
+                               }
+                       case *ast.FuncDecl:
+                               if d.Recv != nil {
+                                       // collect method
+                                       methods = append(methods, d)
+                                       continue
+                               }
+                               if d.Name.Name == "_" || d.Name.Name == "init" {
+                                       continue // blank (_) and init functions are inaccessible
+                               }
+                               pkgScope.Insert(check.lookup(d.Name))
+                       default:
+                               check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
+                       }
                }
        }
        check.files = check.files[0:i]
@@ -71,7 +110,7 @@ func (check *checker) resolve(importer Importer) (*ast.Package, *Package) {
        for _, file := range check.files {
                // build file scope by processing all imports
                importErrors := false
-               fileScope := ast.NewScope(pkgScope)
+               fileScope := &Scope{Outer: pkgScope}
                for _, spec := range file.Imports {
                        if importer == nil {
                                importErrors = true
@@ -97,25 +136,15 @@ func (check *checker) resolve(importer Importer) (*ast.Package, *Package) {
                        // add import to file scope
                        if name == "." {
                                // merge imported scope with file scope
-                               // TODO(gri) Imported packages use Objects but the current
-                               //           package scope is based on ast.Scope and ast.Objects
-                               //           at the moment. Don't try to convert the imported
-                               //           objects for now. Once we get rid of ast.Object
-                               //           dependency, this loop can be enabled again.
-                               panic("cannot handle dot-import")
-                               /*
-                                       for _, obj := range pkg.Scope.Elems {
-                                               check.declareObj(fileScope, pkgScope, obj)
-                                       }
-                               */
+                               for _, obj := range pkg.Scope.Entries {
+                                       check.declareObj(fileScope, pkgScope, obj)
+                               }
                        } else if name != "_" {
                                // declare imported package object in file scope
                                // (do not re-use pkg in the file scope but create
                                // a new object instead; the Decl field is different
                                // for different files)
-                               obj := ast.NewObj(ast.Pkg, name)
-                               obj.Decl = spec
-                               obj.Data = pkg
+                               obj := &Package{Name: name, Scope: pkg.Scope, spec: spec}
                                check.declareObj(fileScope, pkgScope, obj)
                        }
                }
@@ -130,7 +159,7 @@ func (check *checker) resolve(importer Importer) (*ast.Package, *Package) {
                }
                i := 0
                for _, ident := range file.Unresolved {
-                       if !resolve(fileScope, ident) {
+                       if !check.resolveIdent(fileScope, ident) {
                                check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
                                file.Unresolved[i] = ident
                                i++
@@ -141,6 +170,5 @@ func (check *checker) resolve(importer Importer) (*ast.Package, *Package) {
                pkgScope.Outer = Universe // reset outer scope
        }
 
-       // TODO(gri) Once we have a pkgScope of type *Scope, only return *Package.
-       return &ast.Package{Name: pkgName, Scope: pkgScope}, &Package{Name: pkgName, Imports: imports}
+       return &Package{Name: pkgName, Scope: pkgScope, Imports: imports}, methods
 }
index fd2a4a67c12f42a5ed383cf1ffd1e88faa54b30d..d83ca753e0c85fb6b5fe3e6d7e534fe9d8dc2bee 100644 (file)
@@ -7,7 +7,7 @@ package types
 import (
        "fmt"
        "go/ast"
-       "go/parser"
+       //"go/parser"
        "go/scanner"
        "go/token"
        "testing"
@@ -76,60 +76,64 @@ func ResolveQualifiedIdents(fset *token.FileSet, pkg *ast.Package) error {
 }
 
 func TestResolveQualifiedIdents(t *testing.T) {
-       // parse package files
-       fset := token.NewFileSet()
-       files := make([]*ast.File, len(sources))
-       for i, src := range sources {
-               f, err := parser.ParseFile(fset, "", src, parser.DeclarationErrors)
+       return
+       // disabled for now
+       /*
+               // parse package files
+               fset := token.NewFileSet()
+               files := make([]*ast.File, len(sources))
+               for i, src := range sources {
+                       f, err := parser.ParseFile(fset, "", src, parser.DeclarationErrors)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       files[i] = f
+               }
+
+               // resolve package AST
+               astpkg, pkg, err := Check(fset, files)
                if err != nil {
                        t.Fatal(err)
                }
-               files[i] = f
-       }
 
-       // resolve package AST
-       astpkg, pkg, err := Check(fset, files)
-       if err != nil {
-               t.Fatal(err)
-       }
-
-       // check that all packages were imported
-       for _, name := range pkgnames {
-               if pkg.Imports[name] == nil {
-                       t.Errorf("package %s not imported", name)
+               // check that all packages were imported
+               for _, name := range pkgnames {
+                       if pkg.Imports[name] == nil {
+                               t.Errorf("package %s not imported", name)
+                       }
                }
-       }
 
-       // TODO(gri) fix this
-       // unresolved identifiers are not collected at the moment
-       // check that there are no top-level unresolved identifiers
-       for _, f := range astpkg.Files {
-               for _, x := range f.Unresolved {
-                       t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.Name)
+               // TODO(gri) fix this
+               // unresolved identifiers are not collected at the moment
+               // check that there are no top-level unresolved identifiers
+               for _, f := range astpkg.Files {
+                       for _, x := range f.Unresolved {
+                               t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.Name)
+                       }
                }
-       }
 
-       // resolve qualified identifiers
-       if err := ResolveQualifiedIdents(fset, astpkg); err != nil {
-               t.Error(err)
-       }
+               // resolve qualified identifiers
+               if err := ResolveQualifiedIdents(fset, astpkg); err != nil {
+                       t.Error(err)
+               }
 
-       // check that qualified identifiers are resolved
-       ast.Inspect(astpkg, func(n ast.Node) bool {
-               if s, ok := n.(*ast.SelectorExpr); ok {
-                       if x, ok := s.X.(*ast.Ident); ok {
-                               if x.Obj == nil {
-                                       t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name)
-                                       return false
-                               }
-                               if x.Obj.Kind == ast.Pkg && s.Sel != nil && s.Sel.Obj == nil {
-                                       t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name)
+               // check that qualified identifiers are resolved
+               ast.Inspect(astpkg, func(n ast.Node) bool {
+                       if s, ok := n.(*ast.SelectorExpr); ok {
+                               if x, ok := s.X.(*ast.Ident); ok {
+                                       if x.Obj == nil {
+                                               t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name)
+                                               return false
+                                       }
+                                       if x.Obj.Kind == ast.Pkg && s.Sel != nil && s.Sel.Obj == nil {
+                                               t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name)
+                                               return false
+                                       }
                                        return false
                                }
                                return false
                        }
-                       return false
-               }
-               return true
-       })
+                       return true
+               })
+       */
 }
diff --git a/src/pkg/go/types/scope.go b/src/pkg/go/types/scope.go
new file mode 100644 (file)
index 0000000..b8d6d0b
--- /dev/null
@@ -0,0 +1,59 @@
+// 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
+
+// A Scope maintains the set of named language entities declared
+// in the scope and a link to the immediately surrounding (outer)
+// 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
+}
+
+// Lookup returns the object with the given name if it is
+// found in scope s, otherwise it returns nil. Outer scopes
+// are ignored.
+//
+func (s *Scope) Lookup(name string) Object {
+       if s.large != nil {
+               return s.large[name]
+       }
+       for _, obj := range s.Entries {
+               if obj.GetName() == name {
+                       return obj
+               }
+       }
+       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 and returns nil.
+//
+func (s *Scope) Insert(obj Object) Object {
+       name := obj.GetName()
+       if alt := s.Lookup(name); alt != nil {
+               return alt
+       }
+       s.Entries = append(s.Entries, obj)
+
+       // 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.GetName()] = obj
+                       }
+                       s.large = m
+               }
+               s.large[name] = obj
+       }
+
+       return nil
+}
index 26962e8a4a8d82e51462e0b109ce249f79d9032c..492dfb6c67c07e1f6a7786f99e3aae3bdc1bb81f 100644 (file)
@@ -76,10 +76,10 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
        }
 
        // lhs may or may not be typed yet
-       obj := ident.Obj
+       obj := check.lookup(ident)
        var typ Type
-       if obj.Type != nil {
-               typ = obj.Type.(Type)
+       if t := obj.GetType(); t != nil {
+               typ = t
        }
 
        if rhs != nil {
@@ -94,7 +94,7 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
                typ = Typ[Invalid]
                if x.mode != invalid {
                        typ = x.typ
-                       if obj.Kind == ast.Var && isUntyped(typ) {
+                       if _, ok := obj.(*Var); ok && isUntyped(typ) {
                                if x.isNil() {
                                        check.errorf(x.pos(), "use of untyped nil")
                                        x.mode = invalid
@@ -103,15 +103,22 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
                                }
                        }
                }
-               obj.Type = typ
+               switch obj := obj.(type) {
+               case *Const:
+                       obj.Type = typ
+               case *Var:
+                       obj.Type = typ
+               default:
+                       unreachable()
+               }
        }
 
        if x.mode != invalid {
                var z operand
-               switch obj.Kind {
-               case ast.Con:
+               switch obj.(type) {
+               case *Const:
                        z.mode = constant
-               case ast.Var:
+               case *Var:
                        z.mode = variable
                default:
                        unreachable()
@@ -122,12 +129,12 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
        }
 
        // for constants, set their value
-       if obj.Kind == ast.Con {
-               assert(obj.Data == nil)
+       if obj, ok := obj.(*Const); ok {
+               assert(obj.Val == nil)
                if x.mode != invalid {
                        if x.mode == constant {
                                if isConstType(x.typ) {
-                                       obj.Data = x.val
+                                       obj.Val = x.val
                                } else {
                                        check.errorf(x.pos(), "%s has invalid constant type", x)
                                }
@@ -135,22 +142,23 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
                                check.errorf(x.pos(), "%s is not constant", x)
                        }
                }
-               if obj.Data == nil {
+               if obj.Val == nil {
                        // set the constant to its type's zero value to reduce spurious errors
-                       switch typ := underlying(obj.Type.(Type)); {
+                       switch typ := underlying(obj.Type); {
                        case typ == Typ[Invalid]:
                                // ignore
                        case isBoolean(typ):
-                               obj.Data = false
+                               obj.Val = false
                        case isNumeric(typ):
-                               obj.Data = int64(0)
+                               obj.Val = int64(0)
                        case isString(typ):
-                               obj.Data = ""
+                               obj.Val = ""
                        case hasNil(typ):
-                               obj.Data = nilConst
+                               obj.Val = nilConst
                        default:
                                // in all other cases just prevent use of the constant
-                               obj.Kind = ast.Bad
+                               // TODO(gri) re-evaluate this code
+                               obj.Val = nilConst
                        }
                }
        }
@@ -159,7 +167,7 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
 // assignNtoM typechecks a general assignment. If decl is set, the lhs operands
 // must be identifiers. If their types are not set, they are deduced from the
 // types of the corresponding rhs expressions. iota >= 0 indicates that the
-// "assignment" is part of a constant declaration.
+// "assignment" is part of a constant/variable declaration.
 // Precondition: len(lhs) > 0 .
 //
 func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
@@ -187,7 +195,7 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
                        x.mode = value
                        for i, obj := range t.Values {
                                x.expr = nil // TODO(gri) should do better here
-                               x.typ = obj.Type.(Type)
+                               x.typ = obj.Type
                                check.assign1to1(lhs[i], nil, &x, decl, iota)
                        }
                        return
@@ -212,8 +220,15 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
        if iota >= 0 {
                // declaration
                for _, e := range lhs {
-                       if ident, ok := e.(*ast.Ident); ok {
-                               ident.Obj.Type = Typ[Invalid]
+                       if name, ok := e.(*ast.Ident); ok {
+                               switch obj := check.lookup(name).(type) {
+                               case *Const:
+                                       obj.Type = Typ[Invalid]
+                               case *Var:
+                                       obj.Type = Typ[Invalid]
+                               default:
+                                       unreachable()
+                               }
                        }
                }
        }
@@ -411,16 +426,12 @@ func (check *checker) stmt(s ast.Stmt) {
                        named := false // if set, function has named results
                        for i, res := range sig.Results {
                                if len(res.Name) > 0 {
-                                       // a blank (_) result parameter is a named result parameter!
+                                       // a blank (_) result parameter is a named result
                                        named = true
                                }
                                name := ast.NewIdent(res.Name)
                                name.NamePos = s.Pos()
-                               // TODO(gri) Avoid creating new objects here once we
-                               //           move away from ast.Objects completely.
-                               obj := ast.NewObj(ast.Var, res.Name)
-                               obj.Type = res.Type
-                               name.Obj = obj
+                               check.idents[name] = &Var{Name: res.Name, Type: res.Type}
                                lhs[i] = name
                        }
                        if len(s.Results) > 0 || !named {
@@ -432,7 +443,7 @@ func (check *checker) stmt(s ast.Stmt) {
                }
 
        case *ast.BranchStmt:
-               unimplemented()
+               // TODO(gri) implement this
 
        case *ast.BlockStmt:
                check.stmtList(s.List)
@@ -453,7 +464,9 @@ func (check *checker) stmt(s ast.Stmt) {
                tag := s.Tag
                if tag == nil {
                        // use fake true tag value and position it at the opening { of the switch
-                       tag = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true", Obj: Universe.Lookup("true")}
+                       ident := &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"}
+                       check.idents[ident] = Universe.Lookup("true")
+                       tag = ident
                }
                check.expr(&x, tag, nil, -1)
 
@@ -519,7 +532,7 @@ func (check *checker) stmt(s ast.Stmt) {
                // remaining syntactic errors are considered AST errors here.
                // TODO(gri) better factoring of error handling (invalid ASTs)
                //
-               var lhs *ast.Object // lhs identifier object or nil
+               var lhs *Var // lhs variable or nil
                var rhs ast.Expr
                switch guard := s.Assign.(type) {
                case *ast.ExprStmt:
@@ -534,7 +547,7 @@ func (check *checker) stmt(s ast.Stmt) {
                                check.invalidAST(s.Pos(), "incorrect form of type switch guard")
                                return
                        }
-                       lhs = ident.Obj
+                       lhs = check.lookup(ident).(*Var)
                        rhs = guard.Rhs[0]
                default:
                        check.invalidAST(s.Pos(), "incorrect form of type switch guard")
index 5ad864828aada49b84f3ea2e30a3c4e1516b33f9..3867be737653d80c4f59ed9de8e6a9552e05cc88 100644 (file)
@@ -61,7 +61,7 @@ func (T5 /* ERROR "invalid receiver" */) m1() {}
 func (T5 /* ERROR "invalid receiver" */) m2() {}
 
 // Methods associated with non-local or unnamed types.
-// func (int) m() {} TODO(gri) check for methods associated with external (not package-local) types
+func (int /* ERROR "non-local type" */ ) m() {}
 func ([ /* ERROR "expected" */ ]int) m() {}
 func (time /* ERROR "expected" */ .Time) m() {}
 func (x interface /* ERROR "expected" */ {}) m() {}
index fa120fd9e95e25189346c19dfd69204838631911..3894825b2b99239b85b147153fbfdb00141b5a25 100644 (file)
@@ -71,7 +71,6 @@ const (
 
 // A Basic represents a basic type.
 type Basic struct {
-       implementsType
        Kind BasicKind
        Info BasicInfo
        Size int64
@@ -80,14 +79,12 @@ type Basic struct {
 
 // An Array represents an array type [Len]Elt.
 type Array struct {
-       implementsType
        Len int64
        Elt Type
 }
 
 // A Slice represents a slice type []Elt.
 type Slice struct {
-       implementsType
        Elt Type
 }
 
@@ -132,7 +129,6 @@ type Field struct {
 
 // A Struct represents a struct type struct{...}.
 type Struct struct {
-       implementsType
        Fields []*Field
 }
 
@@ -147,19 +143,16 @@ func (typ *Struct) fieldIndex(name string) int {
 
 // A Pointer represents a pointer type *Base.
 type Pointer struct {
-       implementsType
        Base Type
 }
 
 // A Result represents a (multi-value) function call result.
 type Result struct {
-       implementsType
        Values []*Var // Signature.Results of the function called
 }
 
 // A Signature represents a user-defined function type func(...) (...).
 type Signature struct {
-       implementsType
        Recv       *Var   // nil if not a method
        Params     []*Var // (incoming) parameters from left to right; or nil
        Results    []*Var // (outgoing) results from left to right; or nil
@@ -200,7 +193,6 @@ const (
 
 // A builtin represents the type of a built-in function.
 type builtin struct {
-       implementsType
        id          builtinId
        name        string
        nargs       int // number of arguments (minimum if variadic)
@@ -216,35 +208,36 @@ type Method struct {
 
 // An Interface represents an interface type interface{...}.
 type Interface struct {
-       implementsType
        Methods []*Method // TODO(gri) consider keeping them in sorted order
 }
 
 // A Map represents a map type map[Key]Elt.
 type Map struct {
-       implementsType
        Key, Elt Type
 }
 
 // A Chan represents a channel type chan Elt, <-chan Elt, or chan<-Elt.
 type Chan struct {
-       implementsType
        Dir ast.ChanDir
        Elt Type
 }
 
 // A NamedType represents a named type as declared in a type declaration.
 type NamedType struct {
-       implementsType
-       // TODO(gri) remove AstObj once we have moved away from ast.Objects
-       Obj        Object      // corresponding declared object (imported package)
-       AstObj     *ast.Object // corresponding declared object (current package)
-       Underlying Type        // nil if not fully declared yet; never a *NamedType
-       Methods    []*Method   // TODO(gri) consider keeping them in sorted order
-}
-
-// All concrete types embed implementsType which
-// ensures that all types implement the Type interface.
-type implementsType struct{}
-
-func (*implementsType) aType() {}
+       Obj        *TypeName // corresponding declared object
+       Underlying Type      // nil if not fully declared yet; never a *NamedType
+       Methods    []*Method // TODO(gri) consider keeping them in sorted order
+}
+
+func (*Basic) aType()     {}
+func (*Array) aType()     {}
+func (*Slice) aType()     {}
+func (*Struct) aType()    {}
+func (*Pointer) aType()   {}
+func (*Result) aType()    {}
+func (*Signature) aType() {}
+func (*builtin) aType()   {}
+func (*Interface) aType() {}
+func (*Map) aType()       {}
+func (*Chan) aType()      {}
+func (*NamedType) aType() {}
index ef83c840b23d5acecb3454eca7e068f934149c16..8e228fa677e9dcae97b538a6bf34afb6a77ef7e8 100644 (file)
@@ -15,13 +15,13 @@ import (
 
 const filename = "<src>"
 
-func makePkg(t *testing.T, src string) (*ast.Package, error) {
+func makePkg(t *testing.T, src string) (*Package, error) {
        file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors)
        if err != nil {
                return nil, err
        }
-       astpkg, _, err := Check(fset, []*ast.File{file})
-       return astpkg, err
+       pkg, err := Check(fset, []*ast.File{file})
+       return pkg, err
 }
 
 type testEntry struct {
@@ -110,7 +110,7 @@ func TestTypes(t *testing.T) {
                        t.Errorf("%s: %s", src, err)
                        continue
                }
-               typ := underlying(pkg.Scope.Lookup("T").Type.(Type))
+               typ := underlying(pkg.Scope.Lookup("T").GetType())
                str := typeString(typ)
                if str != test.str {
                        t.Errorf("%s: got %s, want %s", test.src, str, test.str)
index 43fe39046a2e9ea15cd3ca9692ac658afcab5169..9668aa8a34d4a01ed146ca091cee91c6337a38fd 100644 (file)
@@ -12,160 +12,125 @@ import (
 )
 
 var (
-       aType    implementsType
-       Universe *ast.Scope
-       Unsafe   *Package // package unsafe
+       Universe     *Scope
+       Unsafe       *Package
+       universeIota *Const
 )
 
 // Predeclared types, indexed by BasicKind.
 var Typ = [...]*Basic{
-       Invalid: {aType, Invalid, 0, 0, "invalid type"},
-
-       Bool:          {aType, Bool, IsBoolean, 1, "bool"},
-       Int:           {aType, Int, IsInteger, 0, "int"},
-       Int8:          {aType, Int8, IsInteger, 1, "int8"},
-       Int16:         {aType, Int16, IsInteger, 2, "int16"},
-       Int32:         {aType, Int32, IsInteger, 4, "int32"},
-       Int64:         {aType, Int64, IsInteger, 8, "int64"},
-       Uint:          {aType, Uint, IsInteger | IsUnsigned, 0, "uint"},
-       Uint8:         {aType, Uint8, IsInteger | IsUnsigned, 1, "uint8"},
-       Uint16:        {aType, Uint16, IsInteger | IsUnsigned, 2, "uint16"},
-       Uint32:        {aType, Uint32, IsInteger | IsUnsigned, 4, "uint32"},
-       Uint64:        {aType, Uint64, IsInteger | IsUnsigned, 8, "uint64"},
-       Uintptr:       {aType, Uintptr, IsInteger | IsUnsigned, 0, "uintptr"},
-       Float32:       {aType, Float32, IsFloat, 4, "float32"},
-       Float64:       {aType, Float64, IsFloat, 8, "float64"},
-       Complex64:     {aType, Complex64, IsComplex, 8, "complex64"},
-       Complex128:    {aType, Complex128, IsComplex, 16, "complex128"},
-       String:        {aType, String, IsString, 0, "string"},
-       UnsafePointer: {aType, UnsafePointer, 0, 0, "Pointer"},
-
-       UntypedBool:    {aType, UntypedBool, IsBoolean | IsUntyped, 0, "untyped boolean"},
-       UntypedInt:     {aType, UntypedInt, IsInteger | IsUntyped, 0, "untyped integer"},
-       UntypedRune:    {aType, UntypedRune, IsInteger | IsUntyped, 0, "untyped rune"},
-       UntypedFloat:   {aType, UntypedFloat, IsFloat | IsUntyped, 0, "untyped float"},
-       UntypedComplex: {aType, UntypedComplex, IsComplex | IsUntyped, 0, "untyped complex"},
-       UntypedString:  {aType, UntypedString, IsString | IsUntyped, 0, "untyped string"},
-       UntypedNil:     {aType, UntypedNil, IsUntyped, 0, "untyped nil"},
+       Invalid: {Invalid, 0, 0, "invalid type"},
+
+       Bool:          {Bool, IsBoolean, 1, "bool"},
+       Int:           {Int, IsInteger, 0, "int"},
+       Int8:          {Int8, IsInteger, 1, "int8"},
+       Int16:         {Int16, IsInteger, 2, "int16"},
+       Int32:         {Int32, IsInteger, 4, "int32"},
+       Int64:         {Int64, IsInteger, 8, "int64"},
+       Uint:          {Uint, IsInteger | IsUnsigned, 0, "uint"},
+       Uint8:         {Uint8, IsInteger | IsUnsigned, 1, "uint8"},
+       Uint16:        {Uint16, IsInteger | IsUnsigned, 2, "uint16"},
+       Uint32:        {Uint32, IsInteger | IsUnsigned, 4, "uint32"},
+       Uint64:        {Uint64, IsInteger | IsUnsigned, 8, "uint64"},
+       Uintptr:       {Uintptr, IsInteger | IsUnsigned, 0, "uintptr"},
+       Float32:       {Float32, IsFloat, 4, "float32"},
+       Float64:       {Float64, IsFloat, 8, "float64"},
+       Complex64:     {Complex64, IsComplex, 8, "complex64"},
+       Complex128:    {Complex128, IsComplex, 16, "complex128"},
+       String:        {String, IsString, 0, "string"},
+       UnsafePointer: {UnsafePointer, 0, 0, "Pointer"},
+
+       UntypedBool:    {UntypedBool, IsBoolean | IsUntyped, 0, "untyped boolean"},
+       UntypedInt:     {UntypedInt, IsInteger | IsUntyped, 0, "untyped integer"},
+       UntypedRune:    {UntypedRune, IsInteger | IsUntyped, 0, "untyped rune"},
+       UntypedFloat:   {UntypedFloat, IsFloat | IsUntyped, 0, "untyped float"},
+       UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, 0, "untyped complex"},
+       UntypedString:  {UntypedString, IsString | IsUntyped, 0, "untyped string"},
+       UntypedNil:     {UntypedNil, IsUntyped, 0, "untyped nil"},
 }
 
 var aliases = [...]*Basic{
-       {aType, Byte, IsInteger | IsUnsigned, 1, "byte"},
-       {aType, Rune, IsInteger, 4, "rune"},
+       {Byte, IsInteger | IsUnsigned, 1, "byte"},
+       {Rune, IsInteger, 4, "rune"},
 }
 
-var predeclaredConstants = [...]*struct {
-       kind BasicKind
-       name string
-       val  interface{}
-}{
-       {UntypedBool, "true", true},
-       {UntypedBool, "false", false},
-       {UntypedInt, "iota", zeroConst},
-       {UntypedNil, "nil", nilConst},
+var predeclaredConstants = [...]*Const{
+       {"true", Typ[UntypedBool], true, nil},
+       {"false", Typ[UntypedBool], false, nil},
+       {"iota", Typ[UntypedInt], zeroConst, nil},
+       {"nil", Typ[UntypedNil], nilConst, nil},
 }
 
 var predeclaredFunctions = [...]*builtin{
-       {aType, _Append, "append", 1, true, false},
-       {aType, _Cap, "cap", 1, false, false},
-       {aType, _Close, "close", 1, false, true},
-       {aType, _Complex, "complex", 2, false, false},
-       {aType, _Copy, "copy", 2, false, true},
-       {aType, _Delete, "delete", 2, false, true},
-       {aType, _Imag, "imag", 1, false, false},
-       {aType, _Len, "len", 1, false, false},
-       {aType, _Make, "make", 1, true, false},
-       {aType, _New, "new", 1, false, false},
-       {aType, _Panic, "panic", 1, false, true},
-       {aType, _Print, "print", 1, true, true},
-       {aType, _Println, "println", 1, true, true},
-       {aType, _Real, "real", 1, false, false},
-       {aType, _Recover, "recover", 0, false, true},
-
-       {aType, _Alignof, "Alignof", 1, false, false},
-       {aType, _Offsetof, "Offsetof", 1, false, false},
-       {aType, _Sizeof, "Sizeof", 1, false, false},
+       {_Append, "append", 1, true, false},
+       {_Cap, "cap", 1, false, false},
+       {_Close, "close", 1, false, true},
+       {_Complex, "complex", 2, false, false},
+       {_Copy, "copy", 2, false, true},
+       {_Delete, "delete", 2, false, true},
+       {_Imag, "imag", 1, false, false},
+       {_Len, "len", 1, false, false},
+       {_Make, "make", 1, true, false},
+       {_New, "new", 1, false, false},
+       {_Panic, "panic", 1, false, true},
+       {_Print, "print", 1, true, true},
+       {_Println, "println", 1, true, true},
+       {_Real, "real", 1, false, false},
+       {_Recover, "recover", 0, false, true},
+
+       {_Alignof, "Alignof", 1, false, false},
+       {_Offsetof, "Offsetof", 1, false, false},
+       {_Sizeof, "Sizeof", 1, false, false},
 }
 
-// commonly used types
-var (
-       emptyInterface = new(Interface)
-)
-
-// commonly used constants
-var (
-       universeIota *ast.Object
-)
-
 func init() {
-       // Universe scope
-       Universe = ast.NewScope(nil)
-
-       // unsafe package and its scope
+       Universe = new(Scope)
        Unsafe = &Package{Name: "unsafe", Scope: new(Scope)}
 
        // predeclared types
        for _, t := range Typ {
-               def(ast.Typ, t.Name, t)
+               def(&TypeName{Name: t.Name, Type: t})
        }
        for _, t := range aliases {
-               def(ast.Typ, t.Name, t)
+               def(&TypeName{Name: t.Name, Type: t})
        }
 
        // error type
        {
                err := &Method{QualifiedName{Name: "Error"}, &Signature{Results: []*Var{{Name: "", Type: Typ[String]}}}}
-               def(ast.Typ, "error", &NamedType{Underlying: &Interface{Methods: []*Method{err}}})
+               def(&TypeName{Name: "error", Type: &NamedType{Underlying: &Interface{Methods: []*Method{err}}}})
        }
 
-       // predeclared constants
-       for _, t := range predeclaredConstants {
-               obj := def(ast.Con, t.name, Typ[t.kind])
-               obj.Data = t.val
+       for _, c := range predeclaredConstants {
+               def(c)
        }
 
-       // predeclared functions
        for _, f := range predeclaredFunctions {
-               def(ast.Fun, f.name, f)
+               def(&Func{Name: f.name, Type: f})
        }
 
-       universeIota = Universe.Lookup("iota")
+       universeIota = Universe.Lookup("iota").(*Const)
 }
 
 // Objects with names containing blanks are internal and not entered into
 // a scope. Objects with exported names are inserted in the unsafe package
 // scope; other objects are inserted in the universe scope.
 //
-func def(kind ast.ObjKind, name string, typ Type) *ast.Object {
-       // insert non-internal objects into respective scope
-       if strings.Index(name, " ") < 0 {
-               // exported identifiers go into package unsafe
-               if ast.IsExported(name) {
-                       var obj Object
-                       switch kind {
-                       case ast.Typ:
-                               obj = &TypeName{Name: name, Type: typ}
-                       case ast.Fun:
-                               obj = &Func{Name: name, Type: typ}
-                       default:
-                               unreachable()
-
-                       }
-                       if Unsafe.Scope.Insert(obj) != nil {
-                               panic("internal error: double declaration")
-                       }
-               } else {
-                       obj := ast.NewObj(kind, name)
-                       obj.Decl = Universe
-                       obj.Type = typ
-                       if typ, ok := typ.(*NamedType); ok {
-                               typ.AstObj = obj
-                       }
-                       if Universe.Insert(obj) != nil {
-                               panic("internal error: double declaration")
-                       }
-                       return obj
-               }
+func def(obj Object) {
+       name := obj.GetName()
+       if strings.Index(name, " ") >= 0 {
+               return // nothing to do
+       }
+       // fix Obj link for named types
+       if typ, ok := obj.GetType().(*NamedType); ok {
+               typ.Obj = obj.(*TypeName)
+       }
+       // exported identifiers go into package unsafe
+       scope := Universe
+       if ast.IsExported(name) {
+               scope = Unsafe.Scope
+       }
+       if scope.Insert(obj) != nil {
+               panic("internal error: double declaration")
        }
-       return nil
 }