go.types/ssa: split the load/parse/typecheck logic off into a separate package.

PLEASE NOTE: the APIs for both "importer" and "ssa" packages
will continue to evolve and both need some polishing; the key
thing is that this CL splits them.

The go.types/importer package contains contains the Importer,
which takes care of the mechanics of loading a set of packages
and type-checking them.  It exposes for each package a
PackageInfo containing:
- the package's ASTs (i.e. the input to the typechecker)
- the types.Package object
- the memoization of the typechecker callbacks for identifier
  resolution, constant folding and expression type inference.

Method-set computation (and hence bridge-method creation) is
now moved to after creation of all packages: since they are no
longer created in topological order, we can't guarantee the
needed delegate methods exist yet.

ssa.Package no longer has public TypeOf, ObjectOf, ValueOf methods.
The private counterparts are valid only during the build phase.

Also:
- added to go/types an informative error (not crash) for an
  importer() returning nil without error.
- removed Package.Name(), barely needed.
- changed Package.String() slightly.
- flag what looks like a bug in makeBridgeMethod. Will follow up.

R=golang-dev, gri
CC=golang-dev
https://golang.org/cl/9898043
diff --git a/go/types/resolver.go b/go/types/resolver.go
index 0a8d003..f9f15cb 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 @@
 						}
 						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 0000000..19355b6
--- /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 3bc7566..30d12f5 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 @@
 	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 @@
 // 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 @@
 	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 @@
 	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 @@
 // 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/importer/util.go b/importer/util.go
new file mode 100644
index 0000000..6ea5743
--- /dev/null
+++ b/importer/util.go
@@ -0,0 +1,123 @@
+package importer
+
+// This file defines various utility functions exposed by the package
+// and used by it.
+
+import (
+	"errors"
+	"go/ast"
+	"go/build"
+	"go/parser"
+	"go/token"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// 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.
+//
+// This utility is provided to facilitate construction of command-line
+// tools with a consistent user interface.
+//
+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.")
+
+	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++ {
+		}
+		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:]
+	}
+
+	return
+}
+
+// MakeGoBuildLoader returns an implementation of the SourceLoader
+// function prototype that locates packages using the go/build
+// libraries.  It may return nil upon gross misconfiguration
+// (e.g. os.Getwd() failed).
+//
+// ctxt specifies the go/build.Context to use; if nil, the default
+// Context is used.
+//
+func MakeGoBuildLoader(ctxt *build.Context) SourceLoader {
+	srcDir, err := os.Getwd()
+	if err != nil {
+		return nil // serious misconfiguration
+	}
+	if ctxt == nil {
+		ctxt = &build.Default
+	}
+	return func(fset *token.FileSet, path string) (files []*ast.File, err error) {
+		// TODO(adonovan): fix: Do we need cwd? Shouldn't
+		// ImportDir(path) / $GOROOT suffice?
+		bp, err := ctxt.Import(path, srcDir, 0)
+		if err != nil {
+			return // import failed
+		}
+		files, err = ParseFiles(fset, bp.Dir, bp.GoFiles...)
+		if err != nil {
+			return nil, err
+		}
+		return
+	}
+}
+
+// ParseFiles parses the Go source files files within directory dir
+// and returns their ASTs, or the first parse error if any.
+//
+// This utility function is provided to facilitate implementing a
+// SourceLoader.
+//
+func ParseFiles(fset *token.FileSet, dir string, files ...string) (parsed []*ast.File, err error) {
+	for _, file := range files {
+		var f *ast.File
+		if !filepath.IsAbs(file) {
+			file = filepath.Join(dir, file)
+		}
+		f, err = parser.ParseFile(fset, file, nil, parser.DeclarationErrors)
+		if err != nil {
+			return // parsing failed
+		}
+		parsed = append(parsed, f)
+	}
+	return
+}
+
+// ---------- Internal helpers ----------
+
+// unparen returns e with any enclosing parentheses stripped.
+func unparen(e ast.Expr) ast.Expr {
+	for {
+		p, ok := e.(*ast.ParenExpr)
+		if !ok {
+			break
+		}
+		e = p.X
+	}
+	return e
+}
+
+func unreachable() {
+	panic("unreachable")
+}
diff --git a/ssa/builder.go b/ssa/builder.go
index 21b2ce0..57852cd 100644
--- a/ssa/builder.go
+++ b/ssa/builder.go
@@ -32,6 +32,7 @@
 
 	"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 @@
 	// 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 @@
 
 // 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 @@
 	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 @@
 			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 @@
 		// 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 @@
 		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 @@
 		// 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 @@
 				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 @@
 			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 @@
 			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) 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 @@
 		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 @@
 
 	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 @@
 	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 @@
 // 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 @@
 		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 @@
 			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 @@
 		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 @@
 		}
 		// 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 @@
 				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 @@
 		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 @@
 	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 @@
 			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 @@
 
 	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 @@
 		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 @@
 
 		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 @@
 // 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 @@
 	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 @@
 	}
 
 	// 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 @@
 	// 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 @@
 		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 @@
 	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 @@
 			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 @@
 			} 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 @@
 			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 @@
 		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 @@
 		// 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 @@
 		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 @@
 			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) 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 @@
 	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 @@
 		var ti Value // t_i, ok := typeassert,ok x <T_i>
 		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 @@
 		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 @@
 		}
 		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 @@
 		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 @@
 		}
 		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) 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 @@
 	// 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 @@
 		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 @@
 			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 @@
 // 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 @@
 // 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 @@
 			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 @@
 			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 @@
 			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 @@
 			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 @@
 
 	// 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 @@
 		// 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 @@
 		p.DumpTo(os.Stderr)
 	}
 
