diff --git a/ssa/builder.go b/ssa/builder.go index 1dca7924..576c7360 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -62,8 +62,8 @@ var ( // SSA Value constants. vZero = intLiteral(0) vOne = intLiteral(1) - vTrue = newLiteral(exact.MakeBool(true), tBool) - vFalse = newLiteral(exact.MakeBool(false), tBool) + vTrue = NewLiteral(exact.MakeBool(true), tBool) + vFalse = NewLiteral(exact.MakeBool(false), tBool) ) // builder holds state associated with the package currently being built. @@ -230,7 +230,7 @@ func (b *builder) exprN(fn *Function, e ast.Expr) Value { tuple = fn.emit(lookup) case *ast.TypeAssertExpr: - return emitTypeTest(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e)) + return emitTypeTest(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e), e.Lparen) case *ast.UnaryExpr: // must be receive <- typ = fn.Pkg.typeOf(e.X).Underlying().(*types.Chan).Elem() @@ -345,7 +345,7 @@ func (b *builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping if !wantAddr { if m, recv := b.findMethod(fn, e.X, id); m != nil { c := &MakeClosure{ - Fn: makeBoundMethodThunk(fn.Prog, m, recv), + Fn: makeBoundMethodThunk(fn.Prog, m, recv.Type()), Bindings: []Value{recv}, } c.setPos(e.Sel.Pos()) @@ -579,7 +579,7 @@ func (b *builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) { // func (b *builder) expr(fn *Function, e ast.Expr) Value { if v := fn.Pkg.info.ValueOf(e); v != nil { - return newLiteral(v, fn.Pkg.typeOf(e)) + return NewLiteral(v, fn.Pkg.typeOf(e)) } switch e := e.(type) { @@ -618,7 +618,7 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value { return b.expr(fn, e.X) case *ast.TypeAssertExpr: // single-result form only - return emitTypeAssert(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e)) + return emitTypeAssert(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e), e.Lparen) case *ast.CallExpr: typ := fn.Pkg.typeOf(e) @@ -709,6 +709,7 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value { Low: low, High: high, } + v.setPos(e.Lbrack) v.setType(fn.Pkg.typeOf(e)) return fn.emit(v) @@ -969,7 +970,8 @@ func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx } else { // Replace a suffix of args with a slice containing it. at := types.NewArray(vt, int64(len(varargs))) - a := emitNew(fn, at, e.Lparen) + // Don't set pos (e.g. to e.Lparen) for implicit Allocs. + a := emitNew(fn, at, token.NoPos) for i, arg := range varargs { iaddr := &IndexAddr{ X: a, @@ -1501,7 +1503,7 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl if casetype == tUntypedNil { condv = emitCompare(fn, token.EQL, x, nilLiteral(x.Type()), token.NoPos) } else { - yok := emitTypeTest(fn, x, casetype) + yok := emitTypeTest(fn, x, casetype, token.NoPos) ti = emitExtract(fn, yok, 0, casetype) condv = emitExtract(fn, yok, 1, tBool) } @@ -1643,7 +1645,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) { 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), xdecl.Type().Deref()) + recv := emitTypeAssert(fn, emitExtract(fn, triple, 1, tEface), xdecl.Type().Deref(), token.NoPos) emitStore(fn, xdecl, recv) if len(comm.Lhs) == 2 { // x, ok := ... diff --git a/ssa/create.go b/ssa/create.go index e1755fd0..435621c0 100644 --- a/ssa/create.go +++ b/ssa/create.go @@ -35,13 +35,14 @@ const ( // func NewProgram(fset *token.FileSet, mode BuilderMode) *Program { prog := &Program{ - Files: fset, - Packages: make(map[string]*Package), - packages: make(map[*types.Package]*Package), - Builtins: make(map[types.Object]*Builtin), - methodSets: make(map[types.Type]MethodSet), - concreteMethods: make(map[*types.Func]*Function), - mode: mode, + Files: fset, + Packages: make(map[string]*Package), + packages: make(map[*types.Package]*Package), + Builtins: make(map[types.Object]*Builtin), + methodSets: make(map[types.Type]MethodSet), + concreteMethods: make(map[*types.Func]*Function), + indirectionWrappers: make(map[*Function]*Function), + mode: mode, } // Create Values for built-in functions. @@ -67,25 +68,9 @@ func (prog *Program) CreatePackages(imp *importer.Importer) { // TODO(adonovan): make this idempotent, so that a second call // to CreatePackages creates only the packages that appeared // in imp since the first. - // - // TODO(adonovan): make it create packages in topological - // order (using info.Imports()) so we can compute method sets - // in postorder (or within createPackage) rather than as a - // second pass. - for path, info := range imp.Packages { createPackage(prog, path, info) } - - // Compute the method sets, now that we have all packages' methods. - for _, pkg := range prog.Packages { - for _, mem := range pkg.Members { - if t, ok := mem.(*Type); ok { - t.Methods = prog.MethodSet(t.NamedType) - t.PtrMethods = prog.MethodSet(pointer(t.NamedType)) - } - } - } } // memberFromObject populates package pkg with a member for the @@ -99,12 +84,12 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { name := obj.Name() switch obj := obj.(type) { case *types.TypeName: - pkg.Members[name] = &Type{NamedType: obj.Type().(*types.Named)} + pkg.Members[name] = &Type{Object: obj} case *types.Const: pkg.Members[name] = &Constant{ name: name, - Value: newLiteral(obj.Val(), obj.Type()), + Value: NewLiteral(obj.Val(), obj.Type()), pos: obj.Pos(), } diff --git a/ssa/emit.go b/ssa/emit.go index eda08cea..d91e435c 100644 --- a/ssa/emit.go +++ b/ssa/emit.go @@ -160,7 +160,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value { // Assignment from one interface type to another? if _, ok := ut_src.(*types.Interface); ok { - return emitTypeAssert(f, val, typ) + return emitTypeAssert(f, val, typ, token.NoPos) } // Untyped nil literal? Return interface-typed nil literal. @@ -174,10 +174,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value { val = emitConv(f, val, DefaultType(ut_src)) } - mi := &MakeInterface{ - X: val, - Methods: f.Prog.MethodSet(t_src), - } + mi := &MakeInterface{X: val} mi.setType(typ) return f.emit(mi) } @@ -188,7 +185,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value { // change yet; this defers the point at which the number of // possible representations explodes. if l, ok := val.(*Literal); ok { - return newLiteral(l.Value, typ) + return NewLiteral(l.Value, typ) } // A representation-changing conversion. @@ -246,7 +243,7 @@ func emitExtract(f *Function, tuple Value, index int, typ types.Type) Value { // emitTypeAssert emits to f a type assertion value := x.(t) and // returns the value. x.Type() must be an interface. // -func emitTypeAssert(f *Function, x Value, t types.Type) Value { +func emitTypeAssert(f *Function, x Value, t types.Type, pos token.Pos) Value { // Simplify infallible assertions. txi := x.Type().Underlying().(*types.Interface) if ti, ok := t.Underlying().(*types.Interface); ok { @@ -255,12 +252,14 @@ func emitTypeAssert(f *Function, x Value, t types.Type) Value { } if isSuperinterface(ti, txi) { c := &ChangeInterface{X: x} + c.setPos(pos) c.setType(t) return f.emit(c) } } a := &TypeAssert{X: x, AssertedType: t} + a.setPos(pos) a.setType(t) return f.emit(a) } @@ -268,7 +267,7 @@ func emitTypeAssert(f *Function, x Value, t types.Type) Value { // emitTypeTest emits to f a type test value,ok := x.(t) and returns // a (value, ok) tuple. x.Type() must be an interface. // -func emitTypeTest(f *Function, x Value, t types.Type) Value { +func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value { // TODO(adonovan): opt: simplify infallible tests as per // emitTypeAssert, and return (x, vTrue). // (Requires that exprN returns a slice of extracted values, @@ -278,6 +277,7 @@ func emitTypeTest(f *Function, x Value, t types.Type) Value { AssertedType: t, CommaOk: true, } + a.setPos(pos) a.setType(types.NewTuple( types.NewVar(token.NoPos, nil, "value", t), varOk, diff --git a/ssa/example_test.go b/ssa/example_test.go index 71173c23..ddd6b5e3 100644 --- a/ssa/example_test.go +++ b/ssa/example_test.go @@ -100,7 +100,7 @@ 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{} + // t1 = make interface{} <- string ("Hello, World!":string) interface{} // *t0 = t1 // t2 = slice a0[:] []interface{} // t3 = fmt.Println(t2) (n int, err error) diff --git a/ssa/func.go b/ssa/func.go index 5bbc3e43..c6d0d712 100644 --- a/ssa/func.go +++ b/ssa/func.go @@ -19,6 +19,9 @@ func addEdge(from, to *BasicBlock) { to.Preds = append(to.Preds, from) } +// Parent returns the function that contains block b. +func (b *BasicBlock) Parent() *Function { return b.parent } + // String returns a human-readable label of this block. // It is not guaranteed unique within the function. // @@ -168,9 +171,10 @@ func (f *Function) labelledBlock(label *ast.Ident) *lblock { // func (f *Function) addParam(name string, typ types.Type, pos token.Pos) *Parameter { v := &Parameter{ - name: name, - typ: typ, - pos: pos, + name: name, + typ: typ, + pos: pos, + parent: f, } f.Params = append(f.Params, v) return v @@ -412,10 +416,11 @@ func (f *Function) lookup(obj types.Object, escaping bool) Value { } outer := f.Enclosing.lookup(obj, true) // escaping v := &Capture{ - name: outer.Name(), - typ: outer.Type(), - pos: outer.Pos(), - outer: outer, + name: outer.Name(), + typ: outer.Type(), + pos: outer.Pos(), + outer: outer, + parent: f, } f.objects[obj] = v f.FreeVars = append(f.FreeVars, v) @@ -612,7 +617,7 @@ func (f *Function) newBasicBlock(comment string) *BasicBlock { b := &BasicBlock{ Index: len(f.Blocks), Comment: comment, - Func: f, + parent: f, } b.Succs = b.succs2[:0] f.Blocks = append(f.Blocks, b) diff --git a/ssa/interp/interp.go b/ssa/interp/interp.go index 130f5a99..583b3420 100644 --- a/ssa/interp/interp.go +++ b/ssa/interp/interp.go @@ -369,7 +369,7 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation { } // prepareCall determines the function value and argument values for a -// function call in a Call, Go or Defer instruction, peforming +// function call in a Call, Go or Defer instruction, performing // interface method lookup if needed. // func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) { @@ -383,23 +383,12 @@ func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) { panic("method invoked on nil interface") } id := call.MethodId() - m := findMethodSet(fr.i, recv.t)[id] - if m == nil { + fn = findMethodSet(fr.i, recv.t)[id] + if fn == nil { // Unreachable in well-typed programs. panic(fmt.Sprintf("method set for dynamic type %v does not contain %s", recv.t, id)) } - _, aptr := recv.v.(*value) // actual pointerness - _, fptr := m.Signature.Recv().Type().(*types.Pointer) // formal pointerness - switch { - case aptr == fptr: - args = append(args, copyVal(recv.v)) - case aptr: - // Calling func(T) with a *T receiver: make a copy. - args = append(args, copyVal(*recv.v.(*value))) - case fptr: - panic("illegal call of *T method with T receiver") - } - fn = m + args = append(args, copyVal(recv.v)) } for _, arg := range call.Args { args = append(args, fr.get(arg)) diff --git a/ssa/lift.go b/ssa/lift.go index c92c02c1..d44ea106 100644 --- a/ssa/lift.go +++ b/ssa/lift.go @@ -349,7 +349,7 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool { fmt.Fprintln(os.Stderr, "liftAlloc: lifting ", alloc, alloc.Name()) } - fn := alloc.Block().Func + fn := alloc.Parent() // Φ-insertion. // diff --git a/ssa/literal.go b/ssa/literal.go index 24ea0ffb..cac0ca45 100644 --- a/ssa/literal.go +++ b/ssa/literal.go @@ -11,25 +11,23 @@ import ( "code.google.com/p/go.tools/go/types" ) -// newLiteral returns a new literal of the specified value and type. +// NewLiteral returns a new literal of the specified value and type. // val must be valid according to the specification of Literal.Value. // -func newLiteral(val exact.Value, typ types.Type) *Literal { - // This constructor exists to provide a single place to - // insert logging/assertions during debugging. +func NewLiteral(val exact.Value, typ types.Type) *Literal { return &Literal{typ, val} } // intLiteral returns an untyped integer literal that evaluates to i. func intLiteral(i int64) *Literal { - return newLiteral(exact.MakeInt64(i), types.Typ[types.UntypedInt]) + return NewLiteral(exact.MakeInt64(i), types.Typ[types.UntypedInt]) } // nilLiteral returns a nil literal of the specified type, which may // be any reference type, including interfaces. // func nilLiteral(typ types.Type) *Literal { - return newLiteral(exact.MakeNil(), typ) + return NewLiteral(exact.MakeNil(), typ) } // zeroLiteral returns a new "zero" literal of the specified type, @@ -41,11 +39,11 @@ func zeroLiteral(t types.Type) *Literal { case *types.Basic: switch { case t.Info()&types.IsBoolean != 0: - return newLiteral(exact.MakeBool(false), t) + return NewLiteral(exact.MakeBool(false), t) case t.Info()&types.IsNumeric != 0: - return newLiteral(exact.MakeInt64(0), t) + return NewLiteral(exact.MakeInt64(0), t) case t.Info()&types.IsString != 0: - return newLiteral(exact.MakeString(""), t) + return NewLiteral(exact.MakeString(""), t) case t.Kind() == types.UnsafePointer: fallthrough case t.Kind() == types.UntypedNil: @@ -56,7 +54,7 @@ func zeroLiteral(t types.Type) *Literal { case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: return nilLiteral(t) case *types.Named: - return newLiteral(zeroLiteral(t.Underlying()).Value, t) + return NewLiteral(zeroLiteral(t.Underlying()).Value, t) case *types.Array, *types.Struct: panic(fmt.Sprint("zeroLiteral applied to aggregate:", t)) } diff --git a/ssa/print.go b/ssa/print.go index b90e8983..49bec845 100644 --- a/ssa/print.go +++ b/ssa/print.go @@ -28,14 +28,14 @@ func (id Id) String() string { func relName(v Value, i Instruction) string { switch v := v.(type) { case *Global: - if i != nil && v.Pkg == i.Block().Func.Pkg { + if i != nil && v.Pkg == i.Parent().Pkg { return v.Name() } return v.FullName() case *Function: var pkg *Package if i != nil { - pkg = i.Block().Func.Pkg + pkg = i.Parent().Pkg } return v.fullName(pkg) } @@ -166,7 +166,7 @@ func (v *ChangeInterface) String() string { } func (v *MakeInterface) String() string { - return fmt.Sprintf("make interface %s <- %s (%s)", v.Type(), v.X.Type(), relName(v.X, v)) + return fmt.Sprintf("make %s <- %s (%s)", v.Type(), v.X.Type(), relName(v.X, v)) } func (v *MakeClosure) String() string { @@ -187,7 +187,7 @@ func (v *MakeClosure) String() string { func (v *MakeSlice) String() string { var b bytes.Buffer - b.WriteString("make slice ") + b.WriteString("make ") b.WriteString(v.Type().String()) b.WriteString(" ") b.WriteString(relName(v.Len, v)) @@ -381,19 +381,22 @@ func (p *Package) DumpTo(w io.Writer) { fmt.Fprintf(w, " func %-*s %s\n", maxname, name, mem.Type()) case *Type: - fmt.Fprintf(w, " type %-*s %s\n", maxname, name, mem.NamedType.Underlying()) - // We display only PtrMethods since its keys - // are a superset of Methods' keys, though the + fmt.Fprintf(w, " type %-*s %s\n", maxname, name, mem.Type().Underlying()) + // We display only mset(*T) since its keys + // are a superset of mset(T)'s keys, though the // methods themselves may differ, // e.g. different bridge methods. - // TODO(adonovan): show pointerness of receivers. + // NB: if mem.Type() is a pointer, mset is empty. + mset := p.Prog.MethodSet(pointer(mem.Type())) var keys ids - for id := range mem.PtrMethods { + for id := range mset { keys = append(keys, id) } sort.Sort(keys) for _, id := range keys { - method := mem.PtrMethods[id] + method := mset[id] + // TODO(adonovan): show pointerness of receiver of declared method, not the index + fmt.Fprintf(w, " method %s %s\n", id, method.Signature) } diff --git a/ssa/promote.go b/ssa/promote.go index 8ab5bf57..6109d603 100644 --- a/ssa/promote.go +++ b/ssa/promote.go @@ -91,7 +91,8 @@ func (c candidate) ptrRecv() bool { } // MethodSet returns the method set for type typ, -// building bridge methods as needed for promoted methods. +// building bridge methods as needed for promoted methods +// and indirection wrappers for *T receiver types. // A nil result indicates an empty set. // // Thread-safe. @@ -104,7 +105,7 @@ func (p *Program) MethodSet(typ types.Type) MethodSet { defer p.methodSetsMu.Unlock() // TODO(adonovan): Using Types as map keys doesn't properly - // de-dup. e.g. *NamedType are canonical but *Struct and + // de-dup. e.g. *Named are canonical but *Struct and // others are not. Need to de-dup using typemap.T. mset := p.methodSets[typ] if mset == nil { @@ -151,7 +152,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet { m := nt.Method(i) concrete := prog.concreteMethods[m] if concrete == nil { - panic(fmt.Sprintf("no ssa.Function for mset(%s)[%s]", t, m.Name())) + panic(fmt.Sprintf("no ssa.Function for methodset(%s)[%s]", t, m.Name())) } addCandidate(nextcands, MakeId(m.Name(), m.Pkg()), m, concrete, node) } @@ -205,7 +206,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet { if cand == nil { continue // blocked; ignore } - if cand.ptrRecv() && !(isPointer(typ) || cand.path.isIndirect()) { + if cand.ptrRecv() && !isPointer(typ) && !cand.path.isIndirect() { // A candidate concrete method f with receiver // *C is promoted into the method set of // (non-pointer) E iff the implicit path selection @@ -214,8 +215,13 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet { } var method *Function if cand.path == nil { - // Trivial member of method-set; no bridge needed. + // Trivial member of method-set; no promotion needed. method = cand.concrete + + if !cand.ptrRecv() && isPointer(typ) { + // Call to method on T from receiver of type *T. + method = indirectionWrapper(method) + } } else { method = makeBridgeMethod(prog, typ, cand) } @@ -332,7 +338,9 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function return fn } -// createParams creates parameters for bridge method fn based on its Signature. +// createParams creates parameters for bridge method fn based on its +// Signature.Params, which do not include the receiver. +// func createParams(fn *Function) { var last *Parameter tparams := fn.Signature.Params() @@ -412,7 +420,7 @@ func makeImethodThunk(prog *Program, typ types.Type, id Id) *Function { // // TODO(adonovan): memoize creation of these functions in the Program. // -func makeBoundMethodThunk(prog *Program, meth *Function, recv Value) *Function { +func makeBoundMethodThunk(prog *Program, meth *Function, recvType types.Type) *Function { if prog.mode&LogSource != 0 { defer logStack("makeBoundMethodThunk %s", meth)() } @@ -423,7 +431,7 @@ func makeBoundMethodThunk(prog *Program, meth *Function, recv Value) *Function { Prog: prog, } - cap := &Capture{name: "recv", typ: recv.Type()} + cap := &Capture{name: "recv", typ: recvType, parent: fn} fn.FreeVars = []*Capture{cap} fn.startBody() createParams(fn) @@ -438,6 +446,53 @@ func makeBoundMethodThunk(prog *Program, meth *Function, recv Value) *Function { return fn } +// Receiver indirection wrapper ------------------------------------ + +// indirectionWrapper returns a synthetic method with *T receiver +// that delegates to meth, which has a T receiver. +// +// func (recv *T) f(...) ... { +// return (*recv).f(...) +// } +// +// EXCLUSIVE_LOCKS_REQUIRED(meth.Prog.methodSetsMu) +// +func indirectionWrapper(meth *Function) *Function { + prog := meth.Prog + fn, ok := prog.indirectionWrappers[meth] + if !ok { + if prog.mode&LogSource != 0 { + defer logStack("makeIndirectionWrapper %s", meth)() + } + + s := meth.Signature + recv := types.NewVar(token.NoPos, meth.Pkg.Types, "recv", + types.NewPointer(s.Recv().Type())) + fn = &Function{ + name: meth.Name(), + Signature: types.NewSignature(recv, s.Params(), s.Results(), s.IsVariadic()), + Prog: prog, + } + + fn.startBody() + fn.addParamObj(recv) + createParams(fn) + // TODO(adonovan): consider emitting a nil-pointer check here + // with a nice error message, like gc does. + var c Call + c.Call.Func = meth + c.Call.Args = append(c.Call.Args, emitLoad(fn, fn.Params[0])) + for _, arg := range fn.Params[1:] { + c.Call.Args = append(c.Call.Args, arg) + } + emitTailCall(fn, &c) + fn.finishBody() + + prog.indirectionWrappers[meth] = fn + } + return fn +} + // Implicit field promotion ---------------------------------------- // For a given struct type and (promoted) field Id, findEmbeddedField diff --git a/ssa/sanity.go b/ssa/sanity.go index e9465373..c7f0f0d9 100644 --- a/ssa/sanity.go +++ b/ssa/sanity.go @@ -41,7 +41,7 @@ func MustSanityCheck(fn *Function, reporter io.Writer) { } func (s *sanity) diagnostic(prefix, format string, args ...interface{}) { - fmt.Fprintf(s.reporter, "%s: function %s", prefix, s.fn.FullName()) + fmt.Fprintf(s.reporter, "%s: function %s", prefix, s.fn) if s.block != nil { fmt.Fprintf(s.reporter, ", block %s", s.block) } @@ -197,8 +197,8 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) { if b.Index != index { s.errorf("block has incorrect Index %d", b.Index) } - if b.Func != s.fn { - s.errorf("block has incorrect Func %s", b.Func.FullName()) + if b.parent != s.fn { + s.errorf("block has incorrect parent %s", b.parent) } // Check all blocks are reachable. @@ -226,8 +226,8 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) { if !found { s.errorf("expected successor edge in predecessor %s; found only: %s", a, a.Succs) } - if a.Func != s.fn { - s.errorf("predecessor %s belongs to different function %s", a, a.Func.FullName()) + if a.parent != s.fn { + s.errorf("predecessor %s belongs to different function %s", a, a.parent) } } for _, c := range b.Succs { @@ -241,8 +241,8 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) { if !found { s.errorf("expected predecessor edge in successor %s; found only: %s", c, c.Preds) } - if c.Func != s.fn { - s.errorf("successor %s belongs to different function %s", c, c.Func.FullName()) + if c.parent != s.fn { + s.errorf("successor %s belongs to different function %s", c, c.parent) } } @@ -286,10 +286,24 @@ func (s *sanity) checkFunction(fn *Function) bool { s.errorf("nil Prog") } for i, l := range fn.Locals { + if l.Parent() != fn { + s.errorf("Local %s at index %d has wrong parent", l.Name(), i) + } if l.Heap { s.errorf("Local %s at index %d has Heap flag set", l.Name(), i) } } + for i, p := range fn.Params { + if p.Parent() != fn { + s.errorf("Param %s at index %d has wrong parent", p.Name(), i) + } + } + for i, fv := range fn.FreeVars { + if fv.Parent() != fn { + s.errorf("FreeVar %s at index %d has wrong parent", fv.Name(), i) + } + } + if fn.Blocks != nil && len(fn.Blocks) == 0 { // Function _had_ blocks (so it's not external) but // they were "optimized" away, even the entry block. diff --git a/ssa/source.go b/ssa/source.go index edff71e6..b276379c 100644 --- a/ssa/source.go +++ b/ssa/source.go @@ -26,17 +26,19 @@ func tokenFileContainsPos(f *token.File, pos token.Pos) bool { // // imp provides ASTs for the program's packages. // +// pkg may be nil if no SSA package has yet been created for the found +// package. Call prog.CreatePackages(imp) to avoid this. +// // The result is (nil, nil, false) if not found. // func (prog *Program) PathEnclosingInterval(imp *importer.Importer, start, end token.Pos) (pkg *Package, path []ast.Node, exact bool) { - for path, info := range imp.Packages { - pkg := prog.Packages[path] + for importPath, info := range imp.Packages { for _, f := range info.Files { if !tokenFileContainsPos(prog.Files.File(f.Package), start) { continue } if path, exact := PathEnclosingInterval(f, start, end); path != nil { - return pkg, path, exact + return prog.Packages[importPath], path, exact } } } @@ -134,7 +136,12 @@ func findNamedFunc(pkg *Package, pos token.Pos) *Function { return mem } case *Type: - for _, meth := range mem.PtrMethods { + for _, meth := range pkg.Prog.MethodSet(mem.Type()) { + if meth.Pos() == pos { + return meth + } + } + for _, meth := range pkg.Prog.MethodSet(pointer(mem.Type())) { if meth.Pos() == pos { return meth } @@ -143,3 +150,77 @@ func findNamedFunc(pkg *Package, pos token.Pos) *Function { } return nil } + +// CanonicalPos returns the canonical position of the AST node n, +// +// 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. +// +// TODO(adonovan): test coverage. +// +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 or *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 + } + + return token.NoPos +} diff --git a/ssa/source_ast.go b/ssa/source_ast.go index af3b896d..fa21d065 100644 --- a/ssa/source_ast.go +++ b/ssa/source_ast.go @@ -20,7 +20,7 @@ import ( // additional whitespace abutting a node to be enclosed by it. // In this example: // -// z := x + y // add them +// z := x + y // add them // <-A-> // <----B-----> // @@ -41,7 +41,7 @@ import ( // interior whitespace of path[0]. // In this example: // -// z := x + y // add them +// z := x + y // add them // <--C--> <---E--> // ^ // D @@ -430,7 +430,10 @@ func childrenOf(n ast.Node) []ast.Node { children = append(children, tok(n.Switch, len("switch"))) case *ast.TypeAssertExpr: - // nop + children = append(children, + tok(n.Lparen-1, len(".")), + tok(n.Lparen, len("(")), + tok(n.Rparen, len(")"))) case *ast.TypeSpec: // TODO(adonovan): TypeSpec.{Doc,Comment}? diff --git a/ssa/ssa.go b/ssa/ssa.go index ad060f1b..12d3eb85 100644 --- a/ssa/ssa.go +++ b/ssa/ssa.go @@ -17,15 +17,16 @@ import ( // A Program is a partial or complete Go program converted to SSA form. // type Program struct { - Files *token.FileSet // position information for the files of this Program [TODO: rename Fset] - Packages map[string]*Package // all loaded Packages, keyed by import path [TODO rename packagesByPath] - packages map[*types.Package]*Package // all loaded Packages, keyed by object [TODO rename Packages] - Builtins map[types.Object]*Builtin // all built-in functions, keyed by typechecker objects. + Files *token.FileSet // position information for the files of this Program [TODO: rename Fset] + Packages map[string]*Package // all loaded Packages, keyed by import path [TODO rename packagesByPath] + packages map[*types.Package]*Package // all loaded Packages, keyed by object [TODO rename Packages] + Builtins map[types.Object]*Builtin // all built-in functions, keyed by typechecker objects. + concreteMethods map[*types.Func]*Function // maps named concrete methods to their code + mode BuilderMode // set of mode bits for SSA construction - methodSets map[types.Type]MethodSet // concrete method sets for all needed types [TODO(adonovan): de-dup] - methodSetsMu sync.Mutex // serializes all accesses to methodSets - concreteMethods map[*types.Func]*Function // maps named concrete methods to their code - mode BuilderMode // set of mode bits for SSA construction + methodSetsMu sync.Mutex // guards methodSets, indirectionWrappers + methodSets map[types.Type]MethodSet // concrete method sets for all needed types [TODO(adonovan): de-dup] + indirectionWrappers map[*Function]*Function // func(*T) wrappers for T-methods } // A Package is a single analyzed Go package containing Members for @@ -51,10 +52,11 @@ type Package struct { // const, var, func and type declarations respectively. // type Member interface { - Name() string // the declared name of the package member - String() string // human-readable information about the value - Pos() token.Pos // position of member's declaration, if known - Type() types.Type // the type of the package member + Name() string // the declared name of the package member + String() string // human-readable information about the value + Pos() token.Pos // position of member's declaration, if known + Type() types.Type // the type of the package member + Token() token.Token // token.{VAR,FUNC,CONST,TYPE} } // An Id identifies the name of a field of a struct type, or the name @@ -77,20 +79,21 @@ type Id struct { Name string } -// A MethodSet contains all the methods for a particular type. +// A MethodSet contains all the methods for a particular type T. // The method sets for T and *T are distinct entities. -// The methods for a non-pointer type T all have receiver type T, but -// the methods for pointer type *T may have receiver type *T or T. +// +// All methods in the method set for T have a receiver type of exactly +// T. The method set of *T may contain synthetic indirection methods +// that wrap methods whose receiver type is T. // type MethodSet map[Id]*Function -// A Type is a Member of a Package representing the name, underlying -// type and method set of a named type declared at package scope. +// A Type is a Member of a Package representing a package-level named type. +// +// Type() returns a *types.Named. // type Type struct { - NamedType *types.Named - Methods MethodSet // concrete method set of N - PtrMethods MethodSet // concrete method set of (*N) + Object *types.TypeName } // A Constant is a Member of Package representing a package-level @@ -190,6 +193,10 @@ type Instruction interface { // memory 'local int' and a definition of a pointer y.) String() string + // Parent returns the function to which this instruction + // belongs. + Parent() *Function + // Block returns the basic block to which this instruction // belongs. Block() *BasicBlock @@ -299,7 +306,7 @@ type Function struct { type BasicBlock struct { Index int // index of this block within Func.Blocks Comment string // optional label; no semantic significance - Func *Function // containing function + parent *Function // parent function Instrs []Instruction // instructions in order Preds, Succs []*BasicBlock // predecessors and successors succs2 [2]*BasicBlock // initial space for Succs. @@ -327,10 +334,10 @@ type BasicBlock struct { // belongs to an enclosing function. // type Capture struct { - name string - typ types.Type - pos token.Pos - + name string + typ types.Type + pos token.Pos + parent *Function referrers []Instruction // Transiently needed during building. @@ -340,10 +347,10 @@ type Capture struct { // A Parameter represents an input parameter of a function. // type Parameter struct { - name string - typ types.Type - pos token.Pos - + name string + typ types.Type + pos token.Pos + parent *Function referrers []Instruction } @@ -584,8 +591,12 @@ type Convert struct { // This operation cannot fail. Use TypeAssert for interface // conversions that may fail dynamically. // -// Pos() returns the ast.CallExpr.Lparen, if the instruction arose -// from an explicit conversion in the source. +// 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. +// +// TODO(adonovan) what about the nil case of e = e.(E)? Test. // // Example printed form: // t1 = change interface interface{} <- I (t0) @@ -596,7 +607,9 @@ type ChangeInterface struct { } // MakeInterface constructs an instance of an interface type from a -// value and its method-set. +// value of a concrete type. +// +// Use Program.MethodSet(X.Type()) to find the method-set of X. // // To construct the zero value of an interface type T, use: // &Literal{exact.MakeNil(), T} @@ -605,12 +618,12 @@ type ChangeInterface struct { // from an explicit conversion in the source. // // Example printed form: -// t1 = make interface interface{} <- int (42:int) +// t1 = make interface{} <- int (42:int) +// t2 = make Stringer <- t0 // type MakeInterface struct { Register - X Value - Methods MethodSet // method set of (non-interface) X + X Value } // The MakeClosure instruction yields a closure value whose code is @@ -641,6 +654,7 @@ type MakeClosure struct { // // Example printed form: // t1 = make map[string]int t0 +// t1 = make StringIntMap t0 // type MakeMap struct { Register @@ -657,6 +671,7 @@ type MakeMap struct { // // Example printed form: // t0 = make chan int 0 +// t0 = make IntChan 0 // type MakeChan struct { Register @@ -677,7 +692,8 @@ type MakeChan struct { // created it. // // Example printed form: -// t1 = make slice []string 1:int t0 +// t1 = make []string 1:int t0 +// t1 = make StringSlice 1:int t0 // type MakeSlice struct { Register @@ -905,6 +921,11 @@ type Next struct { // Type() reflects the actual type of the result, possibly a // 2-types.Tuple; AssertedType is the asserted type. // +// 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. +// // Example printed form: // t1 = typeassert t0.(int) // t3 = typeassert,ok t2.(T) @@ -1168,10 +1189,6 @@ type anInstruction struct { // receiver but the first true argument. Func is not used in this // mode. // -// If the called method's receiver has non-pointer type T, but the -// receiver supplied by the interface value has type *T, an implicit -// load (copy) operation is performed. -// // Example printed form: // t1 = invoke t0.String() // go invoke t3.Run(t2) @@ -1244,21 +1261,25 @@ func (v *Capture) Type() types.Type { return v.typ } func (v *Capture) Name() string { return v.name } func (v *Capture) Referrers() *[]Instruction { return &v.referrers } func (v *Capture) Pos() token.Pos { return v.pos } +func (v *Capture) Parent() *Function { return v.parent } func (v *Global) Type() types.Type { return v.typ } func (v *Global) Name() string { return v.name } func (v *Global) Pos() token.Pos { return v.pos } func (*Global) Referrers() *[]Instruction { return nil } +func (v *Global) Token() token.Token { return token.VAR } func (v *Function) Name() string { return v.name } func (v *Function) Type() types.Type { return v.Signature } func (v *Function) Pos() token.Pos { return v.pos } func (*Function) Referrers() *[]Instruction { return nil } +func (v *Function) Token() token.Token { return token.FUNC } func (v *Parameter) Type() types.Type { return v.typ } func (v *Parameter) Name() string { return v.name } func (v *Parameter) Referrers() *[]Instruction { return &v.referrers } func (v *Parameter) Pos() token.Pos { return v.pos } +func (v *Parameter) Parent() *Function { return v.parent } func (v *Alloc) Type() types.Type { return v.typ } func (v *Alloc) Name() string { return v.name } @@ -1274,18 +1295,21 @@ func (v *Register) asRegister() *Register { return v } func (v *Register) Pos() token.Pos { return v.pos } func (v *Register) setPos(pos token.Pos) { v.pos = pos } +func (v *anInstruction) Parent() *Function { return v.block.parent } func (v *anInstruction) Block() *BasicBlock { return v.block } func (v *anInstruction) SetBlock(block *BasicBlock) { v.block = block } -func (t *Type) Name() string { return t.NamedType.Obj().Name() } -func (t *Type) Pos() token.Pos { return t.NamedType.Obj().Pos() } -func (t *Type) String() string { return t.Name() } -func (t *Type) Type() types.Type { return t.NamedType } +func (t *Type) Name() string { return t.Object.Name() } +func (t *Type) Pos() token.Pos { return t.Object.Pos() } +func (t *Type) String() string { return t.Name() } +func (t *Type) Type() types.Type { return t.Object.Type() } +func (t *Type) Token() token.Token { return token.TYPE } -func (c *Constant) Name() string { return c.name } -func (c *Constant) Pos() token.Pos { return c.pos } -func (c *Constant) String() string { return c.Name() } -func (c *Constant) Type() types.Type { return c.Value.Type() } +func (c *Constant) Name() string { return c.name } +func (c *Constant) Pos() token.Pos { return c.pos } +func (c *Constant) String() string { return c.Name() } +func (c *Constant) Type() types.Type { return c.Value.Type() } +func (c *Constant) Token() token.Token { return token.CONST } // Func returns the package-level function of the specified name, // or nil if not found. @@ -1335,7 +1359,8 @@ func (prog *Program) Value(obj types.Object) Value { } // Package returns the SSA package corresponding to the specified -// type-checker package object. It returns nil if not found. +// type-checker package object. +// It returns nil if no such SSA package has been created. // func (prog *Program) Package(pkg *types.Package) *Package { return prog.packages[pkg] diff --git a/ssa/util.go b/ssa/util.go index c22e6cc3..8bd1db87 100644 --- a/ssa/util.go +++ b/ssa/util.go @@ -45,10 +45,7 @@ func isBlankIdent(e ast.Expr) bool { // isPointer returns true for types whose underlying type is a pointer. func isPointer(typ types.Type) bool { - if nt, ok := typ.(*types.Named); ok { - typ = nt.Underlying() - } - _, ok := typ.(*types.Pointer) + _, ok := typ.Underlying().(*types.Pointer) return ok }