From 0ba53b54bdc5810fa0fc1f63d3d129037c9bb1c3 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Mon, 29 Jul 2013 17:10:11 -0400 Subject: [PATCH] go.tools/ssa: add debug info for x.f where Selection.Kind()==FieldVal. Also: - Implement Program.FuncValue for interface methods (+ test). - go/types.Object.String(): don't package-qualify names unless they are package level objects---otherwise you see "main.x" for locals, struct fields, etc. - go/types.Func.String(): don't assume Type() is *Signature; it could be *Builtin. R=gri CC=golang-dev https://golang.org/cl/12058045 --- go/types/objects.go | 41 ++++++++++++++++++++++----------------- ssa/builder.go | 10 ++++++++-- ssa/emit.go | 1 - ssa/source.go | 5 +++-- ssa/source_test.go | 3 --- ssa/testdata/objlookup.go | 17 ++++++++++++---- 6 files changed, 47 insertions(+), 30 deletions(-) diff --git a/go/types/objects.go b/go/types/objects.go index a5bf00c41a..cd844f838e 100644 --- a/go/types/objects.go +++ b/go/types/objects.go @@ -76,13 +76,15 @@ func (obj *object) Name() string { return obj.name } func (obj *object) Type() Type { return obj.typ } func (obj *object) IsExported() bool { return ast.IsExported(obj.name) } func (obj *object) Id() string { return Id(obj.pkg, obj.name) } +func (obj *object) String() string { panic("abstract") } func (obj *object) toString(kind string, typ Type) string { var buf bytes.Buffer buf.WriteString(kind) buf.WriteByte(' ') - if obj.pkg != nil { + // For package-level objects, package-qualify the name. + if obj.pkg != nil && obj.pkg.scope.Lookup(obj.name) == obj { buf.WriteString(obj.pkg.name) buf.WriteByte('.') } @@ -205,23 +207,24 @@ func (obj *Func) FullName() string { } func (obj *Func) fullname(buf *bytes.Buffer) { - sig := obj.typ.(*Signature) - if recv := sig.Recv(); recv != nil { - buf.WriteByte('(') - if _, ok := recv.Type().(*Interface); ok { - // gcimporter creates abstract methods of - // named interfaces using the interface type - // (not the named type) as the receiver. - // Don't print it in full. - buf.WriteString("interface") - } else { - writeType(buf, recv.Type()) + if sig, _ := obj.typ.(*Signature); sig != nil { // may be a *Builtin + if recv := sig.Recv(); recv != nil { + buf.WriteByte('(') + if _, ok := recv.Type().(*Interface); ok { + // gcimporter creates abstract methods of + // named interfaces using the interface type + // (not the named type) as the receiver. + // Don't print it in full. + buf.WriteString("interface") + } else { + writeType(buf, recv.Type()) + } + buf.WriteByte(')') + buf.WriteByte('.') + } else if obj.pkg != nil { + buf.WriteString(obj.pkg.name) + buf.WriteByte('.') } - buf.WriteByte(')') - buf.WriteByte('.') - } else if obj.pkg != nil { - buf.WriteString(obj.pkg.name) - buf.WriteByte('.') } buf.WriteString(obj.name) } @@ -230,7 +233,9 @@ func (obj *Func) String() string { var buf bytes.Buffer buf.WriteString("func ") obj.fullname(&buf) - writeSignature(&buf, obj.typ.(*Signature)) + if sig, _ := obj.typ.(*Signature); sig != nil { + writeSignature(&buf, sig) + } return buf.String() } diff --git a/ssa/builder.go b/ssa/builder.go index d92f0f33db..2fe0eb24e7 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -391,7 +391,11 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { wantAddr := true v := b.receiver(fn, e.X, wantAddr, escaping, sel) last := len(sel.Index()) - 1 - return &address{addr: emitFieldSelection(fn, v, sel.Index()[last], true, e.Sel.Pos())} + return &address{ + addr: emitFieldSelection(fn, v, sel.Index()[last], true, e.Sel.Pos()), + id: e.Sel, + object: sel.Obj(), + } } case *ast.IndexExpr: @@ -651,7 +655,9 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value { last := len(indices) - 1 v := b.expr(fn, e.X) v = emitImplicitSelections(fn, v, indices[:last]) - return emitFieldSelection(fn, v, indices[last], false, e.Sel.Pos()) + v = emitFieldSelection(fn, v, indices[last], false, e.Sel.Pos()) + emitDebugRef(fn, e.Sel, v) + return v } panic("unexpected expression-relative selector") diff --git a/ssa/emit.go b/ssa/emit.go index 130721d049..73d6e027d9 100644 --- a/ssa/emit.go +++ b/ssa/emit.go @@ -182,7 +182,6 @@ func emitConv(f *Function, val Value, typ types.Type) Value { // Conversion to, or construction of a value of, an interface type? if _, ok := ut_dst.(*types.Interface); ok { - // Assignment from one interface type to another? if _, ok := ut_src.(*types.Interface); ok { c := &ChangeInterface{X: val} diff --git a/ssa/source.go b/ssa/source.go index 421d44d769..d40e15dc1f 100644 --- a/ssa/source.go +++ b/ssa/source.go @@ -224,8 +224,9 @@ func (prog *Program) FuncValue(obj *types.Func) Value { if v := prog.packageLevelValue(obj); v != nil { return v } - // TODO(adonovan): interface method wrappers? other wrappers? - return nil + // Interface method wrapper? + sel := types.NewSelection(types.MethodExpr, recvType(obj), obj, nil, false) + return prog.LookupMethod(sel) } // ConstValue returns the SSA Value denoted by the source-level named diff --git a/ssa/source_test.go b/ssa/source_test.go index 3af1fd99c2..4efde13383 100644 --- a/ssa/source_test.go +++ b/ssa/source_test.go @@ -70,9 +70,6 @@ func TestObjValueLookup(t *testing.T) { for obj := range objs { switch obj := obj.(type) { case *types.Func: - if obj.Name() == "interfaceMethod" { - continue // TODO(adonovan): not yet implemented. - } checkFuncValue(t, prog, obj) case *types.Const: diff --git a/ssa/testdata/objlookup.go b/ssa/testdata/objlookup.go index f8230e83c7..bf1c5a1587 100644 --- a/ssa/testdata/objlookup.go +++ b/ssa/testdata/objlookup.go @@ -12,7 +12,8 @@ package main // its value (x) or its address (&x). // // For const and func objects, the results don't vary by reference and -// are always values not addresses, so no annotations are needed. +// are always values not addresses, so no annotations are needed. The +// declaration is enough. import "fmt" @@ -27,7 +28,7 @@ var globalVar int // &globalVar::Global func globalFunc() {} type I interface { - interfaceMethod() // TODO(adonovan): unimplemented (blacklisted in source_test) + interfaceMethod() } type S struct { @@ -72,8 +73,9 @@ func main() { print(v5) // v5::Const print(v6) // v6::Const - var v7 S // v7::UnOp (load from Alloc) - v7.x = 1 // &v7::Alloc x::nil TODO(adonovan): do better for x + var v7 S // v7::UnOp (load from Alloc) + v7.x = 1 // &v7::Alloc x::Const + print(v7.x) // v7::UnOp x::Field var v8 [1]int // v8::UnOp (load from Alloc) v8[0] = 0 // &v8::Alloc @@ -94,6 +96,13 @@ func main() { var v12 J // v12::UnOp (load from Alloc) v12.method() // &v12::Alloc (implicitly address-taken) + // NB, in the following, 'method' resolves to the *types.Func + // of (*J).method, so it doesn't help us locate the specific + // ssa.Values here: a bound-method closure and a promotion + // wrapper. + _ = v11.method // v11::Const + _ = (*struct{ J }).method + // These vars are optimised away. if false { v13 := 0 // v13::nil