go.tools/go/types: first cut at sorted method sets

- moved single field and method lookup functionality
  from operand.go to new file lookup.go and cleaned
  up the lookup implementation

- implemented method set computation using the same
  basic structure as for field/method lookup, in new
  file methodset.go

- minor related changes

- the method set computation ignores pointer-ness of
  the receiver type at the moment (next CL)

- fixed a couple of bugs (missing pkg info for imported
  embedded types, wrong test for method expressions)

The method set computation is currently verified by
comparing a regular method lookup with a method-set
based method lookup.

R=adonovan
CC=golang-dev
https://golang.org/cl/10235049
This commit is contained in:
Robert Griesemer 2013-06-18 15:59:16 -07:00
parent 7517d8bae3
commit 9a50e157b4
12 changed files with 563 additions and 281 deletions

View File

@ -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

View File

@ -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)"

View File

@ -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")

236
go/types/lookup.go Normal file
View File

@ -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
}

265
go/types/methodset.go Normal file
View File

@ -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] }

View File

@ -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}
}

View File

@ -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
}

View File

@ -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)"

View File

@ -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" */ () {}

View File

@ -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 _() {

View File

@ -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() {

View File

@ -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