]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: Moving from *ast.Objects to types.Objects (step 1).
authorRobert Griesemer <gri@golang.org>
Fri, 11 Jan 2013 21:53:38 +0000 (13:53 -0800)
committerRobert Griesemer <gri@golang.org>
Fri, 11 Jan 2013 21:53:38 +0000 (13:53 -0800)
The existing type checker was relying on augmenting ast.Object
fields (empty interfaces) for its purposes. While this worked
for some time now, it has become increasingly brittle. Also,
the need for package information for Fields and Methods would
have required a new field in each ast.Object. Rather than making
them bigger and the code even more subtle, in this CL we are moving
away from ast.Objects.

The types packge now defines its own objects for different
language entities (Const, Var, TypeName, Func), and they
implement the types.Object interface. Imported packages
create a Package object which holds the exported entities
in a types.Scope of types.Objects.

For type-checking, the current package is still using ast.Objects
to make this transition manageable. In a next step, the type-
checker will also use types.Objects instead, which opens the door
door to resolving ASTs entirely by the type checker. As a result,
the AST and type checker become less entangled, and ASTs can be
manipulated "by hand" or programmatically w/o having to worry
about scope and object invariants that are very hard to maintain.

(As a consequence, a future parser can do less work, and a
future AST will not need to define objects and scopes anymore.
Also, object resolution which is now split across the parser,
the ast, (ast.NewPackage), and even the type checker (for composite
literal keys) can be done in a single place which will be simpler
and more efficient.)

