go.tools/go/types: improved LookupFieldOrMethod, ast.Nodes for Scopes

- LookupFieldOrMethod now computes if any indirection was found on the
  way to an embedded field/method: this is the only information required
  to determine if a result method is in the method set.

- Scopes now provide a link to the ast.Node responsible for them.

Also:
- don't permit unsafe.Offsetof on method values
- report ambiguities in field/method lookup errors
- added some missing checks for anonymous fields
- lots of new tests

Fixes golang/go#5499.

R=adonovan
CC=golang-dev
https://golang.org/cl/10411045
This commit is contained in:
Robert Griesemer 2013-06-21 08:57:26 -07:00
parent 9ff248b00d
commit 2d345c1dd7
17 changed files with 419 additions and 87 deletions

View File

@ -33,7 +33,6 @@ package types
// pass without errors. Please do not file issues against these for now // pass without errors. Please do not file issues against these for now
// since they are known already: // since they are known already:
// //
// BUG(gri): Method sets are computed loosely and don't distinguish between ptr vs non-pointer receivers.
// BUG(gri): Method expressions and method values work accidentally and may not be fully checked. // BUG(gri): Method expressions and method values work accidentally and may not be fully checked.
// BUG(gri): Conversions of constants only change the type, not the value (e.g., int(1.1) is wrong). // BUG(gri): Conversions of constants only change the type, not the value (e.g., int(1.1) is wrong).
// BUG(gri): Some built-ins don't check parameters fully, yet (e.g. append). // BUG(gri): Some built-ins don't check parameters fully, yet (e.g. append).

View File

@ -341,17 +341,22 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
if x.mode == invalid { if x.mode == invalid {
goto Error goto Error
} }
base := x.typ.Deref()
sel := arg.Sel.Name sel := arg.Sel.Name
obj, index := LookupFieldOrMethod(x.typ, check.pkg, arg.Sel.Name) obj, index, indirect := LookupFieldOrMethod(base, check.pkg, arg.Sel.Name)
if obj == nil { switch obj.(type) {
check.invalidArg(x.pos(), "%s has no single field %s", x, sel) case nil:
check.invalidArg(x.pos(), "%s has no single field %s", base, sel)
goto Error
case *Func:
check.invalidArg(arg0.Pos(), "%s is a method value", arg0)
goto Error goto Error
} }
offs := check.ctxt.offsetof(x.typ.Deref(), index) if indirect {
if offs < 0 { check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base)
check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, x)
goto Error goto Error
} }
offs := check.ctxt.offsetof(base, index)
x.mode = constant x.mode = constant
x.val = exact.MakeInt64(offs) x.val = exact.MakeInt64(offs)
x.typ = Typ[Uintptr] x.typ = Typ[Uintptr]

View File

