diff --git a/go/types/api.go b/go/types/api.go index c49a79c7..a1f197f6 100644 --- a/go/types/api.go +++ b/go/types/api.go @@ -29,16 +29,15 @@ import ( "code.google.com/p/go.tools/go/exact" ) -// Check type-checks a package and returns the resulting package object, -// or a nil package and the first error. The package is specified by a -// list of *ast.Files and corresponding file set, and the import path -// the package is identified with. The clean path must not be empty or -// dot ("."). +// Check type-checks a package and returns the resulting complete package +// object, or a nil package and the first error. The package is specified +// by a list of *ast.Files and corresponding file set, and the import path +// the package is identified with. The clean path must not be empty or dot ("."). // // For more control over type-checking and results, use Config.Check. func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error) { var conf Config - pkg, err := conf.check(path, fset, files, nil) + pkg, err := conf.Check(path, fset, files, nil) if err != nil { return nil, err } @@ -52,6 +51,12 @@ type Config struct { // type-checked. IgnoreFuncBodies bool + // If FakeImportC is set, `import "C"` (for packages requiring Cgo) + // and expressions with qualified identifiers referrring to package + // C are silently ignored. + // Caution: Effects may be unpredictable - do not use casually! + FakeImportC bool + // If Error != nil, it is called with each error found // during type checking. The error strings of errors with // detailed position information are formatted as follows: @@ -125,12 +130,20 @@ type Info struct { Selections map[*ast.SelectorExpr]*Selection } -// Check type-checks a package and returns the resulting package object, the first -// error if any, and if info != nil, additional type information. The package is -// specified by a list of *ast.Files and corresponding file set, and the import -// path the package is identified with. The clean path must not be empty or dot ("."). +// Check type-checks a package and returns the resulting package object, +// the first error if any, and if info != nil, additional type information. +// The package is marked as complete if no errors occurred, otherwise it is +// incomplete. +// +// The package is specified by a list of *ast.Files and corresponding file +// set, and the import path the package is identified with. The clean path +// must not be empty or dot ("."). func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) { - return conf.check(path, fset, files, info) + pkg, err := conf.check(path, fset, files, info) + if err == nil { + pkg.complete = true + } + return pkg, err } // IsAssignableTo reports whether a value of type V diff --git a/go/types/call.go b/go/types/call.go index d98964fe..6c4e102f 100644 --- a/go/types/call.go +++ b/go/types/call.go @@ -173,12 +173,12 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) { check.recordObject(ident, pkg) exp := pkg.scope.Lookup(sel) if exp == nil { - check.errorf(e.Pos(), "%s not declared by package %s", sel, ident) + if !pkg.fake { + check.errorf(e.Pos(), "%s not declared by package %s", sel, ident) + } goto Error - } else if !exp.IsExported() { - // gcimported package scopes contain non-exported - // objects such as types used in partially exported - // objects - do not accept them + } + if !exp.IsExported() { check.errorf(e.Pos(), "%s not exported by package %s", sel, ident) goto Error } diff --git a/go/types/check.go b/go/types/check.go index 14aff817..e4d00def 100644 --- a/go/types/check.go +++ b/go/types/check.go @@ -17,7 +17,7 @@ import ( // debugging/development support const ( - debug = true // leave on during development + debug = false // leave on during development trace = false // turn on for detailed type resolution traces ) diff --git a/go/types/gcimporter.go b/go/types/gcimporter.go index fa76d46f..ad15cf3b 100644 --- a/go/types/gcimporter.go +++ b/go/types/gcimporter.go @@ -370,7 +370,7 @@ func (p *gcParser) getPkg(id, name string) *Package { } pkg := p.imports[id] if pkg == nil && name != "" { - pkg = NewPackage(token.NoPos, id, name, NewScope(nil), nil, false) + pkg = NewPackage(token.NoPos, id, name, NewScope(nil), nil) p.imports[id] = pkg } return pkg @@ -985,7 +985,7 @@ func (p *gcParser) parseExport() *Package { } // package was imported completely and without errors - pkg.complete = true + pkg.MarkComplete() return pkg } diff --git a/go/types/methodset.go b/go/types/methodset.go index 4733e565..11986207 100644 --- a/go/types/methodset.go +++ b/go/types/methodset.go @@ -28,8 +28,7 @@ func (s *MethodSet) String() string { var buf bytes.Buffer fmt.Fprintln(&buf, "MethodSet {") for _, f := range s.list { - m := f.obj.(*Func) - fmt.Fprintf(&buf, "\t%s -> %s\n", m.Id(), m) + fmt.Fprintln(&buf, f) } fmt.Fprintln(&buf, "}") return buf.String() diff --git a/go/types/objects.go b/go/types/objects.go index cd844f83..552b17f2 100644 --- a/go/types/objects.go +++ b/go/types/objects.go @@ -122,16 +122,24 @@ func (obj *object) sameId(pkg *Package, name string) bool { } // A Package represents the contents (objects) of a Go package. +// +// A package is complete if all its package scope objects are present. +// Incomplete packages may arise via imports where the exported data +// contains only partial information about transitively imported and +// re-exported packages; or as a result of type-checking a package +// that contains errors. +// type Package struct { object path string // import path, "" for current (non-imported) package scope *Scope // imported objects imports map[string]*Package // map of import paths to imported packages - complete bool // if set, this package was imported completely + complete bool // if set, this package is complete + fake bool // if set, this package is fake (internal use only) } -func NewPackage(pos token.Pos, path, name string, scope *Scope, imports map[string]*Package, complete bool) *Package { - obj := &Package{object{nil, pos, nil, name, Typ[Invalid]}, path, scope, imports, complete} +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.pkg = obj return obj } @@ -142,6 +150,9 @@ 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 } +// MarkComplete marks a package as complete. +func (obj *Package) MarkComplete() { obj.complete = true } + // A Const represents a declared constant. type Const struct { object diff --git a/go/types/resolver.go b/go/types/resolver.go index 7846694e..1c248848 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -165,14 +165,22 @@ func (check *checker) resolveFiles(files []*ast.File) { for iota, spec := range d.Specs { switch s := spec.(type) { case *ast.ImportSpec: + var imp *Package path, _ := strconv.Unquote(s.Path.Value) - imp, err := importer(pkg.imports, path) - if imp == nil && err == nil { - err = errors.New("Config.Import returned nil") - } - if err != nil { - check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err) - continue + if path == "C" && check.conf.FakeImportC { + // TODO(gri) shouldn't create a new one each time + imp = NewPackage(token.NoPos, "C", "C", NewScope(nil), nil) + imp.fake = true + } else { + var err error + imp, err = importer(pkg.imports, path) + if imp == nil && err == nil { + err = errors.New("Config.Import returned nil but no error") + } + if err != nil { + check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err) + continue + } } // local name overrides imported package name @@ -185,7 +193,9 @@ func (check *checker) resolveFiles(files []*ast.File) { } } - imp2 := NewPackage(s.Pos(), path, name, imp.scope, nil, imp.complete) + imp2 := NewPackage(s.Pos(), path, name, imp.scope, nil) + imp2.complete = imp.complete + imp2.fake = imp.fake if s.Name != nil { check.recordObject(s.Name, imp2) } else { diff --git a/go/types/universe.go b/go/types/universe.go index c71b6edd..dac6b14f 100644 --- a/go/types/universe.go +++ b/go/types/universe.go @@ -87,7 +87,8 @@ var predeclaredFunctions = [...]*Builtin{ func init() { Universe = NewScope(nil) - Unsafe = NewPackage(token.NoPos, "unsafe", "unsafe", NewScope(Universe), nil, true) + Unsafe = NewPackage(token.NoPos, "unsafe", "unsafe", NewScope(Universe), nil) + Unsafe.complete = true // predeclared types for _, t := range Typ { diff --git a/ssa/interp/reflect.go b/ssa/interp/reflect.go index 51644953..d8294184 100644 --- a/ssa/interp/reflect.go +++ b/ssa/interp/reflect.go @@ -24,7 +24,7 @@ type opaqueType struct { func (t *opaqueType) String() string { return t.name } // A bogus "reflect" type-checker package. Shared across interpreters. -var reflectTypesPackage = types.NewPackage(token.NoPos, "reflect", "reflect", nil, nil, true) +var reflectTypesPackage = types.NewPackage(token.NoPos, "reflect", "reflect", nil, nil) // rtype is the concrete type the interpreter uses to implement the // reflect.Type interface. Since its type is opaque to the target