Change details:
- Check now takes a []*ast.File instead of a map[string]*ast.File.
It's easier to handle (I deleted code at all use sites) and does
not suffer from undefined order (which is a pain for testing).
- ast.Object.Data is now a *types.Package rather then an *ast.Scope
if the object is a package (obj.Kind == ast.Pkg). Eventually this
will go away altogether.
- Instead of an ast.Importer, Check now uses a types.Importer
(which returns a *types.Package).
- types.NamedType has two object fields (Obj Object and obj *ast.Object);
eventually there will be only Obj. The *ast.Object is needed during
this transition since a NamedType may refer to either an imported
(using types.Object) or locally defined (using *ast.Object) type.
- ast.NewPackage is not used anymore - there's a local copy for
package-level resolution of imports.
- struct fields now take the package origin into account.
- The GcImporter is now returning a *types.Package. It cannot be
used with ast.NewPackage anymore. If that functionality is still
used, a copy of the old GcImporter should be made locally (note
that GcImporter was part of exp/types and it's API was not frozen).
- dot-imports are not handled for the time being (this will come back).

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

18 files changed:
src/pkg/exp/gotype/gotype.go
src/pkg/go/ast/scope.go
src/pkg/go/types/api.go
src/pkg/go/types/check.go
src/pkg/go/types/check_test.go
src/pkg/go/types/conversions.go
src/pkg/go/types/errors.go
src/pkg/go/types/expr.go
src/pkg/go/types/gcimporter.go
src/pkg/go/types/gcimporter_test.go
src/pkg/go/types/objects.go [new file with mode: 0644]
src/pkg/go/types/operand.go
src/pkg/go/types/predicates.go
src/pkg/go/types/resolve.go [new file with mode: 0644]
src/pkg/go/types/resolver_test.go
src/pkg/go/types/types.go
src/pkg/go/types/types_test.go
src/pkg/go/types/universe.go

index 311def89f5f93ca36c65a4db824e43e8d8c54133..d1de18a411331f79a6e631c054287e51ccadd709 100644 (file)
@@ -5,7 +5,6 @@
 package main
 
 import (
-       "errors"
        "flag"
        "fmt"
        "go/ast"
@@ -92,8 +91,7 @@ func parse(fset *token.FileSet, filename string, src []byte) *ast.File {
        return file
 }
 
-func parseStdin(fset *token.FileSet) (files map[string]*ast.File) {
-       files = make(map[string]*ast.File)
+func parseStdin(fset *token.FileSet) (files []*ast.File) {
        src, err := ioutil.ReadAll(os.Stdin)
        if err != nil {
                report(err)
@@ -101,13 +99,12 @@ func parseStdin(fset *token.FileSet) (files map[string]*ast.File) {
        }
        const filename = "<standard input>"
        if file := parse(fset, filename, src); file != nil {
-               files[filename] = file
+               files = []*ast.File{file}
        }
        return
 }
 
-func parseFiles(fset *token.FileSet, filenames []string) (files map[string]*ast.File) {
-       files = make(map[string]*ast.File)
+func parseFiles(fset *token.FileSet, filenames []string) (files []*ast.File) {
        for _, filename := range filenames {
                src, err := ioutil.ReadFile(filename)
                if err != nil {
@@ -115,11 +112,7 @@ func parseFiles(fset *token.FileSet, filenames []string) (files map[string]*ast.
                        continue
                }
                if file := parse(fset, filename, src); file != nil {
-                       if files[filename] != nil {
-                               report(errors.New(fmt.Sprintf("%q: duplicate file", filename)))
-                               continue
-                       }
-                       files[filename] = file
+                       files = append(files, file)
                }
        }
        return
@@ -169,8 +162,8 @@ func processFiles(filenames []string, allFiles bool) {
        processPackage(fset, parseFiles(fset, filenames[0:i]))
 }
 
-func processPackage(fset *token.FileSet, files map[string]*ast.File) {
-       _, err := types.Check(fset, files)
+func processPackage(fset *token.FileSet, files []*ast.File) {
+       _, _, err := types.Check(fset, files)
        if err != nil {
                report(err)
        }
index 6edb31016e03d47f406369a15c2beff243ce99d3..c32369a518477644037da3687b00ee2c13d28600 100644 (file)
@@ -64,19 +64,16 @@ func (s *Scope) String() string {
 // ----------------------------------------------------------------------------
 // Objects
 
-// TODO(gri) Consider replacing the Object struct with an interface
-//           and a corresponding set of object implementations.
-
 // An Object describes a named language entity such as a package,
 // constant, type, variable, function (incl. methods), or label.
 //
 // The Data fields contains object-specific data:
 //
-//     Kind    Data type    Data value
-//     Pkg     *Scope       package scope
-//     Con     int          iota for the respective declaration
-//     Con     != nil       constant value
-//     Typ     *Scope       method scope; nil if no methods
+//     Kind    Data type         Data value
+//     Pkg     *types.Package    package scope
+//     Con     int               iota for the respective declaration
+//     Con     != nil            constant value
+//     Typ     *Scope            (used as method scope during type checking - transient)
 //
 type Object struct {
        Kind ObjKind
index 8ccd969a8d19177d0785d2e49d8acf6d2552e593..c1d762e33c7384985f5091dfdd73064e713b77af 100644 (file)
@@ -41,9 +41,20 @@ type Context struct {
        Expr func(x ast.Expr, typ Type, val interface{})
 
        // If Import is not nil, it is used instead of GcImport.
-       Import ast.Importer
+       Import Importer
 }
 
+// An Importer resolves import paths to Package objects.
+// The imports map records the packages already imported,
+// indexed by package id (canonical import path).
+// An Importer must determine the canonical import path and
+// check the map to see if it is already present in the imports map.
+// If so, the Importer can return the map entry.  Otherwise, the
+// Importer should load the package data for the given path into
+// a new *Package, record pkg in the imports map, and then
+// return pkg.
+type Importer func(imports map[string]*Package, path string) (pkg *Package, err error)
+
 // Default is the default context for type checking.
 var Default = Context{
        // TODO(gri) Perhaps this should depend on GOARCH?
@@ -57,11 +68,17 @@ var Default = Context{
 // it returns the first error. If the context's Error handler is nil,
 // Check terminates as soon as the first error is encountered.
 //
-func (ctxt *Context) Check(fset *token.FileSet, files map[string]*ast.File) (*ast.Package, error) {
+// CAUTION: At the moment, the returned *ast.Package only contains the package
+//          name and scope - the other fields are not set up. The returned
+//          *Package contains the name and imports (but no scope yet). Once
+//          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) {
        return check(ctxt, fset, files)
 }
 
 // Check is shorthand for Default.Check.
-func Check(fset *token.FileSet, files map[string]*ast.File) (*ast.Package, error) {
+func Check(fset *token.FileSet, files []*ast.File) (*ast.Package, *Package, error) {
        return Default.Check(fset, files)
 }
index cebba7abf58f6f6297eac56cd77edc66c716fe81..158941b05329928352d9b7555ae274743a1225e0 100644 (file)
@@ -9,9 +9,7 @@ package types
 import (
        "fmt"
        "go/ast"
-       "go/scanner"
        "go/token"
-       "sort"
 )
 
 // enable for debugging
@@ -23,6 +21,7 @@ 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
@@ -164,7 +163,7 @@ func (check *checker) object(obj *ast.Object, cycleOk bool) {
                check.valueSpec(spec.Pos(), obj, spec.Names, init.Type, init.Values, iota)
 
        case ast.Typ:
-               typ := &NamedType{Obj: obj}
+               typ := &NamedType{obj: obj}
                obj.Type = typ // "mark" object so recursion terminates
                typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk))
                // typecheck associated method signatures
@@ -188,14 +187,18 @@ func (check *checker) object(obj *ast.Object, cycleOk bool) {
                                }
                        }
                        // 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)
                                sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
                                obj.Type = sig
+                               methods = append(methods, &Method{QualifiedName{check.pkg, obj.Name}, sig})
                                check.later(obj, sig, mdecl.Body)
                        }
+                       typ.Methods = methods
+                       obj.Data = nil // don't use obj.Data later, accidentally
                }
 
        case ast.Fun:
@@ -346,33 +349,15 @@ func (check *checker) iterate(f func(*checker, ast.Decl)) {
        }
 }
 
-// sortedFiles returns the sorted list of package files given a package file map.
-func sortedFiles(m map[string]*ast.File) []*ast.File {
-       keys := make([]string, len(m))
-       i := 0
-       for k, _ := range m {
-               keys[i] = k
-               i++
-       }
-       sort.Strings(keys)
-
-       files := make([]*ast.File, len(m))
-       for i, k := range keys {
-               files[i] = m[k]
-       }
-
-       return files
-}
-
 // A bailout panic is raised to indicate early termination.
 type bailout struct{}
 
-func check(ctxt *Context, fset *token.FileSet, files map[string]*ast.File) (pkg *ast.Package, err error) {
+func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (astpkg *ast.Package, pkg *Package, err error) {
        // initialize checker
        check := checker{
                ctxt:     ctxt,
                fset:     fset,
-               files:    sortedFiles(files),
+               files:    files,
                initspec: make(map[*ast.ValueSpec]*ast.ValueSpec),
        }
 
@@ -394,8 +379,9 @@ func check(ctxt *Context, fset *token.FileSet, files map[string]*ast.File) (pkg
        imp := ctxt.Import
        if imp == nil {
                // wrap GcImport to import packages only once by default.
+               // TODO(gri) move this into resolve
                imported := make(map[string]bool)
-               imp = func(imports map[string]*ast.Object, path string) (*ast.Object, error) {
+               imp = func(imports map[string]*Package, path string) (*Package, error) {
                        if imported[path] && imports[path] != nil {
                                return imports[path], nil
                        }
@@ -406,17 +392,13 @@ func check(ctxt *Context, fset *token.FileSet, files map[string]*ast.File) (pkg
                        return pkg, err
                }
        }
-       pkg, err = ast.NewPackage(fset, files, imp, Universe)
-       if err != nil {
-               if list, _ := err.(scanner.ErrorList); len(list) > 0 {
-                       for _, err := range list {
-                               check.err(err)
-                       }
-               } else {
-                       check.err(err)
-               }
-       }
-       check.pkgscope = pkg.Scope
+       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.
+       check.pkg = pkg
+       check.pkgscope = astpkg.Scope
 
        // determine missing constant initialization expressions
        // and associate methods with types
index 4e73d93bafbc60783e6122b928f4e86ca2e01855..285c130596a8e1004df6dbaf78cffc8fdd95b5f2 100644 (file)
@@ -88,18 +88,15 @@ func splitError(err error) (pos, msg string) {
        return
 }
 
-func parseFiles(t *testing.T, testname string, filenames []string) (map[string]*ast.File, []error) {
-       files := make(map[string]*ast.File)
+func parseFiles(t *testing.T, testname string, filenames []string) ([]*ast.File, []error) {
+       var files []*ast.File
        var errlist []error
        for _, filename := range filenames {
-               if _, exists := files[filename]; exists {
-                       t.Fatalf("%s: duplicate file %s", testname, filename)
-               }
                file, err := parser.ParseFile(fset, filename, nil, parser.DeclarationErrors)
                if file == nil {
                        t.Fatalf("%s: could not parse file %s", testname, filename)
                }
-               files[filename] = file
+               files = append(files, file)
                if err != nil {
                        if list, _ := err.(scanner.ErrorList); len(list) > 0 {
                                for _, err := range list {
@@ -121,10 +118,11 @@ var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`)
 // errMap collects the regular expressions of ERROR comments found
 // in files and returns them as a map of error positions to error messages.
 //
-func errMap(t *testing.T, testname string, files map[string]*ast.File) map[string][]string {
+func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string {
        errmap := make(map[string][]string)
 
-       for filename := range files {
+       for _, file := range files {
+               filename := fset.Position(file.Package).Filename
                src, err := ioutil.ReadFile(filename)
                if err != nil {
                        t.Fatalf("%s: could not read %s", testname, filename)
@@ -236,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").Type = &builtin{aType, _Assert, "assert", 1, false, true}
-       def(ast.Fun, "trace").Type = &builtin{aType, _Trace, "trace", 0, true, true}
+       def(ast.Fun, "assert", &builtin{aType, _Assert, "assert", 1, false, true})
+       def(ast.Fun, "trace", &builtin{aType, _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 cbaef8aa9ae5fbaa6bad177edcd638758f662ed8..5a6bea80d178aab4e3ca67427c0d5c0be0643920 100644 (file)
@@ -29,7 +29,7 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota
        }
 
        // TODO(gri) fix this - implement all checks and constant evaluation
-       if x.mode != constant {
+       if x.mode != constant || !isConstType(typ) {
                x.mode = value
        }
        x.expr = conv
index 85a9db729e1da76b1fd6a8837e61018c987f1e47..96446949b4c87529befdf46fdcc11335c27de594 100644 (file)
@@ -311,7 +311,16 @@ func writeType(buf *bytes.Buffer, typ Type) {
                writeType(buf, t.Elt)
 
        case *NamedType:
-               buf.WriteString(t.Obj.Name)
+               var s string
+               switch {
+               case t.obj != nil:
+                       s = t.obj.Name
+               case t.Obj != nil:
+                       s = t.Obj.GetName()
+               default:
+                       s = "<NamedType w/o object>"
+               }
+               buf.WriteString(s)
 
        default:
                fmt.Fprintf(buf, "<type %T>", t)
index 7b80978aad8f850328431d5d6b8eaed329366149..99a038e26d8e6177e6c03056cf4a658d4e059fbe 100644 (file)
@@ -48,14 +48,14 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param
                                obj := name.Obj
                                obj.Type = typ
                                last = obj
-                               params = append(params, &Var{obj.Name, typ})
+                               params = append(params, &Var{Name: obj.Name, Type: typ})
                        }
                } else {
                        // anonymous parameter
                        obj := ast.NewObj(ast.Var, "")
                        obj.Type = typ
                        last = obj
-                       params = append(params, &Var{obj.Name, typ})
+                       params = append(params, &Var{Name: obj.Name, Type: typ})
                }
        }
        // For a variadic function, change the last parameter's object type
@@ -84,7 +84,7 @@ func (check *checker) collectMethods(list *ast.FieldList) (methods []*Method) {
                                continue
                        }
                        for _, name := range f.Names {
-                               methods = append(methods, &Method{name.Name, sig})
+                               methods = append(methods, &Method{QualifiedName{check.pkg, name.Name}, sig})
                        }
                } else {
                        // embedded interface
@@ -137,15 +137,24 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [
                if len(f.Names) > 0 {
                        // named fields
                        for _, name := range f.Names {
-                               fields = append(fields, &Field{name.Name, typ, tag, false})
+                               fields = append(fields, &Field{QualifiedName{check.pkg, name.Name}, typ, tag, false})
                        }
                } else {
                        // anonymous field
                        switch t := deref(typ).(type) {
                        case *Basic:
-                               fields = append(fields, &Field{t.Name, typ, tag, true})
+                               fields = append(fields, &Field{QualifiedName{check.pkg, t.Name}, typ, tag, true})
                        case *NamedType:
-                               fields = append(fields, &Field{t.Obj.Name, typ, tag, true})
+                               var name string
+                               switch {
+                               case t.obj != nil:
+                                       name = t.obj.Name
+                               case t.Obj != nil:
+                                       name = t.Obj.GetName()
+                               default:
+                                       unreachable()
+                               }
+                               fields = append(fields, &Field{QualifiedName{check.pkg, name}, typ, tag, true})
                        default:
                                if typ != Typ[Invalid] {
                                        check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ)
@@ -183,9 +192,6 @@ func (check *checker) unary(x *operand, op token.Token) {
        case token.AND:
                // spec: "As an exception to the addressability
                // requirement x may also be a composite literal."
-               // (The spec doesn't specify whether the literal
-               // can be parenthesized or not, but all compilers
-               // accept parenthesized literals.)
                if _, ok := unparen(x.expr).(*ast.CompositeLit); ok {
                        x.mode = variable
                }
@@ -872,29 +878,33 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                // selector expressions.
                if ident, ok := e.X.(*ast.Ident); ok {
                        if obj := ident.Obj; obj != nil && obj.Kind == ast.Pkg {
-                               exp := obj.Data.(*ast.Scope).Lookup(sel)
+                               exp := obj.Data.(*Package).Scope.Lookup(sel)
                                if exp == nil {
                                        check.errorf(e.Sel.Pos(), "cannot refer to unexported %s", sel)
                                        goto Error
                                }
-                               // simplified version of the code for *ast.Idents:
-                               // imported objects are always fully initialized
-                               switch exp.Kind {
-                               case ast.Con:
-                                       assert(exp.Data != nil)
+                               // Simplified version of the code for *ast.Idents:
+                               // - imported packages use types.Scope and types.Objects
+                               // - imported objects are always fully initialized
+                               switch exp := exp.(type) {
+                               case *Const:
+                                       assert(exp.Val != nil)
                                        x.mode = constant
-                                       x.val = exp.Data
-                               case ast.Typ:
+                                       x.typ = exp.Type
+                                       x.val = exp.Val
+                               case *TypeName:
                                        x.mode = typexpr
-                               case ast.Var:
+                                       x.typ = exp.Type
+                               case *Var:
                                        x.mode = variable
-                               case ast.Fun:
+                                       x.typ = exp.Type
+                               case *Func:
                                        x.mode = value
+                                       x.typ = exp.Type
                                default:
                                        unreachable()
                                }
                                x.expr = e
-                               x.typ = exp.Type.(Type)
                                return
                        }
                }
@@ -903,7 +913,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                if x.mode == invalid {
                        goto Error
                }
-               mode, typ := lookupField(x.typ, sel)
+               mode, typ := lookupField(x.typ, QualifiedName{check.pkg, sel})
                if mode == invalid {
                        check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel)
                        goto Error
@@ -921,7 +931,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                        // pointer vs non-pointer receivers => typechecker is too lenient
                        x.mode = value
                        x.typ = &Signature{
-                               Params:     append([]*Var{{"", x.typ}}, sig.Params...),
+                               Params:     append([]*Var{{Type: x.typ}}, sig.Params...),
                                Results:    sig.Results,
                                IsVariadic: sig.IsVariadic,
                        }
index 96603b1a0ff8b662707008a482571e321360d253..7af014acdaa160894c2e3987725baa095effc7f4 100644 (file)
@@ -2,8 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// This file implements an ast.Importer for gc-generated object files.
-// TODO(gri) Eventually move this into a separate package outside types.
+// This file implements an Importer for gc-generated object files.
 
 package types
 
@@ -83,7 +82,7 @@ func FindPkg(path, srcDir string) (filename, id string) {
 // be the beginning of the export data section. The filename is only used
 // in error messages.
 //
-func GcImportData(imports map[string]*ast.Object, filename, id string, data *bufio.Reader) (pkg *ast.Object, err error) {
+func GcImportData(imports map[string]*Package, filename, id string, data *bufio.Reader) (pkg *Package, err error) {
        // support for gcParser error handling
        defer func() {
                if r := recover(); r != nil {
@@ -104,7 +103,7 @@ func GcImportData(imports map[string]*ast.Object, filename, id string, data *buf
 // The imports map must contains all packages already imported.
 // GcImport satisfies the ast.Importer signature.
 //
-func GcImport(imports map[string]*ast.Object, path string) (pkg *ast.Object, err error) {
+func GcImport(imports map[string]*Package, path string) (pkg *Package, err error) {
        if path == "unsafe" {
                return Unsafe, nil
        }
@@ -156,13 +155,13 @@ func GcImport(imports map[string]*ast.Object, path string) (pkg *ast.Object, err
 // object/archive file and populates its scope with the results.
 type gcParser struct {
        scanner scanner.Scanner
-       tok     rune                   // current token
-       lit     string                 // literal string; only valid for Ident, Int, String tokens
-       id      string                 // package id of imported package
-       imports map[string]*ast.Object // package id -> package object
+       tok     rune                // current token
+       lit     string              // literal string; only valid for Ident, Int, String tokens
+       id      string              // package id of imported package
+       imports map[string]*Package // package id -> package object
 }
 
-func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*ast.Object) {
+func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*Package) {
        p.scanner.Init(src)
        p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
        p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
@@ -187,27 +186,45 @@ func (p *gcParser) next() {
        }
 }
 
-// Declare inserts a named object of the given kind in scope.
-func (p *gcParser) declare(scope *ast.Scope, kind ast.ObjKind, name string) *ast.Object {
-       // the object may have been imported before - if it exists
-       // already in the respective package scope, return that object
+func declConst(scope *Scope, name string) *Const {
+       // the constant may have been imported before - if it exists
+       // already in the respective scope, return that constant
        if obj := scope.Lookup(name); obj != nil {
-               assert(obj.Kind == kind)
-               return obj
+               return obj.(*Const)
        }
+       // otherwise create a new constant and insert it into the scope
+       obj := &Const{Name: name}
+       scope.Insert(obj)
+       return obj
+}
 
-       // otherwise create a new object and insert it into the package scope
-       obj := ast.NewObj(kind, name)
-       if scope.Insert(obj) != nil {
-               unreachable() // Lookup should have found it
+func declTypeName(scope *Scope, name string) *TypeName {
+       if obj := scope.Lookup(name); obj != nil {
+               return obj.(*TypeName)
        }
+       obj := &TypeName{Name: name}
+       // a named type may be referred to before the underlying type
+       // is known - set it up
+       obj.Type = &NamedType{Obj: obj}
+       scope.Insert(obj)
+       return obj
+}
 
-       // if the new type object is a named type it may be referred
-       // to before the underlying type is known - set it up
-       if kind == ast.Typ {
-               obj.Type = &NamedType{Obj: obj}
+func declVar(scope *Scope, name string) *Var {
+       if obj := scope.Lookup(name); obj != nil {
+               return obj.(*Var)
        }
+       obj := &Var{Name: name}
+       scope.Insert(obj)
+       return obj
+}
 
+func declFunc(scope *Scope, name string) *Func {
+       if obj := scope.Lookup(name); obj != nil {
+               return obj.(*Func)
+       }
+       obj := &Func{Name: name}
+       scope.Insert(obj)
        return obj
 }
 
@@ -270,7 +287,7 @@ func (p *gcParser) expectKeyword(keyword string) {
 
 // ImportPath = string_lit .
 //
-func (p *gcParser) parsePkgId() *ast.Object {
+func (p *gcParser) parsePkgId() *Package {
        id, err := strconv.Unquote(p.expect(scanner.String))
        if err != nil {
                p.error(err)
@@ -288,8 +305,7 @@ func (p *gcParser) parsePkgId() *ast.Object {
 
        pkg := p.imports[id]
        if pkg == nil {
-               pkg = ast.NewObj(ast.Pkg, "")
-               pkg.Data = ast.NewScope(nil)
+               pkg = &Package{Scope: new(Scope)}
                p.imports[id] = pkg
        }
 
@@ -315,7 +331,7 @@ func (p *gcParser) parseDotIdent() string {
 
 // ExportedName = "@" ImportPath "." dotIdentifier .
 //
-func (p *gcParser) parseExportedName() (*ast.Object, string) {
+func (p *gcParser) parseExportedName() (*Package, string) {
        p.expect('@')
        pkg := p.parsePkgId()
        p.expect('.')
@@ -364,7 +380,7 @@ func (p *gcParser) parseMapType() Type {
 
 // Name = identifier | "?" | ExportedName  .
 //
-func (p *gcParser) parseName() (name string) {
+func (p *gcParser) parseName() (pkg *Package, name string) {
        switch p.tok {
        case scanner.Ident:
                name = p.lit
@@ -374,7 +390,7 @@ func (p *gcParser) parseName() (name string) {
                p.next()
        case '@':
                // exported name prefixed with package path
-               _, name = p.parseExportedName()
+               pkg, name = p.parseExportedName()
        default:
                p.error("name expected")
        }
@@ -385,7 +401,7 @@ func (p *gcParser) parseName() (name string) {
 //
 func (p *gcParser) parseField() *Field {
        var f Field
-       f.Name = p.parseName()
+       f.Pkg, f.Name = p.parseName()
        f.Type = p.parseType()
        if p.tok == scanner.String {
                f.Tag = p.expect(scanner.String)
@@ -393,7 +409,7 @@ func (p *gcParser) parseField() *Field {
        if f.Name == "" {
                // anonymous field - typ must be T or *T and T must be a type name
                if typ, ok := deref(f.Type).(*NamedType); ok && typ.Obj != nil {
-                       f.Name = typ.Obj.Name
+                       f.Name = typ.Obj.GetName()
                        f.IsAnonymous = true
                } else {
                        p.errorf("anonymous field expected")
@@ -424,7 +440,7 @@ func (p *gcParser) parseStructType() Type {
 // Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
 //
 func (p *gcParser) parseParameter() (par *Var, isVariadic bool) {
-       name := p.parseName()
+       _, name := p.parseName()
        if name == "" {
                name = "_" // cannot access unnamed identifiers
        }
@@ -437,7 +453,7 @@ func (p *gcParser) parseParameter() (par *Var, isVariadic bool) {
        if p.tok == scanner.String {
                p.next()
        }
-       par = &Var{name, typ}
+       par = &Var{Name: name, Type: typ}
        return
 }
 
@@ -475,7 +491,7 @@ func (p *gcParser) parseSignature() *Signature {
        switch p.tok {
        case scanner.Ident, '[', '*', '<', '@':
                // single, unnamed result
-               results = []*Var{{"", p.parseType()}}
+               results = []*Var{{Type: p.parseType()}}
        case '(':
                // named or multiple result(s)
                var variadic bool
@@ -505,9 +521,9 @@ func (p *gcParser) parseInterfaceType() Type {
                if len(methods) > 0 {
                        p.expect(';')
                }
-               name := p.parseName()
+               pkg, name := p.parseName()
                typ := p.parseSignature()
-               methods = append(methods, &Method{name, typ})
+               methods = append(methods, &Method{QualifiedName{pkg, name}, typ})
        }
        p.expect('}')
 
@@ -566,7 +582,7 @@ func (p *gcParser) parseType() Type {
        case '@':
                // TypeName
                pkg, name := p.parseExportedName()
-               return p.declare(pkg.Data.(*ast.Scope), ast.Typ, name).Type.(Type)
+               return declTypeName(pkg.Scope, name).Type
        case '[':
                p.next() // look ahead
                if p.tok == ']' {
@@ -674,7 +690,7 @@ func (p *gcParser) parseNumber() (x operand) {
 func (p *gcParser) parseConstDecl() {
        p.expectKeyword("const")
        pkg, name := p.parseExportedName()
-       obj := p.declare(pkg.Data.(*ast.Scope), ast.Con, name)
+       obj := declConst(pkg.Scope, name)
        var x operand
        if p.tok != '=' {
                obj.Type = p.parseType()
@@ -732,7 +748,7 @@ func (p *gcParser) parseConstDecl() {
                obj.Type = x.typ
        }
        assert(x.val != nil)
-       obj.Data = x.val
+       obj.Val = x.val
 }
 
 // TypeDecl = "type" ExportedName Type .
@@ -740,7 +756,7 @@ func (p *gcParser) parseConstDecl() {
 func (p *gcParser) parseTypeDecl() {
        p.expectKeyword("type")
        pkg, name := p.parseExportedName()
-       obj := p.declare(pkg.Data.(*ast.Scope), ast.Typ, name)
+       obj := declTypeName(pkg.Scope, name)
 
        // The type object may have been imported before and thus already
        // have a type associated with it. We still need to parse the type
@@ -759,17 +775,15 @@ func (p *gcParser) parseTypeDecl() {
 func (p *gcParser) parseVarDecl() {
        p.expectKeyword("var")
        pkg, name := p.parseExportedName()
-       obj := p.declare(pkg.Data.(*ast.Scope), ast.Var, name)
+       obj := declVar(pkg.Scope, name)
        obj.Type = p.parseType()
 }
 
 // Func = Signature [ Body ] .
 // Body = "{" ... "}" .
 //
-func (p *gcParser) parseFunc(scope *ast.Scope, name string) *Signature {
-       obj := p.declare(scope, ast.Fun, name)
+func (p *gcParser) parseFunc() *Signature {
        sig := p.parseSignature()
-       obj.Type = sig
        if p.tok == '{' {
                p.next()
                for i := 1; i > 0; p.next() {
@@ -794,25 +808,26 @@ func (p *gcParser) parseMethodDecl() {
        p.expect(')')
 
        // determine receiver base type object
-       typ := recv.Type.(Type)
+       typ := recv.Type
        if ptr, ok := typ.(*Pointer); ok {
                typ = ptr.Base
        }
-       obj := typ.(*NamedType).Obj
-
-       // determine base type scope
-       var scope *ast.Scope
-       if obj.Data != nil {
-               scope = obj.Data.(*ast.Scope)
-       } else {
-               scope = ast.NewScope(nil)
-               obj.Data = scope
-       }
+       base := typ.(*NamedType)
 
-       // declare method in base type scope
-       name := p.parseName() // unexported method names in imports are qualified with their package.
-       sig := p.parseFunc(scope, name)
+       // parse method name, signature, and possibly inlined body
+       pkg, name := p.parseName() // unexported method names in imports are qualified with their package.
+       sig := p.parseFunc()
        sig.Recv = recv
+
+       // add method to type unless type was imported before
+       // and method exists already
+       // TODO(gri) investigate if this can be avoided
+       for _, m := range base.Methods {
+               if m.Name == name {
+                       return // method was added before
+               }
+       }
+       base.Methods = append(base.Methods, &Method{QualifiedName{pkg, name}, sig})
 }
 
 // FuncDecl = "func" ExportedName Func .
@@ -820,7 +835,8 @@ func (p *gcParser) parseMethodDecl() {
 func (p *gcParser) parseFuncDecl() {
        // "func" already consumed
        pkg, name := p.parseExportedName()
-       p.parseFunc(pkg.Data.(*ast.Scope), name)
+       typ := p.parseFunc()
+       declFunc(pkg.Scope, name).Type = typ
 }
 
 // Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
@@ -852,7 +868,7 @@ func (p *gcParser) parseDecl() {
 // Export        = "PackageClause { Decl } "$$" .
 // PackageClause = "package" identifier [ "safe" ] "\n" .
 //
-func (p *gcParser) parseExport() *ast.Object {
+func (p *gcParser) parseExport() *Package {
        p.expectKeyword("package")
        name := p.expect(scanner.Ident)
        if p.tok != '\n' {
@@ -865,8 +881,7 @@ func (p *gcParser) parseExport() *ast.Object {
 
        pkg := p.imports[p.id]
        if pkg == nil {
-               pkg = ast.NewObj(ast.Pkg, name)
-               pkg.Data = ast.NewScope(nil)
+               pkg = &Package{Name: name, Scope: new(Scope)}
                p.imports[p.id] = pkg
        }
 
index 5f3236e0f3ed7d32139b7fd514bbac8fdc1e396c..8a2e8c21d50fa3c42972466af1a88899a3852ad9 100644 (file)
@@ -51,7 +51,7 @@ func compile(t *testing.T, dirname, filename string) string {
 
 // Use the same global imports map for all tests. The effect is
 // as if all tested packages were imported into a single package.
-var imports = make(map[string]*ast.Object)
+var imports = make(map[string]*Package)
 
 func testPath(t *testing.T, path string) bool {
        t0 := time.Now()
@@ -147,12 +147,34 @@ func TestGcImportedTypes(t *testing.T) {
                        continue
                }
 
-               obj := pkg.Data.(*ast.Scope).Lookup(objName)
-               if obj.Kind != test.kind {
-                       t.Errorf("%s: got kind = %q; want %q", test.name, obj.Kind, test.kind)
+               obj := pkg.Scope.Lookup(objName)
+
+               // TODO(gri) should define an accessor on Object
+               var kind ast.ObjKind
+               var typ Type
+               switch obj := obj.(type) {
+               case *Const:
+                       kind = ast.Con
+                       typ = obj.Type
+               case *TypeName:
+                       kind = ast.Typ
+                       typ = obj.Type
+               case *Var:
+                       kind = ast.Var
+                       typ = obj.Type
+               case *Func:
+                       kind = ast.Fun
+                       typ = obj.Type
+               default:
+                       unreachable()
                }
-               typ := typeString(underlying(obj.Type.(Type)))
-               if typ != test.typ {
+
+               if kind != test.kind {
+                       t.Errorf("%s: got kind = %q; want %q", test.name, kind, test.kind)
+               }
+
+               str := typeString(underlying(typ))
+               if str != test.typ {
                        t.Errorf("%s: got type = %q; want %q", test.name, typ, test.typ)
                }
        }
diff --git a/src/pkg/go/types/objects.go b/src/pkg/go/types/objects.go
new file mode 100644 (file)
index 0000000..39b5b06
--- /dev/null
@@ -0,0 +1,120 @@
+// 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
+
+// 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
+}
+
+// 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
+}
+
+// A Const represents a declared constant.
+type Const struct {
+       implementsObject
+       Name string
+       Type Type
+       Val  interface{}
+}
+
+// A TypeName represents a declared type.
+type TypeName struct {
+       implementsObject
+       Name string
+       Type Type // *NamedType or *Basic
+}
+
+// A Variable represents a declared variable (including function parameters and results).
+type Var struct {
+       implementsObject
+       Name string
+       Type Type
+}
+
+// A Func represents a declared function.
+type Func struct {
+       implementsObject
+       Name string
+       Type Type // *Signature or *Builtin
+}
+
+func (obj *Package) GetName() string  { return obj.Name }
+func (obj *Const) GetName() string    { return obj.Name }
+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 *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]
+       }
+       for _, obj := range s.Elems {
+               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.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
+               }
+               s.large[name] = obj
+       }
+       return nil
+}
index f85e6b40366d9181be91699e7ca424225a1fa8d2..77aacacdc9a5d1272ca24e64428221937ad93cfd 100644 (file)
@@ -222,11 +222,11 @@ type embeddedType struct {
 }
 
 // lookupFieldBreadthFirst searches all types in list for a single entry (field
-// or method) of the given name. If such a field is found, the result describes
-// the field mode and type; otherwise the result mode is invalid.
+// or method) of the given name from the given package. If such a field is found,
+// the result describes the field mode and type; otherwise the result mode is invalid.
 // (This function is similar in structure to FieldByNameFunc in reflect/type.go)
 //
-func lookupFieldBreadthFirst(list []embeddedType, name string) (res lookupResult) {
+func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res lookupResult) {
        // visited records the types that have been searched already.
        visited := make(map[*NamedType]bool)
 
@@ -265,20 +265,23 @@ func lookupFieldBreadthFirst(list []embeddedType, name string) (res lookupResult
                        visited[typ] = true
 
                        // look for a matching attached method
-                       if data := typ.Obj.Data; data != nil {
-                               if obj := data.(*ast.Scope).Lookup(name); obj != nil {
-                                       assert(obj.Type != nil)
-                                       if !potentialMatch(e.multiples, value, obj.Type.(Type)) {
+                       if typ.obj != nil {
+                               assert(typ.obj.Data == nil) // methods must have been moved to typ.Methods
+                       }
+                       for _, m := range typ.Methods {
+                               if identicalNames(name, m.QualifiedName) {
+                                       assert(m.Type != nil)
+                                       if !potentialMatch(e.multiples, value, m.Type) {
                                                return // name collision
                                        }
                                }
                        }
 
-                       switch typ := underlying(typ).(type) {
+                       switch t := typ.Underlying.(type) {
                        case *Struct:
                                // look for a matching field and collect embedded types
-                               for _, f := range typ.Fields {
-                                       if f.Name == name {
+                               for _, f := range t.Fields {
+                                       if identicalNames(name, f.QualifiedName) {
                                                assert(f.Type != nil)
                                                if !potentialMatch(e.multiples, variable, f.Type) {
                                                        return // name collision
@@ -301,8 +304,8 @@ func lookupFieldBreadthFirst(list []embeddedType, name string) (res lookupResult
 
                        case *Interface:
                                // look for a matching method
-                               for _, m := range typ.Methods {
-                                       if m.Name == name {
+                               for _, m := range t.Methods {
+                                       if identicalNames(name, m.QualifiedName) {
                                                assert(m.Type != nil)
                                                if !potentialMatch(e.multiples, value, m.Type) {
                                                        return // name collision
@@ -348,23 +351,27 @@ func findType(list []embeddedType, typ *NamedType) *embeddedType {
        return nil
 }
 
-func lookupField(typ Type, name string) (operandMode, Type) {
+func lookupField(typ Type, name QualifiedName) (operandMode, Type) {
        typ = deref(typ)
 
-       if typ, ok := typ.(*NamedType); ok {
-               if data := typ.Obj.Data; data != nil {
-                       if obj := data.(*ast.Scope).Lookup(name); obj != nil {
-                               assert(obj.Type != nil)
-                               return value, obj.Type.(Type)
+       if t, ok := typ.(*NamedType); ok {
+               if t.obj != nil {
+                       assert(t.obj.Data == nil) // methods must have been moved to t.Methods
+               }
+               for _, m := range t.Methods {
+                       if identicalNames(name, m.QualifiedName) {
+                               assert(m.Type != nil)
+                               return value, m.Type
                        }
                }
+               typ = t.Underlying
        }
 
-       switch typ := underlying(typ).(type) {
+       switch t := typ.(type) {
        case *Struct:
                var next []embeddedType
-               for _, f := range typ.Fields {
-                       if f.Name == name {
+               for _, f := range t.Fields {
+                       if identicalNames(name, f.QualifiedName) {
                                return variable, f.Type
                        }
                        if f.IsAnonymous {
@@ -380,8 +387,8 @@ func lookupField(typ Type, name string) (operandMode, Type) {
                }
 
        case *Interface:
-               for _, m := range typ.Methods {
-                       if m.Name == name {
+               for _, m := range t.Methods {
+                       if identicalNames(name, m.QualifiedName) {
                                return value, m.Type
                        }
                }
index 0f4aad6a125d4637edfc93e1042a06191aac854f..b16b8ce7b06898602bc82f537bbaf7914635f90f 100644 (file)
@@ -6,6 +6,8 @@
 
 package types
 
+import "go/ast"
+
 func isNamed(typ Type) bool {
        if _, ok := typ.(*Basic); ok {
                return ok
@@ -126,11 +128,10 @@ func isIdentical(x, y Type) bool {
                // and identical tags. Two anonymous fields are considered to have the same
                // name. Lower-case field names from different packages are always different.
                if y, ok := y.(*Struct); ok {
-                       // TODO(gri) handle structs from different packages
                        if len(x.Fields) == len(y.Fields) {
                                for i, f := range x.Fields {
                                        g := y.Fields[i]
-                                       if f.Name != g.Name ||
+                                       if !identicalNames(f.QualifiedName, g.QualifiedName) ||
                                                !isIdentical(f.Type, g.Type) ||
                                                f.Tag != g.Tag ||
                                                f.IsAnonymous != g.IsAnonymous {
@@ -183,13 +184,31 @@ 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 {
-                       return x.Obj == y.Obj
+                       switch {
+                       case x.obj != nil:
+                               return x.obj == y.obj
+                       case x.Obj != nil:
+                               return x.Obj == y.Obj
+                       default:
+                               unreachable()
+                       }
                }
        }
 
        return false
 }
 
+// identicalNames returns true if the names a and b are equal.
+func identicalNames(a, b QualifiedName) bool {
+       if a.Name != b.Name {
+               return false
+       }
+       // a.Name == b.Name
+       // TODO(gri) Guarantee that packages are canonicalized
+       //           and then we can compare p == q directly.
+       return ast.IsExported(a.Name) || a.Pkg.Path == b.Pkg.Path
+}
+
 // identicalTypes returns true if both lists a and b have the
 // same length and corresponding objects have identical types.
 func identicalTypes(a, b []*Var) bool {
@@ -212,12 +231,13 @@ func identicalMethods(a, b []*Method) bool {
        if len(a) != len(b) {
                return false
        }
-       m := make(map[string]*Method)
+       m := make(map[QualifiedName]*Method)
        for _, x := range a {
-               m[x.Name] = x
+               assert(m[x.QualifiedName] == nil) // method list must not have duplicate entries
+               m[x.QualifiedName] = x
        }
        for _, y := range b {
-               if x := m[y.Name]; x == nil || !isIdentical(x.Type, y.Type) {
+               if x := m[y.QualifiedName]; x == nil || !isIdentical(x.Type, y.Type) {
                        return false
                }
        }
@@ -275,12 +295,13 @@ func defaultType(typ Type) Type {
 // is missing or simply has the wrong type.
 //
 func missingMethod(typ Type, T *Interface) (method *Method, wrongType bool) {
+       // TODO(gri): this needs to correctly compare method names (taking package into account)
        // 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 ityp, _ := underlying(typ).(*Interface); ityp != nil {
                for _, m := range T.Methods {
-                       mode, sig := lookupField(ityp, m.Name) // TODO(gri) no need to go via lookupField
+                       mode, sig := lookupField(ityp, m.QualifiedName) // TODO(gri) no need to go via lookupField
                        if mode != invalid && !isIdentical(sig, m.Type) {
                                return m, true
                        }
@@ -290,7 +311,7 @@ func missingMethod(typ Type, T *Interface) (method *Method, wrongType bool) {
 
        // a concrete type implements T if it implements all methods of T.
        for _, m := range T.Methods {
-               mode, sig := lookupField(typ, m.Name)
+               mode, sig := lookupField(typ, m.QualifiedName)
                if mode == invalid {
                        return m, false
                }
diff --git a/src/pkg/go/types/resolve.go b/src/pkg/go/types/resolve.go
new file mode 100644 (file)
index 0000000..031be28
--- /dev/null
@@ -0,0 +1,146 @@
+// 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 (
+       "fmt"
+       "go/ast"
+       "strconv"
+)
+
+func (check *checker) declareObj(scope, altScope *ast.Scope, obj *ast.Object) {
+       alt := scope.Insert(obj)
+       if alt == nil && altScope != nil {
+               // see if there is a conflicting declaration in altScope
+               alt = altScope.Lookup(obj.Name)
+       }
+       if alt != nil {
+               prevDecl := ""
+               if pos := alt.Pos(); 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))
+       }
+}
+
+func resolve(scope *ast.Scope, ident *ast.Ident) bool {
+       for ; scope != nil; scope = scope.Outer {
+               if obj := scope.Lookup(ident.Name); obj != nil {
+                       ident.Obj = 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) {
+       // complete package scope
+       pkgName := ""
+       pkgScope := ast.NewScope(Universe)
+
+       i := 0
+       for _, file := range check.files {
+               // package names must match
+               switch name := file.Name.Name; {
+               case pkgName == "":
+                       pkgName = name
+               case name != pkgName:
+                       check.errorf(file.Package, "package %s; expected %s", name, pkgName)
+                       continue // ignore this file
+               }
+
+               // keep this file
+               check.files[i] = file
+               i++
+
+               // collect top-level file objects in package scope
+               for _, obj := range file.Scope.Objects {
+                       check.declareObj(pkgScope, nil, obj)
+               }
+       }
+       check.files = check.files[0:i]
+
+       // package global mapping of imported package ids to package objects
+       imports := make(map[string]*Package)
+
+       // complete file scopes with imports and resolve identifiers
+       for _, file := range check.files {
+               // build file scope by processing all imports
+               importErrors := false
+               fileScope := ast.NewScope(pkgScope)
+               for _, spec := range file.Imports {
+                       if importer == nil {
+                               importErrors = true
+                               continue
+                       }
+                       path, _ := strconv.Unquote(spec.Path.Value)
+                       pkg, err := importer(imports, path)
+                       if err != nil {
+                               check.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err)
+                               importErrors = true
+                               continue
+                       }
+                       // TODO(gri) If a local package name != "." is provided,
+                       // global identifier resolution could proceed even if the
+                       // import failed. Consider adjusting the logic here a bit.
+
+                       // local name overrides imported package name
+                       name := pkg.Name
+                       if spec.Name != nil {
+                               name = spec.Name.Name
+                       }
+
+                       // 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)
+                                       }
+                               */
+                       } 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
+                               check.declareObj(fileScope, pkgScope, obj)
+                       }
+               }
+
+               // resolve identifiers
+               if importErrors {
+                       // don't use the universe scope without correct imports
+                       // (objects in the universe may be shadowed by imports;
+                       // with missing imports, identifiers might get resolved
+                       // incorrectly to universe objects)
+                       pkgScope.Outer = nil
+               }
+               i := 0
+               for _, ident := range file.Unresolved {
+                       if !resolve(fileScope, ident) {
+                               check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
+                               file.Unresolved[i] = ident
+                               i++
+                       }
+
+               }
+               file.Unresolved = file.Unresolved[0:i]
+               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}
+}
index 4e9aa0938d986a0fb35b12b8881e99b7f1146a71..fd2a4a67c12f42a5ed383cf1ffd1e88faa54b30d 100644 (file)
@@ -28,15 +28,19 @@ var sources = []string{
        func f() string {
                return fmt.Sprintf("%d", g())
        }
+       func g() (x int) { return }
        `,
-       `package p
-       import . "go/parser"
-       func g() Mode { return ImportsOnly }`,
+       // TODO(gri) fix this
+       // cannot handle dot-import at the moment
+       /*
+               `package p
+               import . "go/parser"
+               func g() Mode { return ImportsOnly }`,
+       */
 }
 
 var pkgnames = []string{
        "fmt",
-       "go/parser",
        "math",
 }
 
@@ -74,18 +78,17 @@ func ResolveQualifiedIdents(fset *token.FileSet, pkg *ast.Package) error {
 func TestResolveQualifiedIdents(t *testing.T) {
        // parse package files
        fset := token.NewFileSet()
-       files := make(map[string]*ast.File)
+       files := make([]*ast.File, len(sources))
        for i, src := range sources {
-               filename := fmt.Sprintf("file%d", i)
-               f, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors)
+               f, err := parser.ParseFile(fset, "", src, parser.DeclarationErrors)
                if err != nil {
                        t.Fatal(err)
                }
-               files[filename] = f
+               files[i] = f
        }
 
        // resolve package AST
-       pkg, err := ast.NewPackage(fset, files, GcImport, Universe)
+       astpkg, pkg, err := Check(fset, files)
        if err != nil {
                t.Fatal(err)
        }
@@ -97,20 +100,22 @@ func TestResolveQualifiedIdents(t *testing.T) {
                }
        }
 
+       // TODO(gri) fix this
+       // unresolved identifiers are not collected at the moment
        // check that there are no top-level unresolved identifiers
-       for _, f := range pkg.Files {
+       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, pkg); err != nil {
+       if err := ResolveQualifiedIdents(fset, astpkg); err != nil {
                t.Error(err)
        }
 
        // check that qualified identifiers are resolved
-       ast.Inspect(pkg, func(n ast.Node) bool {
+       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 {
index 5a4e81856ea08e27f7c41ce2e7bdb5cccb6bc2dc..69ea32701db2560d46b58bc21838db89d6d1165a 100644 (file)
@@ -91,9 +91,15 @@ type Slice struct {
        Elt Type
 }
 
+// A QualifiedName is a name qualified with the package the declared the name.
+type QualifiedName struct {
+       Pkg  *Package // Pkg.Path == "" for current (non-imported) package
+       Name string   // unqualified type name for anonymous fields
+}
+
 // A Field represents a field of a struct.
 type Field struct {
-       Name        string // unqualified type name for anonymous fields
+       QualifiedName
        Type        Type
        Tag         string
        IsAnonymous bool
@@ -120,12 +126,6 @@ type Pointer struct {
        Base Type
 }
 
-// A Variable represents a variable (including function parameters and results).
-type Var struct {
-       Name string
-       Type Type
-}
-
 // A Result represents a (multi-value) function call result.
 type Result struct {
        implementsType
@@ -183,9 +183,9 @@ type builtin struct {
        isStatement bool // true if the built-in is valid as an expression statement
 }
 
-// A Method represents a method of an interface.
+// A Method represents a method.
 type Method struct {
-       Name string
+       QualifiedName
        Type *Signature
 }
 
@@ -211,8 +211,11 @@ type Chan struct {
 // A NamedType represents a named type as declared in a type declaration.
 type NamedType struct {
        implementsType
-       Obj        *ast.Object // corresponding declared object; Obj.Data.(*ast.Scope) contains methods, if any
+       // TODO(gri) remove obj once we have moved away from ast.Objects
+       obj        *ast.Object // corresponding declared object (current package)
+       Obj        Object      // corresponding declared object (imported 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
index 48a1d61e3bdabb62585cfa88f8f4163119a92529..ef83c840b23d5acecb3454eca7e068f934149c16 100644 (file)
@@ -20,7 +20,8 @@ func makePkg(t *testing.T, src string) (*ast.Package, error) {
        if err != nil {
                return nil, err
        }
-       return Check(fset, map[string]*ast.File{filename: file})
+       astpkg, _, err := Check(fset, []*ast.File{file})
+       return astpkg, err
 }
 
 type testEntry struct {
@@ -153,14 +154,14 @@ var testExprs = []testEntry{
 func TestExprs(t *testing.T) {
        for _, test := range testExprs {
                src := "package p; var _ = " + test.src + "; var (x, y int; s []string; f func(int, float32) int; i interface{}; t interface { foo() })"
-               pkg, err := makePkg(t, src)
+               file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors)
                if err != nil {
                        t.Errorf("%s: %s", src, err)
                        continue
                }
                // TODO(gri) writing the code below w/o the decl variable will
                //           cause a 386 compiler error (out of fixed registers)
-               decl := pkg.Files[filename].Decls[0].(*ast.GenDecl)
+               decl := file.Decls[0].(*ast.GenDecl)
                expr := decl.Specs[0].(*ast.ValueSpec).Values[0]
                str := exprString(expr)
                if str != test.str {
index 1306a59fa50177e5328d75a8767b8e590443da7f..bbc33795d98aa57a9aec6894c0ce442428c026a0 100644 (file)
@@ -12,9 +12,9 @@ import (
 )
 
 var (
-       aType            implementsType
-       Universe, unsafe *ast.Scope
-       Unsafe           *ast.Object // package unsafe
+       aType    implementsType
+       Universe *ast.Scope
+       Unsafe   *Package // package unsafe
 )
 
 // Predeclared types, indexed by BasicKind.
@@ -102,35 +102,31 @@ func init() {
        Universe = ast.NewScope(nil)
 
        // unsafe package and its scope
-       unsafe = ast.NewScope(nil)
-       Unsafe = ast.NewObj(ast.Pkg, "unsafe")
-       Unsafe.Data = unsafe
+       Unsafe = &Package{Name: "unsafe", Scope: new(Scope)}
 
        // predeclared types
        for _, t := range Typ {
-               def(ast.Typ, t.Name).Type = t
+               def(ast.Typ, t.Name, t)
        }
        for _, t := range aliases {
-               def(ast.Typ, t.Name).Type = t
+               def(ast.Typ, t.Name, t)
        }
 
        // error type
        {
-               err := &Method{"Error", &Signature{Results: []*Var{{"", Typ[String]}}}}
-               obj := def(ast.Typ, "error")
-               obj.Type = &NamedType{Underlying: &Interface{Methods: []*Method{err}}, Obj: obj}
+               err := &Method{QualifiedName{Name: "Error"}, &Signature{Results: []*Var{{Name: "", Type: Typ[String]}}}}
+               def(ast.Typ, "error", &NamedType{Underlying: &Interface{Methods: []*Method{err}}})
        }
 
        // predeclared constants
        for _, t := range predeclaredConstants {
-               obj := def(ast.Con, t.name)
-               obj.Type = Typ[t.kind]
+               obj := def(ast.Con, t.name, Typ[t.kind])
                obj.Data = t.val
        }
 
        // predeclared functions
        for _, f := range predeclaredFunctions {
-               def(ast.Fun, f.name).Type = f
+               def(ast.Fun, f.name, f)
        }
 
        universeIota = Universe.Lookup("iota")
@@ -140,19 +136,36 @@ func init() {
 // 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) *ast.Object {
-       obj := ast.NewObj(kind, name)
+func def(kind ast.ObjKind, name string, typ Type) *ast.Object {
        // insert non-internal objects into respective scope
        if strings.Index(name, " ") < 0 {
-               scope := Universe
                // exported identifiers go into package unsafe
                if ast.IsExported(name) {
-                       scope = unsafe
+                       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.obj = obj
+                       }
+                       if Universe.Insert(obj) != nil {
+                               panic("internal error: double declaration")
+                       }
+                       return obj
                }
-               if scope.Insert(obj) != nil {
-                       panic("internal error: double declaration")
-               }
-               obj.Decl = scope
        }
-       return obj
+       return nil
 }