go.tools/go/types: make WriteType cycle-resistent

Also:
- expose WriteSignature
- s/identicalInternal/identical/g

LGTM=adonovan
R=adonovan
CC=golang-codereviews
https://golang.org/cl/57860043
This commit is contained in:
Robert Griesemer 2014-01-28 13:42:46 -08:00
parent 7dbf7c000d
commit e5e20e3af5
4 changed files with 56 additions and 32 deletions

View File

@ -247,7 +247,7 @@ func writeObject(buf *bytes.Buffer, this *Package, obj Object) {
buf.WriteString("func ")
writeFuncName(buf, this, obj)
if typ != nil {
writeSignature(buf, this, typ.(*Signature))
WriteSignature(buf, this, typ.(*Signature))
}
return

View File

@ -110,7 +110,7 @@ func hasNil(typ Type) bool {
// Identical reports whether x and y are identical.
func Identical(x, y Type) bool {
return identicalInternal(x, y, nil)
return identical(x, y, nil)
}
// An ifacePair is a node in a stack of interface type pairs compared for identity.
@ -123,7 +123,7 @@ func (p *ifacePair) identical(q *ifacePair) bool {
return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x
}
func identicalInternal(x, y Type, p *ifacePair) bool {
func identical(x, y Type, p *ifacePair) bool {
if x == y {
return true
}
@ -141,13 +141,13 @@ func identicalInternal(x, y Type, p *ifacePair) bool {
// Two array types are identical if they have identical element types
// and the same array length.
if y, ok := y.(*Array); ok {
return x.len == y.len && identicalInternal(x.elem, y.elem, p)
return x.len == y.len && identical(x.elem, y.elem, p)
}
case *Slice:
// Two slice types are identical if they have identical element types.
if y, ok := y.(*Slice); ok {
return identicalInternal(x.elem, y.elem, p)
return identical(x.elem, y.elem, p)
}
case *Struct:
@ -162,7 +162,7 @@ func identicalInternal(x, y Type, p *ifacePair) bool {
if f.anonymous != g.anonymous ||
x.Tag(i) != y.Tag(i) ||
!f.sameId(g.pkg, g.name) ||
!identicalInternal(f.typ, g.typ, p) {
!identical(f.typ, g.typ, p) {
return false
}
}
@ -173,7 +173,7 @@ func identicalInternal(x, y Type, p *ifacePair) bool {
case *Pointer:
// Two pointer types are identical if they have identical base types.
if y, ok := y.(*Pointer); ok {
return identicalInternal(x.base, y.base, p)
return identical(x.base, y.base, p)
}
case *Tuple:
@ -184,7 +184,7 @@ func identicalInternal(x, y Type, p *ifacePair) bool {
if x != nil {
for i, v := range x.vars {
w := y.vars[i]
if !identicalInternal(v.typ, w.typ, p) {
if !identical(v.typ, w.typ, p) {
return false
}
}
@ -200,8 +200,8 @@ func identicalInternal(x, y Type, p *ifacePair) bool {
// names are not required to match.
if y, ok := y.(*Signature); ok {
return x.variadic == y.variadic &&
identicalInternal(x.params, y.params, p) &&
identicalInternal(x.results, y.results, p)
identical(x.params, y.params, p) &&
identical(x.results, y.results, p)
}
case *Interface:
@ -247,7 +247,7 @@ func identicalInternal(x, y Type, p *ifacePair) bool {
}
for i, f := range a {
g := b[i]
if f.Id() != g.Id() || !identicalInternal(f.typ, g.typ, q) {
if f.Id() != g.Id() || !identical(f.typ, g.typ, q) {
return false
}
}
@ -258,14 +258,14 @@ func identicalInternal(x, y Type, p *ifacePair) bool {
case *Map:
// Two map types are identical if they have identical key and value types.
if y, ok := y.(*Map); ok {
return identicalInternal(x.key, y.key, p) && identicalInternal(x.elem, y.elem, p)
return identical(x.key, y.key, p) && identical(x.elem, y.elem, p)
}
case *Chan:
// Two channel types are identical if they have identical value types
// and the same direction.
if y, ok := y.(*Chan); ok {
return x.dir == y.dir && identicalInternal(x.elem, y.elem, p)
return x.dir == y.dir && identical(x.elem, y.elem, p)
}
case *Named:

View File

@ -141,6 +141,6 @@ func SelectionString(this *Package, s *Selection) string {
buf.WriteByte('(')
WriteType(&buf, this, s.Recv())
fmt.Fprintf(&buf, ") %s", s.obj.Name())
writeSignature(&buf, this, s.Type().(*Signature))
WriteSignature(&buf, this, s.Type().(*Signature))
return buf.String()
}

View File

@ -40,6 +40,22 @@ func TypeString(this *Package, typ Type) string {
// Named types are printed package-qualified if they
// do not belong to this package.
func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
writeType(buf, this, typ, make([]Type, 8))
}
func writeType(buf *bytes.Buffer, this *Package, typ Type, visited []Type) {
// Theoretically, this is a quadratic lookup algorithm, but in
// practice deeply nested composite types with unnamed component
// types are uncommon. This code is likely more efficient than
// using a map.
for _, t := range visited {
if t == typ {
fmt.Fprintf(buf, "○%T", typ) // cycle to typ
return
}
}
visited = append(visited, typ)
switch t := typ.(type) {
case nil:
buf.WriteString("<nil>")
@ -61,11 +77,11 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
case *Array:
fmt.Fprintf(buf, "[%d]", t.len)
WriteType(buf, this, t.elem)
writeType(buf, this, t.elem, visited)
case *Slice:
buf.WriteString("[]")
WriteType(buf, this, t.elem)
writeType(buf, this, t.elem, visited)
case *Struct:
buf.WriteString("struct{")
@ -77,7 +93,7 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
buf.WriteString(f.name)
buf.WriteByte(' ')
}
WriteType(buf, this, f.typ)
writeType(buf, this, f.typ, visited)
if tag := t.Tag(i); tag != "" {
fmt.Fprintf(buf, " %q", tag)
}
@ -86,14 +102,14 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
case *Pointer:
buf.WriteByte('*')
WriteType(buf, this, t.base)
writeType(buf, this, t.base, visited)
case *Tuple:
writeTuple(buf, this, t, false)
writeTuple(buf, this, t, false, visited)
case *Signature:
buf.WriteString("func")
writeSignature(buf, this, t)
writeSignature(buf, this, t, visited)
case *Interface:
// We write the source-level methods and embedded types rather
@ -116,7 +132,7 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
buf.WriteString("; ")
}
buf.WriteString(m.name)
writeSignature(buf, this, m.typ.(*Signature))
writeSignature(buf, this, m.typ.(*Signature), visited)
}
} else {
// print explicit interface methods and embedded types
@ -125,22 +141,22 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
buf.WriteString("; ")
}
buf.WriteString(m.name)
writeSignature(buf, this, m.typ.(*Signature))
writeSignature(buf, this, m.typ.(*Signature), visited)
}
for i, typ := range t.embeddeds {
if i > 0 || len(t.methods) > 0 {
buf.WriteString("; ")
}
WriteType(buf, this, typ)
writeType(buf, this, typ, visited)
}
}
buf.WriteByte('}')
case *Map:
buf.WriteString("map[")
WriteType(buf, this, t.key)
writeType(buf, this, t.key, visited)
buf.WriteByte(']')
WriteType(buf, this, t.elem)
writeType(buf, this, t.elem, visited)
case *Chan:
var s string
@ -163,7 +179,7 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
if parens {
buf.WriteByte('(')
}
WriteType(buf, this, t.elem)
writeType(buf, this, t.elem, visited)
if parens {
buf.WriteByte(')')
}
@ -190,7 +206,7 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
}
}
func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, variadic bool) {
func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, variadic bool, visited []Type) {
buf.WriteByte('(')
if tup != nil {
for i, v := range tup.vars {
@ -206,14 +222,22 @@ func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, variadic bool) {
buf.WriteString("...")
typ = typ.(*Slice).elem
}
WriteType(buf, this, typ)
writeType(buf, this, typ, visited)
}
}
buf.WriteByte(')')
}
func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature) {
writeTuple(buf, this, sig.params, sig.variadic)
// WriteSignature writes the representation of the signature sig to buf,
// without a leading "func" keyword.
// Named types are printed package-qualified if they
// do not belong to this package.
func WriteSignature(buf *bytes.Buffer, this *Package, sig *Signature) {
writeSignature(buf, this, sig, make([]Type, 8))
}
func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature, visited []Type) {
writeTuple(buf, this, sig.params, sig.variadic, visited)
n := sig.results.Len()
if n == 0 {
@ -224,10 +248,10 @@ func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature) {
buf.WriteByte(' ')
if n == 1 && sig.results.vars[0].name == "" {
// single unnamed result
WriteType(buf, this, sig.results.vars[0].typ)
writeType(buf, this, sig.results.vars[0].typ, visited)
return
}
// multiple or named result(s)
writeTuple(buf, this, sig.results, false)
writeTuple(buf, this, sig.results, false, visited)
}