go.tools/ssa: display named types package-qualified using types.TypeString.
Details:
- use relative (non-qualified) names in more places
- Member interface now has Package(), RelString() methods.
- (*Function).DumpTo: add "# Package: " header.
- Added sanity checks for String functions.
R=gri, gri
CC=golang-dev
https://golang.org/cl/26380043
diff --git a/ssa/const.go b/ssa/const.go
index 6c9ee30..5083d99 100644
--- a/ssa/const.go
+++ b/ssa/const.go
@@ -65,21 +65,27 @@
panic(fmt.Sprint("zeroConst: unexpected ", t))
}
-func (c *Const) Name() string {
- var s string
+func (c *Const) valstring() string {
if c.Value == nil {
- s = "nil"
+ return "nil"
} else if c.Value.Kind() == exact.String {
- s = exact.StringVal(c.Value)
+ s := exact.StringVal(c.Value)
const max = 20
if len(s) > max {
s = s[:max-3] + "..." // abbreviate
}
- s = strconv.Quote(s)
+ return strconv.Quote(s)
} else {
- s = c.Value.String()
+ return c.Value.String()
}
- return s + ":" + c.typ.String()
+}
+
+func (c *Const) Name() string {
+ return fmt.Sprintf("%s:%s", c.valstring(), c.typ)
+}
+
+func (v *Const) String() string {
+ return v.Name()
}
func (c *Const) Type() types.Type {
diff --git a/ssa/create.go b/ssa/create.go
index 40e1ae0..6067751 100644
--- a/ssa/create.go
+++ b/ssa/create.go
@@ -71,12 +71,16 @@
switch obj := obj.(type) {
case *types.TypeName:
pkg.values[obj] = nil // for needMethods
- pkg.Members[name] = &Type{object: obj}
+ pkg.Members[name] = &Type{
+ object: obj,
+ pkg: pkg,
+ }
case *types.Const:
c := &NamedConst{
object: obj,
Value: NewConst(obj.Val(), obj.Type()),
+ pkg: pkg,
}
pkg.values[obj] = c.Value
pkg.Members[name] = c
diff --git a/ssa/example_test.go b/ssa/example_test.go
index 64f0edf..520f47d 100644
--- a/ssa/example_test.go
+++ b/ssa/example_test.go
@@ -82,6 +82,7 @@
// const message message = "Hello, World!":untyped string
//
// # Name: main.init
+ // # Package: main
// # Synthetic: package initializer
// func init():
// .0.entry: P:0 S:2
@@ -95,6 +96,7 @@
// return
//
// # Name: main.main
+ // # Package: main
// # Location: hello.go:8:6
// func main():
// .0.entry: P:0 S:0
diff --git a/ssa/func.go b/ssa/func.go
index cf1aa65..b2059ef 100644
--- a/ssa/func.go
+++ b/ssa/func.go
@@ -444,7 +444,7 @@
return f.currentBlock.emit(instr)
}
-// FullName returns the full name of this function, qualified by
+// RelString returns the full name of this function, qualified by
// package name, receiver type, etc.
//
// The specific formatting rules are not guaranteed and may change.
@@ -453,13 +453,13 @@
// "math.IsNaN" // a package-level function
// "IsNaN" // intra-package reference to same
// "(*sync.WaitGroup).Add" // a declared method
-// "(*exp/ssa.Return).Block" // a promotion wrapper method
-// "(ssa.Instruction).Block" // an interface method wrapper
+// "(*Return).Block" // a promotion wrapper method (intra-package ref)
+// "(Instruction).Block" // an interface method wrapper (intra-package ref)
// "func@5.32" // an anonymous function
// "bound$(*T).f" // a bound method wrapper
//
// If from==f.Pkg, suppress package qualification.
-func (f *Function) fullName(from *Package) string {
+func (f *Function) RelString(from *types.Package) string {
// TODO(adonovan): expose less fragile case discrimination
// using f.method.
@@ -490,8 +490,8 @@
// Package-level function.
// Prefix with package name for cross-package references only.
- if from != f.Pkg {
- return fmt.Sprintf("%s.%s", f.Pkg.Object.Path(), f.name)
+ if p := f.pkgobj(); p != from {
+ return fmt.Sprintf("%s.%s", p.Path(), f.name)
}
return f.name
}
@@ -499,7 +499,7 @@
// writeSignature writes to w the signature sig in declaration syntax.
// Derived from types.Signature.String().
//
-func writeSignature(w io.Writer, pkg *Package, name string, sig *types.Signature, params []*Parameter) {
+func writeSignature(w io.Writer, pkg *types.Package, name string, sig *types.Signature, params []*Parameter) {
io.WriteString(w, "func ")
if recv := sig.Recv(); recv != nil {
io.WriteString(w, "(")
@@ -533,16 +533,26 @@
if n == 1 && r.At(0).Name() == "" {
io.WriteString(w, relType(r.At(0).Type(), pkg))
} else {
- io.WriteString(w, r.String()) // TODO(adonovan): use relType
+ io.WriteString(w, relType(r, pkg))
}
}
}
+func (f *Function) pkgobj() *types.Package {
+ if f.Pkg != nil {
+ return f.Pkg.Object
+ }
+ return nil
+}
+
// DumpTo prints to w a human readable "disassembly" of the SSA code of
// all basic blocks of function f.
//
func (f *Function) DumpTo(w io.Writer) {
fmt.Fprintf(w, "# Name: %s\n", f.String())
+ if f.Pkg != nil {
+ fmt.Fprintf(w, "# Package: %s\n", f.Pkg.Object.Path())
+ }
if syn := f.Synthetic; syn != "" {
fmt.Fprintln(w, "# Synthetic:", syn)
}
@@ -558,21 +568,22 @@
fmt.Fprintf(w, "# Recover: %s\n", f.Recover)
}
+ pkgobj := f.pkgobj()
+
if f.FreeVars != nil {
io.WriteString(w, "# Free variables:\n")
for i, fv := range f.FreeVars {
- fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), f.Pkg))
+ fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), pkgobj))
}
}
if len(f.Locals) > 0 {
io.WriteString(w, "# Locals:\n")
for i, l := range f.Locals {
- fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), f.Pkg))
+ fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), pkgobj))
}
}
-
- writeSignature(w, f.Pkg, f.Name(), f.Signature, f.Params)
+ writeSignature(w, pkgobj, f.Name(), f.Signature, f.Params)
io.WriteString(w, ":\n")
if f.Blocks == nil {
@@ -608,7 +619,7 @@
l -= n
// Right-align the type.
if t := v.Type(); t != nil {
- fmt.Fprintf(w, " %*s", l-10, relType(t, f.Pkg))
+ fmt.Fprintf(w, " %*s", l-10, relType(t, pkgobj))
}
case nil:
// Be robust against bad transforms.
diff --git a/ssa/print.go b/ssa/print.go
index c8fcb96..e75caf3 100644
--- a/ssa/print.go
+++ b/ssa/print.go
@@ -14,57 +14,41 @@
"io"
"reflect"
"sort"
- "strings"
"code.google.com/p/go.tools/go/types"
)
// relName returns the name of v relative to i.
-// In most cases, this is identical to v.Name(), but for references to
-// Functions (including methods) and Globals, the FullName is used
-// instead, explicitly package-qualified for cross-package references.
+// In most cases, this is identical to v.Name(), but references to
+// Functions (including methods) and Globals use RelString and
+// all types are displayed with relType, so that only cross-package
+// references are package-qualified.
//
func relName(v Value, i Instruction) string {
+ var from *types.Package
+ if i != nil {
+ from = i.Parent().pkgobj()
+ }
switch v := v.(type) {
- case *Global:
- if i != nil && v.Pkg == i.Parent().Pkg {
- return v.Name()
- }
- return v.FullName()
- case *Function:
- var pkg *Package
- if i != nil {
- pkg = i.Parent().Pkg
- }
- return v.fullName(pkg)
+ case Member: // *Function or *Global
+ return v.RelString(from)
+ case *Const:
+ return v.valstring() + ":" + relType(v.Type(), from)
}
return v.Name()
}
-// relType is like t.String(), but if t is a Named type belonging to
-// package from, optionally wrapped by one or more Pointer
-// constructors, package qualification is suppressed.
-//
-// TODO(gri): provide this functionality in go/types (using a
-// *types.Package, obviously).
-//
-func relType(t types.Type, from *Package) string {
- if from != nil {
- t2 := t
- var nptr int // number of Pointers stripped off
- for {
- ptr, ok := t2.(*types.Pointer)
- if !ok {
- break
- }
- t2 = ptr.Elem()
- nptr++
- }
- if n, ok := t2.(*types.Named); ok && n.Obj().Pkg() == from.Object {
- return strings.Repeat("*", nptr) + n.Obj().Name()
- }
+func relType(t types.Type, from *types.Package) string {
+ return types.TypeString(from, t)
+}
+
+func relString(m Member, from *types.Package) string {
+ // NB: not all globals have an Object (e.g. init$guard),
+ // so use Package().Object not Object.Package().
+ if obj := m.Package().Object; obj != nil && obj != from {
+ return fmt.Sprintf("%s.%s", obj.Path(), m.Name())
}
- return t.String()
+ return m.Name()
}
// Value.String()
@@ -72,10 +56,6 @@
// This method is provided only for debugging.
// It never appears in disassembly, which uses Value.Name().
-func (v *Const) String() string {
- return v.Name()
-}
-
func (v *Parameter) String() string {
return fmt.Sprintf("parameter %s : %s", v.Name(), v.Type())
}
@@ -84,23 +64,10 @@
return fmt.Sprintf("capture %s : %s", v.Name(), v.Type())
}
-func (v *Global) String() string {
- return v.FullName()
-}
-
func (v *Builtin) String() string {
return fmt.Sprintf("builtin %s", v.Name())
}
-func (v *Function) String() string {
- return v.fullName(nil)
-}
-
-// FullName returns g's package-qualified name.
-func (g *Global) FullName() string {
- return fmt.Sprintf("%s.%s", g.Pkg.Object.Path(), g.name)
-}
-
// Instruction.String()
func (v *Alloc) String() string {
@@ -108,7 +75,7 @@
if v.Heap {
op = "new"
}
- return fmt.Sprintf("%s %s (%s)", op, relType(deref(v.Type()), v.Parent().Pkg), v.Comment)
+ return fmt.Sprintf("%s %s (%s)", op, relType(deref(v.Type()), v.Parent().pkgobj()), v.Comment)
}
func (v *Phi) String() string {
@@ -170,7 +137,7 @@
}
func (v *ChangeType) String() string {
- return fmt.Sprintf("changetype %s <- %s (%s)", relType(v.Type(), v.Parent().Pkg), v.X.Type(), relName(v.X, v))
+ return fmt.Sprintf("changetype %s <- %s (%s)", relType(v.Type(), v.Parent().pkgobj()), v.X.Type(), relName(v.X, v))
}
func (v *BinOp) String() string {
@@ -182,7 +149,7 @@
}
func (v *Convert) String() string {
- return fmt.Sprintf("convert %s <- %s (%s)", relType(v.Type(), v.Parent().Pkg), v.X.Type(), relName(v.X, v))
+ return fmt.Sprintf("convert %s <- %s (%s)", relType(v.Type(), v.Parent().pkgobj()), v.X.Type(), relName(v.X, v))
}
func (v *ChangeInterface) String() string {
@@ -190,7 +157,7 @@
}
func (v *MakeInterface) String() string {
- return fmt.Sprintf("make %s <- %s (%s)", relType(v.Type(), v.Parent().Pkg), relType(v.X.Type(), v.Parent().Pkg), relName(v.X, v))
+ return fmt.Sprintf("make %s <- %s (%s)", relType(v.Type(), v.Parent().pkgobj()), relType(v.X.Type(), v.Parent().pkgobj()), relName(v.X, v))
}
func (v *MakeClosure) String() string {
@@ -289,7 +256,7 @@
}
func (v *TypeAssert) String() string {
- return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), v.AssertedType)
+ return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, v.Parent().pkgobj()))
}
func (v *Extract) String() string {
diff --git a/ssa/sanity.go b/ssa/sanity.go
index f6afb06..9b5bb68 100644
--- a/ssa/sanity.go
+++ b/ssa/sanity.go
@@ -367,6 +367,10 @@
if fn.Prog == nil {
s.errorf("nil Prog")
}
+
+ fn.String() // must not crash
+ fn.RelString(fn.pkgobj()) // must not crash
+
// All functions have a package, except wrappers (which are
// shared across packages, or duplicated as weak symbols in a
// separate-compilation model), and error.Error.
@@ -430,6 +434,11 @@
// It does not require that the package is built.
// Unlike sanityCheck (for functions), it just panics at the first error.
func sanityCheckPackage(pkg *Package) {
+ if pkg.Object == nil {
+ panic(fmt.Sprintf("Package %s has no Object", pkg))
+ }
+ pkg.String() // must not crash
+
for name, mem := range pkg.Members {
if name != mem.Name() {
panic(fmt.Sprintf("%s: %T.Name() = %s, want %s",
diff --git a/ssa/ssa.go b/ssa/ssa.go
index a4f968e..c7b550e 100644
--- a/ssa/ssa.go
+++ b/ssa/ssa.go
@@ -61,12 +61,14 @@
// const, var, func and type declarations respectively.
//
type Member interface {
- Name() string // declared name of the package member
- String() string // package-qualified name of the package member
- Object() types.Object // typechecker's object for this member, if any
- Pos() token.Pos // position of member's declaration, if known
- Type() types.Type // type of the package member
- Token() token.Token // token.{VAR,FUNC,CONST,TYPE}
+ Name() string // declared name of the package member
+ String() string // package-qualified name of the package member
+ RelString(*types.Package) string // like String, but relative refs are unqualified
+ Object() types.Object // typechecker's object for this member, if any
+ Pos() token.Pos // position of member's declaration, if known
+ Type() types.Type // type of the package member
+ Token() token.Token // token.{VAR,FUNC,CONST,TYPE}
+ Package() *Package // returns the containing package. (TODO: rename Pkg)
}
// A Type is a Member of a Package representing a package-level named type.
@@ -75,6 +77,7 @@
//
type Type struct {
object *types.TypeName
+ pkg *Package
}
// A NamedConst is a Member of Package representing a package-level
@@ -90,6 +93,7 @@
object *types.Const
Value *Const
pos token.Pos
+ pkg *Package
}
// An SSA value that can be referenced by an instruction.
@@ -1149,7 +1153,7 @@
}
// A DebugRef instruction maps a source-level expression Expr to the
-// SSA value that represents the value (!IsAddr) or address (IsAddr)
+// SSA value X that represents the value (!IsAddr) or address (IsAddr)
// of that expression.
//
// DebugRef is a pseudo-instruction: it has no dynamic effect.
@@ -1361,18 +1365,23 @@
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 *Global) Object() types.Object { return v.object }
+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 (v *Global) Referrers() *[]Instruction { return nil }
+func (v *Global) Token() token.Token { return token.VAR }
+func (v *Global) Object() types.Object { return v.object }
+func (v *Global) String() string { return v.RelString(nil) }
+func (v *Global) Package() *Package { return v.Pkg }
+func (v *Global) RelString(from *types.Package) string { return relString(v, from) }
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 (v *Function) Token() token.Token { return token.FUNC }
func (v *Function) Object() types.Object { return v.object }
+func (v *Function) String() string { return v.RelString(nil) }
+func (v *Function) Package() *Package { return v.Pkg }
func (v *Function) Referrers() *[]Instruction {
if v.Enclosing != nil {
return &v.referrers
@@ -1403,23 +1412,23 @@
func (v *anInstruction) Block() *BasicBlock { return v.block }
func (v *anInstruction) setBlock(block *BasicBlock) { v.block = block }
-func (t *Type) Name() string { return t.object.Name() }
-func (t *Type) Pos() token.Pos { return t.object.Pos() }
-func (t *Type) Type() types.Type { return t.object.Type() }
-func (t *Type) Token() token.Token { return token.TYPE }
-func (t *Type) Object() types.Object { return t.object }
-func (t *Type) String() string {
- return fmt.Sprintf("%s.%s", t.object.Pkg().Path(), t.object.Name())
-}
+func (t *Type) Name() string { return t.object.Name() }
+func (t *Type) Pos() token.Pos { return t.object.Pos() }
+func (t *Type) Type() types.Type { return t.object.Type() }
+func (t *Type) Token() token.Token { return token.TYPE }
+func (t *Type) Object() types.Object { return t.object }
+func (t *Type) String() string { return t.RelString(nil) }
+func (t *Type) Package() *Package { return t.pkg }
+func (t *Type) RelString(from *types.Package) string { return relString(t, from) }
-func (c *NamedConst) Name() string { return c.object.Name() }
-func (c *NamedConst) Pos() token.Pos { return c.object.Pos() }
-func (c *NamedConst) String() string {
- return fmt.Sprintf("%s.%s", c.object.Pkg().Path(), c.object.Name())
-}
-func (c *NamedConst) Type() types.Type { return c.object.Type() }
-func (c *NamedConst) Token() token.Token { return token.CONST }
-func (c *NamedConst) Object() types.Object { return c.object }
+func (c *NamedConst) Name() string { return c.object.Name() }
+func (c *NamedConst) Pos() token.Pos { return c.object.Pos() }
+func (c *NamedConst) String() string { return c.RelString(nil) }
+func (c *NamedConst) Type() types.Type { return c.object.Type() }
+func (c *NamedConst) Token() token.Token { return token.CONST }
+func (c *NamedConst) Object() types.Object { return c.object }
+func (c *NamedConst) Package() *Package { return c.pkg }
+func (c *NamedConst) RelString(from *types.Package) string { return relString(c, from) }
// Func returns the package-level function of the specified name,
// or nil if not found.