diff --git a/go/types/resolver.go b/go/types/resolver.go index 0a8d003d78..f9f15cb7f3 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -5,6 +5,7 @@ package types import ( + "errors" "go/ast" "go/token" "strconv" @@ -104,6 +105,9 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) { } path, _ := strconv.Unquote(s.Path.Value) imp, err := importer(pkg.imports, path) + if imp == nil && err == nil { + err = errors.New("Context.Import returned niil") + } if err != nil { check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err) continue diff --git a/importer/importer.go b/importer/importer.go new file mode 100644 index 0000000000..19355b61e3 --- /dev/null +++ b/importer/importer.go @@ -0,0 +1,184 @@ +// Package importer defines the Importer, which loads, parses and +// type-checks packages of Go code plus their transitive closure, and +// retains both the ASTs and the derived facts. +// +// TODO(adonovan): document and test this package better, with examples. +// Currently it's covered by the ssa/ tests. +// +package importer + +import ( + "go/ast" + "go/token" + + "code.google.com/p/go.tools/go/exact" + "code.google.com/p/go.tools/go/types" +) + +// An Importer's methods are not thread-safe. +type Importer struct { + context *Context // the client context + Fset *token.FileSet // position info for all files seen + Packages map[string]*PackageInfo // keys are import paths + errors map[string]error // cache of errors by import path +} + +// Context specifies the supporting context for the importer. +// +type Context struct { + // TypeChecker contains options relating to the type checker. + // The Importer will override any user-supplied values for its + // Expr, Ident, ImplicitObj and Import fields; other fields + // will be passed through to the type checker. + TypeChecker types.Context + + // If Loader is non-nil, it is used to satisfy imports. + // + // If it is nil, binary object files produced by the gc + // compiler will be loaded instead of source code for all + // imported packages. Such files supply only the types of + // package-level declarations and values of constants, but no + // code, so this mode will not yield a whole program. It is + // intended for analyses that perform intraprocedural analysis + // of a single package. + Loader SourceLoader +} + +// SourceLoader is the signature of a function that locates, reads and +// parses a set of source files given an import path. +// +// fset is the fileset to which the ASTs should be added. +// path is the imported path, e.g. "sync/atomic". +// +// On success, the function returns files, the set of ASTs produced, +// or the first error encountered. +// +// The MakeGoBuildLoader utility can be used to construct a +// SourceLoader based on go/build. +// +type SourceLoader func(fset *token.FileSet, path string) (files []*ast.File, err error) + +// New returns a new, empty Importer using configuration options +// specified by context. +// +func New(context *Context) *Importer { + imp := &Importer{ + context: context, + Fset: token.NewFileSet(), + Packages: make(map[string]*PackageInfo), + errors: make(map[string]error), + } + imp.context.TypeChecker.Import = imp.doImport + return imp +} + +// doImport loads the typechecker package identified by path +// Implements the types.Importer prototype. +// +func (imp *Importer) doImport(imports map[string]*types.Package, path string) (pkg *types.Package, err error) { + // Package unsafe is handled specially, and has no PackageInfo. + if path == "unsafe" { + return types.Unsafe, nil + } + + if info, ok := imp.Packages[path]; ok { + imports[path] = info.Pkg + pkg = info.Pkg + return // positive cache hit + } + + if err = imp.errors[path]; err != nil { + return // negative cache hit + } + + // Load the source/binary for 'path', type-check it, construct + // a PackageInfo and update our map (imp.Packages) and the + // type-checker's map (imports). + var info *PackageInfo + if imp.context.Loader != nil { + info, err = imp.LoadPackage(path) + } else if pkg, err = types.GcImport(imports, path); err == nil { + info = &PackageInfo{Pkg: pkg} + imp.Packages[path] = info + } + + if err == nil { + // Cache success. + pkg = info.Pkg + imports[path] = pkg + return pkg, nil + } + + // Cache failure + imp.errors[path] = err + return nil, err +} + +// LoadPackage loads the package of the specified import-path, +// performs type-checking, and returns the corresponding +// PackageInfo. +// +// Not idempotent! +// Precondition: Importer.context.Loader != nil. +// Not thread-safe! +// TODO(adonovan): rethink this API. +// +func (imp *Importer) LoadPackage(importPath string) (*PackageInfo, error) { + if imp.context.Loader == nil { + panic("Importer.LoadPackage without a SourceLoader") + } + files, err := imp.context.Loader(imp.Fset, importPath) + if err != nil { + return nil, err + } + return imp.CreateSourcePackage(importPath, files) +} + +// CreateSourcePackage invokes the type-checker on files and returns a +// PackageInfo containing the resulting type-checker package, the +// ASTs, and other type information. +// +// The order of files determines the package initialization order. +// +// importPath is the full name under which this package is known, such +// as appears in an import declaration. e.g. "sync/atomic". +// +// The ParseFiles utility may be helpful for parsing a set of Go +// source files. +// +func (imp *Importer) CreateSourcePackage(importPath string, files []*ast.File) (*PackageInfo, error) { + info := &PackageInfo{ + Files: files, + types: make(map[ast.Expr]types.Type), + idents: make(map[*ast.Ident]types.Object), + constants: make(map[ast.Expr]exact.Value), + typecases: make(map[*ast.CaseClause]*types.Var), + } + tc := imp.context.TypeChecker + tc.Expr = func(x ast.Expr, typ types.Type, val exact.Value) { + info.types[x] = typ + if val != nil { + info.constants[x] = val + } + } + tc.Ident = func(ident *ast.Ident, obj types.Object) { + // Invariants: + // - obj is non-nil. + // - isBlankIdent(ident) <=> obj.GetType()==nil + info.idents[ident] = obj + } + tc.ImplicitObj = func(node ast.Node, obj types.Object) { + if cc, ok := node.(*ast.CaseClause); ok { + info.typecases[cc] = obj.(*types.Var) + } + } + var firstErr error + info.Pkg, firstErr = tc.Check(importPath, imp.Fset, files...) + tc.Expr = nil + tc.Ident = nil + if firstErr != nil { + return nil, firstErr + } + imp.Packages[importPath] = info + return info, nil +} diff --git a/ssa/typeinfo.go b/importer/pkginfo.go similarity index 75% rename from ssa/typeinfo.go rename to importer/pkginfo.go index 3bc7566f1f..30d12f575e 100644 --- a/ssa/typeinfo.go +++ b/importer/pkginfo.go @@ -1,28 +1,33 @@ -package ssa +package importer -// This file defines utilities for querying the results of typechecker: -// types of expressions, values of constant expressions, referents of identifiers. +// TODO(gri): absorb this into go/types. import ( + "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" - "fmt" "go/ast" - "go/token" ) -// TypeInfo contains information provided by the type checker about -// the abstract syntax for a single package. -type TypeInfo struct { - fset *token.FileSet +// PackageInfo holds the ASTs and facts derived by the type-checker +// for a single package. +// +// Not mutated once constructed. +// +type PackageInfo struct { + Pkg *types.Package + Files []*ast.File // abstract syntax for the package's files + + // Type-checker deductions. types map[ast.Expr]types.Type // inferred types of expressions - constants map[ast.Expr]*Literal // values of constant expressions - idents map[*ast.Ident]types.Object // canonical type objects for named entities + constants map[ast.Expr]exact.Value // values of constant expressions + idents map[*ast.Ident]types.Object // resoved objects for named entities typecases map[*ast.CaseClause]*types.Var // implicit vars for single-type typecases } // TypeOf returns the type of expression e. // Precondition: e belongs to the package's ASTs. -func (info *TypeInfo) TypeOf(e ast.Expr) types.Type { +// +func (info *PackageInfo) TypeOf(e ast.Expr) types.Type { // For Ident, b.types may be more specific than // b.obj(id.(*ast.Ident)).GetType(), // e.g. in the case of typeswitch. @@ -40,21 +45,18 @@ func (info *TypeInfo) TypeOf(e ast.Expr) types.Type { panic("no type for expression") } -// ValueOf returns the value of expression e if it is a constant, -// nil otherwise. +// ValueOf returns the value of expression e if it is a constant, nil +// otherwise. // -func (info *TypeInfo) ValueOf(e ast.Expr) *Literal { +func (info *PackageInfo) ValueOf(e ast.Expr) exact.Value { return info.constants[e] } // ObjectOf returns the typechecker object denoted by the specified id. // Precondition: id belongs to the package's ASTs. // -func (info *TypeInfo) ObjectOf(id *ast.Ident) types.Object { - if obj, ok := info.idents[id]; ok { - return obj - } - panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s", id.Name, info.fset.Position(id.Pos()))) +func (info *PackageInfo) ObjectOf(id *ast.Ident) types.Object { + return info.idents[id] } // IsType returns true iff expression e denotes a type. @@ -62,10 +64,10 @@ func (info *TypeInfo) ObjectOf(id *ast.Ident) types.Object { // e must be a true expression, not a KeyValueExpr, or an Ident // appearing in a SelectorExpr or declaration. // -func (info *TypeInfo) IsType(e ast.Expr) bool { +func (info *PackageInfo) IsType(e ast.Expr) bool { switch e := e.(type) { case *ast.SelectorExpr: // pkg.Type - if obj := info.isPackageRef(e); obj != nil { + if obj := info.IsPackageRef(e); obj != nil { _, isType := obj.(*types.TypeName) return isType } @@ -82,12 +84,12 @@ func (info *TypeInfo) IsType(e ast.Expr) bool { return false } -// isPackageRef returns the identity of the object if sel is a +// IsPackageRef returns the identity of the object if sel is a // package-qualified reference to a named const, var, func or type. // Otherwise it returns nil. // Precondition: sel belongs to the package's ASTs. // -func (info *TypeInfo) isPackageRef(sel *ast.SelectorExpr) types.Object { +func (info *PackageInfo) IsPackageRef(sel *ast.SelectorExpr) types.Object { if id, ok := sel.X.(*ast.Ident); ok { if pkg, ok := info.ObjectOf(id).(*types.Package); ok { return pkg.Scope().Lookup(nil, sel.Sel.Name) @@ -96,7 +98,22 @@ func (info *TypeInfo) isPackageRef(sel *ast.SelectorExpr) types.Object { return nil } -// builtinCallSignature returns a new Signature describing the +// TypeCaseVar returns the implicit variable created by a single-type +// case clause in a type switch, or nil if not found. +// +func (info *PackageInfo) TypeCaseVar(cc *ast.CaseClause) *types.Var { + return info.typecases[cc] +} + +var ( + tEface = new(types.Interface) + tComplex64 = types.Typ[types.Complex64] + tComplex128 = types.Typ[types.Complex128] + tFloat32 = types.Typ[types.Float32] + tFloat64 = types.Typ[types.Float64] +) + +// BuiltinCallSignature returns a new Signature describing the // effective type of a builtin operator for the particular call e. // // This requires ad-hoc typing rules for all variadic (append, print, @@ -108,11 +125,11 @@ func (info *TypeInfo) isPackageRef(sel *ast.SelectorExpr) types.Object { // The returned Signature is degenerate and only intended for use by // emitCallArgs. // -func builtinCallSignature(info *TypeInfo, e *ast.CallExpr) *types.Signature { +func (info *PackageInfo) BuiltinCallSignature(e *ast.CallExpr) *types.Signature { var params []*types.Var var isVariadic bool - switch builtin := noparens(e.Fun).(*ast.Ident).Name; builtin { + switch builtin := unparen(e.Fun).(*ast.Ident).Name; builtin { case "append": var t0, t1 types.Type t0 = info.TypeOf(e) // infer arg[0] type from result type diff --git a/ssa/importer.go b/importer/util.go similarity index 51% rename from ssa/importer.go rename to importer/util.go index 0e1ebc2f09..6ea57438a3 100644 --- a/ssa/importer.go +++ b/importer/util.go @@ -1,8 +1,7 @@ -package ssa +package importer -// This file defines an implementation of the types.Importer interface -// (func) that loads the transitive closure of dependencies of a -// "main" package. +// This file defines various utility functions exposed by the package +// and used by it. import ( "errors" @@ -13,60 +12,44 @@ import ( "os" "path/filepath" "strings" - - "code.google.com/p/go.tools/go/types" ) -// Prototype of a function that locates, reads and parses a set of -// source files given an import path. +// CreatePackageFromArgs builds an initial Package from a list of +// command-line arguments. +// If args is a list of *.go files, they are parsed and type-checked. +// If args is a Go package import path, that package is imported. +// The rest result contains the suffix of args that were not consumed. // -// fset is the fileset to which the ASTs should be added. -// path is the imported path, e.g. "sync/atomic". +// This utility is provided to facilitate construction of command-line +// tools with a consistent user interface. // -// On success, the function returns files, the set of ASTs produced, -// or the first error encountered. -// -type SourceLoader func(fset *token.FileSet, path string) (files []*ast.File, err error) +func CreatePackageFromArgs(imp *Importer, args []string) (info *PackageInfo, rest []string, err error) { + switch { + case len(args) == 0: + return nil, nil, errors.New("No *.go source files nor package name was specified.") -// doImport loads the typechecker package identified by path -// Implements the types.Importer prototype. -// -func (b *Builder) doImport(imports map[string]*types.Package, path string) (typkg *types.Package, err error) { - // Package unsafe is handled specially, and has no ssa.Package. - if path == "unsafe" { - return types.Unsafe, nil - } - - if pkg := b.Prog.Packages[path]; pkg != nil { - typkg = pkg.Types - imports[path] = typkg - return // positive cache hit - } - - if err = b.importErrs[path]; err != nil { - return // negative cache hit - } - var files []*ast.File - var info *TypeInfo - if b.Context.Mode&UseGCImporter != 0 { - typkg, err = types.GcImport(imports, path) - } else { - files, err = b.Context.Loader(b.Prog.Files, path) - if err == nil { - typkg, info, err = b.typecheck(path, files) + case strings.HasSuffix(args[0], ".go"): + // % tool a.go b.go ... + // Leading consecutive *.go arguments constitute main package. + i := 1 + for ; i < len(args) && strings.HasSuffix(args[i], ".go"); i++ { } - } - if err != nil { - // Cache failure - b.importErrs[path] = err - return nil, err + var files []*ast.File + files, err = ParseFiles(imp.Fset, ".", args[:i]...) + rest = args[i:] + if err == nil { + info, err = imp.CreateSourcePackage("main", files) + } + + default: + // % tool my/package ... + // First argument is import path of main package. + pkgname := args[0] + info, err = imp.LoadPackage(pkgname) + rest = args[1:] } - // Cache success - imports[path] = typkg // cache for just this package. - b.Prog.Packages[path] = b.createPackageImpl(typkg, path, files, info) // cache across all packages - - return typkg, nil + return } // MakeGoBuildLoader returns an implementation of the SourceLoader @@ -121,42 +104,20 @@ func ParseFiles(fset *token.FileSet, dir string, files ...string) (parsed []*ast return } -// CreatePackageFromArgs builds an initial Package from a list of -// command-line arguments. -// If args is a list of *.go files, they are parsed and type-checked. -// If args is a Go package import path, that package is imported. -// rest is the suffix of args that were not consumed. -// -// This utility is provided to facilitate construction of command-line -// tools with a consistent user interface. -// -func CreatePackageFromArgs(builder *Builder, args []string) (pkg *Package, rest []string, err error) { - var pkgname string - var files []*ast.File +// ---------- Internal helpers ---------- - switch { - case len(args) == 0: - err = errors.New("No *.go source files nor package name was specified.") - - case strings.HasSuffix(args[0], ".go"): - // % tool a.go b.go ... - // Leading consecutive *.go arguments constitute main package. - pkgname = "main" - i := 1 - for ; i < len(args) && strings.HasSuffix(args[i], ".go"); i++ { +// unparen returns e with any enclosing parentheses stripped. +func unparen(e ast.Expr) ast.Expr { + for { + p, ok := e.(*ast.ParenExpr) + if !ok { + break } - files, err = ParseFiles(builder.Prog.Files, ".", args[:i]...) - rest = args[i:] - - default: - // % tool my/package ... - // First argument is import path of main package. - pkgname = args[0] - rest = args[1:] - files, err = builder.Context.Loader(builder.Prog.Files, pkgname) + e = p.X } - if err == nil { - pkg, err = builder.CreatePackage(pkgname, files) - } - return + return e +} + +func unreachable() { + panic("unreachable") } diff --git a/ssa/builder.go b/ssa/builder.go index 21b2ce043b..57852cd86e 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -32,6 +32,7 @@ import ( "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" + "code.google.com/p/go.tools/importer" ) type opaqueType struct { @@ -47,10 +48,6 @@ var ( // Type constants. tBool = types.Typ[types.Bool] tByte = types.Typ[types.Byte] - tFloat32 = types.Typ[types.Float32] - tFloat64 = types.Typ[types.Float64] - tComplex64 = types.Typ[types.Complex64] - tComplex128 = types.Typ[types.Complex128] tInt = types.Typ[types.Int] tInvalid = types.Typ[types.Invalid] tUntypedNil = types.Typ[types.UntypedNil] @@ -72,33 +69,10 @@ var ( // A Context specifies the supporting context for SSA construction. // -// TODO(adonovan): make it so empty => default behaviours? -// Currently not the case for Loader. -// type Context struct { // Mode is a bitfield of options controlling verbosity, // logging and additional sanity checks. Mode BuilderMode - - // Loader is a SourceLoader function that finds, loads and - // parses Go source files for a given import path. (It is - // ignored if the mode bits include UseGCImporter.) - // See (e.g.) MakeGoBuildLoader. - Loader SourceLoader - - // RetainAST is an optional user predicate that determines - // whether to retain (true) or discard (false) the AST and its - // type information for each package after BuildPackage has - // finished. - // Implementations must be thread-safe. - // If RetainAST is nil, all ASTs and TypeInfos are discarded. - RetainAST func(*Package) bool - - // TypeChecker contains options relating to the type checker. - // The SSA Builder will override any user-supplied values for - // its Expr, Ident and Import fields; other fields will be - // passed through to the type checker. - TypeChecker types.Context } // BuilderMode is a bitmask of options for diagnostics and checking. @@ -109,63 +83,52 @@ const ( LogFunctions // Dump function SSA code to stderr LogSource // Show source locations as SSA builder progresses SanityCheckFunctions // Perform sanity checking of function bodies - UseGCImporter // Ignore SourceLoader; use gc-compiled object code for all imports NaiveForm // Build naïve SSA form: don't replace local loads/stores with registers BuildSerially // Build packages serially, not in parallel. ) -// A Builder creates the SSA representation of a single program. -// Instances may be created using NewBuilder. +// A Builder builds the SSA representation of a single Go program: a +// Program containing one or more Packages. // -// The SSA Builder constructs a Program containing Package instances -// for packages of Go source code, loading, parsing and recursively -// constructing packages for all imported dependencies as well. -// -// If the UseGCImporter mode flag is specified, binary object files -// produced by the gc compiler will be loaded instead of source code -// for all imported packages. Such files supply only the types of -// package-level declarations and values of constants, but no code, so -// this mode will not yield a whole program. It is intended for -// analyses that perform intraprocedural analysis of a single package. -// -// A typical client will create a Builder with NewBuilder; call -// CreatePackage for the "root" package(s), e.g. main; then call -// BuildPackage on the same set of packages to construct SSA-form code -// for functions and methods. After that, the representation of the -// program (Builder.Prog) is complete and transitively closed, and the -// Builder object can be discarded to reclaim its memory. The -// client's analysis may then begin. +// Use NewBuilder to create a Builder. // type Builder struct { Prog *Program // the program being built Context *Context // the client context - importErrs map[string]error // across-packages import cache of failures - packages map[*types.Package]*Package // SSA packages by types.Package - globals map[types.Object]Value // all package-level funcs and vars, and universal built-ins + imp *importer.Importer // ASTs and types for loaded packages + packages map[*types.Package]*Package // SSA packages by types.Package + globals map[types.Object]Value // all package-level funcs and vars, and universal built-ins } // NewBuilder creates and returns a new SSA builder with options // specified by context. // -func NewBuilder(context *Context) *Builder { +// For each package loaded by imp, a new SSA Package is created for it +// and added to the Program. All such packages must be error-free. +// +// A typical client will create a Builder with NewBuilder; this causes +// all SSA Packages to be created; then call BuildPackage or +// BuildAllPackages to construct SSA-form code for all functions and +// methods in one or more packages. After that, the representation of +// the program (Builder.Prog) is complete and transitively closed, and +// the Builder and Importer objects can be discarded. The client's +// analysis may then begin. +// +func NewBuilder(context *Context, imp *importer.Importer) *Builder { b := &Builder{ Prog: &Program{ - Files: token.NewFileSet(), + Files: imp.Fset, Packages: make(map[string]*Package), Builtins: make(map[types.Object]*Builtin), methodSets: make(map[types.Type]MethodSet), concreteMethods: make(map[*types.Func]*Function), mode: context.Mode, }, - Context: context, - globals: make(map[types.Object]Value), - importErrs: make(map[string]error), - packages: make(map[*types.Package]*Package), - } - - b.Context.TypeChecker.Import = func(imports map[string]*types.Package, path string) (pkg *types.Package, err error) { - return b.doImport(imports, path) + Context: context, + imp: imp, + globals: make(map[types.Object]Value), + packages: make(map[*types.Package]*Package), } // Create Values for built-in functions. @@ -177,9 +140,39 @@ func NewBuilder(context *Context) *Builder { b.Prog.Builtins[obj] = v } } + + // Create ssa.Package for each types.Package. + for path, info := range imp.Packages { + p := b.createPackage(info) + b.Prog.Packages[path] = p + b.packages[p.Types] = p + } + + // Compute the method sets, now that we have all packages' methods. + for _, pkg := range b.Prog.Packages { + for _, mem := range pkg.Members { + switch t := mem.(type) { + case *Type: + t.Methods = b.Prog.MethodSet(t.NamedType) + t.PtrMethods = b.Prog.MethodSet(pointer(t.NamedType)) + } + } + } + return b } +// PackageFor returns the ssa.Package corresponding to the specified +// types.Package, or nil if this builder has not created such a +// package. +// +// TODO(adonovan): better name? +// TODO(adonovan): can we make this a method of Program? +// +func (b *Builder) PackageFor(p *types.Package) *Package { + return b.packages[p] +} + // lookup returns the package-level *Function or *Global (or universal // *Builtin) for the named object obj. // @@ -325,11 +318,11 @@ func (b *Builder) exprN(fn *Function, e ast.Expr) Value { // cases for single-valued CallExpr. var c Call b.setCall(fn, e, &c.Call) - c.typ = fn.Pkg.TypeOf(e) + c.typ = fn.Pkg.typeOf(e) return fn.emit(&c) case *ast.IndexExpr: - mapt := fn.Pkg.TypeOf(e.X).Underlying().(*types.Map) + mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map) typ = mapt.Elem() lookup := &Lookup{ X: b.expr(fn, e.X), @@ -340,10 +333,10 @@ func (b *Builder) exprN(fn *Function, e ast.Expr) Value { tuple = fn.emit(lookup) case *ast.TypeAssertExpr: - return emitTypeTest(fn, b.expr(fn, e.X), fn.Pkg.TypeOf(e)) + return emitTypeTest(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e)) case *ast.UnaryExpr: // must be receive <- - typ = fn.Pkg.TypeOf(e.X).Underlying().(*types.Chan).Elem() + typ = fn.Pkg.typeOf(e.X).Underlying().(*types.Chan).Elem() unop := &UnOp{ Op: token.ARROW, X: b.expr(fn, e.X), @@ -425,7 +418,7 @@ func (b *Builder) builtin(fn *Function, name string, args []ast.Expr, typ types. // We must still evaluate the value, though. (If it // was side-effect free, the whole call would have // been constant-folded.) - t := fn.Pkg.TypeOf(args[0]).Deref().Underlying() + t := fn.Pkg.typeOf(args[0]).Deref().Underlying() if at, ok := t.(*types.Array); ok { b.expr(fn, args[0]) // for effects only return intLiteral(at.Len()) @@ -459,12 +452,12 @@ func (b *Builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping Bindings: []Value{recv}, } c.setPos(e.Sel.Pos()) - c.setType(fn.Pkg.TypeOf(e)) + c.setType(fn.Pkg.typeOf(e)) return fn.emit(c) } } - st := fn.Pkg.TypeOf(e.X).Deref().Underlying().(*types.Struct) + st := fn.Pkg.typeOf(e.X).Deref().Underlying().(*types.Struct) index := -1 for i, n := 0, st.NumFields(); i < n; i++ { f := st.Field(i) @@ -481,7 +474,7 @@ func (b *Builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping panic("field not found, even with promotion: " + e.Sel.Name) } } - fieldType := fn.Pkg.TypeOf(e) + fieldType := fn.Pkg.typeOf(e) pos := e.Sel.Pos() if wantAddr { return b.fieldAddr(fn, e.X, path, index, fieldType, pos, escaping) @@ -506,7 +499,7 @@ func (b *Builder) fieldAddr(fn *Function, base ast.Expr, path *anonFieldPath, in x = b.fieldExpr(fn, base, path.tail, path.index, path.field.Type, token.NoPos) } } else { - switch fn.Pkg.TypeOf(base).Underlying().(type) { + switch fn.Pkg.typeOf(base).Underlying().(type) { case *types.Struct: x = b.addr(fn, base, escaping).(address).addr case *types.Pointer: @@ -584,7 +577,7 @@ func (b *Builder) fieldExpr(fn *Function, base ast.Expr, path *anonFieldPath, in func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { switch e := e.(type) { case *ast.Ident: - obj := fn.Pkg.ObjectOf(e) + obj := fn.Pkg.objectOf(e) v, ok := b.lookup(fn.Pkg, obj) // var (address) if !ok { v = fn.lookup(obj, escaping) @@ -592,7 +585,7 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { return address{addr: v} case *ast.CompositeLit: - t := fn.Pkg.TypeOf(e).Deref() + t := fn.Pkg.typeOf(e).Deref() var v Value if escaping { v = emitNew(fn, t, e.Lbrace) @@ -607,7 +600,7 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { case *ast.SelectorExpr: // p.M where p is a package. - if obj := fn.Pkg.isPackageRef(e); obj != nil { + if obj := fn.Pkg.info.IsPackageRef(e); obj != nil { if v, ok := b.lookup(fn.Pkg, obj); ok { return address{addr: v} } @@ -620,7 +613,7 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { case *ast.IndexExpr: var x Value var et types.Type - switch t := fn.Pkg.TypeOf(e.X).Underlying().(type) { + switch t := fn.Pkg.typeOf(e.X).Underlying().(type) { case *types.Array: x = b.addr(fn, e.X, escaping).(address).addr et = pointer(t.Elem()) @@ -688,8 +681,8 @@ func (b *Builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) { // to fn and returning the Value defined by the expression. // func (b *Builder) expr(fn *Function, e ast.Expr) Value { - if lit := fn.Pkg.ValueOf(e); lit != nil { - return lit + if v := fn.Pkg.info.ValueOf(e); v != nil { + return newLiteral(v, fn.Pkg.typeOf(e)) } switch e := e.(type) { @@ -700,7 +693,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { posn := b.Prog.Files.Position(e.Type.Func) fn2 := &Function{ name: fmt.Sprintf("func@%d.%d", posn.Line, posn.Column), - Signature: fn.Pkg.TypeOf(e.Type).Underlying().(*types.Signature), + Signature: fn.Pkg.typeOf(e.Type).Underlying().(*types.Signature), pos: e.Type.Func, Enclosing: fn, Pkg: fn.Pkg, @@ -717,7 +710,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { return fn2 } v := &MakeClosure{Fn: fn2} - v.setType(fn.Pkg.TypeOf(e)) + v.setType(fn.Pkg.typeOf(e)) for _, fv := range fn2.FreeVars { v.Bindings = append(v.Bindings, fv.outer) fv.outer = nil @@ -728,11 +721,11 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { return b.expr(fn, e.X) case *ast.TypeAssertExpr: // single-result form only - return emitTypeAssert(fn, b.expr(fn, e.X), fn.Pkg.TypeOf(e)) + return emitTypeAssert(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e)) case *ast.CallExpr: - typ := fn.Pkg.TypeOf(e) - if fn.Pkg.IsType(e.Fun) { + typ := fn.Pkg.typeOf(e) + if fn.Pkg.info.IsType(e.Fun) { // Explicit type conversion, e.g. string(x) or big.Int(x) x := b.expr(fn, e.Args[0]) y := emitConv(fn, x, typ) @@ -750,7 +743,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { } // Call to "intrinsic" built-ins, e.g. new, make, panic. if id, ok := e.Fun.(*ast.Ident); ok { - obj := fn.Pkg.ObjectOf(id) + obj := fn.Pkg.objectOf(id) if _, ok := fn.Prog.Builtins[obj]; ok { if v := b.builtin(fn, id.Name, e.Args, typ, e.Lparen); v != nil { return v @@ -775,7 +768,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { X: b.expr(fn, e.X), } v.setPos(e.OpPos) - v.setType(fn.Pkg.TypeOf(e)) + v.setType(fn.Pkg.typeOf(e)) return fn.emit(v) default: panic(e.Op) @@ -788,7 +781,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { case token.SHL, token.SHR: fallthrough case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT: - return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), fn.Pkg.TypeOf(e), e.OpPos) + return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), fn.Pkg.typeOf(e), e.OpPos) case token.EQL, token.NEQ, token.GTR, token.LSS, token.LEQ, token.GEQ: return emitCompare(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), e.OpPos) @@ -799,7 +792,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { case *ast.SliceExpr: var low, high Value var x Value - switch fn.Pkg.TypeOf(e.X).Underlying().(type) { + switch fn.Pkg.typeOf(e.X).Underlying().(type) { case *types.Array: // Potentially escaping. x = b.addr(fn, e.X, true).(address).addr @@ -819,11 +812,11 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { Low: low, High: high, } - v.setType(fn.Pkg.TypeOf(e)) + v.setType(fn.Pkg.typeOf(e)) return fn.emit(v) case *ast.Ident: - obj := fn.Pkg.ObjectOf(e) + obj := fn.Pkg.objectOf(e) // Global or universal? if v, ok := b.lookup(fn.Pkg, obj); ok { if _, ok := obj.(*types.Var); ok { @@ -836,14 +829,14 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { case *ast.SelectorExpr: // p.M where p is a package. - if obj := fn.Pkg.isPackageRef(e); obj != nil { + if obj := fn.Pkg.info.IsPackageRef(e); obj != nil { return b.expr(fn, e.Sel) } // (*T).f or T.f, the method f from the method-set of type T. - if fn.Pkg.IsType(e.X) { + if fn.Pkg.info.IsType(e.X) { id := MakeId(e.Sel.Name, fn.Pkg.Types) - typ := fn.Pkg.TypeOf(e.X) + typ := fn.Pkg.typeOf(e.X) if m := b.Prog.MethodSet(typ)[id]; m != nil { return m } @@ -856,7 +849,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { return b.selector(fn, e, false, false) case *ast.IndexExpr: - switch t := fn.Pkg.TypeOf(e.X).Underlying().(type) { + switch t := fn.Pkg.typeOf(e.X).Underlying().(type) { case *types.Array: // Non-addressable array (in a register). v := &Index{ @@ -868,7 +861,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { case *types.Map: // Maps are not addressable. - mapt := fn.Pkg.TypeOf(e.X).Underlying().(*types.Map) + mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map) v := &Lookup{ X: b.expr(fn, e.X), Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()), @@ -918,7 +911,7 @@ func (b *Builder) stmtList(fn *Function, list []ast.Stmt) { // findMethod returns (nil, nil) if no such method was found. // func (b *Builder) findMethod(fn *Function, base ast.Expr, id Id) (*Function, Value) { - typ := fn.Pkg.TypeOf(base) + typ := fn.Pkg.typeOf(base) // Consult method-set of X. if m := b.Prog.MethodSet(typ)[id]; m != nil { @@ -954,7 +947,7 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { c.HasEllipsis = e.Ellipsis != 0 // Is the call of the form x.f()? - sel, ok := noparens(e.Fun).(*ast.SelectorExpr) + sel, ok := unparen(e.Fun).(*ast.SelectorExpr) // Case 0: e.Fun evaluates normally to a function. if !ok { @@ -963,7 +956,7 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { } // Case 1: call of form x.F() where x is a package name. - if obj := fn.Pkg.isPackageRef(sel); obj != nil { + if obj := fn.Pkg.info.IsPackageRef(sel); obj != nil { // This is a specialization of expr(ast.Ident(obj)). if v, ok := b.lookup(fn.Pkg, obj); ok { if _, ok := v.(*Function); !ok { @@ -980,7 +973,7 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { // an interface. Treat like case 0. // TODO(adonovan): opt: inline expr() here, to make the call static // and to avoid generation of a stub for an interface method. - if fn.Pkg.IsType(sel.X) { + if fn.Pkg.info.IsType(sel.X) { c.Func = b.expr(fn, e.Fun) return } @@ -998,7 +991,7 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { return } - switch t := fn.Pkg.TypeOf(sel.X).Underlying().(type) { + switch t := fn.Pkg.typeOf(sel.X).Underlying().(type) { case *types.Struct, *types.Pointer: // Case 3: x.f() where x.f is a function value in a // struct field f; not a method call. f is a 'var' @@ -1102,9 +1095,9 @@ func (b *Builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) { b.setCallFunc(fn, e, c) // Then append the other actual parameters. - sig, _ := fn.Pkg.TypeOf(e.Fun).Underlying().(*types.Signature) + sig, _ := fn.Pkg.typeOf(e.Fun).Underlying().(*types.Signature) if sig == nil { - sig = builtinCallSignature(fn.Pkg.TypeInfo, e) + sig = fn.Pkg.info.BuiltinCallSignature(e) } c.Args = b.emitCallArgs(fn, sig, e, c.Args) } @@ -1180,7 +1173,7 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global var lval lvalue = blank{} if g != nil { // Mode A: initialized only a single global, g - if isBlankIdent(id) || init.Pkg.ObjectOf(id) != obj { + if isBlankIdent(id) || init.Pkg.objectOf(id) != obj { continue } g.spec = nil @@ -1188,7 +1181,7 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global } else { // Mode B: initialize all globals. if !isBlankIdent(id) { - g2 := b.globals[init.Pkg.ObjectOf(id)].(*Global) + g2 := b.globals[init.Pkg.objectOf(id)].(*Global) if g2.spec == nil { continue // already done } @@ -1222,7 +1215,7 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global result := tuple.Type().(*types.Tuple) for i, id := range spec.Names { if !isBlankIdent(id) { - g := b.globals[init.Pkg.ObjectOf(id)].(*Global) + g := b.globals[init.Pkg.objectOf(id)].(*Global) g.spec = nil // just an optimization emitStore(init, g, emitExtract(init, tuple, i, result.At(i).Type())) } @@ -1246,7 +1239,7 @@ func (b *Builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { for i, id := range spec.Names { var lval lvalue = blank{} if !isBlankIdent(id) { - lval = address{addr: fn.addNamedLocal(fn.Pkg.ObjectOf(id))} + lval = address{addr: fn.addNamedLocal(fn.Pkg.objectOf(id))} } b.exprInPlace(fn, lval, spec.Values[i]) } @@ -1256,7 +1249,7 @@ func (b *Builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { // Locals are implicitly zero-initialized. for _, id := range spec.Names { if !isBlankIdent(id) { - fn.addNamedLocal(fn.Pkg.ObjectOf(id)) + fn.addNamedLocal(fn.Pkg.objectOf(id)) } } @@ -1266,7 +1259,7 @@ func (b *Builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { result := tuple.Type().(*types.Tuple) for i, id := range spec.Names { if !isBlankIdent(id) { - lhs := fn.addNamedLocal(fn.Pkg.ObjectOf(id)) + lhs := fn.addNamedLocal(fn.Pkg.objectOf(id)) emitStore(fn, lhs, emitExtract(fn, tuple, i, result.At(i).Type())) } } @@ -1287,7 +1280,7 @@ func (b *Builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool) { if isDef { // Local may be "redeclared" in the same // scope, so don't blindly create anew. - obj := fn.Pkg.ObjectOf(lhs.(*ast.Ident)) + obj := fn.Pkg.objectOf(lhs.(*ast.Ident)) if _, ok := fn.objects[obj]; !ok { fn.addNamedLocal(obj) } @@ -1350,7 +1343,7 @@ func (b *Builder) arrayLen(fn *Function, elts []ast.Expr) int64 { // func (b *Builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ types.Type) { // TODO(adonovan): document how and why typ ever differs from - // fn.Pkg.TypeOf(e). + // fn.Pkg.typeOf(e). switch t := typ.Underlying().(type) { case *types.Struct: @@ -1577,11 +1570,11 @@ func (b *Builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl var id *ast.Ident switch ass := s.Assign.(type) { case *ast.ExprStmt: // x.(type) - x = b.expr(fn, noparens(ass.X).(*ast.TypeAssertExpr).X) + x = b.expr(fn, unparen(ass.X).(*ast.TypeAssertExpr).X) case *ast.AssignStmt: // y := x.(type) - x = b.expr(fn, noparens(ass.Rhs[0]).(*ast.TypeAssertExpr).X) + x = b.expr(fn, unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X) id = ass.Lhs[0].(*ast.Ident) - y = fn.addNamedLocal(fn.Pkg.ObjectOf(id)) + y = fn.addNamedLocal(fn.Pkg.objectOf(id)) emitStore(fn, y, x) } @@ -1602,7 +1595,7 @@ func (b *Builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl var ti Value // t_i, ok := typeassert,ok x for _, cond := range cc.List { next = fn.newBasicBlock("typeswitch.next") - casetype = fn.Pkg.TypeOf(cond) + casetype = fn.Pkg.typeOf(cond) var condv Value if casetype == tUntypedNil { condv = emitCompare(fn, token.EQL, x, nilLiteral(x.Type()), token.NoPos) @@ -1618,8 +1611,9 @@ func (b *Builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl if id != nil && len(cc.List) == 1 && casetype != tUntypedNil { // Declare a new shadow local variable of the // same name but a more specific type. - y2 := fn.addNamedLocal(fn.Pkg.TypeInfo.typecases[cc]) + y2 := fn.addNamedLocal(fn.Pkg.info.TypeCaseVar(cc)) y2.name += "'" // debugging aid + y2.typ = pointer(casetype) emitStore(fn, y2, ti) } fn.targets = &targets{ @@ -1628,6 +1622,9 @@ func (b *Builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl } b.stmtList(fn, cc.Body) fn.targets = fn.targets.tail + if id != nil { + fn.objects[fn.Pkg.objectOf(id)] = y // restore previous y binding + } emitJump(fn, done) fn.currentBlock = next } @@ -1689,13 +1686,13 @@ func (b *Builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) { case *ast.AssignStmt: // x := <-ch states = append(states, SelectState{ Dir: ast.RECV, - Chan: b.expr(fn, noparens(comm.Rhs[0]).(*ast.UnaryExpr).X), + Chan: b.expr(fn, unparen(comm.Rhs[0]).(*ast.UnaryExpr).X), }) case *ast.ExprStmt: // <-ch states = append(states, SelectState{ Dir: ast.RECV, - Chan: b.expr(fn, noparens(comm.X).(*ast.UnaryExpr).X), + Chan: b.expr(fn, unparen(comm.X).(*ast.UnaryExpr).X), }) } } @@ -1744,12 +1741,12 @@ func (b *Builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) { } switch comm := clause.Comm.(type) { case *ast.AssignStmt: // x := <-states[state].Chan - xdecl := fn.addNamedLocal(fn.Pkg.ObjectOf(comm.Lhs[0].(*ast.Ident))) + xdecl := fn.addNamedLocal(fn.Pkg.objectOf(comm.Lhs[0].(*ast.Ident))) recv := emitTypeAssert(fn, emitExtract(fn, triple, 1, tEface), xdecl.Type().Deref()) emitStore(fn, xdecl, recv) if len(comm.Lhs) == 2 { // x, ok := ... - okdecl := fn.addNamedLocal(fn.Pkg.ObjectOf(comm.Lhs[1].(*ast.Ident))) + okdecl := fn.addNamedLocal(fn.Pkg.objectOf(comm.Lhs[1].(*ast.Ident))) emitStore(fn, okdecl, emitExtract(fn, triple, 2, okdecl.Type().Deref())) } } @@ -2025,10 +2022,10 @@ func (b *Builder) rangeChan(fn *Function, x Value, tk types.Type) (k Value, loop func (b *Builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock) { var tk, tv types.Type if !isBlankIdent(s.Key) { - tk = fn.Pkg.TypeOf(s.Key) + tk = fn.Pkg.typeOf(s.Key) } if s.Value != nil && !isBlankIdent(s.Value) { - tv = fn.Pkg.TypeOf(s.Value) + tv = fn.Pkg.typeOf(s.Value) } // If iteration variables are defined (:=), this @@ -2039,10 +2036,10 @@ func (b *Builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock) { // always creates a new one. if s.Tok == token.DEFINE { if tk != nil { - fn.addNamedLocal(fn.Pkg.ObjectOf(s.Key.(*ast.Ident))) + fn.addNamedLocal(fn.Pkg.objectOf(s.Key.(*ast.Ident))) } if tv != nil { - fn.addNamedLocal(fn.Pkg.ObjectOf(s.Value.(*ast.Ident))) + fn.addNamedLocal(fn.Pkg.objectOf(s.Value.(*ast.Ident))) } } @@ -2129,7 +2126,7 @@ start: fn.emit(&Send{ Chan: b.expr(fn, s.Chan), X: emitConv(fn, b.expr(fn, s.Value), - fn.Pkg.TypeOf(s.Chan).Underlying().(*types.Chan).Elem()), + fn.Pkg.typeOf(s.Chan).Underlying().(*types.Chan).Elem()), pos: s.Arrow, }) @@ -2336,7 +2333,7 @@ func (b *Builder) buildFunction(fn *Function) { fn.FullName(), fn.Prog.Files.Position(fn.pos))() } fn.startBody() - fn.createSyntacticParams(fn.Pkg.idents) + fn.createSyntacticParams() b.stmt(fn, fn.syntax.body) if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb.Preds != nil) { // Run function calls deferred in this function when @@ -2354,6 +2351,8 @@ func (b *Builder) buildFunction(fn *Function) { // tree (for funcs and vars only); it will be used during the build // phase. // +// (CREATE phase.) +// func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { name := obj.Name() switch obj := obj.(type) { @@ -2418,6 +2417,8 @@ func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.No // typechecker object (var, func, const or type) associated with the // specified decl. // +// (CREATE phase.) +// func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) { switch decl := decl.(type) { case *ast.GenDecl: // import, const, type or var @@ -2426,7 +2427,7 @@ func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) { for _, spec := range decl.Specs { for _, id := range spec.(*ast.ValueSpec).Names { if !isBlankIdent(id) { - b.memberFromObject(pkg, pkg.ObjectOf(id), nil) + b.memberFromObject(pkg, pkg.objectOf(id), nil) } } } @@ -2435,7 +2436,7 @@ func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) { for _, spec := range decl.Specs { for _, id := range spec.(*ast.ValueSpec).Names { if !isBlankIdent(id) { - b.memberFromObject(pkg, pkg.ObjectOf(id), spec) + b.memberFromObject(pkg, pkg.objectOf(id), spec) } } } @@ -2444,7 +2445,7 @@ func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) { for _, spec := range decl.Specs { id := spec.(*ast.TypeSpec).Name if !isBlankIdent(id) { - b.memberFromObject(pkg, pkg.ObjectOf(id), nil) + b.memberFromObject(pkg, pkg.objectOf(id), nil) } } } @@ -2458,97 +2459,27 @@ func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) { return // init blocks aren't functions } if !isBlankIdent(id) { - b.memberFromObject(pkg, pkg.ObjectOf(id), decl) + b.memberFromObject(pkg, pkg.objectOf(id), decl) } } } -// typecheck invokes the type-checker on files and returns the -// type-checker's package so formed, plus the AST type information. -// -func (b *Builder) typecheck(importPath string, files []*ast.File) (*types.Package, *TypeInfo, error) { - info := &TypeInfo{ - fset: b.Prog.Files, - types: make(map[ast.Expr]types.Type), - idents: make(map[*ast.Ident]types.Object), - constants: make(map[ast.Expr]*Literal), - typecases: make(map[*ast.CaseClause]*types.Var), - } - tc := b.Context.TypeChecker - tc.Expr = func(x ast.Expr, typ types.Type, val exact.Value) { - info.types[x] = typ - if val != nil { - info.constants[x] = newLiteral(val, typ) - } - } - tc.Ident = func(ident *ast.Ident, obj types.Object) { - // Invariants: - // - obj is non-nil. - // - isBlankIdent(ident) <=> obj.GetType()==nil - info.idents[ident] = obj - } - tc.ImplicitObj = func(node ast.Node, obj types.Object) { - if cc, ok := node.(*ast.CaseClause); ok { - info.typecases[cc] = obj.(*types.Var) - } - } - typkg, firstErr := tc.Check(importPath, b.Prog.Files, files...) - tc.Expr = nil - tc.Ident = nil - if firstErr != nil { - return nil, nil, firstErr - } - return typkg, info, nil -} - -// CreatePackage creates a package from the specified set of files, -// performs type-checking, and allocates all global SSA Values for the -// package. It returns a new SSA Package providing access to these -// values. The order of files determines the package initialization order. -// -// importPath is the full name under which this package is known, such -// as appears in an import declaration. e.g. "sync/atomic". -// -// The ParseFiles() utility may be helpful for parsing a set of Go -// source files. -// -func (b *Builder) CreatePackage(importPath string, files []*ast.File) (*Package, error) { - typkg, info, err := b.typecheck(importPath, files) - if err != nil { - return nil, err - } - return b.createPackageImpl(typkg, importPath, files, info), nil -} - -// createPackageImpl constructs an SSA Package from an error-free -// types.Package typkg and populates its Members mapping. It returns -// the newly constructed ssa.Package. +// createPackage constructs an SSA Package from an error-free +// package described by info, and populates its Members mapping. // // The real work of building SSA form for each function is not done // until a subsequent call to BuildPackage. // -// If files is non-nil, its declarations will be used to generate code -// for functions, methods and init blocks in a subsequent call to -// BuildPackage; info must contains the type information for those files. -// Otherwise, typkg is assumed to have been imported -// from the gc compiler's object files; no code will be available. +// (CREATE phase.) // -func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, files []*ast.File, info *TypeInfo) *Package { +func (b *Builder) createPackage(info *importer.PackageInfo) *Package { p := &Package{ - Prog: b.Prog, - Types: typkg, - Members: make(map[string]Member), - Files: files, - nTo1Vars: make(map[*ast.ValueSpec]bool), + Prog: b.Prog, + Members: make(map[string]Member), + Types: info.Pkg, + info: info, // transient (CREATE and BUILD phases) } - if files != nil { - p.TypeInfo = info - } - - b.packages[typkg] = p - b.Prog.Packages[importPath] = p - // Add init() function (but not to Members since it can't be referenced). p.Init = &Function{ name: "init", @@ -2559,10 +2490,9 @@ func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, fil // CREATE phase. // Allocate all package members: vars, funcs and consts and types. - if len(files) > 0 { + if len(info.Files) > 0 { // Go source package. - - for _, file := range p.Files { + for _, file := range info.Files { for _, decl := range file.Decls { b.membersFromDecl(p, decl) } @@ -2571,22 +2501,12 @@ func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, fil // GC-compiled binary package. // No code. // No position information. - scope := p.Types.Scope() for i, n := 0, scope.NumEntries(); i < n; i++ { b.memberFromObject(p, scope.At(i), nil) } } - // Compute the method sets - for _, mem := range p.Members { - switch t := mem.(type) { - case *Type: - t.Methods = b.Prog.MethodSet(t.NamedType) - t.PtrMethods = b.Prog.MethodSet(pointer(t.NamedType)) - } - } - // Add initializer guard variable. initguard := &Global{ Pkg: p, @@ -2599,6 +2519,8 @@ func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, fil p.DumpTo(os.Stderr) } + p.info = nil + return p } @@ -2620,7 +2542,7 @@ func (b *Builder) buildDecl(pkg *Package, decl ast.Decl) { if isBlankIdent(id) { continue } - nt := pkg.ObjectOf(id).Type().(*types.Named) + nt := pkg.objectOf(id).Type().(*types.Named) for i, n := 0, nt.NumMethods(); i < n; i++ { b.buildFunction(b.Prog.concreteMethods[nt.Method(i)]) } @@ -2656,7 +2578,7 @@ func (b *Builder) buildDecl(pkg *Package, decl ast.Decl) { init.targets = init.targets.tail init.currentBlock = next - } else if m, ok := b.globals[pkg.ObjectOf(id)]; ok { + } else if m, ok := b.globals[pkg.objectOf(id)]; ok { // Package-level function. b.buildFunction(m.(*Function)) } @@ -2694,11 +2616,23 @@ func (b *Builder) BuildPackage(p *Package) { if !atomic.CompareAndSwapInt32(&p.started, 0, 1) { return // already started } - if p.Files == nil { + info := b.imp.Packages[p.Types.Path()] + if info == nil { + panic("no PackageInfo for " + p.Types.Path()) + } + if info.Files == nil { return // nothing to do } + + // TODO(adonovan): consider splitting Builder up into a + // package-specific part (needn't be exported) containing + // info, nto1Vars which actually traverses the AST, plus the + // shared portion (Builder). + p.info = info + p.nTo1Vars = make(map[*ast.ValueSpec]bool) + if b.Context.Mode&LogSource != 0 { - defer logStack("build package %s", p.Types.Path())() + defer logStack("build %s", p)() } init := p.Init init.startBody() @@ -2717,7 +2651,7 @@ func (b *Builder) BuildPackage(p *Package) { // transitive closure of dependencies, // e.g. when using GcImporter. seen := make(map[*types.Package]bool) - for _, file := range p.Files { + for _, file := range info.Files { for _, imp := range file.Imports { path, _ := strconv.Unquote(imp.Path.Value) if path == "unsafe" { @@ -2731,7 +2665,7 @@ func (b *Builder) BuildPackage(p *Package) { p2 := b.packages[typkg] if p2 == nil { - panic("Building " + p.Name() + ": CreatePackage has not been called for package " + path) + panic("Building " + p.String() + ": CreatePackage has not been called for package " + path) } var v Call @@ -2750,17 +2684,14 @@ func (b *Builder) BuildPackage(p *Package) { // // We also ensure all functions and methods are built, even if // they are unreachable. - for _, file := range p.Files { + for _, file := range info.Files { for _, decl := range file.Decls { b.buildDecl(p, decl) } } - // Clear out the typed ASTs unless otherwise requested. - if retain := b.Context.RetainAST; retain == nil || !retain(p) { - p.Files = nil - p.TypeInfo = nil - } + // We no longer need ASTs or go/types deductions. + p.info = nil p.nTo1Vars = nil // Finish up. @@ -2770,3 +2701,17 @@ func (b *Builder) BuildPackage(p *Package) { init.emit(new(Ret)) init.finishBody() } + +// Only valid during p's build phase! +func (p *Package) objectOf(id *ast.Ident) types.Object { + if o := p.info.ObjectOf(id); o != nil { + return o + } + panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s", + id.Name, p.Prog.Files.Position(id.Pos()))) +} + +// Only valid during p's build phase! +func (p *Package) typeOf(e ast.Expr) types.Type { + return p.info.TypeOf(e) +} diff --git a/ssa/example_test.go b/ssa/example_test.go index fe19b4e946..2df4ab9025 100644 --- a/ssa/example_test.go +++ b/ssa/example_test.go @@ -1,6 +1,7 @@ package ssa_test import ( + "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/ssa" "fmt" "go/ast" @@ -35,24 +36,27 @@ func main() { fmt.Println(message) } ` - - // Construct a builder. Imports will be loaded as if by 'go build'. - builder := ssa.NewBuilder(&ssa.Context{Loader: ssa.MakeGoBuildLoader(nil)}) + // Construct an importer. Imports will be loaded as if by 'go build'. + imp := importer.New(&importer.Context{Loader: importer.MakeGoBuildLoader(nil)}) // Parse the input file. - file, err := parser.ParseFile(builder.Prog.Files, "hello.go", hello, parser.DeclarationErrors) + file, err := parser.ParseFile(imp.Fset, "hello.go", hello, parser.DeclarationErrors) if err != nil { fmt.Printf(err.Error()) // parse error return } // Create a "main" package containing one file. - mainPkg, err := builder.CreatePackage("main", []*ast.File{file}) + info, err := imp.CreateSourcePackage("main", []*ast.File{file}) if err != nil { fmt.Printf(err.Error()) // type error return } + // Construct an SSA builder. + builder := ssa.NewBuilder(&ssa.Context{}, imp) + mainPkg := builder.PackageFor(info.Pkg) + // Print out the package. mainPkg.DumpTo(os.Stdout) fmt.Println() @@ -70,7 +74,7 @@ func main() { // Output: // - // Package main: + // package main: // var init$guard *bool // func main func() // const message message = "Hello, World!":untyped string diff --git a/ssa/func.go b/ssa/func.go index 688b8ee676..5bbc3e4366 100644 --- a/ssa/func.go +++ b/ssa/func.go @@ -213,21 +213,18 @@ func (f *Function) startBody() { // and named result locals) for all the parameters declared in the // syntax. In addition it populates the f.objects mapping. // -// idents must be a mapping from syntactic identifiers to their -// canonical type objects. -// // Preconditions: // f.syntax != nil, i.e. this is a Go source function. // f.startBody() was called. // Postcondition: // len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0) // -func (f *Function) createSyntacticParams(idents map[*ast.Ident]types.Object) { +func (f *Function) createSyntacticParams() { // Receiver (at most one inner iteration). if f.syntax.recvField != nil { for _, field := range f.syntax.recvField.List { for _, n := range field.Names { - f.addSpilledParam(idents[n]) + f.addSpilledParam(f.Pkg.objectOf(n)) } // Anonymous receiver? No need to spill. if field.Names == nil { @@ -241,7 +238,7 @@ func (f *Function) createSyntacticParams(idents map[*ast.Ident]types.Object) { n := len(f.Params) // 1 if has recv, 0 otherwise for _, field := range f.syntax.paramFields.List { for _, n := range field.Names { - f.addSpilledParam(idents[n]) + f.addSpilledParam(f.Pkg.objectOf(n)) } // Anonymous parameter? No need to spill. if field.Names == nil { @@ -255,7 +252,7 @@ func (f *Function) createSyntacticParams(idents map[*ast.Ident]types.Object) { for _, field := range f.syntax.resultFields.List { // Implicit "var" decl of locals for named results. for _, n := range field.Names { - f.namedResults = append(f.namedResults, f.addNamedLocal(idents[n])) + f.namedResults = append(f.namedResults, f.addNamedLocal(f.Pkg.objectOf(n))) } } } diff --git a/ssa/interp/interp.go b/ssa/interp/interp.go index 35bc06945a..130f5a99df 100644 --- a/ssa/interp/interp.go +++ b/ssa/interp/interp.go @@ -530,7 +530,7 @@ func setGlobal(i *interpreter, pkg *ssa.Package, name string, v value) { *g = v return } - panic("no global variable: " + pkg.Name() + "." + name) + panic("no global variable: " + pkg.Types.Path() + "." + name) } // Interpret interprets the Go program whose main package is mainpkg. diff --git a/ssa/interp/interp_test.go b/ssa/interp/interp_test.go index 67e2890fd0..909da9254f 100644 --- a/ssa/interp/interp_test.go +++ b/ssa/interp/interp_test.go @@ -10,6 +10,7 @@ import ( "strings" "testing" + "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/ssa" "code.google.com/p/go.tools/ssa/interp" ) @@ -141,11 +142,11 @@ func run(t *testing.T, dir, input string) bool { inputs = append(inputs, dir+i) } - b := ssa.NewBuilder(&ssa.Context{ - Mode: ssa.SanityCheckFunctions, - Loader: ssa.MakeGoBuildLoader(nil), - }) - files, err := ssa.ParseFiles(b.Prog.Files, ".", inputs...) + impctx := &importer.Context{ + Loader: importer.MakeGoBuildLoader(nil), + } + imp := importer.New(impctx) + files, err := importer.ParseFiles(imp.Fset, ".", inputs...) if err != nil { t.Errorf("ssa.ParseFiles(%s) failed: %s", inputs, err.Error()) return false @@ -163,13 +164,15 @@ func run(t *testing.T, dir, input string) bool { }() hint = fmt.Sprintf("To dump SSA representation, run:\n%% go run exp/ssa/ssadump.go -build=CFP %s\n", input) - mainpkg, err := b.CreatePackage("main", files) + info, err := imp.CreateSourcePackage("main", files) if err != nil { t.Errorf("ssa.Builder.CreatePackage(%s) failed: %s", inputs, err.Error()) - return false } + b := ssa.NewBuilder(&ssa.Context{Mode: ssa.SanityCheckFunctions}, imp) + mainpkg := b.PackageFor(info.Pkg) + b.BuildAllPackages() b = nil // discard Builder diff --git a/ssa/print.go b/ssa/print.go index a4067f9b3e..7970e38b9b 100644 --- a/ssa/print.go +++ b/ssa/print.go @@ -356,11 +356,11 @@ func (s *MapUpdate) String() string { } func (p *Package) String() string { - return "Package " + p.Types.Path() + return "package " + p.Types.Path() } func (p *Package) DumpTo(w io.Writer) { - fmt.Fprintf(w, "Package %s:\n", p.Types.Path()) + fmt.Fprintf(w, "%s:\n", p) var names []string maxname := 0 diff --git a/ssa/promote.go b/ssa/promote.go index aa78918d7f..929e97574e 100644 --- a/ssa/promote.go +++ b/ssa/promote.go @@ -149,7 +149,11 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet { if nt, ok := t.(*types.Named); ok { for i, n := 0, nt.NumMethods(); i < n; i++ { m := nt.Method(i) - addCandidate(nextcands, MakeId(m.Name(), m.Pkg()), m, prog.concreteMethods[m], node) + concrete := prog.concreteMethods[m] + if concrete == nil { + panic(fmt.Sprint("no ssa.Function for mset(%s)[%s]", t, m.Name())) + } + addCandidate(nextcands, MakeId(m.Name(), m.Pkg()), m, concrete, node) } t = nt.Underlying() } @@ -311,6 +315,9 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function c.Call.Args = append(c.Call.Args, v) } else { c.Call.Recv = v + // TODO(adonovan): fix: this looks wrong! Need to + // find method index within + // v.Type().Underlying().(*types.Interface).Methods() c.Call.Method = 0 } for _, arg := range fn.Params[1:] { diff --git a/ssa/source.go b/ssa/source.go index 10f685e1fe..edff71e674 100644 --- a/ssa/source.go +++ b/ssa/source.go @@ -6,6 +6,7 @@ package ssa // since neither depends on SSA internals. import ( + "code.google.com/p/go.tools/importer" "go/ast" "go/token" ) @@ -23,16 +24,19 @@ func tokenFileContainsPos(f *token.File, pos token.Pos) bool { // program prog. exact is defined as for standalone // PathEnclosingInterval. // +// imp provides ASTs for the program's packages. +// // The result is (nil, nil, false) if not found. // -func (prog *Program) PathEnclosingInterval(start, end token.Pos) (pkg *Package, path []ast.Node, exact bool) { - for _, pkg = range prog.Packages { - for _, f := range pkg.Files { +func (prog *Program) PathEnclosingInterval(imp *importer.Importer, start, end token.Pos) (pkg *Package, path []ast.Node, exact bool) { + for path, info := range imp.Packages { + pkg := prog.Packages[path] + for _, f := range info.Files { if !tokenFileContainsPos(prog.Files.File(f.Package), start) { continue } - if path, exact = PathEnclosingInterval(f, start, end); path != nil { - return + if path, exact := PathEnclosingInterval(f, start, end); path != nil { + return pkg, path, exact } } } diff --git a/ssa/source_test.go b/ssa/source_test.go index 74fe541956..8a68ead6ce 100644 --- a/ssa/source_test.go +++ b/ssa/source_test.go @@ -7,6 +7,7 @@ package ssa_test import ( "bytes" + "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/ssa" "fmt" "go/ast" @@ -235,8 +236,8 @@ func TestEnclosingFunction(t *testing.T) { "900", "func@2.27"}, } for _, test := range tests { - b := ssa.NewBuilder(new(ssa.Context)) // (NB: no Loader) - f, start, end := findInterval(t, b.Prog.Files, test.input, test.substr) + imp := importer.New(new(importer.Context)) // (NB: no Loader) + f, start, end := findInterval(t, imp.Fset, test.input, test.substr) if f == nil { continue } @@ -245,12 +246,14 @@ func TestEnclosingFunction(t *testing.T) { t.Errorf("EnclosingFunction(%q) not exact", test.substr) continue } - pkg, err := b.CreatePackage("main", []*ast.File{f}) + info, err := imp.CreateSourcePackage("main", []*ast.File{f}) if err != nil { t.Error(err.Error()) continue } + b := ssa.NewBuilder(new(ssa.Context), imp) + pkg := b.PackageFor(info.Pkg) b.BuildPackage(pkg) name := "(none)" diff --git a/ssa/ssa.go b/ssa/ssa.go index a15337e0cf..79f1bf860c 100644 --- a/ssa/ssa.go +++ b/ssa/ssa.go @@ -11,6 +11,7 @@ import ( "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" + "code.google.com/p/go.tools/importer" ) // A Program is a partial or complete Go program converted to SSA form. @@ -18,7 +19,7 @@ import ( // lifetime. // type Program struct { - Files *token.FileSet // position information for the files of this Program + Files *token.FileSet // position information for the files of this Program [TODO: rename Fset] Packages map[string]*Package // all loaded Packages, keyed by import path Builtins map[types.Object]*Builtin // all built-in functions, keyed by typechecker objects. @@ -35,18 +36,14 @@ type Program struct { // type Package struct { Prog *Program // the owning program - Types *types.Package // the type checker's package object for this package. + Types *types.Package // the type checker's package object for this package Members map[string]Member // all exported and unexported members of the package Init *Function // the package's (concatenated) init function - // These fields are available between package creation and SSA - // building, but are then cleared unless Context.RetainAST(pkg). - Files []*ast.File // abstract syntax for the package's files - *TypeInfo // type-checker intermediate results - // The following fields are set transiently during building, // then cleared. started int32 // atomically tested and set at start of build phase + info *importer.PackageInfo // package ASTs and type information nTo1Vars map[*ast.ValueSpec]bool // set of n:1 ValueSpecs already built } @@ -1286,8 +1283,6 @@ func (t *Type) Pos() token.Pos { return t.NamedType.Obj().Pos() } func (t *Type) String() string { return t.Name() } func (t *Type) Type() types.Type { return t.NamedType } -func (p *Package) Name() string { return p.Types.Name() } - func (c *Constant) Name() string { return c.name } func (c *Constant) Pos() token.Pos { return c.pos } func (c *Constant) String() string { return c.Name() } diff --git a/ssa/ssadump.go b/ssa/ssadump.go index 0f4417dac4..9e64cc8688 100644 --- a/ssa/ssadump.go +++ b/ssa/ssadump.go @@ -11,6 +11,7 @@ import ( "os" "runtime/pprof" + "code.google.com/p/go.tools/importer" "code.google.com/p/go.tools/ssa" "code.google.com/p/go.tools/ssa/interp" ) @@ -50,6 +51,8 @@ func main() { flag.Parse() args := flag.Args() + impctx := importer.Context{Loader: importer.MakeGoBuildLoader(nil)} + var mode ssa.BuilderMode for _, c := range *buildFlag { switch c { @@ -64,7 +67,7 @@ func main() { case 'N': mode |= ssa.NaiveForm case 'G': - mode |= ssa.UseGCImporter + impctx.Loader = nil case 'L': mode |= ssa.BuildSerially default: @@ -99,19 +102,24 @@ func main() { defer pprof.StopCPUProfile() } - context := &ssa.Context{ - Mode: mode, - Loader: ssa.MakeGoBuildLoader(nil), - } - b := ssa.NewBuilder(context) - mainpkg, args, err := ssa.CreatePackageFromArgs(b, args) + // Load, parse and type-check the program. + imp := importer.New(&impctx) + info, args, err := importer.CreatePackageFromArgs(imp, args) if err != nil { log.Fatal(err.Error()) } + + // Build SSA-form program representation. + context := &ssa.Context{ + Mode: mode, + } + b := ssa.NewBuilder(context, imp) + mainpkg := b.PackageFor(info.Pkg) b.BuildAllPackages() b = nil // discard Builder + // Run the interpreter. if *runFlag { - interp.Interpret(mainpkg, interpMode, mainpkg.Name(), args) + interp.Interpret(mainpkg, interpMode, info.Pkg.Path(), args) } } diff --git a/ssa/util.go b/ssa/util.go index f16f6ff129..c22e6cc3d5 100644 --- a/ssa/util.go +++ b/ssa/util.go @@ -18,8 +18,8 @@ func unreachable() { //// AST utilities -// noparens returns e with any enclosing parentheses stripped. -func noparens(e ast.Expr) ast.Expr { +// unparen returns e with any enclosing parentheses stripped. +func unparen(e ast.Expr) ast.Expr { for { p, ok := e.(*ast.ParenExpr) if !ok {