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
|
// 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).
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"}},
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 == "_" {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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" */ () {}
|
||||||
|
|
|
@ -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
|
|
@ -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" */ )
|
||||||
|
|
|
@ -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 *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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue