exp/ssa: document principal interfaces in package doc.
Also:
- add a runnable example to the package doc.
- fix Literal.Name() double-quoting bug.
- eliminate underlyingType in favour of Type.Underlying()
- eliminate indirectType in favour of Type.Deref()
(NB, this does remove an assertion).
- Refactor generation of init() calls now that the need to
iterate over the syntax (not types) is not considered a
workaround for a bug.
- GorootLoader renamed to GoBuildLoader and parameterized over
a *go/build.Context.
R=gri
CC=golang-dev
https://golang.org/cl/9100047
diff --git a/ssa/builder.go b/ssa/builder.go
index e366400..96c94af 100644
--- a/ssa/builder.go
+++ b/ssa/builder.go
@@ -83,7 +83,7 @@
// 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.) GoRootLoader.
+ // See (e.g.) MakeGoBuildLoader.
Loader SourceLoader
// RetainAST is an optional user predicate that determines
@@ -416,7 +416,7 @@
}
case "new":
- return emitNew(fn, indirectType(typ.Underlying()), pos)
+ return emitNew(fn, typ.Underlying().Deref(), pos)
case "len", "cap":
// Special case: len or cap of an array or *array is
@@ -1724,12 +1724,12 @@
switch comm := clause.Comm.(type) {
case *ast.AssignStmt: // x := <-states[state].Chan
xdecl := fn.addNamedLocal(fn.Pkg.ObjectOf(comm.Lhs[0].(*ast.Ident)))
- recv := emitTypeAssert(fn, emitExtract(fn, triple, 1, tEface), indirectType(xdecl.Type()))
+ 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)))
- emitStore(fn, okdecl, emitExtract(fn, triple, 2, indirectType(okdecl.Type())))
+ emitStore(fn, okdecl, emitExtract(fn, triple, 2, okdecl.Type().Deref()))
}
}
b.stmtList(fn, clause.Body)
@@ -2684,32 +2684,35 @@
init.currentBlock = doinit
emitStore(init, initguard, vTrue)
- // TODO(gri): fix: the types.Package.Imports map may contains
- // entries for other package's import statements, if produced
- // by GcImport. Project it down to just the ones for us.
- imports := make(map[string]*types.Package)
+ // Call the init() function of each package we import.
+ // We iterate over the syntax (p.Files.Imports) not the types
+ // (p.Types.Imports()) because the latter may contain the
+ // transitive closure of dependencies,
+ // e.g. when using GcImporter.
+ seen := make(map[*types.Package]bool)
for _, file := range p.Files {
for _, imp := range file.Imports {
path, _ := strconv.Unquote(imp.Path.Value)
- if path != "unsafe" {
- imports[path] = p.Types.Imports()[path]
+ if path == "unsafe" {
+ continue
}
- }
- }
+ typkg := p.Types.Imports()[path]
+ if seen[typkg] {
+ continue
+ }
+ seen[typkg] = true
- // Call the init() function of each package we import.
- // Order is unspecified (and is in fact nondeterministic).
- for name, imported := range imports {
- p2 := b.packages[imported]
- if p2 == nil {
- panic("Building " + p.Name() + ": CreatePackage has not been called for package " + name)
- }
+ p2 := b.packages[typkg]
+ if p2 == nil {
+ panic("Building " + p.Name() + ": CreatePackage has not been called for package " + path)
+ }
- var v Call
- v.Call.Func = p2.Init
- v.Call.pos = init.pos
- v.setType(types.NewTuple())
- init.emit(&v)
+ var v Call
+ v.Call.Func = p2.Init
+ v.Call.pos = init.pos
+ v.setType(types.NewTuple())
+ init.emit(&v)
+ }
}
// Visit the package's var decls and init funcs in source
diff --git a/ssa/doc.go b/ssa/doc.go
index bb84503..1ebbedc 100644
--- a/ssa/doc.go
+++ b/ssa/doc.go
@@ -27,8 +27,9 @@
// By supplying an instance of the SourceLocator function prototype,
// clients may control how the builder locates, loads and parses Go
// sources files for imported packages. This package provides
-// GorootLoader, which uses go/build to locate packages in the Go
-// source distribution, and go/parser to parse them.
+// MakeGoBuildLoader, which creates a loader that uses go/build to
+// locate packages in the Go source distribution, and go/parser to
+// parse them.
//
// The builder initially builds a naive SSA form in which all local
// variables are addresses of stack locations with explicit loads and
@@ -38,6 +39,61 @@
// subsequent analyses; this pass can be skipped by setting the
// NaiveForm builder flag.
//
+// The primary interfaces of this package are:
+//
+// - Member: a named member of a Go package.
+// - Value: an expression that yields a value.
+// - Instruction: a statement that consumes values and performs computation.
+//
+// A computation that yields a result implements both the Value and
+// Instruction interfaces. The following table shows for each
+// concrete type which of these interfaces it implements.
+//
+// Value? Instruction? Member?
+// *Alloc ✔ ✔
+// *BinOp ✔ ✔
+// *Builtin ✔ ✔
+// *Call ✔ ✔
+// *Capture ✔
+// *ChangeInterface ✔ ✔
+// *ChangeType ✔ ✔
+// *Constant ✔ (const)
+// *Convert ✔ ✔
+// *Defer ✔
+// *Extract ✔ ✔
+// *Field ✔ ✔
+// *FieldAddr ✔ ✔
+// *Function ✔ ✔ (func)
+// *Global ✔ ✔ (var)
+// *Go ✔
+// *If ✔
+// *Index ✔ ✔
+// *IndexAddr ✔ ✔
+// *Jump ✔
+// *Literal ✔
+// *Lookup ✔ ✔
+// *MakeChan ✔ ✔
+// *MakeClosure ✔ ✔
+// *MakeInterface ✔ ✔
+// *MakeMap ✔ ✔
+// *MakeSlice ✔ ✔
+// *MapUpdate ✔
+// *Next ✔ ✔
+// *Panic ✔
+// *Parameter ✔
+// *Phi ✔ ✔
+// *Range ✔ ✔
+// *Ret ✔
+// *RunDefers ✔
+// *Select ✔ ✔
+// *Slice ✔ ✔
+// *Type ✔ (type)
+// *TypeAssert ✔ ✔
+// *UnOp ✔ ✔
+//
+// Other key types in this package include: Program, Package, Function
+// and BasicBlock.
+//
// The program representation constructed by this package is fully
// resolved internally, i.e. it does not rely on the names of Values,
// Packages, Functions, Types or BasicBlocks for the correct
@@ -101,16 +157,15 @@
// TODO(adonovan): demonstrate more features in the example:
// parameters and control flow at the least.
//
-// TODO(adonovan): Consider how token.Pos source location information
-// should be made available generally. Currently it is only present
-// in package Members and selected Instructions.
-//
// TODO(adonovan): Consider the exceptional control-flow implications
// of defer and recover().
//
-// TODO(adonovan): build tables/functions that relate source variables
-// to SSA variables to assist user interfaces that make queries about
-// specific source entities.
+// TODO(adonovan): Consider how token.Pos source location information
+// should be made available generally. Currently it is only present
+// in package Members and selected Instructions for which there is a
+// direct source correspondence. We'll need to work harder to tie all
+// defs/uses of named variables together, esp. because SSA splits them
+// into separate webs.
//
// TODO(adonovan): it is practically impossible for clients to
// construct well-formed SSA functions/packages/programs directly; we
diff --git a/ssa/emit.go b/ssa/emit.go
index 83b3c4a..43f184d 100644
--- a/ssa/emit.go
+++ b/ssa/emit.go
@@ -24,7 +24,7 @@
//
func emitLoad(f *Function, addr Value) Value {
v := &UnOp{Op: token.MUL, X: addr}
- v.setType(indirectType(addr.Type()))
+ v.setType(addr.Type().Deref())
return f.emit(v)
}
@@ -200,7 +200,7 @@
func emitStore(f *Function, addr, val Value) {
f.emit(&Store{
Addr: addr,
- Val: emitConv(f, val, indirectType(addr.Type())),
+ Val: emitConv(f, val, addr.Type().Deref()),
})
}
diff --git a/ssa/example_test.go b/ssa/example_test.go
new file mode 100644
index 0000000..27f6017
--- /dev/null
+++ b/ssa/example_test.go
@@ -0,0 +1,62 @@
+package ssa_test
+
+import (
+ "code.google.com/p/go.exp/ssa"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "os"
+)
+
+// This example demonstrates the SSA builder.
+func Example() {
+ const hello = `
+package main
+
+import "fmt"
+
+func main() {
+ fmt.Println("Hello, World!")
+}
+`
+
+ // Construct a builder. Imports will be loaded as if by 'go build'.
+ builder := ssa.NewBuilder(&ssa.Context{Loader: ssa.MakeGoBuildLoader(nil)})
+
+ // Parse the input file.
+ file, err := parser.ParseFile(builder.Prog.Files, "hello.go", hello, parser.DeclarationErrors)
+ if err != nil {
+ fmt.Printf("Parsing failed: %s\n", err.Error())
+ return
+ }
+
+ // Create a "main" package containing one file.
+ mainPkg, err := builder.CreatePackage("main", []*ast.File{file})
+ if err != nil {
+ fmt.Printf("Type-checking failed: %s\n", err.Error())
+ return
+ }
+
+ // Build SSA code for bodies of functions in mainPkg.
+ builder.BuildPackage(mainPkg)
+
+ // Print out the package-level functions.
+ for _, mem := range mainPkg.Members {
+ if fn, ok := mem.(*ssa.Function); ok {
+ fn.DumpTo(os.Stdout)
+ }
+ }
+
+ // Output:
+ // # Name: main.main
+ // # Declared at hello.go:6:6
+ // func main():
+ // .0.entry: P:0 S:0
+ // a0 = new [1]interface{} *[1]interface{}
+ // t0 = &a0[0:untyped integer] *interface{}
+ // t1 = make interface interface{} <- string ("Hello, World!":string) interface{}
+ // *t0 = t1
+ // t2 = slice a0[:] []interface{}
+ // t3 = fmt.Println(t2) (n int, err error)
+ // ret
+}
diff --git a/ssa/func.go b/ssa/func.go
index 2867527..a6308aa 100644
--- a/ssa/func.go
+++ b/ssa/func.go
@@ -543,7 +543,7 @@
if len(f.Locals) > 0 {
io.WriteString(w, "# Locals:\n")
for i, l := range f.Locals {
- fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), indirectType(l.Type()))
+ fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), l.Type().Deref())
}
}
diff --git a/ssa/importer.go b/ssa/importer.go
index dc099a0..e2aa57a 100644
--- a/ssa/importer.go
+++ b/ssa/importer.go
@@ -69,28 +69,35 @@
return typkg, nil
}
-// GorootLoader is an implementation of the SourceLoader function
-// prototype that loads and parses Go source files from the package
-// directory beneath $GOROOT/src/pkg.
+// 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).
//
-// TODO(adonovan): get rsc and adg (go/build owners) to review this.
-// TODO(adonovan): permit clients to specify a non-default go/build.Context.
+// ctxt specifies the go/build.Context to use; if nil, the default
+// Context is used.
//
-func GorootLoader(fset *token.FileSet, path string) (files []*ast.File, err error) {
- // TODO(adonovan): fix: Do we need cwd? Shouldn't ImportDir(path) / $GOROOT suffice?
+func MakeGoBuildLoader(ctxt *build.Context) SourceLoader {
srcDir, err := os.Getwd()
if err != nil {
- return // serious misconfiguration
+ return nil // serious misconfiguration
}
- bp, err := build.Import(path, srcDir, 0)
- if err != nil {
- return // import failed
+ if ctxt == nil {
+ ctxt = &build.Default
}
- files, err = ParseFiles(fset, bp.Dir, bp.GoFiles...)
- if err != nil {
- return nil, err
+ 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
}
- return
}
// ParseFiles parses the Go source files files within directory dir
diff --git a/ssa/interp/interp.go b/ssa/interp/interp.go
index d29445e..b22d4bb 100644
--- a/ssa/interp/interp.go
+++ b/ssa/interp/interp.go
@@ -238,11 +238,11 @@
// local
addr = fr.env[instr].(*value)
}
- *addr = zero(indirectType(instr.Type()))
+ *addr = zero(instr.Type().Deref())
case *ssa.MakeSlice:
slice := make([]value, asInt(fr.get(instr.Cap)))
- tElt := underlyingType(instr.Type()).(*types.Slice).Elem()
+ tElt := instr.Type().Underlying().(*types.Slice).Elem()
for i := range slice {
slice[i] = zero(tElt)
}
@@ -253,7 +253,7 @@
if instr.Reserve != nil {
reserve = asInt(fr.get(instr.Reserve))
}
- fr.env[instr] = makeMap(underlyingType(instr.Type()).(*types.Map).Key(), reserve)
+ fr.env[instr] = makeMap(instr.Type().Underlying().(*types.Map).Key(), reserve)
case *ssa.Range:
fr.env[instr] = rangeIter(fr.get(instr.X), instr.X.Type())
@@ -347,7 +347,7 @@
}
var recvV iface
if chosen != -1 {
- recvV.t = underlyingType(instr.States[chosen].Chan.Type()).(*types.Chan).Elem()
+ recvV.t = instr.States[chosen].Chan.Type().Underlying().(*types.Chan).Elem()
if recvOk {
// No need to copy since send makes an unaliased copy.
recvV.v = recv.Interface().(value)
@@ -469,7 +469,7 @@
locals: make([]value, len(fn.Locals)),
}
for i, l := range fn.Locals {
- fr.locals[i] = zero(indirectType(l.Type()))
+ fr.locals[i] = zero(l.Type().Deref())
fr.env[l] = &fr.locals[i]
}
for i, p := range fn.Params {
@@ -553,7 +553,7 @@
for _, m := range pkg.Members {
switch v := m.(type) {
case *ssa.Global:
- cell := zero(indirectType(v.Type()))
+ cell := zero(v.Type().Deref())
i.globals[v] = &cell
}
}
diff --git a/ssa/interp/interp_test.go b/ssa/interp/interp_test.go
index d7b6302..b6ce6f7 100644
--- a/ssa/interp/interp_test.go
+++ b/ssa/interp/interp_test.go
@@ -140,7 +140,10 @@
inputs = append(inputs, dir+i)
}
- b := ssa.NewBuilder(&ssa.Context{Mode: ssa.SanityCheckFunctions, Loader: ssa.GorootLoader})
+ b := ssa.NewBuilder(&ssa.Context{
+ Mode: ssa.SanityCheckFunctions,
+ Loader: ssa.MakeGoBuildLoader(nil),
+ })
files, err := ssa.ParseFiles(b.Prog.Files, ".", inputs...)
if err != nil {
t.Errorf("ssa.ParseFiles(%s) failed: %s", inputs, err.Error())
diff --git a/ssa/interp/ops.go b/ssa/interp/ops.go
index f017d93..4ba11bf 100644
--- a/ssa/interp/ops.go
+++ b/ssa/interp/ops.go
@@ -28,7 +28,7 @@
}
// By destination type:
- switch t := underlyingType(l.Type()).(type) {
+ switch t := l.Type().Underlying().(type) {
case *types.Basic:
switch t.Kind() {
case types.Bool, types.UntypedBool:
@@ -78,7 +78,7 @@
}
case *types.Slice:
- switch et := underlyingType(t.Elem()).(type) {
+ switch et := t.Elem().Underlying().(type) {
case *types.Basic:
switch et.Kind() {
case types.Byte: // string -> []byte
@@ -276,7 +276,7 @@
if ok {
v = copyVal(v)
} else {
- v = zero(underlyingType(instr.X.Type()).(*types.Map).Elem())
+ v = zero(instr.X.Type().Underlying().(*types.Map).Elem())
}
if instr.CommaOk {
v = tuple{v, ok}
@@ -758,7 +758,7 @@
case token.ARROW: // receive
v, ok := <-x.(chan value)
if !ok {
- v = zero(underlyingType(instr.X.Type()).(*types.Chan).Elem())
+ v = zero(instr.X.Type().Underlying().(*types.Chan).Elem())
}
if instr.CommaOk {
v = tuple{v, ok}
@@ -833,7 +833,7 @@
func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value {
var v value
err := ""
- if idst, ok := underlyingType(instr.AssertedType).(*types.Interface); ok {
+ if idst, ok := instr.AssertedType.Underlying().(*types.Interface); ok {
v = itf
err = checkInterface(i, idst, itf)
@@ -1092,8 +1092,8 @@
// Possible cases are described with the ssa.Convert operator.
//
func conv(t_dst, t_src types.Type, x value) value {
- ut_src := underlyingType(t_src)
- ut_dst := underlyingType(t_dst)
+ ut_src := t_src.Underlying()
+ ut_dst := t_dst.Underlying()
// Destination type is not an "untyped" type.
if b, ok := ut_dst.(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
@@ -1322,7 +1322,7 @@
//
func checkInterface(i *interpreter, itype *types.Interface, x iface) string {
mset := findMethodSet(i, x.t)
- it := underlyingType(itype).(*types.Interface)
+ it := itype.Underlying().(*types.Interface)
for i, n := 0, it.NumMethods(); i < n; i++ {
m := it.Method(i)
id := ssa.MakeId(m.Name(), m.Pkg())
@@ -1332,22 +1332,3 @@
}
return "" // ok
}
-
-// underlyingType returns the underlying type of typ.
-// Copied from go/types.underlying.
-//
-func underlyingType(typ types.Type) types.Type {
- if typ, ok := typ.(*types.Named); ok {
- return typ.Underlying()
- }
- return typ
-}
-
-// indirectType(typ) assumes that typ is a pointer type,
-// or named alias thereof, and returns its base type.
-// Panic ensues if it is not a pointer.
-// Copied from exp/ssa.indirectType.
-//
-func indirectType(ptr types.Type) types.Type {
- return underlyingType(ptr).(*types.Pointer).Elem()
-}
diff --git a/ssa/interp/reflect.go b/ssa/interp/reflect.go
index 961d4a6..5b42f1e 100644
--- a/ssa/interp/reflect.go
+++ b/ssa/interp/reflect.go
@@ -72,7 +72,7 @@
func ext۰reflect۰rtype۰Bits(fn *ssa.Function, args []value) value {
// Signature: func (t reflect.rtype) int
rt := args[0].(rtype).t
- basic, ok := underlyingType(rt).(*types.Basic)
+ basic, ok := rt.Underlying().(*types.Basic)
if !ok {
panic(fmt.Sprintf("reflect.Type.Bits(%T): non-basic type", rt))
}
@@ -272,7 +272,7 @@
func ext۰reflect۰Value۰Index(fn *ssa.Function, args []value) value {
// Signature: func (v reflect.Value, i int) Value
i := args[1].(int)
- t := underlyingType(rV2T(args[0]).t)
+ t := rV2T(args[0]).t.Underlying()
switch v := rV2V(args[0]).(type) {
case array:
return makeReflectValue(t.(*types.Array).Elem(), v[i])
@@ -307,7 +307,7 @@
case iface:
return makeReflectValue(x.t, x.v)
case *value:
- return makeReflectValue(underlyingType(rV2T(args[0]).t).(*types.Pointer).Elem(), *x)
+ return makeReflectValue(rV2T(args[0]).t.Underlying().(*types.Pointer).Elem(), *x)
default:
panic(fmt.Sprintf("reflect.(Value).Elem(%T)", x))
}
@@ -318,7 +318,7 @@
// Signature: func (v reflect.Value, i int) reflect.Value
v := args[0]
i := args[1].(int)
- return makeReflectValue(underlyingType(rV2T(v).t).(*types.Struct).Field(i).Type, rV2V(v).(structure)[i])
+ return makeReflectValue(rV2T(v).t.Underlying().(*types.Struct).Field(i).Type, rV2V(v).(structure)[i])
}
func ext۰reflect۰Value۰Interface(fn *ssa.Function, args []value) value {
diff --git a/ssa/literal.go b/ssa/literal.go
index 237f266..da47855 100644
--- a/ssa/literal.go
+++ b/ssa/literal.go
@@ -63,13 +63,16 @@
}
func (l *Literal) Name() string {
- s := l.Value.String()
+ var s string
if l.Value.Kind() == exact.String {
- const n = 20
- if len(s) > n {
- s = s[:n-3] + "..." // abbreviate
+ s = exact.StringVal(l.Value)
+ const maxLit = 20
+ if len(s) > maxLit {
+ s = s[:maxLit-3] + "..." // abbreviate
}
s = strconv.Quote(s)
+ } else {
+ s = l.Value.String()
}
return s + ":" + l.Type_.String()
}
diff --git a/ssa/lvalue.go b/ssa/lvalue.go
index 5d32fab..2692707 100644
--- a/ssa/lvalue.go
+++ b/ssa/lvalue.go
@@ -31,7 +31,7 @@
}
func (a address) typ() types.Type {
- return indirectType(a.addr.Type())
+ return a.addr.Type().Deref()
}
// An element is an lvalue represented by m[k], the location of an
diff --git a/ssa/print.go b/ssa/print.go
index 872286a..e246484 100644
--- a/ssa/print.go
+++ b/ssa/print.go
@@ -83,7 +83,7 @@
if v.Heap {
op = "new"
}
- return fmt.Sprintf("%s %s", op, indirectType(v.Type()))
+ return fmt.Sprintf("%s %s", op, v.Type().Deref())
}
func (v *Phi) String() string {
@@ -225,7 +225,7 @@
}
func (v *FieldAddr) String() string {
- st := indirectType(v.X.Type()).Underlying().(*types.Struct)
+ st := v.X.Type().Deref().Underlying().(*types.Struct)
// Be robust against a bad index.
name := "?"
if 0 <= v.Field && v.Field < st.NumFields() {
diff --git a/ssa/promote.go b/ssa/promote.go
index c395c3b..0e0a21b 100644
--- a/ssa/promote.go
+++ b/ssa/promote.go
@@ -288,7 +288,7 @@
// Iterate over selections e.A.B.C.f in the natural order [A,B,C].
for _, p := range cand.path.reverse() {
// Loop invariant: v holds a pointer to a struct.
- if _, ok := indirectType(v.Type()).Underlying().(*types.Struct); !ok {
+ if _, ok := v.Type().Deref().Underlying().(*types.Struct); !ok {
panic(fmt.Sprint("not a *struct: ", v.Type(), p.field.Type))
}
sel := &FieldAddr{
diff --git a/ssa/ssa.go b/ssa/ssa.go
index 91e3016..45c71e2 100644
--- a/ssa/ssa.go
+++ b/ssa/ssa.go
@@ -104,6 +104,9 @@
// Pos() returns the position of the declaring ast.ValueSpec.Names[*]
// identifier.
//
+// NB: a Constant is not a Value; it contains a literal Value, which
+// it augments with the name and position of its 'const' declaration.
+//
type Constant struct {
Name_ string
Value *Literal
@@ -372,9 +375,9 @@
//
// Builtins are immutable values. Builtins do not have addresses.
//
-// Type() returns an inscrutable *types.builtin. Built-in functions
-// may have polymorphic or variadic types that are not expressible in
-// Go's type system.
+// Type() returns a *types.Builtin.
+// Built-in functions may have polymorphic or variadic types that are
+// not expressible in Go's type system.
//
type Builtin struct {
Object *types.Func // canonical types.Universe object for this built-in
diff --git a/ssa/ssadump.go b/ssa/ssadump.go
index 8ad4faa..6694031 100644
--- a/ssa/ssadump.go
+++ b/ssa/ssadump.go
@@ -101,7 +101,7 @@
context := &ssa.Context{
Mode: mode,
- Loader: ssa.GorootLoader,
+ Loader: ssa.MakeGoBuildLoader(nil),
}
b := ssa.NewBuilder(context)
mainpkg, args, err := ssa.CreatePackageFromArgs(b, args)
diff --git a/ssa/typeinfo.go b/ssa/typeinfo.go
index 427c4f0..7ecc69c 100644
--- a/ssa/typeinfo.go
+++ b/ssa/typeinfo.go
@@ -56,6 +56,8 @@
// IsType returns true iff expression e denotes a type.
// Precondition: e belongs to the package's ASTs.
+// 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 {
switch e := e.(type) {
diff --git a/ssa/util.go b/ssa/util.go
index d046391..790b932 100644
--- a/ssa/util.go
+++ b/ssa/util.go
@@ -57,20 +57,6 @@
return types.NewPointer(typ)
}
-// indirect(typ) assumes that typ is a pointer type,
-// or named alias thereof, and returns its base type.
-// Panic ensures if it is not a pointer.
-//
-func indirectType(ptr types.Type) types.Type {
- if v, ok := ptr.Underlying().(*types.Pointer); ok {
- return v.Elem()
- }
- // When debugging it is convenient to comment out this line
- // and let it continue to print the (illegal) SSA form.
- panic("indirect() of non-pointer type: " + ptr.String())
- return nil
-}
-
// methodIndex returns the method (and its index) named id within the
// method table of named or interface type typ. If not found,
// panic ensues.