diff --git a/go/types/builtins.go b/go/types/builtins.go index 4bd7d338..05e792c4 100644 --- a/go/types/builtins.go +++ b/go/types/builtins.go @@ -342,12 +342,12 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota goto Error } sel := arg.Sel.Name - res := lookupField(x.typ, check.pkg, arg.Sel.Name) - if res.index == nil { + _, index, _ := LookupFieldOrMethod(x.typ, check.pkg, arg.Sel.Name) + if index == nil { check.invalidArg(x.pos(), "%s has no single field %s", x, sel) goto Error } - offs := check.ctxt.offsetof(x.typ.Deref(), res.index) + offs := check.ctxt.offsetof(x.typ.Deref(), index) if offs < 0 { check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, x) goto Error diff --git a/go/types/expr.go b/go/types/expr.go index 0b91ce96..afa72b2b 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -1158,7 +1158,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key) continue } - i := utyp.index(check.pkg, key.Name) + i := fieldIndex(utyp.fields, check.pkg, key.Name) if i < 0 { check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name) continue @@ -1310,24 +1310,27 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle if x.mode == invalid { goto Error } - res := lookupField(x.typ, check.pkg, sel) - if res.mode == invalid { + // TODO(gri) use collision information for better error message + obj, _, _ := LookupFieldOrMethod(x.typ, check.pkg, sel) + if obj == nil { check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel) goto Error } - check.callIdent(e.Sel, res.obj) + check.callIdent(e.Sel, obj) if x.mode == typexpr { // method expression - sig, ok := res.obj.Type().(*Signature) + m, ok := obj.(*Func) if !ok { check.invalidOp(e.Pos(), "%s has no method %s", x, sel) goto Error } + // the receiver type becomes the type of the first function // argument of the method expression's function type // TODO(gri) at the moment, method sets don't correctly track // pointer vs non-pointer receivers => typechecker is too lenient var params []*Var + sig := m.typ.(*Signature) if sig.params != nil { params = sig.params.vars } @@ -1339,8 +1342,19 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle } } else { // regular selector - x.mode = res.mode - x.typ = res.obj.Type() + switch obj := obj.(type) { + case *Field: + x.mode = variable + x.typ = obj.typ + case *Func: + // TODO(gri) Temporary assert to verify corresponding lookup via method sets. + // Remove eventually. + assert(NewMethodSet(x.typ).Lookup(check.pkg, sel) == obj) + x.mode = value + x.typ = obj.typ + default: + unreachable() + } } case *ast.IndexExpr: @@ -1509,7 +1523,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle if typ == Typ[Invalid] { goto Error } - if method, wrongType := missingMethod(typ, T); method != nil { + if method, wrongType := MissingMethod(typ, T); method != nil { var msg string if wrongType { msg = "%s cannot have dynamic type %s (wrong type for method %s)" diff --git a/go/types/gcimporter.go b/go/types/gcimporter.go index b6ab88ec..42aacf06 100644 --- a/go/types/gcimporter.go +++ b/go/types/gcimporter.go @@ -463,8 +463,10 @@ func (p *gcParser) parseField() (*Field, string) { // anonymous field - typ must be T or *T and T must be a type name switch typ := typ.Deref().(type) { case *Basic: // basic types are named types + pkg = nil name = typ.name case *Named: + pkg = typ.obj.pkg name = typ.obj.name default: p.errorf("anonymous field expected") diff --git a/go/types/lookup.go b/go/types/lookup.go new file mode 100644 index 00000000..e031ad86 --- /dev/null +++ b/go/types/lookup.go @@ -0,0 +1,236 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements various field and method lookup functions. + +package types + +import "go/ast" + +// LookupFieldOrMethod looks up a field or method with given package and name in typ. +// If an entry is found, obj is the corresponding *Field or *Func. For fields, index +// is the index sequence to reach the (possibly embedded) field; for methods, index +// is nil; and collision is false. If no entry is found, obj is nil, index is undefined, +// and collision indicates if the reason for not finding an entry was a name collision. +// +func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index []int, collision bool) { + if name == "_" { + return // empty fields/methods are never found + } + + isPtr := false + if p, ok := typ.Underlying().(*Pointer); ok { + typ = p.base + isPtr = true + } + + // TODO(gri) consult isPtr for precise method set computation + _ = isPtr + + // named types that we have seen already + seen := make(map[*Named]bool) + + // We treat the top-most level separately because it's simpler + // (no incoming multiples) and because it's the common case. + + if t, _ := typ.(*Named); t != nil { + seen[t] = true + if m := lookupMethod(t.methods, pkg, name); m != nil { + assert(m.typ != nil) + return m, nil, false + } + typ = t.underlying + } + + // embedded named types at the current and next lower depth + type embedded struct { + typ *Named + index []int // field index sequence + multiples bool + } + var current, next []embedded + + switch t := typ.(type) { + case *Struct: + for i, f := range t.fields { + if f.isMatch(pkg, name) { + assert(f.typ != nil) + return f, []int{i}, false + } + if f.anonymous { + // Ignore embedded basic types - only user-defined + // named types can have methods or struct fields. + if t, _ := f.typ.Deref().(*Named); t != nil { + next = append(next, embedded{t, []int{i}, false}) + } + } + } + + case *Interface: + if m := lookupMethod(t.methods, pkg, name); m != nil { + assert(m.typ != nil) + return m, nil, false + } + } + + // search the next depth if we don't have a match yet and there's work to do + for obj == nil && len(next) > 0 { + // Consolidate next: collect multiple entries with the same + // type into a single entry marked as containing multiples. + n := 0 // number of entries w/ unique type + prev := make(map[*Named]int) // index at which type was previously seen + for _, e := range next { + if i, found := prev[e.typ]; found { + next[i].multiples = true + // ignore this entry + } else { + prev[e.typ] = n + next[n] = e + n++ + } + } + // next[:n] is the list of embedded entries to process + + // The underlying arrays of current and next are different, thus + // swapping is safe and they never share the same underlying array. + current, next = next[:n], current[:0] // don't waste underlying array + + // look for name in all types at this depth + for _, e := range current { + if seen[e.typ] { + continue + } + seen[e.typ] = true + + // look for a matching attached method + if m := lookupMethod(e.typ.methods, pkg, name); m != nil { + // potential match + assert(m.typ != nil) + if obj != nil || e.multiples { + return nil, nil, true + } + obj = m + index = nil + } + + switch t := e.typ.underlying.(type) { + case *Struct: + // look for a matching field and collect embedded types + for i, f := range t.fields { + if f.isMatch(pkg, name) { + assert(f.typ != nil) + if obj != nil || e.multiples { + return nil, nil, true + } + obj = f + index = append(index[:0], e.index...) + index = append(index, i) + continue + } + // Collect embedded struct fields for searching the next + // lower depth, but only if we have not seen a match yet + // (if we have a match it is either the desired field or + // we have a name collision on the same depth; in either + // case we don't need to look further). + // Embedded fields are always of the form T or *T where + // T is a named type. If e.typ appeared multiple times at + // this depth, f.typ appears multiple times at the next + // depth. + if obj == nil && f.anonymous { + // Ignore embedded basic types - only user-defined + // named types can have methods or have struct fields. + if t, _ := f.typ.Deref().(*Named); t != nil { + var copy []int + copy = append(copy, e.index...) + copy = append(copy, i) + next = append(next, embedded{t, copy, e.multiples}) + } + } + } + + case *Interface: + // look for a matching method + if m := lookupMethod(t.methods, pkg, name); m != nil { + assert(m.typ != nil) + if obj != nil || e.multiples { + return nil, nil, true + } + obj = m + index = nil + } + } + } + } + + return +} + +// MissingMethod returns (nil, false) if typ implements T, otherwise +// 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 *Func, 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? + + // fast path for common case + if T.IsEmpty() { + return + } + + if ityp, _ := typ.Underlying().(*Interface); ityp != nil { + for _, m := range T.methods { + obj := lookupMethod(ityp.methods, m.pkg, m.name) + if obj != nil && !IsIdentical(obj.Type(), m.typ) { + return m, true + } + } + return + } + + // a concrete type implements T if it implements all methods of T. + for _, m := range T.methods { + obj, _, _ := LookupFieldOrMethod(typ, m.pkg, m.name) + if obj == nil { + return m, false + } + if !IsIdentical(obj.Type(), m.typ) { + return m, true + } + } + return +} + +// fieldIndex returns the index for the field with matching package and name, or a value < 0. +func fieldIndex(fields []*Field, pkg *Package, name string) int { + if name == "_" { + return -1 // blank identifiers are never found + } + for i, f := range fields { + // spec: + // "Two identifiers are different if they are spelled differently, + // or if they appear in different packages and are not exported. + // Otherwise, they are the same." + if f.name == name && (ast.IsExported(name) || f.pkg.path == pkg.path) { + return i + } + } + return -1 +} + +// lookupMethod returns the method with matching package and name, or nil. +func lookupMethod(methods []*Func, pkg *Package, name string) *Func { + assert(name != "_") + for _, m := range methods { + // spec: + // "Two identifiers are different if they are spelled differently, + // or if they appear in different packages and are not exported. + // Otherwise, they are the same." + if m.name == name && (ast.IsExported(name) || m.pkg.path == pkg.path) { + return m + } + } + return nil +} diff --git a/go/types/methodset.go b/go/types/methodset.go new file mode 100644 index 00000000..0313731f --- /dev/null +++ b/go/types/methodset.go @@ -0,0 +1,265 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements method sets. + +package types + +import ( + "bytes" + "fmt" + "go/ast" + "sort" +) + +// A MethodSet is an ordered set of methods. +type MethodSet struct { + list []*Func +} + +func (s *MethodSet) String() string { + var buf bytes.Buffer + fmt.Fprint(&buf, "MethodSet{") + if len(s.list) > 0 { + fmt.Fprintln(&buf) + } + for _, m := range s.list { + fmt.Fprintf(&buf, "\t%s\n", key(m.pkg, m.name)) + } + fmt.Fprintln(&buf, "}") + return buf.String() +} + +// Len returns the number of methods in s. +func (s *MethodSet) Len() int { return len(s.list) } + +// At returns the i'th method in s. +func (s *MethodSet) At(i int) *Func { return s.list[i] } + +// Lookup returns the method with matching package and name, or nil if not found. +func (s *MethodSet) Lookup(pkg *Package, name string) *Func { + k := key(pkg, name) + + i := sort.Search(len(s.list), func(i int) bool { + m := s.list[i] + return key(m.pkg, m.name) >= k + }) + + if i < len(s.list) { + m := s.list[i] + if key(m.pkg, m.name) == k { + return m + } + } + return nil +} + +// NewMethodSet computes the method set for the given type. +// BUG(gri): The pointer-ness of the receiver type is still ignored. +func NewMethodSet(typ Type) *MethodSet { + isPtr := false + if p, ok := typ.Underlying().(*Pointer); ok { + typ = p.base + isPtr = true + } + + // TODO(gri) consult isPtr for precise method set computation + _ = isPtr + + // method set up to the current depth + // TODO(gri) allocate lazily, method sets are often empty + base := make(methodSet) + + // named types that we have seen already + seen := make(map[*Named]bool) + + // We treat the top-most level separately because it's simpler + // (no incoming multiples) and because it's the common case. + + if t, _ := typ.(*Named); t != nil { + seen[t] = true + base.add(t.methods, false) + typ = t.underlying + } + + // embedded named types at the current and next lower depth + type embedded struct { + typ *Named + multiples bool + } + var current, next []embedded + + switch t := typ.(type) { + case *Struct: + for _, f := range t.fields { + // Fields and methods must be distinct at the most shallow depth. + // If they are not, the type checker reported an error before, so + // we are ignoring potential conflicts here. + if f.anonymous { + // Ignore embedded basic types - only user-defined + // named types can have methods or struct fields. + if t, _ := f.typ.Deref().(*Named); t != nil { + next = append(next, embedded{t, false}) + } + } + } + + case *Interface: + base.add(t.methods, false) + } + + // collect methods at next lower depth + for len(next) > 0 { + // Consolidate next: collect multiple entries with the same + // type into a single entry marked as containing multiples. + n := 0 // number of entries w/ unique type + prev := make(map[*Named]int) // index at which type was previously seen + for _, e := range next { + if i, found := prev[e.typ]; found { + next[i].multiples = true + // ignore this entry + } else { + prev[e.typ] = n + next[n] = e + n++ + } + } + // next[:n] is the list of embedded entries to process + + // The underlying arrays of current and next are different, thus + // swapping is safe and they never share the same underlying array. + current, next = next[:n], current[:0] // don't waste underlying array + + // field and method sets at this depth + fset := make(fieldSet) + mset := make(methodSet) + + for _, e := range current { + if seen[e.typ] { + continue + } + seen[e.typ] = true + + mset.add(e.typ.methods, e.multiples) + + switch t := e.typ.underlying.(type) { + case *Struct: + for _, f := range t.fields { + fset.add(f, e.multiples) + + // Embedded fields are always of the form T or *T where + // T is a named type. If typ appeared multiple times at + // this depth, f.Type appears multiple times at the next + // depth. + if f.anonymous { + // Ignore embedded basic types - only user-defined + // named types can have methods or have struct fields. + if t, _ := f.typ.Deref().(*Named); t != nil { + next = append(next, embedded{t, e.multiples}) + } + } + } + + case *Interface: + mset.add(t.methods, e.multiples) + } + } + + // Add methods and collisions at this depth to base if no entries with matching + // names exist already. + for k, m := range mset { + if _, found := base[k]; !found { + // Fields collide with methods of the same name at this depth. + if _, found := fset[k]; found { + m = nil // collision + } + base[k] = m + } + } + + // Multiple fields with matching names collide at this depth and shadow all + // entries further down; add them as collisions to base if no entries with + // matching names exist already. + for k, f := range fset { + if f == nil { + if _, found := base[k]; !found { + base[k] = nil + } + } + } + } + + // collect methods + var list []*Func + for _, m := range base { + if m != nil { + list = append(list, m) + } + } + sort.Sort(byKey(list)) + + return &MethodSet{list} +} + +// key computes a unique (lookup and sort) key given a package and name. +func key(pkg *Package, name string) string { + if ast.IsExported(name) { + return name + } + if pkg == nil { + panic("unexported object without package information: " + name) + } + return pkg.path + "." + name +} + +// A fieldSet is a set of fields and name collisions. +// A conflict indicates that multiple fields with the same package and name appeared. +type fieldSet map[string]*Field // a nil entry indicates a name collision + +// Add adds field f to the field set s. +// If multiples is set, f appears multiple times +// and is treated as a collision at this level. +func (s fieldSet) add(f *Field, multiples bool) { + k := key(f.pkg, f.name) + // if f is not in the set, add it + if !multiples { + if _, found := s[k]; !found { + s[k] = f + return + } + } + s[k] = nil // collision +} + +// A methodSet is a set of methods and name collisions. +// A conflict indicates that multiple methods with the same package and name appeared. +type methodSet map[string]*Func // a nil entry indicates a name collision + +// Add adds all methods in list to the method set s. +// If multiples is set, every method in list appears multiple times +// and is treated as a collision at this level. +func (s methodSet) add(list []*Func, multiples bool) { + for _, m := range list { + k := key(m.pkg, m.name) + // if m is not in the set, add it + if !multiples { + if _, found := s[k]; !found { + s[k] = m + continue + } + } + s[k] = nil // collision + } +} + +// byKey function lists can be sorted by key(pkg, name). +type byKey []*Func + +func (a byKey) Len() int { return len(a) } +func (a byKey) Less(i, j int) bool { + x := a[i] + y := a[j] + return key(x.pkg, x.name) < key(y.pkg, y.name) +} +func (a byKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] } diff --git a/go/types/operand.go b/go/types/operand.go index eccd0803..b12ba569 100644 --- a/go/types/operand.go +++ b/go/types/operand.go @@ -142,7 +142,7 @@ func (x *operand) isAssignable(ctxt *Context, T Type) bool { // T is an interface type and x implements T // (Do this check first as it might succeed early.) if Ti, ok := Tu.(*Interface); ok { - if m, _ := missingMethod(x.typ, Ti); m == nil { + if m, _ := MissingMethod(x.typ, Ti); m == nil { return true } } @@ -206,215 +206,3 @@ func (x *operand) isInteger() bool { isInteger(x.typ) || x.mode == constant && isRepresentableConst(x.val, nil, UntypedInt) // no context required for UntypedInt } - -// lookupResult represents the result of a struct field/method lookup. -type lookupResult struct { - mode operandMode - obj Object // *Field or *Func; valid if mode != invalid - index []int // field index sequence; nil for methods -} - -type embeddedType struct { - typ *Named - index []int // field index sequence - multiples bool // if set, typ is embedded multiple times at the same level -} - -func lookupMethod(methods []*Func, pkg *Package, name string) *Func { - if name == "_" { - return nil // blank identifiers are never found - } - for _, m := range methods { - // spec: - // "Two identifiers are different if they are spelled differently, - // or if they appear in different packages and are not exported. - // Otherwise, they are the same." - if m.name == name && (ast.IsExported(name) || m.pkg.path == pkg.path) { - return m - } - } - return nil -} - -// lookupFieldBreadthFirst searches all types in list for a single entry (field -// or method) of the given name from the given package. If such a field is found, -// the result describes the field mode and type; otherwise the result mode is invalid. -// (This function is similar in structure to FieldByNameFunc in reflect/type.go) -// -func lookupFieldBreadthFirst(list []embeddedType, pkg *Package, name string) (res lookupResult) { - // visited records the types that have been searched already. - visited := make(map[*Named]bool) - - // embedded types of the next lower level - var next []embeddedType - - // potentialMatch is invoked every time a match is found. - potentialMatch := func(multiples bool, mode operandMode, obj Object) bool { - if multiples || res.mode != invalid { - // name appeared already at this level - annihilate - res.mode = invalid - return false - } - // first appearance of name - res.mode = mode - res.obj = obj - res.index = nil - return true - } - - // Search the current level if there is any work to do and collect - // embedded types of the next lower level in the next list. - for len(list) > 0 { - // The res.mode indicates whether we have found a match already - // on this level (mode != invalid), or not (mode == invalid). - assert(res.mode == invalid) - - // start with empty next list (don't waste underlying array) - next = next[:0] - - // look for name in all types at this level - for _, e := range list { - typ := e.typ - if visited[typ] { - continue - } - visited[typ] = true - - // look for a matching attached method - if m := lookupMethod(typ.methods, pkg, name); m != nil { - assert(m.typ != nil) - if !potentialMatch(e.multiples, value, m) { - return // name collision - } - } - - switch t := typ.underlying.(type) { - case *Struct: - // look for a matching field and collect embedded types - for i, f := range t.fields { - if f.isMatch(pkg, name) { - assert(f.typ != nil) - if !potentialMatch(e.multiples, variable, f) { - return // name collision - } - var index []int - index = append(index, e.index...) // copy e.index - index = append(index, i) - res.index = index - continue - } - // Collect embedded struct fields for searching the next - // lower level, but only if we have not seen a match yet - // (if we have a match it is either the desired field or - // we have a name collision on the same level; in either - // case we don't need to look further). - // Embedded fields are always of the form T or *T where - // T is a named type. If typ appeared multiple times at - // this level, f.Type appears multiple times at the next - // level. - if f.anonymous && res.mode == invalid { - // Ignore embedded basic types - only user-defined - // named types can have methods or have struct fields. - if t, _ := f.typ.Deref().(*Named); t != nil { - var index []int - index = append(index, e.index...) // copy e.index - index = append(index, i) - next = append(next, embeddedType{t, index, e.multiples}) - } - } - } - - case *Interface: - // look for a matching method - if m := lookupMethod(t.methods, pkg, name); m != nil { - assert(m.typ != nil) - if !potentialMatch(e.multiples, value, m) { - return // name collision - } - } - } - } - - if res.mode != invalid { - // we found a single match on this level - return - } - - // No match and no collision so far. - // Compute the list to search for the next level. - list = list[:0] // don't waste underlying array - for _, e := range next { - // Instead of adding the same type multiple times, look for - // it in the list and mark it as multiple if it was added - // before. - // We use a sequential search (instead of a map for next) - // because the lists tend to be small, can easily be reused, - // and explicit search appears to be faster in this case. - if alt := findType(list, e.typ); alt != nil { - alt.multiples = true - } else { - list = append(list, e) - } - } - - } - - return -} - -func findType(list []embeddedType, typ *Named) *embeddedType { - for i := range list { - if p := &list[i]; p.typ == typ { - return p - } - } - return nil -} - -func lookupField(typ Type, pkg *Package, name string) lookupResult { - if name == "_" { - return lookupResult{mode: invalid} // empty fields/methods are never found - } - - typ = typ.Deref() - - if t, ok := typ.(*Named); ok { - if m := lookupMethod(t.methods, pkg, name); m != nil { - assert(m.typ != nil) - return lookupResult{value, m, nil} - } - typ = t.underlying - } - - switch t := typ.(type) { - case *Struct: - var next []embeddedType - for i, f := range t.fields { - if f.isMatch(pkg, name) { - return lookupResult{variable, f, []int{i}} - } - if f.anonymous { - // Possible optimization: If the embedded type - // is a pointer to the current type we could - // ignore it. - // Ignore embedded basic types - only user-defined - // named types can have methods or have struct fields. - if t, _ := f.typ.Deref().(*Named); t != nil { - next = append(next, embeddedType{t, []int{i}, false}) - } - } - } - if len(next) > 0 { - return lookupFieldBreadthFirst(next, pkg, name) - } - - case *Interface: - if m := lookupMethod(t.methods, pkg, name); m != nil { - assert(m.typ != nil) - return lookupResult{value, m, nil} - } - } - - // not found - return lookupResult{mode: invalid} -} diff --git a/go/types/predicates.go b/go/types/predicates.go index fa6a17c5..b86f1f42 100644 --- a/go/types/predicates.go +++ b/go/types/predicates.go @@ -274,41 +274,3 @@ func defaultType(typ Type) Type { } return typ } - -// missingMethod returns (nil, false) if typ implements T, otherwise -// it returns the first missing method required by T and whether it -// is missing or simply has the wrong type. -// TODO(gri) make method of Type and/or stand-alone predicate. -// -func missingMethod(typ Type, T *Interface) (method *Func, wrongType bool) { - // TODO(gri): this needs to correctly compare method names (taking package into account) - // 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 T.IsEmpty() { - return - } - // T.methods.NumEntries() > 0 - - if ityp, _ := typ.Underlying().(*Interface); ityp != nil { - for _, m := range T.methods { - res := lookupField(ityp, m.pkg, m.name) // TODO(gri) no need to go via lookupField - if res.mode != invalid && !IsIdentical(res.obj.Type(), m.typ) { - return m, true - } - } - return - } - - // a concrete type implements T if it implements all methods of T. - for _, m := range T.methods { - res := lookupField(typ, m.pkg, m.name) - if res.mode == invalid { - return m, false - } - if !IsIdentical(res.obj.Type(), m.typ) { - return m, true - } - } - return -} diff --git a/go/types/stmt.go b/go/types/stmt.go index 3da415f5..b6f2d92e 100644 --- a/go/types/stmt.go +++ b/go/types/stmt.go @@ -670,7 +670,7 @@ func (check *checker) stmt(s ast.Stmt) { for _, expr := range clause.List { typ = check.typOrNil(expr, false) if typ != nil && typ != Typ[Invalid] { - if method, wrongType := missingMethod(typ, T); method != nil { + if method, wrongType := MissingMethod(typ, T); method != nil { var msg string if wrongType { msg = "%s cannot have dynamic type %s (wrong type for method %s)" diff --git a/go/types/testdata/decls2a.src b/go/types/testdata/decls2a.src index 80dea53f..aec2349f 100644 --- a/go/types/testdata/decls2a.src +++ b/go/types/testdata/decls2a.src @@ -7,6 +7,7 @@ package decls2 import "time" +import "unsafe" // T1 declared before its methods. type T1 struct{ @@ -17,6 +18,22 @@ func (T1) m() {} func (T1) m /* ERROR "redeclared" */ () {} func (x *T1) f /* ERROR "field and method" */ () {} +// Conflict between embedded field and method name, +// with the embedded field being a basic type. +type T1b struct { + int +} + +func (T1b) int /* ERROR "field and method" */ () {} + +type T1c struct { + unsafe.Pointer +} + +func (T1c) Pointer /* ERROR "field and method" */ () int { return 0 } + +var _ = T1c{}.Pointer + // T2's method declared before the type. func (*T2) f /* ERROR "field and method" */ () {} diff --git a/go/types/testdata/decls3.src b/go/types/testdata/decls3.src index 6aa9f90e..9b54e0c0 100644 --- a/go/types/testdata/decls3.src +++ b/go/types/testdata/decls3.src @@ -6,6 +6,8 @@ package decls3 +import "unsafe" + // fields with the same name at the same level cancel each other out func _() { @@ -44,6 +46,16 @@ func issue4355() { _ = t /* ERROR "no single field or method" */ .X } +func _() { + type Pointer int + type A struct{ Pointer } + type B struct{ unsafe.Pointer } + type T struct{ A; B } + + var t T + _ = t /* ERROR "no single field or method" */ .Pointer +} + // Embedded fields can be predeclared types. func _() { diff --git a/go/types/testdata/expr3.src b/go/types/testdata/expr3.src index 48f6bc77..ccb084cc 100644 --- a/go/types/testdata/expr3.src +++ b/go/types/testdata/expr3.src @@ -98,6 +98,7 @@ func indexes() { type T struct { x int + y func() } func (*T) m() {} @@ -108,6 +109,9 @@ func method_expressions() { _ = T.m var f func(*T) = (*T).m var g func(*T) = ( /* ERROR "cannot initialize" */ T).m + + _ = T /* ERROR "has no method" */ .y + _ = ( /* ERROR "has no method" */ *T).y } func struct_literals() { diff --git a/go/types/types.go b/go/types/types.go index 0b0b372d..228d9f2e 100644 --- a/go/types/types.go +++ b/go/types/types.go @@ -157,24 +157,6 @@ func (s *Struct) Tag(i int) string { return "" } -// Index returns the index for the field in s with matching package and name. -// TODO(gri) should this be exported? -func (s *Struct) index(pkg *Package, name string) int { - if name == "_" { - return -1 // blank identifiers are never found - } - for i, f := range s.fields { - // spec: - // "Two identifiers are different if they are spelled differently, - // or if they appear in different packages and are not exported. - // Otherwise, they are the same." - if f.name == name && (ast.IsExported(name) || f.pkg.path == pkg.path) { - return i - } - } - return -1 -} - // A Pointer represents a pointer type. type Pointer struct { base Type