Commit c8eb71b0 authored by Robert Griesemer's avatar Robert Griesemer

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
parent 73aaa44c
......@@ -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 (
......
......@@ -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('}')
......
......@@ -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,
}
......
......@@ -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}
}
......
......@@ -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
}
}
}
......
......@@ -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
}
}
......
......@@ -127,7 +127,7 @@ type (
I2 interface {
m1()
}
I3 interface {
I3 interface { /* ERROR "multiple methods named m1" */
m1()
m1 /* ERROR "redeclared" */ ()
}
......
......@@ -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{}
......
......@@ -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
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment