From 9a9fb35468f58c7041129e21ca236006a8d5e12e Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Tue, 10 Sep 2013 14:11:17 -0400 Subject: [PATCH] go.types/go/types: document primary vs. secondary Package distinction. And: add accessor to get the primary from a secondary Package. This change documents a surprising fact about the current go/types resolver implementation, namely that each ast.ImportSpec import "fmt" creates a new ("secondary") Package object for fmt with the same String, Name, Path and Scope as the canonical ("primary") fmt package, but with a different identity. This change also adds an accessor Package.Primary() that returns the primary package associated with a secondary package object, if any. IMHO the current design is wrong, and the resolver should not create secondary packages at all. Even if a package is imported under a non-default name, as in import f "fmt" ... f.Print we should just regard f as a reference to the existing package "fmt", not as the defining identifier for a secondary package. What we would lose by such a change (the connection of the two f's in 'f.Print' and 'import f "fmt"') seems a small price to pay. This CL is thus just a minimal change to permit clients to make progress under the status quo. R=r, gri, crawshaw CC=golang-dev https://golang.org/cl/13626043 --- go/types/objects.go | 23 ++++++++++++++++++++++- go/types/resolver.go | 1 + 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/go/types/objects.go b/go/types/objects.go index 552b17f28a..a2d5900cc5 100644 --- a/go/types/objects.go +++ b/go/types/objects.go @@ -129,6 +129,20 @@ func (obj *object) sameId(pkg *Package, name string) bool { // re-exported packages; or as a result of type-checking a package // that contains errors. // +// There are two kinds of Package objects, primary and secondary. +// A primary Package has no declaring identifier, and is referenced by +// each ast.File.Name within that package. +// A secondary Package object is created for each ast.ImportSpec that +// imports it; its declaring identifier is the ImportSpec.Name (if +// any), and each qualified reference (e.g. fmt.Println) is a +// reference to a secondary Package. +// The Primary() method of a secondary package returns its primary +// package; called on a primary package, it returns nil. +// The Path(), Name() and Scope() attributes of primary and secondary +// Packages are equal. +// TODO(gri): consider whether this distinction carries its weight; +// adonovan thinks not. +// type Package struct { object path string // import path, "" for current (non-imported) package @@ -136,10 +150,11 @@ type Package struct { imports map[string]*Package // map of import paths to imported packages complete bool // if set, this package is complete fake bool // if set, this package is fake (internal use only) + primary *Package // associated primary package (nil => self) } func NewPackage(pos token.Pos, path, name string, scope *Scope, imports map[string]*Package) *Package { - obj := &Package{object{nil, pos, nil, name, Typ[Invalid]}, path, scope, imports, false, false} + obj := &Package{object{nil, pos, nil, name, Typ[Invalid]}, path, scope, imports, false, false, nil} obj.pkg = obj return obj } @@ -149,6 +164,12 @@ func (obj *Package) Path() string { return obj.path } func (obj *Package) Scope() *Scope { return obj.scope } func (obj *Package) Imports() map[string]*Package { return obj.imports } func (obj *Package) Complete() bool { return obj.complete } +func (obj *Package) Primary() *Package { + if obj.primary != nil { + return obj.primary + } + return obj +} // MarkComplete marks a package as complete. func (obj *Package) MarkComplete() { obj.complete = true } diff --git a/go/types/resolver.go b/go/types/resolver.go index 020159891e..c92a67478e 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -192,6 +192,7 @@ func (check *checker) resolveFiles(files []*ast.File) { } imp2 := NewPackage(s.Pos(), path, name, imp.scope, nil) + imp2.primary = imp imp2.complete = imp.complete imp2.fake = imp.fake if s.Name != nil {