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) +}