From e405e03b161146d1858ff1e428a93601a75a1ddd Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 20 Dec 2013 16:35:31 -0800 Subject: [PATCH] go.tools/go/types: complete set of Interface accessors Also: Provide GcCompatibilityMode for printing types (intended for testing with gc-generated export data only). (TBR adonovan) R=adonovan TBR=adonovan CC=golang-codereviews https://golang.org/cl/44780043 --- go/types/expr.go | 4 +-- go/types/lookup.go | 2 +- go/types/operand.go | 2 +- go/types/types.go | 35 +++++++++++++++----- go/types/typestring.go | 72 ++++++++++++++++++++++++++++-------------- go/types/typexpr.go | 19 ++++++++++- 6 files changed, 98 insertions(+), 36 deletions(-) diff --git a/go/types/expr.go b/go/types/expr.go index aaae1c2342..af96bbdf3e 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -522,7 +522,7 @@ func (check *checker) convertUntyped(x *operand, target Type) { } } case *Interface: - if !x.isNil() && t.NumMethods() > 0 /* empty interfaces are ok */ { + if !x.isNil() && !t.Empty() /* empty interfaces are ok */ { goto Error } // Update operand types to the default type rather then @@ -535,7 +535,7 @@ func (check *checker) convertUntyped(x *operand, target Type) { target = Typ[UntypedNil] } else { // cannot assign untyped values to non-empty interfaces - if t.NumMethods() > 0 { + if !t.Empty() { goto Error } target = defaultType(x.typ) diff --git a/go/types/lookup.go b/go/types/lookup.go index 842a416180..d28959a638 100644 --- a/go/types/lookup.go +++ b/go/types/lookup.go @@ -246,7 +246,7 @@ func consolidateMultiples(list []embeddedType) []embeddedType { // func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) { // fast path for common case - if T.NumMethods() == 0 { + if T.Empty() { return } diff --git a/go/types/operand.go b/go/types/operand.go index 6d22d3d4a7..51acd4858e 100644 --- a/go/types/operand.go +++ b/go/types/operand.go @@ -266,7 +266,7 @@ func (x *operand) isAssignableTo(conf *Config, T Type) bool { return Vb.kind == UntypedBool && isBoolean(Tu) } case *Interface: - return x.isNil() || t.NumMethods() == 0 + return x.isNil() || t.Empty() case *Pointer, *Signature, *Slice, *Map, *Chan: return x.isNil() } diff --git a/go/types/types.go b/go/types/types.go index d5f0276889..5733f49ffc 100644 --- a/go/types/types.go +++ b/go/types/types.go @@ -247,15 +247,15 @@ func (s *Signature) IsVariadic() bool { return s.isVariadic } // An Interface represents an interface type. type Interface struct { - methods []*Func // explicitly declared methods - types []*Named // explicitly embedded types + methods []*Func // ordered list of explicitly declared methods + embeddeds []*Named // ordered list of explicitly embedded types allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset) mset cachedMethodSet // method set for interface, lazily initialized } -// NewInterface returns a new interface for the given methods. -func NewInterface(methods []*Func, types []*Named) *Interface { +// NewInterface returns a new interface for the given methods and embedded types. +func NewInterface(methods []*Func, embeddeds []*Named) *Interface { typ := new(Interface) var mset objset @@ -271,11 +271,11 @@ func NewInterface(methods []*Func, types []*Named) *Interface { sort.Sort(byUniqueMethodName(methods)) var allMethods []*Func - if types == nil { + if embeddeds == nil { allMethods = methods } else { allMethods = append(allMethods, methods...) - for _, t := range types { + for _, t := range embeddeds { it := t.Underlying().(*Interface) for _, tm := range it.allMethods { // Make a copy of the method and adjust its receiver type. @@ -286,21 +286,40 @@ func NewInterface(methods []*Func, types []*Named) *Interface { allMethods = append(allMethods, &newm) } } + sort.Sort(byUniqueTypeName(embeddeds)) sort.Sort(byUniqueMethodName(allMethods)) } typ.methods = methods - typ.types = types + typ.embeddeds = embeddeds typ.allMethods = allMethods return typ } -// NumMethods returns the number of methods of interface t. +// NumExplicitMethods returns the number of explicitly declared methods of interface t. +func (t *Interface) NumExplicitMethods() int { return len(t.methods) } + +// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods(). +// The methods are ordered by their unique Id. +func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] } + +// NumEmbeddeds returns the number of embedded types in interface t. +func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) } + +// Embedded returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds(). +// The types are ordered by the corresponding TypeName's unique Id. +func (t *Interface) Embedded(i int) *Named { return t.embeddeds[i] } + +// NumMethods returns the total number of methods of interface t. func (t *Interface) NumMethods() int { return len(t.allMethods) } // Method returns the i'th method of interface t for 0 <= i < t.NumMethods(). +// The methods are ordered by their unique Id. func (t *Interface) Method(i int) *Func { return t.allMethods[i] } +// Empty returns true if t is the empty interface. +func (t *Interface) Empty() bool { return len(t.allMethods) == 0 } + // A Map represents a map type. type Map struct { key, elem Type diff --git a/go/types/typestring.go b/go/types/typestring.go index 233e86d808..03de10a997 100644 --- a/go/types/typestring.go +++ b/go/types/typestring.go @@ -9,9 +9,25 @@ package types import ( "bytes" "fmt" - "sort" ) +// If GcCompatibilityMode is set, printing of types is modified +// to match the representation of some types in the gc compiler: +// +// - byte and rune lose their alias name and simply stand for +// uint8 and int32 respectively +// +// - embedded interfaces get flattened (the embedding info is lost, +// and certain recursive interface types cannot be printed anymore) +// +// This makes it easier to compare packages computed with the type- +// checker vs packages imported from gc export data. +// +// Caution: This flag affects all uses of WriteType, globally. +// It is only provided for testing in conjunction with +// gc-generated data. It may be removed at any time. +var GcCompatibilityMode bool + // TypeString returns the string representation of typ. // Named types are printed package-qualified if they // do not belong to this package. @@ -33,6 +49,15 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) { if t.kind == UnsafePointer { buf.WriteString("unsafe.") } + if GcCompatibilityMode { + // forget the alias names + switch t.kind { + case Byte: + t = Typ[Uint8] + case Rune: + t = Typ[Int32] + } + } buf.WriteString(t.name) case *Array: @@ -84,30 +109,31 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) { // } // buf.WriteString("interface{") - // Sort methods instead of printing them in source order. - // This is needed at the moment because interfaces are - // created by providing the list of source methods and - // embedded interfaces, but only have an accessor to the list - // of all methods (which is sorted by name). By sorting here - // we guarantee that the list is printed the same independent - // of how the Interface was created. - // TODO(gri) remove this extra step once we have the complete - // set of accessors for Interface. - var methods []*Func - methods = append(methods, t.methods...) // make a copy - sort.Sort(byUniqueMethodName(methods)) - for i, m := range methods { - if i > 0 { - buf.WriteString("; ") + if GcCompatibilityMode { + // print flattened interface + // (useful to compare against gc-generated interfaces) + for i, m := range t.allMethods { + if i > 0 { + buf.WriteString("; ") + } + buf.WriteString(m.name) + writeSignature(buf, this, m.typ.(*Signature)) } - buf.WriteString(m.name) - writeSignature(buf, this, m.typ.(*Signature)) - } - for i, typ := range t.types { - if i > 0 || len(t.methods) > 0 { - buf.WriteString("; ") + } else { + // print explicit interface methods and embedded types + for i, m := range t.methods { + if i > 0 { + buf.WriteString("; ") + } + buf.WriteString(m.name) + writeSignature(buf, this, m.typ.(*Signature)) + } + for i, typ := range t.embeddeds { + if i > 0 || len(t.methods) > 0 { + buf.WriteString("; ") + } + WriteType(buf, this, typ) } - WriteType(buf, this, typ) } buf.WriteByte('}') diff --git a/go/types/typexpr.go b/go/types/typexpr.go index 19d428f6fd..7541e92ece 100644 --- a/go/types/typexpr.go +++ b/go/types/typexpr.go @@ -527,7 +527,7 @@ func (check *checker) interfaceType(ityp *ast.InterfaceType, def *Named, cycleOk check.errorf(pos, "%s is not an interface", named) continue } - iface.types = append(iface.types, named) + iface.embeddeds = append(iface.embeddeds, named) // collect embedded methods for _, m := range embed.allMethods { if check.declareInSet(&mset, pos, m) { @@ -564,11 +564,28 @@ func (check *checker) interfaceType(ityp *ast.InterfaceType, def *Named, cycleOk *m.typ.(*Signature) = *sig // update signature (don't replace it!) } + // TODO(gri) The list of explicit methods is only sorted for now to + // produce the same Interface as NewInterface. We may be able to + // claim source order in the future. Revisit. + sort.Sort(byUniqueMethodName(iface.methods)) + + // TODO(gri) The list of embedded types is only sorted for now to + // produce the same Interface as NewInterface. We may be able to + // claim source order in the future. Revisit. + sort.Sort(byUniqueTypeName(iface.embeddeds)) + sort.Sort(byUniqueMethodName(iface.allMethods)) return iface } +// byUniqueTypeName named type lists can be sorted by their unique type names. +type byUniqueTypeName []*Named + +func (a byUniqueTypeName) Len() int { return len(a) } +func (a byUniqueTypeName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() } +func (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + // byUniqueMethodName method lists can be sorted by their unique method names. type byUniqueMethodName []*Func