diff --git a/ssa/const.go b/ssa/const.go index 6c9ee301..5083d997 100644 --- a/ssa/const.go +++ b/ssa/const.go @@ -65,21 +65,27 @@ func zeroConst(t types.Type) *Const { 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 40e1ae00..60677514 100644 --- a/ssa/create.go +++ b/ssa/create.go @@ -71,12 +71,16 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { 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 64f0edf6..520f47d9 100644 --- a/ssa/example_test.go +++ b/ssa/example_test.go @@ -82,6 +82,7 @@ func main() { // 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 @@ func main() { // 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 cf1aa656..b2059ef2 100644 --- a/ssa/func.go +++ b/ssa/func.go @@ -444,7 +444,7 @@ func (f *Function) emit(instr Instruction) Value { 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 @@ func (f *Function) emit(instr Instruction) Value { // "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 @@ func (f *Function) fullName(from *Package) string { // 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 @@ func (f *Function) fullName(from *Package) string { // 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 @@ func writeSignature(w io.Writer, pkg *Package, name string, sig *types.Signature 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 @@ func (f *Function) DumpTo(w io.Writer) { 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 @@ func (f *Function) DumpTo(w io.Writer) { 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 c8fcb96d..e75caf31 100644 --- a/ssa/print.go +++ b/ssa/print.go @@ -14,57 +14,41 @@ import ( "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 @@ func relType(t types.Type, from *Package) string { // 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 @@ func (v *Capture) String() string { 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 @@ func (v *Alloc) String() string { 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 *Call) String() string { } 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 *UnOp) String() string { } 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 *ChangeInterface) String() string { } 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 *Next) String() string { } 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 f6afb062..9b5bb68a 100644 --- a/ssa/sanity.go +++ b/ssa/sanity.go @@ -367,6 +367,10 @@ func (s *sanity) checkFunction(fn *Function) bool { 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 @@ func (s *sanity) checkFunction(fn *Function) bool { // 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 a4f968ea..c7b550ec 100644 --- a/ssa/ssa.go +++ b/ssa/ssa.go @@ -61,12 +61,14 @@ type Package struct { // 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 Member interface { // type Type struct { object *types.TypeName + pkg *Package } // A NamedConst is a Member of Package representing a package-level @@ -90,6 +93,7 @@ type NamedConst struct { object *types.Const Value *Const pos token.Pos + pkg *Package } // An SSA value that can be referenced by an instruction. @@ -1149,7 +1153,7 @@ type MapUpdate struct { } // 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) 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 *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) 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.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.