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
This commit is contained in:
Alan Donovan 2013-05-31 16:14:13 -04:00
parent 710a117e33
commit be28dbb86f
16 changed files with 524 additions and 392 deletions

View File

@ -5,6 +5,7 @@
package types package types
import ( import (
"errors"
"go/ast" "go/ast"
"go/token" "go/token"
"strconv" "strconv"
@ -104,6 +105,9 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
} }
path, _ := strconv.Unquote(s.Path.Value) path, _ := strconv.Unquote(s.Path.Value)
imp, err := importer(pkg.imports, path) imp, err := importer(pkg.imports, path)
if imp == nil && err == nil {
err = errors.New("Context.Import returned niil")
}
if err != nil { if err != nil {
check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err) check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
continue continue

184
importer/importer.go Normal file
View File

@ -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
}

View File

@ -1,28 +1,33 @@
package ssa package importer
// This file defines utilities for querying the results of typechecker: // TODO(gri): absorb this into go/types.
// types of expressions, values of constant expressions, referents of identifiers.
import ( import (
"code.google.com/p/go.tools/go/exact"
"code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/go/types"
"fmt"
"go/ast" "go/ast"
"go/token"
) )
// TypeInfo contains information provided by the type checker about // PackageInfo holds the ASTs and facts derived by the type-checker
// the abstract syntax for a single package. // for a single package.
type TypeInfo struct { //
fset *token.FileSet // 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 types map[ast.Expr]types.Type // inferred types of expressions
constants map[ast.Expr]*Literal // values of constant expressions constants map[ast.Expr]exact.Value // values of constant expressions
idents map[*ast.Ident]types.Object // canonical type objects for named entities idents map[*ast.Ident]types.Object // resoved objects for named entities
typecases map[*ast.CaseClause]*types.Var // implicit vars for single-type typecases typecases map[*ast.CaseClause]*types.Var // implicit vars for single-type typecases
} }
// TypeOf returns the type of expression e. // TypeOf returns the type of expression e.
// Precondition: e belongs to the package's ASTs. // 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 // For Ident, b.types may be more specific than
// b.obj(id.(*ast.Ident)).GetType(), // b.obj(id.(*ast.Ident)).GetType(),
// e.g. in the case of typeswitch. // 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") panic("no type for expression")
} }
// ValueOf returns the value of expression e if it is a constant, // ValueOf returns the value of expression e if it is a constant, nil
// nil otherwise. // otherwise.
// //
func (info *TypeInfo) ValueOf(e ast.Expr) *Literal { func (info *PackageInfo) ValueOf(e ast.Expr) exact.Value {
return info.constants[e] return info.constants[e]
} }
// ObjectOf returns the typechecker object denoted by the specified id. // ObjectOf returns the typechecker object denoted by the specified id.
// Precondition: id belongs to the package's ASTs. // Precondition: id belongs to the package's ASTs.
// //
func (info *TypeInfo) ObjectOf(id *ast.Ident) types.Object { func (info *PackageInfo) ObjectOf(id *ast.Ident) types.Object {
if obj, ok := info.idents[id]; ok { return info.idents[id]
return obj
}
panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s", id.Name, info.fset.Position(id.Pos())))
} }
// IsType returns true iff expression e denotes a type. // 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 // e must be a true expression, not a KeyValueExpr, or an Ident
// appearing in a SelectorExpr or declaration. // 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) { switch e := e.(type) {
case *ast.SelectorExpr: // pkg.Type case *ast.SelectorExpr: // pkg.Type
if obj := info.isPackageRef(e); obj != nil { if obj := info.IsPackageRef(e); obj != nil {
_, isType := obj.(*types.TypeName) _, isType := obj.(*types.TypeName)
return isType return isType
} }
@ -82,12 +84,12 @@ func (info *TypeInfo) IsType(e ast.Expr) bool {
return false 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. // package-qualified reference to a named const, var, func or type.
// Otherwise it returns nil. // Otherwise it returns nil.
// Precondition: sel belongs to the package's ASTs. // 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 id, ok := sel.X.(*ast.Ident); ok {
if pkg, ok := info.ObjectOf(id).(*types.Package); ok { if pkg, ok := info.ObjectOf(id).(*types.Package); ok {
return pkg.Scope().Lookup(nil, sel.Sel.Name) return pkg.Scope().Lookup(nil, sel.Sel.Name)
@ -96,7 +98,22 @@ func (info *TypeInfo) isPackageRef(sel *ast.SelectorExpr) types.Object {
return nil 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. // effective type of a builtin operator for the particular call e.
// //
// This requires ad-hoc typing rules for all variadic (append, print, // 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 // The returned Signature is degenerate and only intended for use by
// emitCallArgs. // emitCallArgs.
// //
func builtinCallSignature(info *TypeInfo, e *ast.CallExpr) *types.Signature { func (info *PackageInfo) BuiltinCallSignature(e *ast.CallExpr) *types.Signature {
var params []*types.Var var params []*types.Var
var isVariadic bool var isVariadic bool
switch builtin := noparens(e.Fun).(*ast.Ident).Name; builtin { switch builtin := unparen(e.Fun).(*ast.Ident).Name; builtin {
case "append": case "append":
var t0, t1 types.Type var t0, t1 types.Type
t0 = info.TypeOf(e) // infer arg[0] type from result type t0 = info.TypeOf(e) // infer arg[0] type from result type

View File

@ -1,8 +1,7 @@
package ssa package importer
// This file defines an implementation of the types.Importer interface // This file defines various utility functions exposed by the package
// (func) that loads the transitive closure of dependencies of a // and used by it.
// "main" package.
import ( import (
"errors" "errors"
@ -13,60 +12,44 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"code.google.com/p/go.tools/go/types"
) )
// Prototype of a function that locates, reads and parses a set of // CreatePackageFromArgs builds an initial Package from a list of
// source files given an import path. // 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. // This utility is provided to facilitate construction of command-line
// path is the imported path, e.g. "sync/atomic". // tools with a consistent user interface.
// //
// On success, the function returns files, the set of ASTs produced, func CreatePackageFromArgs(imp *Importer, args []string) (info *PackageInfo, rest []string, err error) {
// or the first error encountered. switch {
// case len(args) == 0:
type SourceLoader func(fset *token.FileSet, path string) (files []*ast.File, err error) return nil, nil, errors.New("No *.go source files nor package name was specified.")
// doImport loads the typechecker package identified by path case strings.HasSuffix(args[0], ".go"):
// Implements the types.Importer prototype. // % tool a.go b.go ...
// // Leading consecutive *.go arguments constitute main package.
func (b *Builder) doImport(imports map[string]*types.Package, path string) (typkg *types.Package, err error) { i := 1
// Package unsafe is handled specially, and has no ssa.Package. for ; i < len(args) && strings.HasSuffix(args[i], ".go"); i++ {
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)
} }
} var files []*ast.File
if err != nil { files, err = ParseFiles(imp.Fset, ".", args[:i]...)
// Cache failure rest = args[i:]
b.importErrs[path] = err if err == nil {
return nil, err 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 return
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 // MakeGoBuildLoader returns an implementation of the SourceLoader
@ -121,42 +104,20 @@ func ParseFiles(fset *token.FileSet, dir string, files ...string) (parsed []*ast
return return
} }
// CreatePackageFromArgs builds an initial Package from a list of // ---------- Internal helpers ----------
// 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 { // unparen returns e with any enclosing parentheses stripped.
case len(args) == 0: func unparen(e ast.Expr) ast.Expr {
err = errors.New("No *.go source files nor package name was specified.") for {
p, ok := e.(*ast.ParenExpr)
case strings.HasSuffix(args[0], ".go"): if !ok {
// % tool a.go b.go ... break
// 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]...) e = p.X
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 { return e
pkg, err = builder.CreatePackage(pkgname, files) }
}
return func unreachable() {
panic("unreachable")
} }

View File

@ -32,6 +32,7 @@ import (
"code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/exact"
"code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/go/types"
"code.google.com/p/go.tools/importer"
) )
type opaqueType struct { type opaqueType struct {
@ -47,10 +48,6 @@ var (
// Type constants. // Type constants.
tBool = types.Typ[types.Bool] tBool = types.Typ[types.Bool]
tByte = types.Typ[types.Byte] 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] tInt = types.Typ[types.Int]
tInvalid = types.Typ[types.Invalid] tInvalid = types.Typ[types.Invalid]
tUntypedNil = types.Typ[types.UntypedNil] tUntypedNil = types.Typ[types.UntypedNil]
@ -72,33 +69,10 @@ var (
// A Context specifies the supporting context for SSA construction. // 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 { type Context struct {
// Mode is a bitfield of options controlling verbosity, // Mode is a bitfield of options controlling verbosity,
// logging and additional sanity checks. // logging and additional sanity checks.
Mode BuilderMode 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. // BuilderMode is a bitmask of options for diagnostics and checking.
@ -109,63 +83,52 @@ const (
LogFunctions // Dump function SSA code to stderr LogFunctions // Dump function SSA code to stderr
LogSource // Show source locations as SSA builder progresses LogSource // Show source locations as SSA builder progresses
SanityCheckFunctions // Perform sanity checking of function bodies 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 NaiveForm // Build naïve SSA form: don't replace local loads/stores with registers
BuildSerially // Build packages serially, not in parallel. BuildSerially // Build packages serially, not in parallel.
) )
// A Builder creates the SSA representation of a single program. // A Builder builds the SSA representation of a single Go program: a
// Instances may be created using NewBuilder. // Program containing one or more Packages.
// //
// The SSA Builder constructs a Program containing Package instances // Use NewBuilder to create a Builder.
// 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.
// //
type Builder struct { type Builder struct {
Prog *Program // the program being built Prog *Program // the program being built
Context *Context // the client context Context *Context // the client context
importErrs map[string]error // across-packages import cache of failures imp *importer.Importer // ASTs and types for loaded packages
packages map[*types.Package]*Package // SSA packages by types.Package 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 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 // NewBuilder creates and returns a new SSA builder with options
// specified by context. // 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{ b := &Builder{
Prog: &Program{ Prog: &Program{
Files: token.NewFileSet(), Files: imp.Fset,
Packages: make(map[string]*Package), Packages: make(map[string]*Package),
Builtins: make(map[types.Object]*Builtin), Builtins: make(map[types.Object]*Builtin),
methodSets: make(map[types.Type]MethodSet), methodSets: make(map[types.Type]MethodSet),
concreteMethods: make(map[*types.Func]*Function), concreteMethods: make(map[*types.Func]*Function),
mode: context.Mode, mode: context.Mode,
}, },
Context: context, Context: context,
globals: make(map[types.Object]Value), imp: imp,
importErrs: make(map[string]error), globals: make(map[types.Object]Value),
packages: make(map[*types.Package]*Package), 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)
} }
// Create Values for built-in functions. // Create Values for built-in functions.
@ -177,9 +140,39 @@ func NewBuilder(context *Context) *Builder {
b.Prog.Builtins[obj] = v 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 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 // lookup returns the package-level *Function or *Global (or universal
// *Builtin) for the named object obj. // *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. // cases for single-valued CallExpr.
var c Call var c Call
b.setCall(fn, e, &c.Call) b.setCall(fn, e, &c.Call)
c.typ = fn.Pkg.TypeOf(e) c.typ = fn.Pkg.typeOf(e)
return fn.emit(&c) return fn.emit(&c)
case *ast.IndexExpr: case *ast.IndexExpr:
mapt := fn.Pkg.TypeOf(e.X).Underlying().(*types.Map) mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map)
typ = mapt.Elem() typ = mapt.Elem()
lookup := &Lookup{ lookup := &Lookup{
X: b.expr(fn, e.X), X: b.expr(fn, e.X),
@ -340,10 +333,10 @@ func (b *Builder) exprN(fn *Function, e ast.Expr) Value {
tuple = fn.emit(lookup) tuple = fn.emit(lookup)
case *ast.TypeAssertExpr: 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 <- 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{ unop := &UnOp{
Op: token.ARROW, Op: token.ARROW,
X: b.expr(fn, e.X), 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 // We must still evaluate the value, though. (If it
// was side-effect free, the whole call would have // was side-effect free, the whole call would have
// been constant-folded.) // 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 { if at, ok := t.(*types.Array); ok {
b.expr(fn, args[0]) // for effects only b.expr(fn, args[0]) // for effects only
return intLiteral(at.Len()) return intLiteral(at.Len())
@ -459,12 +452,12 @@ func (b *Builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping
Bindings: []Value{recv}, Bindings: []Value{recv},
} }
c.setPos(e.Sel.Pos()) c.setPos(e.Sel.Pos())
c.setType(fn.Pkg.TypeOf(e)) c.setType(fn.Pkg.typeOf(e))
return fn.emit(c) 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 index := -1
for i, n := 0, st.NumFields(); i < n; i++ { for i, n := 0, st.NumFields(); i < n; i++ {
f := st.Field(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) panic("field not found, even with promotion: " + e.Sel.Name)
} }
} }
fieldType := fn.Pkg.TypeOf(e) fieldType := fn.Pkg.typeOf(e)
pos := e.Sel.Pos() pos := e.Sel.Pos()
if wantAddr { if wantAddr {
return b.fieldAddr(fn, e.X, path, index, fieldType, pos, escaping) 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) x = b.fieldExpr(fn, base, path.tail, path.index, path.field.Type, token.NoPos)
} }
} else { } else {
switch fn.Pkg.TypeOf(base).Underlying().(type) { switch fn.Pkg.typeOf(base).Underlying().(type) {
case *types.Struct: case *types.Struct:
x = b.addr(fn, base, escaping).(address).addr x = b.addr(fn, base, escaping).(address).addr
case *types.Pointer: 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 { func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
switch e := e.(type) { switch e := e.(type) {
case *ast.Ident: case *ast.Ident:
obj := fn.Pkg.ObjectOf(e) obj := fn.Pkg.objectOf(e)
v, ok := b.lookup(fn.Pkg, obj) // var (address) v, ok := b.lookup(fn.Pkg, obj) // var (address)
if !ok { if !ok {
v = fn.lookup(obj, escaping) 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} return address{addr: v}
case *ast.CompositeLit: case *ast.CompositeLit:
t := fn.Pkg.TypeOf(e).Deref() t := fn.Pkg.typeOf(e).Deref()
var v Value var v Value
if escaping { if escaping {
v = emitNew(fn, t, e.Lbrace) 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: case *ast.SelectorExpr:
// p.M where p is a package. // 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 { if v, ok := b.lookup(fn.Pkg, obj); ok {
return address{addr: v} return address{addr: v}
} }
@ -620,7 +613,7 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
case *ast.IndexExpr: case *ast.IndexExpr:
var x Value var x Value
var et types.Type 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: case *types.Array:
x = b.addr(fn, e.X, escaping).(address).addr x = b.addr(fn, e.X, escaping).(address).addr
et = pointer(t.Elem()) 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. // to fn and returning the Value defined by the expression.
// //
func (b *Builder) expr(fn *Function, e ast.Expr) Value { func (b *Builder) expr(fn *Function, e ast.Expr) Value {
if lit := fn.Pkg.ValueOf(e); lit != nil { if v := fn.Pkg.info.ValueOf(e); v != nil {
return lit return newLiteral(v, fn.Pkg.typeOf(e))
} }
switch e := e.(type) { 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) posn := b.Prog.Files.Position(e.Type.Func)
fn2 := &Function{ fn2 := &Function{
name: fmt.Sprintf("func@%d.%d", posn.Line, posn.Column), 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, pos: e.Type.Func,
Enclosing: fn, Enclosing: fn,
Pkg: fn.Pkg, Pkg: fn.Pkg,
@ -717,7 +710,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
return fn2 return fn2
} }
v := &MakeClosure{Fn: fn2} v := &MakeClosure{Fn: fn2}
v.setType(fn.Pkg.TypeOf(e)) v.setType(fn.Pkg.typeOf(e))
for _, fv := range fn2.FreeVars { for _, fv := range fn2.FreeVars {
v.Bindings = append(v.Bindings, fv.outer) v.Bindings = append(v.Bindings, fv.outer)
fv.outer = nil fv.outer = nil
@ -728,11 +721,11 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
return b.expr(fn, e.X) return b.expr(fn, e.X)
case *ast.TypeAssertExpr: // single-result form only 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: case *ast.CallExpr:
typ := fn.Pkg.TypeOf(e) typ := fn.Pkg.typeOf(e)
if fn.Pkg.IsType(e.Fun) { if fn.Pkg.info.IsType(e.Fun) {
// Explicit type conversion, e.g. string(x) or big.Int(x) // Explicit type conversion, e.g. string(x) or big.Int(x)
x := b.expr(fn, e.Args[0]) x := b.expr(fn, e.Args[0])
y := emitConv(fn, x, typ) 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. // Call to "intrinsic" built-ins, e.g. new, make, panic.
if id, ok := e.Fun.(*ast.Ident); ok { 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 _, ok := fn.Prog.Builtins[obj]; ok {
if v := b.builtin(fn, id.Name, e.Args, typ, e.Lparen); v != nil { if v := b.builtin(fn, id.Name, e.Args, typ, e.Lparen); v != nil {
return v return v
@ -775,7 +768,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
X: b.expr(fn, e.X), X: b.expr(fn, e.X),
} }
v.setPos(e.OpPos) v.setPos(e.OpPos)
v.setType(fn.Pkg.TypeOf(e)) v.setType(fn.Pkg.typeOf(e))
return fn.emit(v) return fn.emit(v)
default: default:
panic(e.Op) panic(e.Op)
@ -788,7 +781,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
case token.SHL, token.SHR: case token.SHL, token.SHR:
fallthrough fallthrough
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT: 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: 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) 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: case *ast.SliceExpr:
var low, high Value var low, high Value
var x Value var x Value
switch fn.Pkg.TypeOf(e.X).Underlying().(type) { switch fn.Pkg.typeOf(e.X).Underlying().(type) {
case *types.Array: case *types.Array:
// Potentially escaping. // Potentially escaping.
x = b.addr(fn, e.X, true).(address).addr 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, Low: low,
High: high, High: high,
} }
v.setType(fn.Pkg.TypeOf(e)) v.setType(fn.Pkg.typeOf(e))
return fn.emit(v) return fn.emit(v)
case *ast.Ident: case *ast.Ident:
obj := fn.Pkg.ObjectOf(e) obj := fn.Pkg.objectOf(e)
// Global or universal? // Global or universal?
if v, ok := b.lookup(fn.Pkg, obj); ok { if v, ok := b.lookup(fn.Pkg, obj); ok {
if _, ok := obj.(*types.Var); ok { if _, ok := obj.(*types.Var); ok {
@ -836,14 +829,14 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
case *ast.SelectorExpr: case *ast.SelectorExpr:
// p.M where p is a package. // 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) return b.expr(fn, e.Sel)
} }
// (*T).f or T.f, the method f from the method-set of type T. // (*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) 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 { if m := b.Prog.MethodSet(typ)[id]; m != nil {
return m return m
} }
@ -856,7 +849,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
return b.selector(fn, e, false, false) return b.selector(fn, e, false, false)
case *ast.IndexExpr: case *ast.IndexExpr:
switch t := fn.Pkg.TypeOf(e.X).Underlying().(type) { switch t := fn.Pkg.typeOf(e.X).Underlying().(type) {
case *types.Array: case *types.Array:
// Non-addressable array (in a register). // Non-addressable array (in a register).
v := &Index{ v := &Index{
@ -868,7 +861,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
case *types.Map: case *types.Map:
// Maps are not addressable. // Maps are not addressable.
mapt := fn.Pkg.TypeOf(e.X).Underlying().(*types.Map) mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map)
v := &Lookup{ v := &Lookup{
X: b.expr(fn, e.X), X: b.expr(fn, e.X),
Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()), 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. // findMethod returns (nil, nil) if no such method was found.
// //
func (b *Builder) findMethod(fn *Function, base ast.Expr, id Id) (*Function, Value) { 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. // Consult method-set of X.
if m := b.Prog.MethodSet(typ)[id]; m != nil { 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 c.HasEllipsis = e.Ellipsis != 0
// Is the call of the form x.f()? // 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. // Case 0: e.Fun evaluates normally to a function.
if !ok { 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. // 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)). // This is a specialization of expr(ast.Ident(obj)).
if v, ok := b.lookup(fn.Pkg, obj); ok { if v, ok := b.lookup(fn.Pkg, obj); ok {
if _, ok := v.(*Function); !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. // an interface. Treat like case 0.
// TODO(adonovan): opt: inline expr() here, to make the call static // TODO(adonovan): opt: inline expr() here, to make the call static
// and to avoid generation of a stub for an interface method. // 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) c.Func = b.expr(fn, e.Fun)
return return
} }
@ -998,7 +991,7 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
return 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 *types.Struct, *types.Pointer:
// Case 3: x.f() where x.f is a function value in a // Case 3: x.f() where x.f is a function value in a
// struct field f; not a method call. f is a 'var' // 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) b.setCallFunc(fn, e, c)
// Then append the other actual parameters. // 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 { if sig == nil {
sig = builtinCallSignature(fn.Pkg.TypeInfo, e) sig = fn.Pkg.info.BuiltinCallSignature(e)
} }
c.Args = b.emitCallArgs(fn, sig, e, c.Args) 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{} var lval lvalue = blank{}
if g != nil { if g != nil {
// Mode A: initialized only a single global, g // 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 continue
} }
g.spec = nil g.spec = nil
@ -1188,7 +1181,7 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global
} else { } else {
// Mode B: initialize all globals. // Mode B: initialize all globals.
if !isBlankIdent(id) { if !isBlankIdent(id) {
g2 := b.globals[init.Pkg.ObjectOf(id)].(*Global) g2 := b.globals[init.Pkg.objectOf(id)].(*Global)
if g2.spec == nil { if g2.spec == nil {
continue // already done continue // already done
} }
@ -1222,7 +1215,7 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global
result := tuple.Type().(*types.Tuple) result := tuple.Type().(*types.Tuple)
for i, id := range spec.Names { for i, id := range spec.Names {
if !isBlankIdent(id) { 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 g.spec = nil // just an optimization
emitStore(init, g, emitExtract(init, tuple, i, result.At(i).Type())) 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 { for i, id := range spec.Names {
var lval lvalue = blank{} var lval lvalue = blank{}
if !isBlankIdent(id) { 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]) 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. // Locals are implicitly zero-initialized.
for _, id := range spec.Names { for _, id := range spec.Names {
if !isBlankIdent(id) { 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) result := tuple.Type().(*types.Tuple)
for i, id := range spec.Names { for i, id := range spec.Names {
if !isBlankIdent(id) { 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())) 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 { if isDef {
// Local may be "redeclared" in the same // Local may be "redeclared" in the same
// scope, so don't blindly create anew. // 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 { if _, ok := fn.objects[obj]; !ok {
fn.addNamedLocal(obj) 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) { func (b *Builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ types.Type) {
// TODO(adonovan): document how and why typ ever differs from // TODO(adonovan): document how and why typ ever differs from
// fn.Pkg.TypeOf(e). // fn.Pkg.typeOf(e).
switch t := typ.Underlying().(type) { switch t := typ.Underlying().(type) {
case *types.Struct: case *types.Struct:
@ -1577,11 +1570,11 @@ func (b *Builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
var id *ast.Ident var id *ast.Ident
switch ass := s.Assign.(type) { switch ass := s.Assign.(type) {
case *ast.ExprStmt: // x.(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) 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) id = ass.Lhs[0].(*ast.Ident)
y = fn.addNamedLocal(fn.Pkg.ObjectOf(id)) y = fn.addNamedLocal(fn.Pkg.objectOf(id))
emitStore(fn, y, x) 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 <T_i> var ti Value // t_i, ok := typeassert,ok x <T_i>
for _, cond := range cc.List { for _, cond := range cc.List {
next = fn.newBasicBlock("typeswitch.next") next = fn.newBasicBlock("typeswitch.next")
casetype = fn.Pkg.TypeOf(cond) casetype = fn.Pkg.typeOf(cond)
var condv Value var condv Value
if casetype == tUntypedNil { if casetype == tUntypedNil {
condv = emitCompare(fn, token.EQL, x, nilLiteral(x.Type()), token.NoPos) 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 { if id != nil && len(cc.List) == 1 && casetype != tUntypedNil {
// Declare a new shadow local variable of the // Declare a new shadow local variable of the
// same name but a more specific type. // 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.name += "'" // debugging aid
y2.typ = pointer(casetype)
emitStore(fn, y2, ti) emitStore(fn, y2, ti)
} }
fn.targets = &targets{ fn.targets = &targets{
@ -1628,6 +1622,9 @@ func (b *Builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
} }
b.stmtList(fn, cc.Body) b.stmtList(fn, cc.Body)
fn.targets = fn.targets.tail fn.targets = fn.targets.tail
if id != nil {
fn.objects[fn.Pkg.objectOf(id)] = y // restore previous y binding
}
emitJump(fn, done) emitJump(fn, done)
fn.currentBlock = next fn.currentBlock = next
} }
@ -1689,13 +1686,13 @@ func (b *Builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
case *ast.AssignStmt: // x := <-ch case *ast.AssignStmt: // x := <-ch
states = append(states, SelectState{ states = append(states, SelectState{
Dir: ast.RECV, 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 case *ast.ExprStmt: // <-ch
states = append(states, SelectState{ states = append(states, SelectState{
Dir: ast.RECV, 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) { switch comm := clause.Comm.(type) {
case *ast.AssignStmt: // x := <-states[state].Chan 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()) recv := emitTypeAssert(fn, emitExtract(fn, triple, 1, tEface), xdecl.Type().Deref())
emitStore(fn, xdecl, recv) emitStore(fn, xdecl, recv)
if len(comm.Lhs) == 2 { // x, ok := ... 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())) 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) { func (b *Builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock) {
var tk, tv types.Type var tk, tv types.Type
if !isBlankIdent(s.Key) { if !isBlankIdent(s.Key) {
tk = fn.Pkg.TypeOf(s.Key) tk = fn.Pkg.typeOf(s.Key)
} }
if s.Value != nil && !isBlankIdent(s.Value) { 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 // 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. // always creates a new one.
if s.Tok == token.DEFINE { if s.Tok == token.DEFINE {
if tk != nil { if tk != nil {
fn.addNamedLocal(fn.Pkg.ObjectOf(s.Key.(*ast.Ident))) fn.addNamedLocal(fn.Pkg.objectOf(s.Key.(*ast.Ident)))
} }
if tv != nil { 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{ fn.emit(&Send{
Chan: b.expr(fn, s.Chan), Chan: b.expr(fn, s.Chan),
X: emitConv(fn, b.expr(fn, s.Value), 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, pos: s.Arrow,
}) })
@ -2336,7 +2333,7 @@ func (b *Builder) buildFunction(fn *Function) {
fn.FullName(), fn.Prog.Files.Position(fn.pos))() fn.FullName(), fn.Prog.Files.Position(fn.pos))()
} }
fn.startBody() fn.startBody()
fn.createSyntacticParams(fn.Pkg.idents) fn.createSyntacticParams()
b.stmt(fn, fn.syntax.body) b.stmt(fn, fn.syntax.body)
if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb.Preds != nil) { if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb.Preds != nil) {
// Run function calls deferred in this function when // 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 // tree (for funcs and vars only); it will be used during the build
// phase. // phase.
// //
// (CREATE phase.)
//
func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
name := obj.Name() name := obj.Name()
switch obj := obj.(type) { 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 // typechecker object (var, func, const or type) associated with the
// specified decl. // specified decl.
// //
// (CREATE phase.)
//
func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) { func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) {
switch decl := decl.(type) { switch decl := decl.(type) {
case *ast.GenDecl: // import, const, type or var 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 _, spec := range decl.Specs {
for _, id := range spec.(*ast.ValueSpec).Names { for _, id := range spec.(*ast.ValueSpec).Names {
if !isBlankIdent(id) { 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 _, spec := range decl.Specs {
for _, id := range spec.(*ast.ValueSpec).Names { for _, id := range spec.(*ast.ValueSpec).Names {
if !isBlankIdent(id) { 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 { for _, spec := range decl.Specs {
id := spec.(*ast.TypeSpec).Name id := spec.(*ast.TypeSpec).Name
if !isBlankIdent(id) { 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 return // init blocks aren't functions
} }
if !isBlankIdent(id) { 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 // createPackage constructs an SSA Package from an error-free
// type-checker's package so formed, plus the AST type information. // package described by info, and populates its Members mapping.
//
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.
// //
// The real work of building SSA form for each function is not done // The real work of building SSA form for each function is not done
// until a subsequent call to BuildPackage. // until a subsequent call to BuildPackage.
// //
// If files is non-nil, its declarations will be used to generate code // (CREATE phase.)
// 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.
// //
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{ p := &Package{
Prog: b.Prog, Prog: b.Prog,
Types: typkg, Members: make(map[string]Member),
Members: make(map[string]Member), Types: info.Pkg,
Files: files, info: info, // transient (CREATE and BUILD phases)
nTo1Vars: make(map[*ast.ValueSpec]bool),
} }
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). // Add init() function (but not to Members since it can't be referenced).
p.Init = &Function{ p.Init = &Function{
name: "init", name: "init",
@ -2559,10 +2490,9 @@ func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, fil
// CREATE phase. // CREATE phase.
// Allocate all package members: vars, funcs and consts and types. // Allocate all package members: vars, funcs and consts and types.
if len(files) > 0 { if len(info.Files) > 0 {
// Go source package. // Go source package.
for _, file := range info.Files {
for _, file := range p.Files {
for _, decl := range file.Decls { for _, decl := range file.Decls {
b.membersFromDecl(p, decl) b.membersFromDecl(p, decl)
} }
@ -2571,22 +2501,12 @@ func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, fil
// GC-compiled binary package. // GC-compiled binary package.
// No code. // No code.
// No position information. // No position information.
scope := p.Types.Scope() scope := p.Types.Scope()
for i, n := 0, scope.NumEntries(); i < n; i++ { for i, n := 0, scope.NumEntries(); i < n; i++ {
b.memberFromObject(p, scope.At(i), nil) 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. // Add initializer guard variable.
initguard := &Global{ initguard := &Global{
Pkg: p, Pkg: p,
@ -2599,6 +2519,8 @@ func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, fil
p.DumpTo(os.Stderr) p.DumpTo(os.Stderr)
} }
p.info = nil
return p return p
} }
@ -2620,7 +2542,7 @@ func (b *Builder) buildDecl(pkg *Package, decl ast.Decl) {
if isBlankIdent(id) { if isBlankIdent(id) {
continue continue
} }
nt := pkg.ObjectOf(id).Type().(*types.Named) nt := pkg.objectOf(id).Type().(*types.Named)
for i, n := 0, nt.NumMethods(); i < n; i++ { for i, n := 0, nt.NumMethods(); i < n; i++ {
b.buildFunction(b.Prog.concreteMethods[nt.Method(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.targets = init.targets.tail
init.currentBlock = next 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. // Package-level function.
b.buildFunction(m.(*Function)) b.buildFunction(m.(*Function))
} }
@ -2694,11 +2616,23 @@ func (b *Builder) BuildPackage(p *Package) {
if !atomic.CompareAndSwapInt32(&p.started, 0, 1) { if !atomic.CompareAndSwapInt32(&p.started, 0, 1) {
return // already started 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 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 { if b.Context.Mode&LogSource != 0 {
defer logStack("build package %s", p.Types.Path())() defer logStack("build %s", p)()
} }
init := p.Init init := p.Init
init.startBody() init.startBody()
@ -2717,7 +2651,7 @@ func (b *Builder) BuildPackage(p *Package) {
// transitive closure of dependencies, // transitive closure of dependencies,
// e.g. when using GcImporter. // e.g. when using GcImporter.
seen := make(map[*types.Package]bool) seen := make(map[*types.Package]bool)
for _, file := range p.Files { for _, file := range info.Files {
for _, imp := range file.Imports { for _, imp := range file.Imports {
path, _ := strconv.Unquote(imp.Path.Value) path, _ := strconv.Unquote(imp.Path.Value)
if path == "unsafe" { if path == "unsafe" {
@ -2731,7 +2665,7 @@ func (b *Builder) BuildPackage(p *Package) {
p2 := b.packages[typkg] p2 := b.packages[typkg]
if p2 == nil { 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 var v Call
@ -2750,17 +2684,14 @@ func (b *Builder) BuildPackage(p *Package) {
// //
// We also ensure all functions and methods are built, even if // We also ensure all functions and methods are built, even if
// they are unreachable. // they are unreachable.
for _, file := range p.Files { for _, file := range info.Files {
for _, decl := range file.Decls { for _, decl := range file.Decls {
b.buildDecl(p, decl) b.buildDecl(p, decl)
} }
} }
// Clear out the typed ASTs unless otherwise requested. // We no longer need ASTs or go/types deductions.
if retain := b.Context.RetainAST; retain == nil || !retain(p) { p.info = nil
p.Files = nil
p.TypeInfo = nil
}
p.nTo1Vars = nil p.nTo1Vars = nil
// Finish up. // Finish up.
@ -2770,3 +2701,17 @@ func (b *Builder) BuildPackage(p *Package) {
init.emit(new(Ret)) init.emit(new(Ret))
init.finishBody() 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)
}

View File

@ -1,6 +1,7 @@
package ssa_test package ssa_test
import ( import (
"code.google.com/p/go.tools/importer"
"code.google.com/p/go.tools/ssa" "code.google.com/p/go.tools/ssa"
"fmt" "fmt"
"go/ast" "go/ast"
@ -35,24 +36,27 @@ func main() {
fmt.Println(message) fmt.Println(message)
} }
` `
// Construct an importer. Imports will be loaded as if by 'go build'.
// Construct a builder. Imports will be loaded as if by 'go build'. imp := importer.New(&importer.Context{Loader: importer.MakeGoBuildLoader(nil)})
builder := ssa.NewBuilder(&ssa.Context{Loader: ssa.MakeGoBuildLoader(nil)})
// Parse the input file. // 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 { if err != nil {
fmt.Printf(err.Error()) // parse error fmt.Printf(err.Error()) // parse error
return return
} }
// Create a "main" package containing one file. // 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 { if err != nil {
fmt.Printf(err.Error()) // type error fmt.Printf(err.Error()) // type error
return return
} }
// Construct an SSA builder.
builder := ssa.NewBuilder(&ssa.Context{}, imp)
mainPkg := builder.PackageFor(info.Pkg)
// Print out the package. // Print out the package.
mainPkg.DumpTo(os.Stdout) mainPkg.DumpTo(os.Stdout)
fmt.Println() fmt.Println()
@ -70,7 +74,7 @@ func main() {
// Output: // Output:
// //
// Package main: // package main:
// var init$guard *bool // var init$guard *bool
// func main func() // func main func()
// const message message = "Hello, World!":untyped string // const message message = "Hello, World!":untyped string

View File

@ -213,21 +213,18 @@ func (f *Function) startBody() {
// and named result locals) for all the parameters declared in the // and named result locals) for all the parameters declared in the
// syntax. In addition it populates the f.objects mapping. // syntax. In addition it populates the f.objects mapping.
// //
// idents must be a mapping from syntactic identifiers to their
// canonical type objects.
//
// Preconditions: // Preconditions:
// f.syntax != nil, i.e. this is a Go source function. // f.syntax != nil, i.e. this is a Go source function.
// f.startBody() was called. // f.startBody() was called.
// Postcondition: // Postcondition:
// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0) // 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). // Receiver (at most one inner iteration).
if f.syntax.recvField != nil { if f.syntax.recvField != nil {
for _, field := range f.syntax.recvField.List { for _, field := range f.syntax.recvField.List {
for _, n := range field.Names { for _, n := range field.Names {
f.addSpilledParam(idents[n]) f.addSpilledParam(f.Pkg.objectOf(n))
} }
// Anonymous receiver? No need to spill. // Anonymous receiver? No need to spill.
if field.Names == nil { 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 n := len(f.Params) // 1 if has recv, 0 otherwise
for _, field := range f.syntax.paramFields.List { for _, field := range f.syntax.paramFields.List {
for _, n := range field.Names { for _, n := range field.Names {
f.addSpilledParam(idents[n]) f.addSpilledParam(f.Pkg.objectOf(n))
} }
// Anonymous parameter? No need to spill. // Anonymous parameter? No need to spill.
if field.Names == nil { 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 { for _, field := range f.syntax.resultFields.List {
// Implicit "var" decl of locals for named results. // Implicit "var" decl of locals for named results.
for _, n := range field.Names { 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)))
} }
} }
} }

View File

@ -530,7 +530,7 @@ func setGlobal(i *interpreter, pkg *ssa.Package, name string, v value) {
*g = v *g = v
return 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. // Interpret interprets the Go program whose main package is mainpkg.

View File

@ -10,6 +10,7 @@ import (
"strings" "strings"
"testing" "testing"
"code.google.com/p/go.tools/importer"
"code.google.com/p/go.tools/ssa" "code.google.com/p/go.tools/ssa"
"code.google.com/p/go.tools/ssa/interp" "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) inputs = append(inputs, dir+i)
} }
b := ssa.NewBuilder(&ssa.Context{ impctx := &importer.Context{
Mode: ssa.SanityCheckFunctions, Loader: importer.MakeGoBuildLoader(nil),
Loader: ssa.MakeGoBuildLoader(nil), }
}) imp := importer.New(impctx)
files, err := ssa.ParseFiles(b.Prog.Files, ".", inputs...) files, err := importer.ParseFiles(imp.Fset, ".", inputs...)
if err != nil { if err != nil {
t.Errorf("ssa.ParseFiles(%s) failed: %s", inputs, err.Error()) t.Errorf("ssa.ParseFiles(%s) failed: %s", inputs, err.Error())
return false 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) 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 { if err != nil {
t.Errorf("ssa.Builder.CreatePackage(%s) failed: %s", inputs, err.Error()) t.Errorf("ssa.Builder.CreatePackage(%s) failed: %s", inputs, err.Error())
return false return false
} }
b := ssa.NewBuilder(&ssa.Context{Mode: ssa.SanityCheckFunctions}, imp)
mainpkg := b.PackageFor(info.Pkg)
b.BuildAllPackages() b.BuildAllPackages()
b = nil // discard Builder b = nil // discard Builder

View File

@ -356,11 +356,11 @@ func (s *MapUpdate) String() string {
} }
func (p *Package) String() string { func (p *Package) String() string {
return "Package " + p.Types.Path() return "package " + p.Types.Path()
} }
func (p *Package) DumpTo(w io.Writer) { 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 var names []string
maxname := 0 maxname := 0

View File

@ -149,7 +149,11 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
if nt, ok := t.(*types.Named); ok { if nt, ok := t.(*types.Named); ok {
for i, n := 0, nt.NumMethods(); i < n; i++ { for i, n := 0, nt.NumMethods(); i < n; i++ {
m := nt.Method(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() 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) c.Call.Args = append(c.Call.Args, v)
} else { } else {
c.Call.Recv = v 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 c.Call.Method = 0
} }
for _, arg := range fn.Params[1:] { for _, arg := range fn.Params[1:] {

View File

@ -6,6 +6,7 @@ package ssa
// since neither depends on SSA internals. // since neither depends on SSA internals.
import ( import (
"code.google.com/p/go.tools/importer"
"go/ast" "go/ast"
"go/token" "go/token"
) )
@ -23,16 +24,19 @@ func tokenFileContainsPos(f *token.File, pos token.Pos) bool {
// program prog. exact is defined as for standalone // program prog. exact is defined as for standalone
// PathEnclosingInterval. // PathEnclosingInterval.
// //
// imp provides ASTs for the program's packages.
//
// The result is (nil, nil, false) if not found. // The result is (nil, nil, false) if not found.
// //
func (prog *Program) PathEnclosingInterval(start, end token.Pos) (pkg *Package, path []ast.Node, exact bool) { func (prog *Program) PathEnclosingInterval(imp *importer.Importer, start, end token.Pos) (pkg *Package, path []ast.Node, exact bool) {
for _, pkg = range prog.Packages { for path, info := range imp.Packages {
for _, f := range pkg.Files { pkg := prog.Packages[path]
for _, f := range info.Files {
if !tokenFileContainsPos(prog.Files.File(f.Package), start) { if !tokenFileContainsPos(prog.Files.File(f.Package), start) {
continue continue
} }
if path, exact = PathEnclosingInterval(f, start, end); path != nil { if path, exact := PathEnclosingInterval(f, start, end); path != nil {
return return pkg, path, exact
} }
} }
} }

View File

@ -7,6 +7,7 @@ package ssa_test
import ( import (
"bytes" "bytes"
"code.google.com/p/go.tools/importer"
"code.google.com/p/go.tools/ssa" "code.google.com/p/go.tools/ssa"
"fmt" "fmt"
"go/ast" "go/ast"
@ -235,8 +236,8 @@ func TestEnclosingFunction(t *testing.T) {
"900", "func@2.27"}, "900", "func@2.27"},
} }
for _, test := range tests { for _, test := range tests {
b := ssa.NewBuilder(new(ssa.Context)) // (NB: no Loader) imp := importer.New(new(importer.Context)) // (NB: no Loader)
f, start, end := findInterval(t, b.Prog.Files, test.input, test.substr) f, start, end := findInterval(t, imp.Fset, test.input, test.substr)
if f == nil { if f == nil {
continue continue
} }
@ -245,12 +246,14 @@ func TestEnclosingFunction(t *testing.T) {
t.Errorf("EnclosingFunction(%q) not exact", test.substr) t.Errorf("EnclosingFunction(%q) not exact", test.substr)
continue continue
} }
pkg, err := b.CreatePackage("main", []*ast.File{f}) info, err := imp.CreateSourcePackage("main", []*ast.File{f})
if err != nil { if err != nil {
t.Error(err.Error()) t.Error(err.Error())
continue continue
} }
b := ssa.NewBuilder(new(ssa.Context), imp)
pkg := b.PackageFor(info.Pkg)
b.BuildPackage(pkg) b.BuildPackage(pkg)
name := "(none)" name := "(none)"

View File

@ -11,6 +11,7 @@ import (
"code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/exact"
"code.google.com/p/go.tools/go/types" "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. // A Program is a partial or complete Go program converted to SSA form.
@ -18,7 +19,7 @@ import (
// lifetime. // lifetime.
// //
type Program struct { 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 Packages map[string]*Package // all loaded Packages, keyed by import path
Builtins map[types.Object]*Builtin // all built-in functions, keyed by typechecker objects. Builtins map[types.Object]*Builtin // all built-in functions, keyed by typechecker objects.
@ -35,18 +36,14 @@ type Program struct {
// //
type Package struct { type Package struct {
Prog *Program // the owning program 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 Members map[string]Member // all exported and unexported members of the package
Init *Function // the package's (concatenated) init function 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, // The following fields are set transiently during building,
// then cleared. // then cleared.
started int32 // atomically tested and set at start of build phase 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 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) String() string { return t.Name() }
func (t *Type) Type() types.Type { return t.NamedType } 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) Name() string { return c.name }
func (c *Constant) Pos() token.Pos { return c.pos } func (c *Constant) Pos() token.Pos { return c.pos }
func (c *Constant) String() string { return c.Name() } func (c *Constant) String() string { return c.Name() }

View File

@ -11,6 +11,7 @@ import (
"os" "os"
"runtime/pprof" "runtime/pprof"
"code.google.com/p/go.tools/importer"
"code.google.com/p/go.tools/ssa" "code.google.com/p/go.tools/ssa"
"code.google.com/p/go.tools/ssa/interp" "code.google.com/p/go.tools/ssa/interp"
) )
@ -50,6 +51,8 @@ func main() {
flag.Parse() flag.Parse()
args := flag.Args() args := flag.Args()
impctx := importer.Context{Loader: importer.MakeGoBuildLoader(nil)}
var mode ssa.BuilderMode var mode ssa.BuilderMode
for _, c := range *buildFlag { for _, c := range *buildFlag {
switch c { switch c {
@ -64,7 +67,7 @@ func main() {
case 'N': case 'N':
mode |= ssa.NaiveForm mode |= ssa.NaiveForm
case 'G': case 'G':
mode |= ssa.UseGCImporter impctx.Loader = nil
case 'L': case 'L':
mode |= ssa.BuildSerially mode |= ssa.BuildSerially
default: default:
@ -99,19 +102,24 @@ func main() {
defer pprof.StopCPUProfile() defer pprof.StopCPUProfile()
} }
context := &ssa.Context{ // Load, parse and type-check the program.
Mode: mode, imp := importer.New(&impctx)
Loader: ssa.MakeGoBuildLoader(nil), info, args, err := importer.CreatePackageFromArgs(imp, args)
}
b := ssa.NewBuilder(context)
mainpkg, args, err := ssa.CreatePackageFromArgs(b, args)
if err != nil { if err != nil {
log.Fatal(err.Error()) 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.BuildAllPackages()
b = nil // discard Builder b = nil // discard Builder
// Run the interpreter.
if *runFlag { if *runFlag {
interp.Interpret(mainpkg, interpMode, mainpkg.Name(), args) interp.Interpret(mainpkg, interpMode, info.Pkg.Path(), args)
} }
} }

View File

@ -18,8 +18,8 @@ func unreachable() {
//// AST utilities //// AST utilities
// noparens returns e with any enclosing parentheses stripped. // unparen returns e with any enclosing parentheses stripped.
func noparens(e ast.Expr) ast.Expr { func unparen(e ast.Expr) ast.Expr {
for { for {
p, ok := e.(*ast.ParenExpr) p, ok := e.(*ast.ParenExpr)
if !ok { if !ok {