+	p.info = nil
+
 	return p
 }
 
@@ -2620,7 +2542,7 @@
 				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 @@
 			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 @@
 	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 @@
 	// 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 @@
 
 			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 @@
 	//
 	// 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 @@
 	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 fe19b4e..2df4ab9 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 @@
 	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 @@
 
 	// 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 688b8ee..5bbc3e4 100644
--- a/ssa/func.go
+++ b/ssa/func.go
@@ -213,21 +213,18 @@
 // 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 @@
 		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 @@
 		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/importer.go b/ssa/importer.go
deleted file mode 100644
index 0e1ebc2..0000000
--- a/ssa/importer.go
+++ /dev/null
@@ -1,162 +0,0 @@
-package ssa
-
-// This file defines an implementation of the types.Importer interface
-// (func) that loads the transitive closure of dependencies of a
-// "main" package.
-
-import (
-	"errors"
-	"go/ast"
-	"go/build"
-	"go/parser"
-	"go/token"
-	"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.
-//
-// 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.
-//
-type SourceLoader func(fset *token.FileSet, path string) (files []*ast.File, err error)
-
-// 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)
-		}
-	}
-	if err != nil {
-		// Cache failure
-		b.importErrs[path] = err
-		return nil, err
-	}
-
-	// 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
-}
-
-// MakeGoBuildLoader returns an implementation of the SourceLoader
-// function prototype that locates packages using the go/build
-// libraries.  It may return nil upon gross misconfiguration
-// (e.g. os.Getwd() failed).
-//
-// ctxt specifies the go/build.Context to use; if nil, the default
-// Context is used.
-//
-func MakeGoBuildLoader(ctxt *build.Context) SourceLoader {
-	srcDir, err := os.Getwd()
-	if err != nil {
-		return nil // serious misconfiguration
-	}
-	if ctxt == nil {
-		ctxt = &build.Default
-	}
-	return func(fset *token.FileSet, path string) (files []*ast.File, err error) {
-		// TODO(adonovan): fix: Do we need cwd? Shouldn't
-		// ImportDir(path) / $GOROOT suffice?
-		bp, err := ctxt.Import(path, srcDir, 0)
-		if err != nil {
-			return // import failed
-		}
-		files, err = ParseFiles(fset, bp.Dir, bp.GoFiles...)
-		if err != nil {
-			return nil, err
-		}
-		return
-	}
-}
-
-// ParseFiles parses the Go source files files within directory dir
-// and returns their ASTs, or the first parse error if any.
-//
-// This utility function is provided to facilitate implementing a
-// SourceLoader.
-//
-func ParseFiles(fset *token.FileSet, dir string, files ...string) (parsed []*ast.File, err error) {
-	for _, file := range files {
-		var f *ast.File
-		if !filepath.IsAbs(file) {
-			file = filepath.Join(dir, file)
-		}
-		f, err = parser.ParseFile(fset, file, nil, parser.DeclarationErrors)
-		if err != nil {
-			return // parsing failed
-		}
-		parsed = append(parsed, f)
-	}
-	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
-
-	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++ {
-		}
-		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)
-	}
-	if err == nil {
-		pkg, err = builder.CreatePackage(pkgname, files)
-	}
-	return
-}
diff --git a/ssa/interp/interp.go b/ssa/interp/interp.go
index 35bc069..130f5a9 100644
--- a/ssa/interp/interp.go
+++ b/ssa/interp/interp.go
@@ -530,7 +530,7 @@
 		*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 67e2890..909da92 100644
--- a/ssa/interp/interp_test.go
+++ b/ssa/interp/interp_test.go
@@ -10,6 +10,7 @@
 	"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 @@
 		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 @@
 	}()
 
 	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 a4067f9..7970e38 100644
--- a/ssa/print.go
+++ b/ssa/print.go
@@ -356,11 +356,11 @@
 }
 
 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 aa78918..929e975 100644
--- a/ssa/promote.go
+++ b/ssa/promote.go
@@ -149,7 +149,11 @@
 			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 @@
 		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 10f685e..edff71e 100644
--- a/ssa/source.go
+++ b/ssa/source.go
@@ -6,6 +6,7 @@
 // since neither depends on SSA internals.
 
 import (
+	"code.google.com/p/go.tools/importer"
 	"go/ast"
 	"go/token"
 )
@@ -23,16 +24,19 @@
 // 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 74fe541..8a68ead 100644
--- a/ssa/source_test.go
+++ b/ssa/source_test.go
@@ -7,6 +7,7 @@
 
 import (
 	"bytes"
+	"code.google.com/p/go.tools/importer"
 	"code.google.com/p/go.tools/ssa"
 	"fmt"
 	"go/ast"
@@ -235,8 +236,8 @@
 			"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 @@
 			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 a15337e..79f1bf8 100644
--- a/ssa/ssa.go
+++ b/ssa/ssa.go
@@ -11,6 +11,7 @@
 
 	"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 @@
 // 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 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) 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 0f4417d..9e64cc8 100644
--- a/ssa/ssadump.go
+++ b/ssa/ssadump.go
@@ -11,6 +11,7 @@
 	"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 @@
 	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 @@
 		case 'N':
 			mode |= ssa.NaiveForm
 		case 'G':
-			mode |= ssa.UseGCImporter
+			impctx.Loader = nil
 		case 'L':
 			mode |= ssa.BuildSerially
 		default:
@@ -99,19 +102,24 @@
 		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 f16f6ff..c22e6cc 100644
--- a/ssa/util.go
+++ b/ssa/util.go
@@ -18,8 +18,8 @@
 
 //// 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 {