go/types: fix lookup of methods of pointers to type parameters

Since the type bound of a type parameter is an interface, no method
is found if the receiver is a pointer to a type parameter (pointers
to interfaces have no methods).

Ignore the indirection in this case.

Change-Id: Ie2af78b4cfb2f70b5d4f1d2afc631716a10ff7d9
This commit is contained in:
Robert Griesemer 2020-03-31 23:04:38 -07:00
parent f81ad49939
commit 62683a7752
4 changed files with 48 additions and 5 deletions

View File

@ -4,6 +4,7 @@ so we have a better track record. I only switched to this file in Nov 2019, henc
----------------------------------------------------------------------------------------------------
TODO
- revisit all uses of derefUnpack in lookup.go (see local comments); create test cases
- implement type-checking for type parameters with pointer designation in contracts
- figure out how to translate methods with pointer designation into interface bounds
- review use of Contract.TParams field - it seems like it's only needed for length checks?

View File

@ -82,7 +82,17 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
return // blank fields/methods are never found
}
typ, isPtr := derefUnpack(T)
typ, isPtr := deref(T)
// If we have a type parameter, ignore isPtr otherwise we would
// return immediately below since the type parameter bound is an
// interface. This is needed for methods on variables that are
// pointers to values of type parameter type.
// TODO(gri) Should this be done in derefUnpack?
if tpar, _ := typ.(*TypeParam); tpar != nil {
typ = tpar.bound
isPtr = false
}
// *typ where typ is an interface has no methods.
if isPtr && IsInterface(typ) {
@ -166,6 +176,9 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
// this depth, f.typ appears multiple times at the next
// depth.
if obj == nil && f.embedded {
// TODO(gri) investigate derefUnpack here (see
// comment in the beginning on unpacking type
// parameters).
typ, isPtr := derefUnpack(f.typ)
// TODO(gri) optimization: ignore types that can't
// have fields or methods (only Named, Struct, and
@ -409,8 +422,7 @@ func deref(typ Type) (Type, bool) {
return typ, false
}
// derefUnpack is like deref but it also unpacks type parameters
// and parameterized types.
// derefUnpack is like deref but it also unpacks type parameters.
func derefUnpack(typ Type) (Type, bool) {
typ, ptr := deref(typ)
if tpar, _ := typ.(*TypeParam); tpar != nil {

View File

@ -47,3 +47,34 @@ func _() {
var m map[int]int
Equal(m, nil)
}
// If we have a receiver of pointer type (below: *T) we must ignore
// the pointer in the implementation of the method lookup because
// the type bound of T is an interface an pointer to interface types
// have no methods and then the lookup would fail.
contract C(T) {
T m()
}
// using contract C
func _(type T C)(x *T) {
x.m()
}
// using an interface as bound
func _(type T interface{ m() })(x *T) {
x.m()
}
// This is the original (simplified) program causing the same issue.
type GraphP(type Node, Edge GP) struct {
nodes []*Node
}
contract GP(Node, Edge) {
Node Edges() []*Edge
}
func (g *GraphP(Node, Edge)) Edges(n *Node) []*Edge {
return n.Edges()
}

View File

@ -4,13 +4,12 @@
package p
// TODO(gri) this should work
contract C(T) {
T m()
}
func _(type T C)(x *T) {
x.m /* ERROR no field or method m */ ()
x.m()
}
/*