@ -21,6 +21,12 @@ const (
trace = false // turn on for detailed type resolution traces trace = false // turn on for detailed type resolution traces
) )
// If retainASTLinks is set, scopes maintain a link to the node
// responsible for it.
// TODO(gri) Decide if this should be a permanent (always present)
// or optional feature (enabled with a mode flag).
const retainASTLinks = true
// exprInfo stores type and constant value for an untyped expression. // exprInfo stores type and constant value for an untyped expression.
type exprInfo struct { type exprInfo struct {
isLhs bool // expression is lhs operand of a shift with delayed type check isLhs bool // expression is lhs operand of a shift with delayed type check

View File

@ -55,6 +55,7 @@ var tests = []struct {
{"expr1", []string{"testdata/expr1.src"}}, {"expr1", []string{"testdata/expr1.src"}},
{"expr2", []string{"testdata/expr2.src"}}, {"expr2", []string{"testdata/expr2.src"}},
{"expr3", []string{"testdata/expr3.src"}}, {"expr3", []string{"testdata/expr3.src"}},
{"methodsets", []string{"testdata/methodsets.src"}},
{"shifts", []string{"testdata/shifts.src"}}, {"shifts", []string{"testdata/shifts.src"}},
{"builtins", []string{"testdata/builtins.src"}}, {"builtins", []string{"testdata/builtins.src"}},
{"conversions", []string{"testdata/conversions.src"}}, {"conversions", []string{"testdata/conversions.src"}},

View File

@ -200,10 +200,24 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [
} else { } else {
// anonymous field // anonymous field
pos := f.Type.Pos() pos := f.Type.Pos()
switch t := typ.Deref().(type) { t, isPtr := deref(typ)
switch t := t.(type) {
case *Basic: case *Basic:
add(f, nil, t.name, true, pos) add(f, nil, t.name, true, pos)
case *Named: case *Named:
// spec: "An embedded type must be specified as a type name
// T or as a pointer to a non-interface type name *T, and T
// itself may not be a pointer type."
switch t.underlying.(type) {
case *Pointer:
check.errorf(pos, "anonymous field type cannot be a pointer")
continue // ignore this field
case *Interface:
if isPtr {
check.errorf(pos, "anonymous field type cannot be a pointer to an interface")
continue // ignore this field
}
}
add(f, nil, t.obj.name, true, pos) add(f, nil, t.obj.name, true, pos)
default: default:
if typ != Typ[Invalid] { if typ != Typ[Invalid] {
@ -1310,25 +1324,39 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
if x.mode == invalid { if x.mode == invalid {
goto Error goto Error
} }
// TODO(gri) use collision information for better error message
obj, _ := LookupFieldOrMethod(x.typ, check.pkg, sel) obj, index, indirect := LookupFieldOrMethod(x.typ, check.pkg, sel)
if obj == nil { if obj == nil {
check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel) if index != nil {
// TODO(gri) should provide actual type where the conflict happens
check.invalidOp(e.Pos(), "ambiguous selector %s", sel)
} else {
check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel)
}
goto Error goto Error
} }
check.callIdent(e.Sel, obj) check.callIdent(e.Sel, obj)
if x.mode == typexpr { if x.mode == typexpr {
// method expression // method expression
m, ok := obj.(*Func) m, _ := obj.(*Func)
if !ok { if m == nil {
check.invalidOp(e.Pos(), "%s has no method %s", x, sel) check.invalidOp(e.Pos(), "%s has no method %s", x, sel)
goto Error goto Error
} }
// verify that m is in the method set of x.typ
// (the receiver is nil if f is an interface method)
if recv := m.typ.(*Signature).recv; recv != nil {
if _, isPtr := deref(recv.typ); isPtr && !indirect {
check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ)
goto Error
}
}
// the receiver type becomes the type of the first function // the receiver type becomes the type of the first function
// argument of the method expression's function type // 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 var params []*Var
sig := m.typ.(*Signature) sig := m.typ.(*Signature)
if sig.params != nil { if sig.params != nil {
@ -1340,6 +1368,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
results: sig.results, results: sig.results,
isVariadic: sig.isVariadic, isVariadic: sig.isVariadic,
} }
} else { } else {
// regular selector // regular selector
switch obj := obj.(type) { switch obj := obj.(type) {
@ -1350,6 +1379,22 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
// TODO(gri) Temporary assert to verify corresponding lookup via method sets. // TODO(gri) Temporary assert to verify corresponding lookup via method sets.
// Remove eventually. // Remove eventually.
assert(NewMethodSet(x.typ).Lookup(check.pkg, sel) == obj) assert(NewMethodSet(x.typ).Lookup(check.pkg, sel) == obj)
// TODO(gri) This code appears elsewhere, too. Factor!
// verify that obj is in the method set of x.typ (or &(x.typ) if x is addressable)
// (the receiver is nil if obj is an interface method)
//
// spec: "A method call x.m() is valid if the method set of (the type of) x
// contains m and the argument list can be assigned to the parameter
// list of m. If x is addressable and &x's method set contains m, x.m()
// is shorthand for (&x).m()".
if recv := obj.typ.(*Signature).recv; recv != nil {
if _, isPtr := deref(recv.typ); isPtr && !indirect && x.mode != variable {
check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x)
goto Error
}
}
x.mode = value x.mode = value
x.typ = obj.typ x.typ = obj.typ
default: default:
@ -1708,6 +1753,9 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
case *ast.FuncType: case *ast.FuncType:
scope := NewScope(check.topScope) scope := NewScope(check.topScope)
if retainASTLinks {
scope.node = e
}
params, isVariadic := check.collectParams(scope, e.Params, true) params, isVariadic := check.collectParams(scope, e.Params, true)
results, _ := check.collectParams(scope, e.Results, false) results, _ := check.collectParams(scope, e.Results, false)
x.mode = typexpr x.mode = typexpr

View File

@ -8,8 +8,15 @@ package types
import "go/ast" import "go/ast"
// TODO(gri) The named type consolidation and seen maps below must be
// indexed by unique keys for a given type. Verify that named
// types always have only one representation (even when imported
// indirectly via different packages.)
// LookupFieldOrMethod looks up a field or method with given package and name // LookupFieldOrMethod looks up a field or method with given package and name
// in typ and returns the corresponding *Field or *Func, and an index sequence. // in typ and returns the corresponding *Field or *Func, an index sequence,
// and a bool indicating if there were any pointer indirections on the path
// to the field or method.
// //
// The last index entry is the field or method index in the (possibly embedded) // The last index entry is the field or method index in the (possibly embedded)
// type where the entry was found, either: // type where the entry was found, either:
@ -24,34 +31,27 @@ import "go/ast"
// If no entry is found, a nil object is returned. In this case, the returned // If no entry is found, a nil object is returned. In this case, the returned
// index sequence points to an ambiguous entry if it exists, or it is nil. // index sequence points to an ambiguous entry if it exists, or it is nil.
// //
func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index []int) { func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index []int, indirect bool) {
if name == "_" { if name == "_" {
return // empty fields/methods are never found 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)
// embedded represents an embedded named type // embedded represents an embedded named type
type embedded struct { type embedded struct {
typ *Named // nil means use the outer typ variable instead typ *Named // nil means use the outer typ variable instead
index []int // field/method indices, starting with index at depth 0 index []int // embedded field indices, starting with index at depth 0
multiples bool // if set, type appears multiple times at its depth indirect bool // if set, there was a pointer indirection on the path to this field
multiples bool // if set, typ appears multiple times at this depth
} }
// Start with typ as single entry at lowest depth. // Start with typ as single entry at lowest depth.
// If typ is not a named type, insert a nil type instead. // If typ is not a named type, insert a nil type instead.
typ, isPtr := deref(typ)
t, _ := typ.(*Named) t, _ := typ.(*Named)
current := []embedded{{t, nil, false}} current := []embedded{{t, nil, isPtr, false}}
// named types that we have seen already
seen := make(map[*Named]bool)
// search current depth if there's work to do // search current depth if there's work to do
for len(current) > 0 { for len(current) > 0 {
@ -64,6 +64,11 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
// we simply continue with the underlying type. // we simply continue with the underlying type.
if e.typ != nil { if e.typ != nil {
if seen[e.typ] { if seen[e.typ] {
// We have seen this type before, at a more shallow depth
// (note that multiples of this type at the current depth
// were eliminated before). The type at that depth shadows
// this same type at the current depth, so we can ignore
// this one.
continue continue
} }
seen[e.typ] = true seen[e.typ] = true
@ -78,6 +83,8 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
return return
} }
obj = m obj = m
indirect = e.indirect
continue // we can't have a matching field or interface method
} }
// continue with underlying type // continue with underlying type
@ -96,7 +103,8 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
return return
} }
obj = f obj = f
continue indirect = e.indirect
continue // we can't have a matching interface method
} }
// Collect embedded struct fields for searching the next // Collect embedded struct fields for searching the next
// lower depth, but only if we have not seen a match yet // lower depth, but only if we have not seen a match yet
@ -109,9 +117,10 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
// depth. // depth.
if obj == nil && f.anonymous { if obj == nil && f.anonymous {
// Ignore embedded basic types - only user-defined // Ignore embedded basic types - only user-defined
// named types can have methods or have struct fields. // named types can have methods or struct fields.
if t, _ := f.typ.Deref().(*Named); t != nil { typ, isPtr := deref(f.typ)
next = append(next, embedded{t, concat(e.index, i), e.multiples}) if t, _ := typ.Deref().(*Named); t != nil {
next = append(next, embedded{t, concat(e.index, i), e.indirect || isPtr, e.multiples})
} }
} }
} }
@ -126,6 +135,7 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
return return
} }
obj = m obj = m
indirect = e.indirect
} }
} }
} }
@ -155,23 +165,15 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
} }
index = nil index = nil
indirect = false
return // not found return // not found
} }
// concat returns the result of concatenating list and i.
// The result does not share its underlying array with list.
func concat(list []int, i int) []int {
var t []int
t = append(t, list...)
return append(t, i)
}
// MissingMethod returns (nil, false) if typ implements T, otherwise // MissingMethod returns (nil, false) if typ implements T, otherwise
// it returns the first missing method required by T and whether it // it returns the first missing method required by T and whether it
// is missing or simply has the wrong type. // is missing or simply has the wrong type.
// //
func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) { 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 // 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? // Note: This is stronger than the current spec. Should the spec require this?
@ -192,10 +194,24 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) {
// a concrete type implements T if it implements all methods of T. // a concrete type implements T if it implements all methods of T.
for _, m := range T.methods { for _, m := range T.methods {
obj, _ := LookupFieldOrMethod(typ, m.pkg, m.name) obj, _, indirect := LookupFieldOrMethod(typ, m.pkg, m.name)
if obj == nil { if obj == nil {
return m, false return m, false
} }
f, _ := obj.(*Func)
if f == nil {
return m, false
}
// verify that f is in the method set of typ
// (the receiver is nil if f is an interface method)
if recv := f.typ.(*Signature).recv; recv != nil {
if _, isPtr := deref(recv.typ); isPtr && !indirect {
return m, false
}
}
if !IsIdentical(obj.Type(), m.typ) { if !IsIdentical(obj.Type(), m.typ) {
return m, true return m, true
} }
@ -203,6 +219,25 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) {
return return
} }
// Deref dereferences typ if it is a pointer and returns its base and true.
// Otherwise it returns (typ, false).
// In contrast, Type.Deref also dereferenciates if type is a named type
// that is a pointer.
func deref(typ Type) (Type, bool) {
if p, _ := typ.(*Pointer); p != nil {
return p.base, true
}
return typ, false
}
// concat returns the result of concatenating list and i.
// The result does not share its underlying array with list.
func concat(list []int, i int) []int {
var t []int
t = append(t, list...)
return append(t, i)
}
// fieldIndex returns the index for the field with matching package and name, or a value < 0. // 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 { func fieldIndex(fields []*Field, pkg *Package, name string) int {
if name == "_" { if name == "_" {

View File

@ -88,6 +88,9 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
check.callIdent(file.Name, pkg) check.callIdent(file.Name, pkg)
fileScope = NewScope(pkg.scope) fileScope = NewScope(pkg.scope)
if retainASTLinks {
fileScope.node = file
}
scopes = append(scopes, fileScope) scopes = append(scopes, fileScope)
for _, decl := range file.Decls { for _, decl := range file.Decls {

View File

@ -20,11 +20,12 @@ import (
type Scope struct { type Scope struct {
parent *Scope parent *Scope
entries []Object entries []Object
node ast.Node
} }
// NewScope returns a new, empty scope. // NewScope returns a new, empty scope.
func NewScope(parent *Scope) *Scope { func NewScope(parent *Scope) *Scope {
return &Scope{parent, nil} return &Scope{parent: parent}
} }
// Parent returns the scope's containing (parent) scope. // Parent returns the scope's containing (parent) scope.
@ -32,6 +33,14 @@ func (s *Scope) Parent() *Scope {
return s.parent return s.parent
} }
// Node returns the ast.Node responsible for this scope.
// The result is nil if there is no corresponding node
// (e.g., for the universe scope, package scope, or
// imported packages).
func (s *Scope) Node() ast.Node {
return s.node
}
// NumEntries() returns the number of scope entries. // NumEntries() returns the number of scope entries.
// If s == nil, the result is 0. // If s == nil, the result is 0.
func (s *Scope) NumEntries() int { func (s *Scope) NumEntries() int {

View File

@ -40,15 +40,12 @@ func (ctxt *Context) offsetsof(s *Struct) []int64 {
} }
// offsetof returns the offset of the field specified via // offsetof returns the offset of the field specified via
// the index sequence relative to typ. It returns a value // the index sequence relative to typ. All embedded fields
// < 0 if the field is in an embedded pointer type. // must be structs (rather than pointer to structs).
func (ctxt *Context) offsetof(typ Type, index []int) int64 { func (ctxt *Context) offsetof(typ Type, index []int) int64 {
var o int64 var o int64
for _, i := range index { for _, i := range index {
s, _ := typ.Underlying().(*Struct) s := typ.Underlying().(*Struct)
if s == nil {
return -1
}
o += ctxt.offsetsof(s)[i] o += ctxt.offsetsof(s)[i]
typ = s.fields[i].typ typ = s.fields[i].typ
} }

View File

@ -299,8 +299,12 @@ func (check *checker) multipleDefaults(list []ast.Stmt) {
} }
} }
func (check *checker) openScope() { func (check *checker) openScope(node ast.Node) {
check.topScope = NewScope(check.topScope) s := NewScope(check.topScope)
if retainASTLinks {
s.node = node
}
check.topScope = s
} }
func (check *checker) closeScope() { func (check *checker) closeScope() {
@ -516,12 +520,12 @@ func (check *checker) stmt(s ast.Stmt) {
// TODO(gri) implement this // TODO(gri) implement this
case *ast.BlockStmt: case *ast.BlockStmt:
check.openScope() check.openScope(s)
check.stmtList(s.List) check.stmtList(s.List)
check.closeScope() check.closeScope()
case *ast.IfStmt: case *ast.IfStmt:
check.openScope() check.openScope(s)
check.optionalStmt(s.Init) check.optionalStmt(s.Init)
var x operand var x operand
check.expr(&x, s.Cond, nil, -1) check.expr(&x, s.Cond, nil, -1)
@ -533,7 +537,7 @@ func (check *checker) stmt(s ast.Stmt) {
check.closeScope() check.closeScope()
case *ast.SwitchStmt: case *ast.SwitchStmt:
check.openScope() check.openScope(s)
check.optionalStmt(s.Init) check.optionalStmt(s.Init)
var x operand var x operand
tag := s.Tag tag := s.Tag
@ -594,14 +598,14 @@ func (check *checker) stmt(s ast.Stmt) {
} }
} }
} }
check.openScope() check.openScope(clause)
check.stmtList(clause.Body) check.stmtList(clause.Body)
check.closeScope() check.closeScope()
} }
check.closeScope() check.closeScope()
case *ast.TypeSwitchStmt: case *ast.TypeSwitchStmt:
check.openScope() check.openScope(s)
check.optionalStmt(s.Init) check.optionalStmt(s.Init)
// A type switch guard must be of the form: // A type switch guard must be of the form:
@ -682,7 +686,7 @@ func (check *checker) stmt(s ast.Stmt) {
} }
} }
} }
check.openScope() check.openScope(clause)
// If lhs exists, declare a corresponding object in the case-local scope if necessary. // If lhs exists, declare a corresponding object in the case-local scope if necessary.
if lhs != nil { if lhs != nil {
// A single-type case clause implicitly declares a new variable shadowing lhs. // A single-type case clause implicitly declares a new variable shadowing lhs.
@ -704,14 +708,14 @@ func (check *checker) stmt(s ast.Stmt) {
if clause == nil { if clause == nil {
continue // error reported before continue // error reported before
} }
check.openScope() check.openScope(clause)
check.optionalStmt(clause.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt) check.optionalStmt(clause.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt)
check.stmtList(clause.Body) check.stmtList(clause.Body)
check.closeScope() check.closeScope()
} }
case *ast.ForStmt: case *ast.ForStmt:
check.openScope() check.openScope(s)
check.optionalStmt(s.Init) check.optionalStmt(s.Init)
if s.Cond != nil { if s.Cond != nil {
var x operand var x operand
@ -725,7 +729,7 @@ func (check *checker) stmt(s ast.Stmt) {
check.closeScope() check.closeScope()
case *ast.RangeStmt: case *ast.RangeStmt:
check.openScope() check.openScope(s)
// check expression to iterate over // check expression to iterate over
decl := s.Tok == token.DEFINE decl := s.Tok == token.DEFINE
var x operand var x operand

View File

@ -320,6 +320,8 @@ type S2 struct{ // offset
*S1 // 0 *S1 // 0
} // 8 } // 8
func (S2) m() {}
func _Alignof() { func _Alignof() {
var x int var x int
_ = unsafe /* ERROR "argument" */ .Alignof() _ = unsafe /* ERROR "argument" */ .Alignof()
@ -368,9 +370,13 @@ func _Offsetof() {
assert(unsafe.Offsetof(y1.d) == 48) // relative to S1 assert(unsafe.Offsetof(y1.d) == 48) // relative to S1
assert(unsafe.Offsetof(y1.e) == 56) // relative to S1 assert(unsafe.Offsetof(y1.e) == 56) // relative to S1
var y1p *S1
assert(unsafe.Offsetof(y1p.S0) == 32)
var y2 S2 var y2 S2
assert(unsafe.Offsetof(y2.S1) == 0) assert(unsafe.Offsetof(y2.S1) == 0)
_ = unsafe.Offsetof(y2 /* ERROR "embedded via a pointer" */ .x) _ = unsafe.Offsetof(y2 /* ERROR "embedded via a pointer" */ .x)
_ = unsafe.Offsetof(y2 /* ERROR "method value" */ .m)
} }
func _Sizeof() { func _Sizeof() {

View File

@ -60,7 +60,7 @@ type (
type ( type (
p1 pi /* ERROR "no single field or method foo" */ .foo p1 pi /* ERROR "no field or method foo" */ .foo
p2 unsafe.Pointer p2 unsafe.Pointer
) )
@ -96,9 +96,8 @@ type (
u, v, a /* ERROR "redeclared" */ float32 u, v, a /* ERROR "redeclared" */ float32
} }
S2 struct { S2 struct {
U // anonymous field S0 // anonymous field
// TODO(gri) recognize double-declaration below S0 /* ERROR "redeclared" */ int
// U /* ERROR "redeclared" */ int
} }
S3 struct { S3 struct {
x S2 x S2

View File

@ -32,7 +32,7 @@ type T1c struct {
func (T1c) Pointer /* ERROR "field and method" */ () int { return 0 } func (T1c) Pointer /* ERROR "field and method" */ () int { return 0 }
var _ = T1c /* ERROR "no single field or method" */ {}.Pointer var _ = T1c{}.Pointer
// T2's method declared before the type. // T2's method declared before the type.
func (*T2) f /* ERROR "field and method" */ () {} func (*T2) f /* ERROR "field and method" */ () {}

View File

@ -18,7 +18,7 @@ func _() {
) )
var t T3 var t T3
_ = t /* ERROR "no single field or method" */ .X _ = t /* ERROR "ambiguous selector" */ .X
} }
func _() { func _() {
@ -30,7 +30,7 @@ func _() {
) )
var t T4 var t T4
_ = t /* ERROR "no single field or method" */ .X _ = t /* ERROR "ambiguous selector" */ .X
} }
func issue4355() { func issue4355() {
@ -43,7 +43,7 @@ func issue4355() {
) )
var t T5 var t T5
_ = t /* ERROR "no single field or method" */ .X _ = t /* ERROR "ambiguous selector" */ .X
} }
func _() { func _() {
@ -53,7 +53,7 @@ func _() {
type T struct{ A; B } type T struct{ A; B }
var t T var t T
_ = t /* ERROR "no single field or method" */ .Pointer _ = t /* ERROR "ambiguous selector" */ .Pointer
} }
// Embedded fields can be predeclared types. // Embedded fields can be predeclared types.
@ -78,6 +78,32 @@ func _() {
_ = y.f _ = y.f
} }
// Restrictions on embedded field types.
func _() {
type I1 interface{}
type I2 interface{}
type P1 *int
type P2 *int
type T1 struct {
I1
* /* ERROR "cannot be a pointer to an interface" */ I2
* /* ERROR "cannot be a pointer to an interface" */ error
P1 /* ERROR "cannot be a pointer" */
* /* ERROR "cannot be a pointer" */ P2
}
// unsafe.Pointers are not regular pointers
type T2 struct {
unsafe.Pointer
}
type T3 struct {
*unsafe.Pointer
}
}
// Borrowed from the FieldByName test cases in reflect/all_test.go. // Borrowed from the FieldByName test cases in reflect/all_test.go.
type D1 struct { type D1 struct {
@ -165,9 +191,9 @@ type S13 struct {
} }
func _() { func _() {
_ = struct /* ERROR "no single field or method" */ {}{}.Foo _ = struct /* ERROR "no field or method" */ {}{}.Foo
_ = S0{}.A _ = S0{}.A
_ = S0 /* ERROR "no single field or method" */ {}.D _ = S0 /* ERROR "no field or method" */ {}.D
_ = S1{}.A _ = S1{}.A
_ = S1{}.B _ = S1{}.B
_ = S1{}.S0 _ = S1{}.S0
@ -176,17 +202,17 @@ func _() {
_ = S2{}.S1 _ = S2{}.S1
_ = S2{}.B _ = S2{}.B
_ = S2{}.C _ = S2{}.C
_ = S2 /* ERROR "no single field or method" */ {}.D _ = S2 /* ERROR "no field or method" */ {}.D
_ = S3 /* ERROR "no single field or method" */ {}.S1 _ = S3 /* ERROR "ambiguous selector" */ {}.S1
_ = S3{}.A _ = S3{}.A
_ = S3 /* ERROR "no single field or method" */ {}.B _ = S3 /* ERROR "ambiguous selector" */ {}.B
_ = S3{}.D _ = S3{}.D
_ = S3{}.E _ = S3{}.E
_ = S4{}.A _ = S4{}.A
_ = S4 /* ERROR "no single field or method" */ {}.B _ = S4 /* ERROR "no field or method" */ {}.B
_ = S5 /* ERROR "no single field or method" */ {}.X _ = S5 /* ERROR "ambiguous selector" */ {}.X
_ = S5{}.Y _ = S5{}.Y
_ = S10 /* ERROR "no single field or method" */ {}.X _ = S10 /* ERROR "ambiguous selector" */ {}.X
_ = S10{}.Y _ = S10{}.Y
} }
@ -262,4 +288,4 @@ type R22 R21
type R23 R21 type R23 R21
type R24 R21 type R24 R21
var _ = R0 /* ERROR "no single field or method" */ {}.X var _ = R0 /* ERROR "ambiguous selector" */ {}.X

View File

@ -104,11 +104,13 @@ type T struct {
func (*T) m() {} func (*T) m() {}
func method_expressions() { func method_expressions() {
_ = T /* ERROR "no single field or method" */ .a _ = T /* ERROR "no field or method" */ .a
_ = T /* ERROR "has no method" */ .x _ = T /* ERROR "has no method" */ .x
_ = T.m _ = T /* ERROR "not in method set" */ .m
var f func(*T) = (*T).m _ = (*T).m
var g func(*T) = ( /* ERROR "cannot initialize" */ T).m
var f func(*T) = T /* ERROR "not in method set" */ .m
var g func(*T) = (*T).m
_ = T /* ERROR "has no method" */ .y _ = T /* ERROR "has no method" */ .y
_ = ( /* ERROR "has no method" */ *T).y _ = ( /* ERROR "has no method" */ *T).y
@ -274,7 +276,8 @@ func type_asserts() {
var t I var t I
_ = t /* ERROR "use of .* outside type switch" */ .(type) _ = t /* ERROR "use of .* outside type switch" */ .(type)
_ = t.(T) _ = t.(T /* ERROR "missing method m" */ )
_ = t.(*T)
_ = t.(T1 /* ERROR "missing method m" */ ) _ = t.(T1 /* ERROR "missing method m" */ )
_ = t.(T2 /* ERROR "wrong type for method m" */ ) _ = t.(T2 /* ERROR "wrong type for method m" */ )
_ = t.(I2 /* ERROR "wrong type for method m" */ ) _ = t.(I2 /* ERROR "wrong type for method m" */ )

190
go/types/testdata/methodsets.src vendored Normal file
View File

@ -0,0 +1,190 @@
// 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.
package methodsets
type T0 struct {}
func (T0) v0() {}
func (*T0) p0() {}
type T1 struct {} // like T0 with different method names
func (T1) v1() {}
func (*T1) p1() {}
type T2 interface {
v2()
p2()
}
type T3 struct {
T0
*T1
T2
}
// Method expressions
func _() {
var (
_ func(T0) = T0.v0
_ = T0 /* ERROR "not in method set" */ .p0
_ func (*T0) = (*T0).v0
_ func (*T0) = (*T0).p0
// T1 is like T0
_ func(T2) = T2.v2
_ func(T2) = T2.p2
_ func(T3) = T3.v0
_ func(T3) = T3 /* ERROR "not in method set" */ .p0
_ func(T3) = T3.v1
_ func(T3) = T3.p1
_ func(T3) = T3.v2
_ func(T3) = T3.p2
_ func(*T3) = (*T3).v0
_ func(*T3) = (*T3).p0
_ func(*T3) = (*T3).v1
_ func(*T3) = (*T3).p1
_ func(*T3) = (*T3).v2
_ func(*T3) = (*T3).p2
)
}
// Method values with addressable receivers
func _() {
var (
v0 T0
_ func() = v0.v0
_ func() = v0.p0
)
var (
p0 *T0
_ func() = p0.v0
_ func() = p0.p0
)
// T1 is like T0
var (
v2 T2
_ func() = v2.v2
_ func() = v2.p2
)
var (
v4 T3
_ func() = v4.v0
_ func() = v4.p0
_ func() = v4.v1
_ func() = v4.p1
_ func() = v4.v2
_ func() = v4.p2
)
var (
p4 *T3
_ func() = p4.v0
_ func() = p4.p0
_ func() = p4.v1
_ func() = p4.p1
_ func() = p4.v2
_ func() = p4.p2
)
}
// Method calls with addressable receivers
func _() {
var v0 T0
v0.v0()
v0.p0()
var p0 *T0
p0.v0()
p0.p0()
// T1 is like T0
var v2 T2
v2.v2()
v2.p2()
var v4 T3
v4.v0()
v4.p0()
v4.v1()
v4.p1()
v4.v2()
v4.p2()
var p4 *T3
p4.v0()
p4.p0()
p4.v1()
p4.p1()
p4.v2()
p4.p2()
}
// Method values with value receivers
func _() {
var (
_ func() = T0{}.v0
_ func() = T0 /* ERROR "not in method set" */ {}.p0
_ func() = (&T0{}).v0
_ func() = (&T0{}).p0
// T1 is like T0
// no values for T2
_ func() = T3{}.v0
_ func() = T3 /* ERROR "not in method set" */ {}.p0
_ func() = T3{}.v1
_ func() = T3{}.p1
_ func() = T3{}.v2
_ func() = T3{}.p2
_ func() = (&T3{}).v0
_ func() = (&T3{}).p0
_ func() = (&T3{}).v1
_ func() = (&T3{}).p1
_ func() = (&T3{}).v2
_ func() = (&T3{}).p2
)
}
// Method calls with value receivers
// TODO(gri) For internal reasons, these expressions are evaluated twice,
// hence the double (equal) error messages for now. Fix this.
func _() {
T0{}.v0()
T0 /* ERROR "not in method set" */ /* ERROR "not in method set" */ {}.p0()
(&T0{}).v0()
(&T0{}).p0()
// T1 is like T0
// no values for T2
T3{}.v0()
T3 /* ERROR "not in method set" */ /* ERROR "not in method set" */ {}.p0()
T3{}.v1()
T3{}.p1()
T3{}.v2()
T3{}.p2()
(&T3{}).v0()
(&T3{}).p0()
(&T3{}).v1()
(&T3{}).p1()
(&T3{}).v2()
(&T3{}).p2()
}

View File

@ -382,6 +382,7 @@ func (t *Interface) Deref() Type { return t }
func (t *Map) Deref() Type { return t } func (t *Map) Deref() Type { return t }
func (t *Chan) Deref() Type { return t } func (t *Chan) Deref() Type { return t }
func (t *Named) Deref() Type { func (t *Named) Deref() Type {
// TODO(gri) Is this the right operation here given how Deref is used?
if p, ok := t.underlying.(*Pointer); ok { if p, ok := t.underlying.(*Pointer); ok {
return p.base return p.base
} }