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.