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:
parent
9ff248b00d
commit
2d345c1dd7
|
@ -33,7 +33,6 @@ package types
|
|||
// pass without errors. Please do not file issues against these for now
|
||||
// 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): 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).
|
||||
|
|
|
@ -341,17 +341,22 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
|
|||
if x.mode == invalid {
|
||||
goto Error
|
||||
}
|
||||
base := x.typ.Deref()
|
||||
sel := arg.Sel.Name
|
||||
obj, index := LookupFieldOrMethod(x.typ, check.pkg, arg.Sel.Name)
|
||||
if obj == nil {
|
||||
check.invalidArg(x.pos(), "%s has no single field %s", x, sel)
|
||||
obj, index, indirect := LookupFieldOrMethod(base, check.pkg, arg.Sel.Name)
|
||||
switch obj.(type) {
|
||||
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
|
||||
}
|
||||
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)
|
||||
if indirect {
|
||||
check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base)
|
||||
goto Error
|
||||
}
|
||||
offs := check.ctxt.offsetof(base, index)
|
||||
x.mode = constant
|
||||
x.val = exact.MakeInt64(offs)
|
||||
x.typ = Typ[Uintptr]
|
||||
|
|
|
@ -21,6 +21,12 @@ const (
|
|||
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.
|
||||
type exprInfo struct {
|
||||
isLhs bool // expression is lhs operand of a shift with delayed type check
|
||||
|
|
|
@ -55,6 +55,7 @@ var tests = []struct {
|
|||
{"expr1", []string{"testdata/expr1.src"}},
|
||||
{"expr2", []string{"testdata/expr2.src"}},
|
||||
{"expr3", []string{"testdata/expr3.src"}},
|
||||
{"methodsets", []string{"testdata/methodsets.src"}},
|
||||
{"shifts", []string{"testdata/shifts.src"}},
|
||||
{"builtins", []string{"testdata/builtins.src"}},
|
||||
{"conversions", []string{"testdata/conversions.src"}},
|
||||
|
|
|
@ -200,10 +200,24 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [
|
|||
} else {
|
||||
// anonymous field
|
||||
pos := f.Type.Pos()
|
||||
switch t := typ.Deref().(type) {
|
||||
t, isPtr := deref(typ)
|
||||
switch t := t.(type) {
|
||||
case *Basic:
|
||||
add(f, nil, t.name, true, pos)
|
||||
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)
|
||||
default:
|
||||
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 {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
check.callIdent(e.Sel, obj)
|
||||
|
||||
if x.mode == typexpr {
|
||||
// method expression
|
||||
m, ok := obj.(*Func)
|
||||
if !ok {
|
||||
m, _ := obj.(*Func)
|
||||
if m == nil {
|
||||
check.invalidOp(e.Pos(), "%s has no method %s", x, sel)
|
||||
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
|
||||
// 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 {
|
||||
|
@ -1340,6 +1368,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
|||
results: sig.results,
|
||||
isVariadic: sig.isVariadic,
|
||||
}
|
||||
|
||||
} else {
|
||||
// regular selector
|
||||
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.
|
||||
// Remove eventually.
|
||||
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.typ = obj.typ
|
||||
default:
|
||||
|
@ -1708,6 +1753,9 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
|||
|
||||
case *ast.FuncType:
|
||||
scope := NewScope(check.topScope)
|
||||
if retainASTLinks {
|
||||
scope.node = e
|
||||
}
|
||||
params, isVariadic := check.collectParams(scope, e.Params, true)
|
||||
results, _ := check.collectParams(scope, e.Results, false)
|
||||
x.mode = typexpr
|
||||
|
|
|
@ -8,8 +8,15 @@ package types
|
|||
|
||||
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
|
||||
// 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)
|
||||
// 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
|
||||
// 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 == "_" {
|
||||
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
|
||||
type embedded struct {
|
||||
typ *Named // nil means use the outer typ variable instead
|
||||
index []int // field/method indices, starting with index at depth 0
|
||||
multiples bool // if set, type appears multiple times at its depth
|
||||
index []int // embedded field indices, starting with index at depth 0
|
||||
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.
|
||||
// If typ is not a named type, insert a nil type instead.
|
||||
typ, isPtr := deref(typ)
|
||||
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
|
||||
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.
|
||||
if e.typ != nil {
|
||||
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
|
||||
}
|
||||
seen[e.typ] = true
|
||||
|
@ -78,6 +83,8 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
|
|||
return
|
||||
}
|
||||
obj = m
|
||||
indirect = e.indirect
|
||||
continue // we can't have a matching field or interface method
|
||||
}
|
||||
|
||||
// continue with underlying type
|
||||
|
@ -96,7 +103,8 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
|
|||
return
|
||||
}
|
||||
obj = f
|
||||
continue
|
||||
indirect = e.indirect
|
||||
continue // we can't have a matching interface method
|
||||
}
|
||||
// Collect embedded struct fields for searching the next
|
||||
// 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.
|
||||
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 {
|
||||
next = append(next, embedded{t, concat(e.index, i), e.multiples})
|
||||
// named types can have methods or struct fields.
|
||||
typ, isPtr := deref(f.typ)
|
||||
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
|
||||
}
|
||||
obj = m
|
||||
indirect = e.indirect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -155,23 +165,15 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
|
|||
}
|
||||
|
||||
index = nil
|
||||
indirect = false
|
||||
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
|
||||
// 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?
|
||||
|
||||
|
@ -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.
|
||||
for _, m := range T.methods {
|
||||
obj, _ := LookupFieldOrMethod(typ, m.pkg, m.name)
|
||||
obj, _, indirect := LookupFieldOrMethod(typ, m.pkg, m.name)
|
||||
if obj == nil {
|
||||
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) {
|
||||
return m, true
|
||||
}
|
||||
|
@ -203,6 +219,25 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) {
|
|||
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.
|
||||
func fieldIndex(fields []*Field, pkg *Package, name string) int {
|
||||
if name == "_" {
|
||||
|
|
|
@ -88,6 +88,9 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
|||
check.callIdent(file.Name, pkg)
|
||||
|
||||
fileScope = NewScope(pkg.scope)
|
||||
if retainASTLinks {
|
||||
fileScope.node = file
|
||||
}
|
||||
scopes = append(scopes, fileScope)
|
||||
|
||||
for _, decl := range file.Decls {
|
||||
|
|
|
@ -20,11 +20,12 @@ import (
|
|||
type Scope struct {
|
||||
parent *Scope
|
||||
entries []Object
|
||||
node ast.Node
|
||||
}
|
||||
|
||||
// NewScope returns a new, empty scope.
|
||||
func NewScope(parent *Scope) *Scope {
|
||||
return &Scope{parent, nil}
|
||||
return &Scope{parent: parent}
|
||||
}
|
||||
|
||||
// Parent returns the scope's containing (parent) scope.
|
||||
|
@ -32,6 +33,14 @@ func (s *Scope) Parent() *Scope {
|
|||
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.
|
||||
// If s == nil, the result is 0.
|
||||
func (s *Scope) NumEntries() int {
|
||||
|
|
|
@ -40,15 +40,12 @@ func (ctxt *Context) offsetsof(s *Struct) []int64 {
|
|||
}
|
||||
|
||||
// offsetof returns the offset of the field specified via
|
||||
// the index sequence relative to typ. It returns a value
|
||||
// < 0 if the field is in an embedded pointer type.
|
||||
// the index sequence relative to typ. All embedded fields
|
||||
// must be structs (rather than pointer to structs).
|
||||
func (ctxt *Context) offsetof(typ Type, index []int) int64 {
|
||||
var o int64
|
||||
for _, i := range index {
|
||||
s, _ := typ.Underlying().(*Struct)
|
||||
if s == nil {
|
||||
return -1
|
||||
}
|
||||
s := typ.Underlying().(*Struct)
|
||||
o += ctxt.offsetsof(s)[i]
|
||||
typ = s.fields[i].typ
|
||||
}
|
||||
|
|
|
@ -299,8 +299,12 @@ func (check *checker) multipleDefaults(list []ast.Stmt) {
|
|||
}
|
||||
}
|
||||
|
||||
func (check *checker) openScope() {
|
||||
check.topScope = NewScope(check.topScope)
|
||||
func (check *checker) openScope(node ast.Node) {
|
||||
s := NewScope(check.topScope)
|
||||
if retainASTLinks {
|
||||
s.node = node
|
||||
}
|
||||
check.topScope = s
|
||||
}
|
||||
|
||||
func (check *checker) closeScope() {
|
||||
|
@ -516,12 +520,12 @@ func (check *checker) stmt(s ast.Stmt) {
|
|||
// TODO(gri) implement this
|
||||
|
||||
case *ast.BlockStmt:
|
||||
check.openScope()
|
||||
check.openScope(s)
|
||||
check.stmtList(s.List)
|
||||
check.closeScope()
|
||||
|
||||
case *ast.IfStmt:
|
||||
check.openScope()
|
||||
check.openScope(s)
|
||||
check.optionalStmt(s.Init)
|
||||
var x operand
|
||||
check.expr(&x, s.Cond, nil, -1)
|
||||
|
@ -533,7 +537,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
|||
check.closeScope()
|
||||
|
||||
case *ast.SwitchStmt:
|
||||
check.openScope()
|
||||
check.openScope(s)
|
||||
check.optionalStmt(s.Init)
|
||||
var x operand
|
||||
tag := s.Tag
|
||||
|
@ -594,14 +598,14 @@ func (check *checker) stmt(s ast.Stmt) {
|
|||
}
|
||||
}
|
||||
}
|
||||
check.openScope()
|
||||
check.openScope(clause)
|
||||
check.stmtList(clause.Body)
|
||||
check.closeScope()
|
||||
}
|
||||
check.closeScope()
|
||||
|
||||
case *ast.TypeSwitchStmt:
|
||||
check.openScope()
|
||||
check.openScope(s)
|
||||
check.optionalStmt(s.Init)
|
||||
|
||||
// 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 != nil {
|
||||
// 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 {
|
||||
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.stmtList(clause.Body)
|
||||
check.closeScope()
|
||||
}
|
||||
|
||||
case *ast.ForStmt:
|
||||
check.openScope()
|
||||
check.openScope(s)
|
||||
check.optionalStmt(s.Init)
|
||||
if s.Cond != nil {
|
||||
var x operand
|
||||
|
@ -725,7 +729,7 @@ func (check *checker) stmt(s ast.Stmt) {
|
|||
check.closeScope()
|
||||
|
||||
case *ast.RangeStmt:
|
||||
check.openScope()
|
||||
check.openScope(s)
|
||||
// check expression to iterate over
|
||||
decl := s.Tok == token.DEFINE
|
||||
var x operand
|
||||
|
|
|
@ -320,6 +320,8 @@ type S2 struct{ // offset
|
|||
*S1 // 0
|
||||
} // 8
|
||||
|
||||
func (S2) m() {}
|
||||
|
||||
func _Alignof() {
|
||||
var x int
|
||||
_ = unsafe /* ERROR "argument" */ .Alignof()
|
||||
|
@ -368,9 +370,13 @@ func _Offsetof() {
|
|||
assert(unsafe.Offsetof(y1.d) == 48) // relative to S1
|
||||
assert(unsafe.Offsetof(y1.e) == 56) // relative to S1
|
||||
|
||||
var y1p *S1
|
||||
assert(unsafe.Offsetof(y1p.S0) == 32)
|
||||
|
||||
var y2 S2
|
||||
assert(unsafe.Offsetof(y2.S1) == 0)
|
||||
_ = unsafe.Offsetof(y2 /* ERROR "embedded via a pointer" */ .x)
|
||||
_ = unsafe.Offsetof(y2 /* ERROR "method value" */ .m)
|
||||
}
|
||||
|
||||
func _Sizeof() {
|
||||
|
|
|
@ -60,7 +60,7 @@ type (
|
|||
|
||||
|
||||
type (
|
||||
p1 pi /* ERROR "no single field or method foo" */ .foo
|
||||
p1 pi /* ERROR "no field or method foo" */ .foo
|
||||
p2 unsafe.Pointer
|
||||
)
|
||||
|
||||
|
@ -96,9 +96,8 @@ type (
|
|||
u, v, a /* ERROR "redeclared" */ float32
|
||||
}
|
||||
S2 struct {
|
||||
U // anonymous field
|
||||
// TODO(gri) recognize double-declaration below
|
||||
// U /* ERROR "redeclared" */ int
|
||||
S0 // anonymous field
|
||||
S0 /* ERROR "redeclared" */ int
|
||||
}
|
||||
S3 struct {
|
||||
x S2
|
||||
|
|
|
@ -32,7 +32,7 @@ type T1c struct {
|
|||
|
||||
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.
|
||||
func (*T2) f /* ERROR "field and method" */ () {}
|
||||
|
|
|
@ -18,7 +18,7 @@ func _() {
|
|||
)
|
||||
|
||||
var t T3
|
||||
_ = t /* ERROR "no single field or method" */ .X
|
||||
_ = t /* ERROR "ambiguous selector" */ .X
|
||||
}
|
||||
|
||||
func _() {
|
||||
|
@ -30,7 +30,7 @@ func _() {
|
|||
)
|
||||
|
||||
var t T4
|
||||
_ = t /* ERROR "no single field or method" */ .X
|
||||
_ = t /* ERROR "ambiguous selector" */ .X
|
||||
}
|
||||
|
||||
func issue4355() {
|
||||
|
@ -43,7 +43,7 @@ func issue4355() {
|
|||
)
|
||||
|
||||
var t T5
|
||||
_ = t /* ERROR "no single field or method" */ .X
|
||||
_ = t /* ERROR "ambiguous selector" */ .X
|
||||
}
|
||||
|
||||
func _() {
|
||||
|
@ -53,7 +53,7 @@ func _() {
|
|||
type T struct{ A; B }
|
||||
|
||||
var t T
|
||||
_ = t /* ERROR "no single field or method" */ .Pointer
|
||||
_ = t /* ERROR "ambiguous selector" */ .Pointer
|
||||
}
|
||||
|
||||
// Embedded fields can be predeclared types.
|
||||
|
@ -78,6 +78,32 @@ func _() {
|
|||
_ = 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.
|
||||
|
||||
type D1 struct {
|
||||
|
@ -165,9 +191,9 @@ type S13 struct {
|
|||
}
|
||||
|
||||
func _() {
|
||||
_ = struct /* ERROR "no single field or method" */ {}{}.Foo
|
||||
_ = struct /* ERROR "no field or method" */ {}{}.Foo
|
||||
_ = S0{}.A
|
||||
_ = S0 /* ERROR "no single field or method" */ {}.D
|
||||
_ = S0 /* ERROR "no field or method" */ {}.D
|
||||
_ = S1{}.A
|
||||
_ = S1{}.B
|
||||
_ = S1{}.S0
|
||||
|
@ -176,17 +202,17 @@ func _() {
|
|||
_ = S2{}.S1
|
||||
_ = S2{}.B
|
||||
_ = S2{}.C
|
||||
_ = S2 /* ERROR "no single field or method" */ {}.D
|
||||
_ = S3 /* ERROR "no single field or method" */ {}.S1
|
||||
_ = S2 /* ERROR "no field or method" */ {}.D
|
||||
_ = S3 /* ERROR "ambiguous selector" */ {}.S1
|
||||
_ = S3{}.A
|
||||
_ = S3 /* ERROR "no single field or method" */ {}.B
|
||||
_ = S3 /* ERROR "ambiguous selector" */ {}.B
|
||||
_ = S3{}.D
|
||||
_ = S3{}.E
|
||||
_ = S4{}.A
|
||||
_ = S4 /* ERROR "no single field or method" */ {}.B
|
||||
_ = S5 /* ERROR "no single field or method" */ {}.X
|
||||
_ = S4 /* ERROR "no field or method" */ {}.B
|
||||
_ = S5 /* ERROR "ambiguous selector" */ {}.X
|
||||
_ = S5{}.Y
|
||||
_ = S10 /* ERROR "no single field or method" */ {}.X
|
||||
_ = S10 /* ERROR "ambiguous selector" */ {}.X
|
||||
_ = S10{}.Y
|
||||
}
|
||||
|
||||
|
@ -262,4 +288,4 @@ type R22 R21
|
|||
type R23 R21
|
||||
type R24 R21
|
||||
|
||||
var _ = R0 /* ERROR "no single field or method" */ {}.X
|
||||
var _ = R0 /* ERROR "ambiguous selector" */ {}.X
|
|
@ -104,11 +104,13 @@ type T struct {
|
|||
func (*T) m() {}
|
||||
|
||||
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.m
|
||||
var f func(*T) = (*T).m
|
||||
var g func(*T) = ( /* ERROR "cannot initialize" */ T).m
|
||||
_ = T /* ERROR "not in method set" */ .m
|
||||
_ = (*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
|
||||
_ = ( /* ERROR "has no method" */ *T).y
|
||||
|
@ -274,7 +276,8 @@ func type_asserts() {
|
|||
|
||||
var t I
|
||||
_ = 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.(T2 /* ERROR "wrong type for method m" */ )
|
||||
_ = t.(I2 /* ERROR "wrong type for method m" */ )
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -382,6 +382,7 @@ func (t *Interface) Deref() Type { return t }
|
|||
func (t *Map) Deref() Type { return t }
|
||||
func (t *Chan) Deref() Type { return t }
|
||||
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 {
|
||||
return p.base
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue