From c8eb71b057dc28874902e9344d8a82de3edbb9eb Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 28 Dec 2012 14:30:36 -0800 Subject: [PATCH] go/types: Steps towards removing ast.Object from exported API. - introduced type Method for methods - renamed StructField -> Field - removed ObjList - methods are not sorted anymore in interfaces (for now) R=adonovan CC=golang-dev https://golang.org/cl/7023043 --- src/pkg/go/types/api.go | 2 ++ src/pkg/go/types/errors.go | 4 +-- src/pkg/go/types/expr.go | 39 +++++++++++++----------- src/pkg/go/types/gcimporter.go | 15 +++++----- src/pkg/go/types/operand.go | 14 ++++----- src/pkg/go/types/predicates.go | 45 ++++++++++++++++++++-------- src/pkg/go/types/testdata/decls0.src | 2 +- src/pkg/go/types/types.go | 40 ++++++++++--------------- src/pkg/go/types/types_test.go | 5 +--- src/pkg/go/types/universe.go | 5 ++-- 10 files changed, 92 insertions(+), 79 deletions(-) diff --git a/src/pkg/go/types/api.go b/src/pkg/go/types/api.go index a4f6af7847..8ccd969a8d 100644 --- a/src/pkg/go/types/api.go +++ b/src/pkg/go/types/api.go @@ -5,6 +5,8 @@ // Package types declares the data structures for representing // Go types and implements typechecking of package files. // +// WARNING: THE TYPES API IS SUBJECT TO SIGNIFICANT CHANGE. +// package types import ( diff --git a/src/pkg/go/types/errors.go b/src/pkg/go/types/errors.go index f0ca69af5c..728ea9fcbf 100644 --- a/src/pkg/go/types/errors.go +++ b/src/pkg/go/types/errors.go @@ -197,7 +197,7 @@ func typeString(typ Type) string { return buf.String() } -func writeParams(buf *bytes.Buffer, params ObjList, isVariadic bool) { +func writeParams(buf *bytes.Buffer, params []*ast.Object, isVariadic bool) { buf.WriteByte('(') for i, par := range params { if i > 0 { @@ -287,7 +287,7 @@ func writeType(buf *bytes.Buffer, typ Type) { buf.WriteString("; ") } buf.WriteString(m.Name) - writeSignature(buf, m.Type.(*Signature)) + writeSignature(buf, m.Type) } buf.WriteByte('}') diff --git a/src/pkg/go/types/expr.go b/src/pkg/go/types/expr.go index bf3be532a3..8063f391c2 100644 --- a/src/pkg/go/types/expr.go +++ b/src/pkg/go/types/expr.go @@ -22,7 +22,7 @@ import ( // - clients need access to builtins type information // - API tests are missing (e.g., identifiers should be handled as expressions in callbacks) -func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (params ObjList, isVariadic bool) { +func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (params []*ast.Object, isVariadic bool) { if list == nil { return } @@ -70,7 +70,7 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param return } -func (check *checker) collectMethods(list *ast.FieldList) (methods ObjList) { +func (check *checker) collectMethods(list *ast.FieldList) (methods []*Method) { if list == nil { return } @@ -81,14 +81,13 @@ func (check *checker) collectMethods(list *ast.FieldList) (methods ObjList) { if len(f.Names) > 0 { // methods (the parser ensures that there's only one // and we don't care if a constructed AST has more) - if _, ok := typ.(*Signature); !ok { + sig, ok := typ.(*Signature) + if !ok { check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ) continue } for _, name := range f.Names { - obj := name.Obj - obj.Type = typ - methods = append(methods, obj) + methods = append(methods, &Method{name.Name, sig}) } } else { // embedded interface @@ -101,14 +100,20 @@ func (check *checker) collectMethods(list *ast.FieldList) (methods ObjList) { } } } - // check for double declarations - methods.Sort() - prev := "" - for _, obj := range methods { - if obj.Name == prev { - check.errorf(list.Pos(), "multiple methods named %s", prev) + // Check for double declarations. + // The parser inserts methods into an interface-local scope, so local + // double declarations are reported by the parser already. We need to + // check again for conflicts due to embedded interfaces. This will lead + // to a 2nd error message if the double declaration was reported before + // by the parser. + // TODO(gri) clean this up a bit + seen := make(map[string]bool) + for _, m := range methods { + if seen[m.Name] { + check.errorf(list.Pos(), "multiple methods named %s", m.Name) return // keep multiple entries, lookup will only return the first entry } + seen[m.Name] = true } return } @@ -125,7 +130,7 @@ func (check *checker) tag(t *ast.BasicLit) string { return "" } -func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*StructField) { +func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field) { if list == nil { return } @@ -135,15 +140,15 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [ if len(f.Names) > 0 { // named fields for _, name := range f.Names { - fields = append(fields, &StructField{name.Name, typ, tag, false}) + fields = append(fields, &Field{name.Name, typ, tag, false}) } } else { // anonymous field switch t := deref(typ).(type) { case *Basic: - fields = append(fields, &StructField{t.Name, typ, tag, true}) + fields = append(fields, &Field{t.Name, typ, tag, true}) case *NamedType: - fields = append(fields, &StructField{t.Obj.Name, typ, tag, true}) + fields = append(fields, &Field{t.Obj.Name, typ, tag, true}) default: if typ != Typ[Invalid] { check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ) @@ -921,7 +926,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle arg.Type = x.typ x.mode = value x.typ = &Signature{ - Params: append(ObjList{arg}, sig.Params...), + Params: append([]*ast.Object{arg}, sig.Params...), Results: sig.Results, IsVariadic: sig.IsVariadic, } diff --git a/src/pkg/go/types/gcimporter.go b/src/pkg/go/types/gcimporter.go index 4318e6aa21..732bb46682 100644 --- a/src/pkg/go/types/gcimporter.go +++ b/src/pkg/go/types/gcimporter.go @@ -383,8 +383,8 @@ func (p *gcParser) parseName() (name string) { // Field = Name Type [ string_lit ] . // -func (p *gcParser) parseField() *StructField { - var f StructField +func (p *gcParser) parseField() *Field { + var f Field f.Name = p.parseName() f.Type = p.parseType() if p.tok == scanner.String { @@ -406,7 +406,7 @@ func (p *gcParser) parseField() *StructField { // FieldList = Field { ";" Field } . // func (p *gcParser) parseStructType() Type { - var fields []*StructField + var fields []*Field parseField := func() { fields = append(fields, p.parseField()) @@ -510,12 +510,12 @@ func (p *gcParser) parseSignature() *Signature { // visible in the export data. // func (p *gcParser) parseInterfaceType() Type { - var methods ObjList + var methods []*Method parseMethod := func() { - obj := ast.NewObj(ast.Fun, p.parseName()) - obj.Type = p.parseSignature() - methods = append(methods, obj) + name := p.parseName() + typ := p.parseSignature() + methods = append(methods, &Method{name, typ}) } p.expectKeyword("interface") @@ -529,7 +529,6 @@ func (p *gcParser) parseInterfaceType() Type { } p.expect('}') - methods.Sort() return &Interface{Methods: methods} } diff --git a/src/pkg/go/types/operand.go b/src/pkg/go/types/operand.go index e232f356e6..f85e6b4036 100644 --- a/src/pkg/go/types/operand.go +++ b/src/pkg/go/types/operand.go @@ -301,10 +301,10 @@ func lookupFieldBreadthFirst(list []embeddedType, name string) (res lookupResult case *Interface: // look for a matching method - for _, obj := range typ.Methods { - if obj.Name == name { - assert(obj.Type != nil) - if !potentialMatch(e.multiples, value, obj.Type.(Type)) { + for _, m := range typ.Methods { + if m.Name == name { + assert(m.Type != nil) + if !potentialMatch(e.multiples, value, m.Type) { return // name collision } } @@ -380,9 +380,9 @@ func lookupField(typ Type, name string) (operandMode, Type) { } case *Interface: - for _, obj := range typ.Methods { - if obj.Name == name { - return value, obj.Type.(Type) + for _, m := range typ.Methods { + if m.Name == name { + return value, m.Type } } } diff --git a/src/pkg/go/types/predicates.go b/src/pkg/go/types/predicates.go index ff6825ba3b..21d08fe14c 100644 --- a/src/pkg/go/types/predicates.go +++ b/src/pkg/go/types/predicates.go @@ -165,7 +165,7 @@ func isIdentical(x, y Type) bool { // the same names and identical function types. Lower-case method names from // different packages are always different. The order of the methods is irrelevant. if y, ok := y.(*Interface); ok { - return identicalTypes(x.Methods, y.Methods) // methods are sorted + return identicalMethods(x.Methods, y.Methods) // methods are sorted } case *Map: @@ -194,17 +194,36 @@ func isIdentical(x, y Type) bool { // identicalTypes returns true if both lists a and b have the // same length and corresponding objects have identical types. -func identicalTypes(a, b ObjList) bool { - if len(a) == len(b) { - for i, x := range a { - y := b[i] - if !isIdentical(x.Type.(Type), y.Type.(Type)) { - return false - } +func identicalTypes(a, b []*ast.Object) bool { + if len(a) != len(b) { + return false + } + for i, x := range a { + y := b[i] + if !isIdentical(x.Type.(Type), y.Type.(Type)) { + return false } - return true } - return false + return true +} + +// identicalMethods returns true if both lists a and b have the +// same length and corresponding methods have identical types. +// TODO(gri) make this more efficient +func identicalMethods(a, b []*Method) bool { + if len(a) != len(b) { + return false + } + m := make(map[string]*Method) + for _, x := range a { + m[x.Name] = x + } + for _, y := range b { + if x := m[y.Name]; x == nil || !isIdentical(x.Type, y.Type) { + return false + } + } + return true } // underlying returns the underlying type of typ. @@ -257,14 +276,14 @@ func defaultType(typ Type) Type { // it returns the first missing method required by T and whether it // is missing or simply has the wrong type. // -func missingMethod(typ Type, T *Interface) (method *ast.Object, wrongType bool) { +func missingMethod(typ Type, T *Interface) (method *Method, wrongType bool) { // TODO(gri): distinguish pointer and non-pointer receivers // an interface type implements T if it has no methods with conflicting signatures // Note: This is stronger than the current spec. Should the spec require this? if ityp, _ := underlying(typ).(*Interface); ityp != nil { for _, m := range T.Methods { mode, sig := lookupField(ityp, m.Name) // TODO(gri) no need to go via lookupField - if mode != invalid && !isIdentical(sig, m.Type.(Type)) { + if mode != invalid && !isIdentical(sig, m.Type) { return m, true } } @@ -277,7 +296,7 @@ func missingMethod(typ Type, T *Interface) (method *ast.Object, wrongType bool) if mode == invalid { return m, false } - if !isIdentical(sig, m.Type.(Type)) { + if !isIdentical(sig, m.Type) { return m, true } } diff --git a/src/pkg/go/types/testdata/decls0.src b/src/pkg/go/types/testdata/decls0.src index 70623c6166..33d4b38014 100644 --- a/src/pkg/go/types/testdata/decls0.src +++ b/src/pkg/go/types/testdata/decls0.src @@ -127,7 +127,7 @@ type ( I2 interface { m1() } - I3 interface { + I3 interface { /* ERROR "multiple methods named m1" */ m1() m1 /* ERROR "redeclared" */ () } diff --git a/src/pkg/go/types/types.go b/src/pkg/go/types/types.go index 87df029564..871d34ecf7 100644 --- a/src/pkg/go/types/types.go +++ b/src/pkg/go/types/types.go @@ -4,13 +4,9 @@ package types -import ( - "go/ast" - "sort" -) +import "go/ast" // All types implement the Type interface. -// TODO(gri) Eventually determine what common Type functionality should be exported. type Type interface { aType() } @@ -95,7 +91,8 @@ type Slice struct { Elt Type } -type StructField struct { +// A Field represents a field of a struct. +type Field struct { Name string // unqualified type name for anonymous fields Type Type Tag string @@ -105,7 +102,7 @@ type StructField struct { // A Struct represents a struct type struct{...}. type Struct struct { implementsType - Fields []*StructField + Fields []*Field } func (typ *Struct) fieldIndex(name string) int { @@ -126,16 +123,16 @@ type Pointer struct { // A Result represents a (multi-value) function call result. type Result struct { implementsType - Values ObjList // Signature.Results of the function called + Values []*ast.Object // Signature.Results of the function called } // A Signature represents a user-defined function type func(...) (...). type Signature struct { implementsType - Recv *ast.Object // nil if not a method - Params ObjList // (incoming) parameters from left to right; or nil - Results ObjList // (outgoing) results from left to right; or nil - IsVariadic bool // true if the last parameter's type is of the form ...T + Recv *ast.Object // nil if not a method + Params []*ast.Object // (incoming) parameters from left to right; or nil + Results []*ast.Object // (outgoing) results from left to right; or nil + IsVariadic bool // true if the last parameter's type is of the form ...T } // builtinId is an id of a builtin function. @@ -180,10 +177,16 @@ type builtin struct { isStatement bool // true if the built-in is valid as an expression statement } +// A Method represents a method of an interface. +type Method struct { + Name string + Type *Signature +} + // An Interface represents an interface type interface{...}. type Interface struct { implementsType - Methods ObjList // interface methods sorted by name; or nil + Methods []*Method // TODO(gri) consider keeping them in sorted order } // A Map represents a map type map[Key]Elt. @@ -206,17 +209,6 @@ type NamedType struct { Underlying Type // nil if not fully declared yet; never a *NamedType } -// An ObjList represents an ordered (in some fashion) list of objects. -type ObjList []*ast.Object - -// ObjList implements sort.Interface. -func (list ObjList) Len() int { return len(list) } -func (list ObjList) Less(i, j int) bool { return list[i].Name < list[j].Name } -func (list ObjList) Swap(i, j int) { list[i], list[j] = list[j], list[i] } - -// Sort sorts an object list by object name. -func (list ObjList) Sort() { sort.Sort(list) } - // All concrete types embed implementsType which // ensures that all types implement the Type interface. type implementsType struct{} diff --git a/src/pkg/go/types/types_test.go b/src/pkg/go/types/types_test.go index 3345a63d96..48a1d61e3b 100644 --- a/src/pkg/go/types/types_test.go +++ b/src/pkg/go/types/types_test.go @@ -88,10 +88,7 @@ var testTypes = []testEntry{ // interfaces dup("interface{}"), dup("interface{m()}"), - {`interface{ - m(int) float32 - String() string - }`, `interface{String() string; m(int) float32}`}, // methods are sorted + dup(`interface{m(int) float32; String() string}`), // TODO(gri) add test for interface w/ anonymous field // maps diff --git a/src/pkg/go/types/universe.go b/src/pkg/go/types/universe.go index 0fbaa3329d..3b45fb74a9 100644 --- a/src/pkg/go/types/universe.go +++ b/src/pkg/go/types/universe.go @@ -118,10 +118,9 @@ func init() { { res := ast.NewObj(ast.Var, "") res.Type = Typ[String] - err := ast.NewObj(ast.Fun, "Error") - err.Type = &Signature{Results: ObjList{res}} + err := &Method{"Error", &Signature{Results: []*ast.Object{res}}} obj := def(ast.Typ, "error") - obj.Type = &NamedType{Underlying: &Interface{Methods: ObjList{err}}, Obj: obj} + obj.Type = &NamedType{Underlying: &Interface{Methods: []*Method{err}}, Obj: obj} } // predeclared constants -- 2.48.1