diff --git a/ssa/builder_test.go b/ssa/builder_test.go
index e40bb100..211395bd 100644
--- a/ssa/builder_test.go
+++ b/ssa/builder_test.go
@@ -38,7 +38,7 @@ func main() {
f, err := parser.ParseFile(imp.Fset, "", test, parser.DeclarationErrors)
if err != nil {
- t.Errorf("parse error: %s", err)
+ t.Error(err)
return
}
diff --git a/ssa/create.go b/ssa/create.go
index 4fc95bb6..e8eaf868 100644
--- a/ssa/create.go
+++ b/ssa/create.go
@@ -23,7 +23,6 @@ const (
SanityCheckFunctions // Perform sanity checking of function bodies
NaiveForm // Build naïve SSA form: don't replace local loads/stores with registers
BuildSerially // Build packages serially, not in parallel.
- DebugInfo // Include DebugRef instructions [TODO(adonovan): finer grain?]
)
// NewProgram returns a new SSA Program initially containing no
diff --git a/ssa/doc.go b/ssa/doc.go
index 113dce8a..2aba2ff9 100644
--- a/ssa/doc.go
+++ b/ssa/doc.go
@@ -59,6 +59,7 @@
// *ChangeType ✔ ✔
// *Const ✔
// *Convert ✔ ✔
+// *DebugRef ✔
// *Defer ✔
// *Extract ✔ ✔
// *Field ✔ ✔
@@ -86,7 +87,9 @@
// *Ret ✔
// *RunDefers ✔
// *Select ✔ ✔
+// *Send ✔
// *Slice ✔ ✔
+// *Store ✔
// *Type ✔ (type)
// *TypeAssert ✔ ✔
// *UnOp ✔ ✔
@@ -110,16 +113,14 @@
// TODO(adonovan): Consider the exceptional control-flow implications
// of defer and recover().
//
-// 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): write an example showing how to visit all functions
// in a Program, including package init functions, methods of named
// and anon types, and functions used as values but never called
-// directly.
+// directly. See AllFunctions().
+//
+// TODO(adonovan): write a how-to document for all the various cases
+// of trying to determine corresponding elements across the four
+// domains of source locations, ast.Nodes, types.Objects,
+// ssa.Values/Instructions.
//
package ssa
diff --git a/ssa/emit.go b/ssa/emit.go
index 73d6e027..55a4af8d 100644
--- a/ssa/emit.go
+++ b/ssa/emit.go
@@ -31,22 +31,28 @@ func emitLoad(f *Function, addr Value) *UnOp {
}
// emitDebugRef emits to f a DebugRef pseudo-instruction associating
-// reference id with local var/const value v.
+// expression e with value v.
//
-func emitDebugRef(f *Function, id *ast.Ident, v Value) {
+func emitDebugRef(f *Function, e ast.Expr, v Value) {
if !f.debugInfo() {
return // debugging not enabled
}
- if isBlankIdent(id) {
- return
+ if v == nil || e == nil {
+ panic("nil")
}
- obj := f.Pkg.objectOf(id)
- if obj.Parent() == types.Universe {
- return // skip nil/true/false
+ var obj types.Object
+ if id, ok := e.(*ast.Ident); ok {
+ if isBlankIdent(id) {
+ return
+ }
+ obj = f.Pkg.objectOf(id)
+ if _, ok := obj.(*types.Const); ok {
+ return
+ }
}
f.emit(&DebugRef{
X: v,
- pos: id.Pos(),
+ Expr: unparen(e),
object: obj,
})
}
diff --git a/ssa/example_test.go b/ssa/example_test.go
index 48fa8237..de2a1794 100644
--- a/ssa/example_test.go
+++ b/ssa/example_test.go
@@ -42,14 +42,14 @@ func main() {
// Parse the input file.
file, err := parser.ParseFile(imp.Fset, "hello.go", hello, parser.DeclarationErrors)
if err != nil {
- fmt.Printf(err.Error()) // parse error
+ fmt.Print(err.Error()) // parse error
return
}
// Create a "main" package containing one file.
info := imp.CreateSourcePackage("main", []*ast.File{file})
if info.Err != nil {
- fmt.Printf(info.Err.Error()) // type error
+ fmt.Print(info.Err.Error()) // type error
return
}
diff --git a/ssa/func.go b/ssa/func.go
index 2d4a42d6..09a4c02a 100644
--- a/ssa/func.go
+++ b/ssa/func.go
@@ -374,10 +374,18 @@ func (f *Function) removeNilBlocks() {
f.Blocks = f.Blocks[:j]
}
+// SetDebugMode sets the debug mode for package pkg. If true, all its
+// functions will include full debug info. This greatly increases
+// the size of the instruction stream.
+//
+func (pkg *Package) SetDebugMode(debug bool) {
+ // TODO(adonovan): do we want ast.File granularity?
+ pkg.debug = debug
+}
+
// debugInfo reports whether debug info is wanted for this function.
func (f *Function) debugInfo() bool {
- // TODO(adonovan): make the policy finer grained.
- return f.Prog.mode&DebugInfo != 0
+ return f.Pkg.debug
}
// addNamedLocal creates a local variable, adds it to function f and
diff --git a/ssa/lvalue.go b/ssa/lvalue.go
index c2056661..3c499481 100644
--- a/ssa/lvalue.go
+++ b/ssa/lvalue.go
@@ -25,7 +25,7 @@ type lvalue interface {
type address struct {
addr Value
starPos token.Pos // source position, if from explicit *addr
- id *ast.Ident // source syntax, if from *ast.Ident
+ expr ast.Expr // source syntax [debug mode]
object types.Object // source var, if from *ast.Ident
}
@@ -38,16 +38,16 @@ func (a *address) load(fn *Function) Value {
func (a *address) store(fn *Function, v Value) {
store := emitStore(fn, a.addr, v)
store.pos = a.starPos
- if a.id != nil {
+ if a.expr != nil {
// store.Val is v converted for assignability.
- emitDebugRef(fn, a.id, store.Val)
+ emitDebugRef(fn, a.expr, store.Val)
}
}
func (a *address) address(fn *Function) Value {
- if a.id != nil {
+ if a.expr != nil {
// NB: this kind of DebugRef yields the object's address.
- emitDebugRef(fn, a.id, a.addr)
+ emitDebugRef(fn, a.expr, a.addr)
}
return a.addr
}
diff --git a/ssa/print.go b/ssa/print.go
index b1310142..e5315325 100644
--- a/ssa/print.go
+++ b/ssa/print.go
@@ -8,6 +8,7 @@ import (
"fmt"
"go/ast"
"io"
+ "reflect"
"sort"
"code.google.com/p/go.tools/go/types"
@@ -348,8 +349,14 @@ func (s *MapUpdate) String() string {
}
func (s *DebugRef) String() string {
- p := s.Parent().Prog.Fset.Position(s.pos)
- return fmt.Sprintf("; %s is %s @ %d:%d", s.X.Name(), s.object, p.Line, p.Column)
+ p := s.Parent().Prog.Fset.Position(s.Pos())
+ var descr interface{}
+ if s.object != nil {
+ descr = s.object // e.g. "var x int"
+ } else {
+ descr = reflect.TypeOf(s.Expr) // e.g. "*ast.CallExpr"
+ }
+ return fmt.Sprintf("; %s is %s @ %d:%d", s.X.Name(), descr, p.Line, p.Column)
}
func (p *Package) String() string {
diff --git a/ssa/source.go b/ssa/source.go
index 714c9e1d..c74f7c75 100644
--- a/ssa/source.go
+++ b/ssa/source.go
@@ -3,6 +3,9 @@ package ssa
// This file defines utilities for working with source positions
// or source-level named entities ("objects").
+// TODO(adonovan): test that {Value,Instruction}.Pos() positions match
+// the originating syntax, as specified.
+
import (
"go/ast"
"go/token"
@@ -114,78 +117,39 @@ func findNamedFunc(pkg *Package, pos token.Pos) *Function {
return nil
}
-// CanonicalPos returns the canonical position of the AST node n,
+// ValueForExpr returns the SSA Value that corresponds to non-constant
+// expression e.
//
-// For each Node kind that may generate an SSA Value or Instruction,
-// exactly one token within it is designated as "canonical". The
-// position of that token is returned by {Value,Instruction}.Pos().
-// The specifications of those methods determine the implementation of
-// this function.
+// It returns nil if no value was found, e.g.
+// - the expression is not lexically contained within f;
+// - f was not built with debug information; or
+// - e is a constant expression. (For efficiency, no debug
+// information is stored for constants. Use
+// importer.PackageInfo.ValueOf(e) instead.)
+// - the value was optimised away.
//
-// TODO(adonovan): test coverage.
+// The types of e and the result are equal (modulo "untyped" bools
+// resulting from comparisons) and they have equal "pointerness".
//
-func CanonicalPos(n ast.Node) token.Pos {
- // Comments show the Value/Instruction kinds v that may be
- // created by n such that CanonicalPos(n) == v.Pos().
- switch n := n.(type) {
- case *ast.ParenExpr:
- return CanonicalPos(n.X)
-
- case *ast.CallExpr:
- // f(x): *Call, *Go, *Defer.
- // T(x): *ChangeType, *Convert, *MakeInterface, *ChangeInterface.
- // make(): *MakeMap, *MakeChan, *MakeSlice.
- // new(): *Alloc.
- // panic(): *Panic.
- return n.Lparen
-
- case *ast.Ident:
- return n.NamePos // *Parameter, *Alloc, *Capture
-
- case *ast.TypeAssertExpr:
- return n.Lparen // *ChangeInterface or *TypeAssertExpr
-
- case *ast.SelectorExpr:
- return n.Sel.NamePos // *MakeClosure, *Field, *FieldAddr
-
- case *ast.FuncLit:
- return n.Type.Func // *Function or *MakeClosure
-
- case *ast.CompositeLit:
- return n.Lbrace // *Alloc or *Slice
-
- case *ast.BinaryExpr:
- return n.OpPos // *Phi or *BinOp
-
- case *ast.UnaryExpr:
- return n.OpPos // *Phi or *UnOp
-
- case *ast.IndexExpr:
- return n.Lbrack // *Index or *IndexAddr
-
- case *ast.SliceExpr:
- return n.Lbrack // *Slice
-
- case *ast.SelectStmt:
- return n.Select // *Select
-
- case *ast.RangeStmt:
- return n.For // *Range
-
- case *ast.ReturnStmt:
- return n.Return // *Ret
-
- case *ast.SendStmt:
- return n.Arrow // *Send
-
- case *ast.StarExpr:
- return n.Star // *Store
-
- case *ast.KeyValueExpr:
- return n.Colon // *MapUpdate
+// (Tip: to find the ssa.Value given a source position, use
+// importer.PathEnclosingInterval to locate the ast.Node, then
+// EnclosingFunction to locate the Function, then ValueForExpr to find
+// the ssa.Value.)
+//
+func (f *Function) ValueForExpr(e ast.Expr) Value {
+ if f.debugInfo() { // (opt)
+ e = unparen(e)
+ for _, b := range f.Blocks {
+ for _, instr := range b.Instrs {
+ if ref, ok := instr.(*DebugRef); ok {
+ if ref.Expr == e {
+ return ref.X
+ }
+ }
+ }
+ }
}
-
- return token.NoPos
+ return nil
}
// --- Lookup functions for source-level named entities (types.Objects) ---
@@ -233,7 +197,7 @@ func (prog *Program) FuncValue(obj *types.Func) Value {
//
func (prog *Program) ConstValue(obj *types.Const) *Const {
// TODO(adonovan): opt: share (don't reallocate)
- // Consts for const objects.
+ // Consts for const objects and constant ast.Exprs.
// Universal constant? {true,false,nil}
if obj.Parent() == types.Universe {
@@ -250,10 +214,8 @@ func (prog *Program) ConstValue(obj *types.Const) *Const {
// identifier denoting the source-level named variable obj.
//
// VarValue returns nil if a local variable was not found, perhaps
-// because its package was not built, the DebugInfo flag was not set
-// during SSA construction, or the value was optimized away.
-//
-// TODO(adonovan): test on x.f where x is a field.
+// because its package was not built, the debug information was not
+// requested during SSA construction, or the value was optimized away.
//
// ref must be the path to an ast.Ident (e.g. from
// PathEnclosingInterval), and that ident must resolve to obj.
@@ -315,5 +277,5 @@ func (prog *Program) VarValue(obj *types.Var, ref []ast.Node) Value {
}
}
- return nil // e.g. DebugInfo unset, or var optimized away
+ return nil // e.g. debug info not requested, or var optimized away
}
diff --git a/ssa/source_test.go b/ssa/source_test.go
index 4efde133..090d8aa8 100644
--- a/ssa/source_test.go
+++ b/ssa/source_test.go
@@ -7,7 +7,9 @@ import (
"go/ast"
"go/parser"
"go/token"
+ "os"
"regexp"
+ "strings"
"testing"
"code.google.com/p/go.tools/go/exact"
@@ -20,7 +22,7 @@ func TestObjValueLookup(t *testing.T) {
imp := importer.New(new(importer.Config)) // (uses GCImporter)
f, err := parser.ParseFile(imp.Fset, "testdata/objlookup.go", nil, parser.DeclarationErrors|parser.ParseComments)
if err != nil {
- t.Errorf("parse error: %s", err)
+ t.Error(err)
return
}
@@ -46,12 +48,13 @@ func TestObjValueLookup(t *testing.T) {
return
}
- prog := ssa.NewProgram(imp.Fset, ssa.DebugInfo /*|ssa.LogFunctions*/)
+ prog := ssa.NewProgram(imp.Fset, 0 /*|ssa.LogFunctions*/)
for _, info := range imp.Packages {
prog.CreatePackage(info)
}
- pkg := prog.Package(info.Pkg)
- pkg.Build()
+ mainPkg := prog.Package(info.Pkg)
+ mainPkg.SetDebugMode(true)
+ mainPkg.Build()
// Gather all idents and objects in file.
objs := make(map[types.Object]bool)
@@ -178,3 +181,75 @@ func checkVarValue(t *testing.T, prog *ssa.Program, ref []ast.Node, obj *types.V
}
}
}
+
+// Ensure that, in debug mode, we can determine the ssa.Value
+// corresponding to every ast.Expr.
+func TestValueForExpr(t *testing.T) {
+ imp := importer.New(new(importer.Config)) // (uses GCImporter)
+ f, err := parser.ParseFile(imp.Fset, "testdata/valueforexpr.go", nil,
+ parser.DeclarationErrors|parser.ParseComments)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ info := imp.CreateSourcePackage("main", []*ast.File{f})
+ if info.Err != nil {
+ t.Error(info.Err.Error())
+ return
+ }
+
+ prog := ssa.NewProgram(imp.Fset, 0)
+ for _, info := range imp.Packages {
+ prog.CreatePackage(info)
+ }
+ mainPkg := prog.Package(info.Pkg)
+ mainPkg.SetDebugMode(true)
+ mainPkg.Build()
+
+ fn := mainPkg.Func("f")
+
+ if false {
+ fn.DumpTo(os.Stderr) // debugging
+ }
+
+ // Find the actual AST node for each canonical position.
+ parenExprByPos := make(map[token.Pos]*ast.ParenExpr)
+ ast.Inspect(f, func(n ast.Node) bool {
+ if n != nil {
+ if e, ok := n.(*ast.ParenExpr); ok {
+ parenExprByPos[e.Pos()] = e
+ }
+ }
+ return true
+ })
+
+ // Find all annotations of form /*@kind*/.
+ for _, c := range f.Comments {
+ text := strings.TrimSpace(c.Text())
+ if text == "" || text[0] != '@' {
+ continue
+ }
+ text = text[1:]
+ pos := c.End() + 1
+ position := imp.Fset.Position(pos)
+ var e ast.Expr
+ if target := parenExprByPos[pos]; target == nil {
+ t.Errorf("%s: annotation doesn't precede ParenExpr: %q", position, text)
+ continue
+ } else {
+ e = target.X
+ }
+
+ v := fn.ValueForExpr(e) // (may be nil)
+ got := strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa.")
+ if want := text; got != want {
+ t.Errorf("%s: got value %q, want %q", position, got, want)
+ }
+ if v != nil {
+ if !types.IsIdentical(v.Type(), info.TypeOf(e)) {
+ t.Errorf("%s: got type %s, want %s", position, info.TypeOf(e), v.Type())
+ }
+ }
+ }
+}
diff --git a/ssa/ssa.go b/ssa/ssa.go
index c08b3e63..a0e7b912 100644
--- a/ssa/ssa.go
+++ b/ssa/ssa.go
@@ -41,6 +41,7 @@ type Package struct {
Members map[string]Member // all package members keyed by name
values map[types.Object]Value // package-level vars & funcs (incl. methods), keyed by object
init *Function // Func("init"); the package's (concatenated) init function
+ debug bool // include full debug info in this package.
// The following fields are set transiently, then cleared
// after building.
@@ -125,14 +126,20 @@ type Value interface {
// Instruction.Operands contains the inverse of this relation.
Referrers() *[]Instruction
- // Pos returns the location of the source construct that
- // gave rise to this value, or token.NoPos if it was not
- // explicit in the source.
+ // Pos returns the location of the AST token most closely
+ // associated with the operation that gave rise to this value,
+ // or token.NoPos if it was not explicit in the source.
//
- // For each ast.Expr type, a particular field is designated as
- // the canonical location for the expression, e.g. the Lparen
- // for an *ast.CallExpr. This enables us to find the value
- // corresponding to a given piece of source syntax.
+ // For each ast.Node type, a particular token is designated as
+ // the closest location for the expression, e.g. the Lparen
+ // for an *ast.CallExpr. This permits a compact but
+ // approximate mapping from Values to source positions for use
+ // in diagnostic messages, for example.
+ //
+ // (Do not use this position to determine which Value
+ // corresponds to an ast.Expr; use Function.ValueForExpr
+ // instead. NB: it requires that the function was built with
+ // debug information.)
//
Pos() token.Pos
}
@@ -191,15 +198,22 @@ type Instruction interface {
// Values.)
Operands(rands []*Value) []*Value
- // Pos returns the location of the source construct that
- // gave rise to this instruction, or token.NoPos if it was not
- // explicit in the source.
+ // Pos returns the location of the AST token most closely
+ // associated with the operation that gave rise to this
+ // instruction, or token.NoPos if it was not explicit in the
+ // source.
//
- // For each ast.Expr type, a particular field is designated as
- // the canonical location for the expression, e.g. the Lparen
- // for an *ast.CallExpr. This enables us to find the
- // instruction corresponding to a given piece of source
- // syntax.
+ // For each ast.Node type, a particular token is designated as
+ // the closest location for the expression, e.g. the Go token
+ // for an *ast.GoStmt. This permits a compact but approximate
+ // mapping from Instructions to source positions for use in
+ // diagnostic messages, for example.
+ //
+ // (Do not use this position to determine which Instruction
+ // corresponds to an ast.Expr; see the notes for Value.Pos.
+ // This position may be used to determine which non-Value
+ // Instruction corresponds to some ast.Stmts, but not all: If
+ // and Jump instructions have no Pos(), for example.)
//
Pos() token.Pos
}
@@ -791,10 +805,11 @@ type Lookup struct {
// It represents one goal state and its corresponding communication.
//
type SelectState struct {
- Dir ast.ChanDir // direction of case
- Chan Value // channel to use (for send or receive)
- Send Value // value to send (for send)
- Pos token.Pos // position of token.ARROW
+ Dir ast.ChanDir // direction of case
+ Chan Value // channel to use (for send or receive)
+ Send Value // value to send (for send)
+ Pos token.Pos // position of token.ARROW
+ DebugNode ast.Node // ast.SendStmt or ast.UnaryExpr(<-) [debug mode]
}
// The Select instruction tests whether (or blocks until) one or more
@@ -834,7 +849,7 @@ type SelectState struct {
//
type Select struct {
Register
- States []SelectState
+ States []*SelectState
Blocking bool
}
@@ -906,8 +921,9 @@ type Next struct {
//
// Pos() returns the ast.CallExpr.Lparen if the instruction arose from
// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the
-// instruction arose from an explicit e.(T) operation; or token.NoPos
-// otherwise.
+// instruction arose from an explicit e.(T) operation; or the
+// ast.CaseClause.Case if the instruction arose from a case of a
+// type-switch statement.
//
// Example printed form:
// t1 = typeassert t0.(int)
@@ -1037,6 +1053,8 @@ type Panic struct {
//
// See CallCommon for generic function call documentation.
//
+// Pos() returns the ast.GoStmt.Go.
+//
// Example printed form:
// go println(t0, t1)
// go t3()
@@ -1045,6 +1063,7 @@ type Panic struct {
type Go struct {
anInstruction
Call CallCommon
+ pos token.Pos
}
// The Defer instruction pushes the specified call onto a stack of
@@ -1052,6 +1071,8 @@ type Go struct {
//
// See CallCommon for generic function call documentation.
//
+// Pos() returns the ast.DeferStmt.Defer.
+//
// Example printed form:
// defer println(t0, t1)
// defer t3()
@@ -1060,6 +1081,7 @@ type Go struct {
type Defer struct {
anInstruction
Call CallCommon
+ pos token.Pos
}
// The Send instruction sends X on channel Chan.
@@ -1107,15 +1129,15 @@ type MapUpdate struct {
}
// A DebugRef instruction provides the position information for a
-// specific source-level reference that denotes the SSA value X.
+// specific source-level expression that compiles to the SSA value X.
//
// DebugRef is a pseudo-instruction: it has no dynamic effect.
//
-// Pos() returns the ast.Ident or ast.Selector.Sel of the source-level
-// reference.
+// Pos() returns Expr.Pos(), the position of the source-level
+// expression.
//
-// Object() returns the source-level (var/const) object denoted by
-// that reference.
+// Object() returns the source-level (var/const/func) object denoted
+// by Expr if it is an *ast.Ident; otherwise it is nil.
//
// (By representing these as instructions, rather than out-of-band,
// consistency is maintained during transformation passes by the
@@ -1124,8 +1146,8 @@ type MapUpdate struct {
type DebugRef struct {
anInstruction
X Value // the value whose position we're declaring
- pos token.Pos // location of the reference
- object types.Object // the identity of the source var/const
+ Expr ast.Expr // the referring expression
+ object types.Object // the identity of the source var/const/func
}
// Embeddable mix-ins and helpers for common parts of other structs. -----------
@@ -1385,8 +1407,8 @@ func (p *Package) Type(name string) (t *Type) {
}
func (v *Call) Pos() token.Pos { return v.Call.pos }
-func (s *Defer) Pos() token.Pos { return s.Call.pos }
-func (s *Go) Pos() token.Pos { return s.Call.pos }
+func (s *Defer) Pos() token.Pos { return s.pos }
+func (s *Go) Pos() token.Pos { return s.pos }
func (s *MapUpdate) Pos() token.Pos { return s.pos }
func (s *Panic) Pos() token.Pos { return s.pos }
func (s *Ret) Pos() token.Pos { return s.pos }
@@ -1395,7 +1417,7 @@ func (s *Store) Pos() token.Pos { return s.pos }
func (s *If) Pos() token.Pos { return token.NoPos }
func (s *Jump) Pos() token.Pos { return token.NoPos }
func (s *RunDefers) Pos() token.Pos { return token.NoPos }
-func (s *DebugRef) Pos() token.Pos { return s.pos }
+func (s *DebugRef) Pos() token.Pos { return s.Expr.Pos() }
// Operands.
diff --git a/ssa/ssadump.go b/ssa/ssadump.go
index e6fa7efa..a6cc5563 100644
--- a/ssa/ssadump.go
+++ b/ssa/ssadump.go
@@ -54,11 +54,12 @@ func main() {
impctx := importer.Config{Loader: importer.MakeGoBuildLoader(nil)}
+ var debugMode bool
var mode ssa.BuilderMode
for _, c := range *buildFlag {
switch c {
case 'D':
- mode |= ssa.DebugInfo
+ debugMode = true
case 'P':
mode |= ssa.LogPackages | ssa.BuildSerially
case 'F':
@@ -115,7 +116,7 @@ func main() {
// Create and build SSA-form program representation.
prog := ssa.NewProgram(imp.Fset, mode)
for _, info := range imp.Packages {
- prog.CreatePackage(info)
+ prog.CreatePackage(info).SetDebugMode(debugMode)
}
prog.BuildAll()
diff --git a/ssa/stdlib_test.go b/ssa/stdlib_test.go
index 97a7c133..070a1215 100644
--- a/ssa/stdlib_test.go
+++ b/ssa/stdlib_test.go
@@ -20,6 +20,8 @@ import (
"code.google.com/p/go.tools/ssa"
)
+const debugMode = false
+
func allPackages() []string {
var pkgs []string
root := filepath.Join(runtime.GOROOT(), "src/pkg") + "/"
@@ -70,10 +72,10 @@ func TestStdlib(t *testing.T) {
alloc := memstats.Alloc
// Create SSA packages.
- prog := ssa.NewProgram(imp.Fset, ssa.DebugInfo|ssa.SanityCheckFunctions)
+ prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions)
for _, info := range imp.Packages {
if info.Err == nil {
- prog.CreatePackage(info)
+ prog.CreatePackage(info).SetDebugMode(debugMode)
}
}
diff --git a/ssa/testdata/valueforexpr.go b/ssa/testdata/valueforexpr.go
new file mode 100644
index 00000000..bd7bcd66
--- /dev/null
+++ b/ssa/testdata/valueforexpr.go
@@ -0,0 +1,45 @@
+//+build ignore
+
+package main
+
+// This file is the input to TestCanonicalPos in source_test.go, which
+// ensures that each expression e immediately following a /*@kind*/(x)
+// annotation, when passed to Function.ValueForExpr(e), returns a
+// non-nil Value of the same type as e and of kind 'kind'.
+
+func f(param int) {
+ _ = /*@*/ (1 + 2) // (constant)
+ i := 0
+ /*@Call*/ (print( /*@BinOp*/ (i + 1)))
+ ch := /*@MakeChan*/ (make(chan int))
+ /*@UnOp*/ (<-ch)
+ x := /*@UnOp*/ (<-ch)
+ select {
+ case /*@Extract*/ (<-ch):
+ case x := /*@Extract*/ (<-ch):
+ }
+ defer /*@Function*/ (func() {
+ })()
+ go /*@Function*/ (func() {
+ })()
+ y := 0
+ if true && /*@BinOp*/ (bool(y > 0)) {
+ y = 1
+ }
+ _ = /*@Phi*/ (y)
+ map1 := /*@MakeMap*/ (make(map[string]string))
+ _ = /*@MakeMap*/ (map[string]string{"": ""})
+ _ = /*@MakeSlice*/ (make([]int, 0))
+ _ = /*@MakeClosure*/ (func() { print(param) })
+ sl := /*@Slice*/ ([]int{})
+ _ = /*@Alloc*/ (&struct{}{})
+ _ = /*@Slice*/ (sl[:0])
+ _ = /*@Alloc*/ (new(int))
+ var iface interface{}
+ _ = /*@TypeAssert*/ (iface.(int))
+ _ = /*@UnOp*/ (sl[0])
+ _ = /*@IndexAddr*/ (&sl[0])
+ _ = /*@Index*/ ([2]int{}[0])
+ var p *int
+ _ = /*@UnOp*/ (*p)
+}