mirror of https://github.com/golang/go.git
go/types: fix type inference and method calls with parameterized receivers
This change expands type inference to parameterized named types and clarifies handling of type parameters during type inference (specifically, an inferred type may be a type parameter itself). The change also implements method selectors with parameterized receiver types by using type inference to deduce the receiver type parameters. Add more tests and enable many disabled tests that now work correctly. Change-Id: I8e10f9166fec9a8454f14b8fb089230c70422a1b
This commit is contained in:
parent
1a0bc57392
commit
4fa7fd828d
|
|
@ -344,6 +344,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
obj Object
|
||||
index []int
|
||||
indirect bool
|
||||
madeCopy bool // for debugging purposes only
|
||||
)
|
||||
|
||||
sel := e.Sel.Name
|
||||
|
|
@ -471,10 +472,21 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
// methods may not have a fully set up signature yet
|
||||
if m, _ := obj.(*Func); m != nil {
|
||||
check.objDecl(m, nil)
|
||||
// TODO(gri) fix this
|
||||
if IsParameterized(x.typ) {
|
||||
check.errorf(x.pos(), "method expressions/values/calls with parameterized receiver types not implemented yet")
|
||||
goto Error
|
||||
// If m has a parameterized receiver type, infer the type parameter
|
||||
// values from the actual receiver provided and then substitute the
|
||||
// type parameters in the signature accordingly.
|
||||
if len(m.tparams) > 0 {
|
||||
// check.dump("### recv typ = %s", x.typ)
|
||||
// check.dump("### method = %s tparams = %s", m, m.tparams)
|
||||
recv := m.typ.(*Signature).recv
|
||||
targs := check.infer(recv.pos, m.tparams, NewTuple(recv), []*operand{x})
|
||||
// check.dump("### inferred targs = %s", targs)
|
||||
// Don't modify m. Instead - for now - make a copy of m and use that instead.
|
||||
// (If we modify m, some tests will fail; possibly because the m is in use.)
|
||||
copy := *m
|
||||
copy.typ = check.subst(e.Pos(), m.typ, m.tparams, targs)
|
||||
obj = ©
|
||||
madeCopy = true // if we made a copy, the method set verification below can't work
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -522,7 +534,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
// addressability, should we report the type &(x.typ) instead?
|
||||
check.recordSelection(e, MethodVal, x.typ, obj, index, indirect)
|
||||
|
||||
if debug {
|
||||
if debug && !madeCopy {
|
||||
// Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
|
||||
// TODO(gri) This only works because we call LookupFieldOrMethod
|
||||
// _before_ calling NewMethodSet: LookupFieldOrMethod completes
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ func isParameterized(typ Type, seen map[Type]bool) (res bool) {
|
|||
}()
|
||||
|
||||
switch t := typ.(type) {
|
||||
case nil, *Basic, *Named: // TODO(gri) should nil be handled here?
|
||||
case nil, *Basic: // TODO(gri) should nil be handled here?
|
||||
break
|
||||
|
||||
case *Array:
|
||||
|
|
@ -159,6 +159,9 @@ func isParameterized(typ Type, seen map[Type]bool) (res bool) {
|
|||
case *Chan:
|
||||
return isParameterized(t.elem, seen)
|
||||
|
||||
case *Named:
|
||||
return isParameterizedList(t.targs, seen)
|
||||
|
||||
case *TypeParam:
|
||||
return true
|
||||
|
||||
|
|
|
|||
|
|
@ -118,18 +118,12 @@ func (p *ifacePair) identical(q *ifacePair) bool {
|
|||
return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x
|
||||
}
|
||||
|
||||
// If a non-nil tparams is provided, type inference is done for type parameters in x.
|
||||
func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair, tparams []Type) bool {
|
||||
if x == y {
|
||||
return true
|
||||
}
|
||||
|
||||
// make sure type parameter is in x if we have one
|
||||
// TODO(gri) this may not be needed: we should only be inferring in one direction,
|
||||
// from (given) argument types, to possibly free parameter types
|
||||
if _, ok := x.(*TypeParam); !ok {
|
||||
x, y = y, x
|
||||
}
|
||||
|
||||
switch x := x.(type) {
|
||||
case *Basic:
|
||||
// Basic types are singletons except for the rune and byte
|
||||
|
|
@ -283,19 +277,40 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair, tparams
|
|||
case *Named:
|
||||
// Two named types are identical if their type names originate
|
||||
// in the same type declaration.
|
||||
// if y, ok := y.(*Named); ok {
|
||||
// return x.obj == y.obj
|
||||
// }
|
||||
if y, ok := y.(*Named); ok {
|
||||
return x.obj == y.obj
|
||||
// Without type inference, type parameters (if any) must match
|
||||
// exactly and thus the type names must match exactly as well.
|
||||
if tparams == nil {
|
||||
// TODO(gri) Why is x == y not sufficient? And if it is,
|
||||
// we can just return false here because x == y
|
||||
// is caught in the very beginning of this function.
|
||||
return x.obj == y.obj
|
||||
}
|
||||
|
||||
// TODO(gri) This is not always correct: two types may have the same names
|
||||
// in the same package if one of them is nested in a function.
|
||||
// Extremely unlikely but we need an always correct solution.
|
||||
if x.obj.pkg == y.obj.pkg && stripArgNames(x.obj.name) == stripArgNames(y.obj.name) {
|
||||
assert(len(x.targs) == len(y.targs))
|
||||
for i, x := range x.targs {
|
||||
if !check.identical0(x, y.targs[i], cmpTags, p, tparams) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case *TypeParam:
|
||||
if y, ok := y.(*TypeParam); ok {
|
||||
// TODO(gri) do we need to look at type names here?
|
||||
// - consider type-checking a generic function calling another generic function
|
||||
// - what about self-recursive calls?
|
||||
return x.index == y.index
|
||||
}
|
||||
// TODO(gri) do we need to look at type names here?
|
||||
// - consider type-checking a generic function calling another generic function
|
||||
// - what about self-recursive calls?
|
||||
// (may need a map keyed by type parameters with the values the respective inferred types)
|
||||
if tparams == nil {
|
||||
return false
|
||||
return false // x and y being equal is caught in the very beginning of this function
|
||||
}
|
||||
if x := tparams[x.index]; x != nil {
|
||||
return check.identical0(x, y, cmpTags, p, tparams)
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ func _(type F, C FloatComplex)(c C) {
|
|||
_ = imag(c)
|
||||
re := real(c)
|
||||
im := imag(c)
|
||||
var _ F = re // TODO(gri) why does this work? (no type conversion needed?)
|
||||
var _ F = im // TODO(gri) why does this work? (no type conversion needed?)
|
||||
c = C(complex(re, im))
|
||||
var fre F = F(re)
|
||||
var fim F = F(im)
|
||||
c = C(complex(fre, fim))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,8 +53,7 @@ func (m *Map(K, V)) Insert(key K, val V) bool {
|
|||
(*pn).val = val
|
||||
return false
|
||||
}
|
||||
// TODO(gri) investigate assignment
|
||||
// *pn = &node(K, V){key: key, val: val}
|
||||
*pn = &node(K, V){key: key, val: val}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -86,8 +85,7 @@ func (m *Map(K, V)) InOrder() *Iterator(K, V) {
|
|||
// Stop sending values if sender.Send returns false,
|
||||
// meaning that nothing is listening at the receiver end.
|
||||
return f(n.left) &&
|
||||
// TODO
|
||||
// sender.Send(keyValue(K, V){n.key, n.val}) &&
|
||||
sender.Send(keyValue(K, V){n.key, n.val}) &&
|
||||
f(n.right)
|
||||
}
|
||||
go func() {
|
||||
|
|
|
|||
|
|
@ -53,8 +53,7 @@ func (m *Map(K, V)) Insert(key K, val V) bool {
|
|||
(*pn).val = val
|
||||
return false
|
||||
}
|
||||
// TODO(gri) look into this assignment
|
||||
// *pn = &node(K, V){key: key, val: val}
|
||||
*pn = &node(K, V){key: key, val: val}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -86,8 +85,7 @@ func (m *Map(K, V)) InOrder() *Iterator(K, V) {
|
|||
// Stop sending values if sender.Send returns false,
|
||||
// meaning that nothing is listening at the receiver end.
|
||||
return f(n.left) &&
|
||||
// TODO
|
||||
// sender.Send(keyValue(K, V){n.key, n.val}) &&
|
||||
sender.Send(keyValue(K, V){n.key, n.val}) &&
|
||||
f(n.right)
|
||||
}
|
||||
go func() {
|
||||
|
|
@ -96,9 +94,7 @@ func (m *Map(K, V)) InOrder() *Iterator(K, V) {
|
|||
}()
|
||||
// TODO(gri) The design draft doesn't require that we repeat
|
||||
// the type parameters here. Fix the implementation.
|
||||
_ = receiver
|
||||
panic(0)
|
||||
//return &Iterator(K, V){receiver}
|
||||
return &Iterator(K, V){receiver}
|
||||
// return &Iterator{receiver}
|
||||
}
|
||||
|
||||
|
|
@ -116,12 +112,7 @@ func (it *Iterator(K, V)) Next() (K, V, bool) {
|
|||
var zerov V
|
||||
return zerok, zerov, false
|
||||
}
|
||||
// TODO(gri) investigate return
|
||||
// return keyval.key, keyval.val, true
|
||||
_ = keyval
|
||||
var k K
|
||||
var v V
|
||||
return k, v, true
|
||||
return keyval.key, keyval.val, true
|
||||
}
|
||||
|
||||
// chans
|
||||
|
|
@ -134,6 +125,15 @@ type chans_Sender(type T) struct {
|
|||
done <-chan bool
|
||||
}
|
||||
|
||||
func (s *chans_Sender(T)) Send(v T) bool {
|
||||
select {
|
||||
case s.values <- v:
|
||||
return true
|
||||
case <-s.done:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (s *chans_Sender(T)) Close() {
|
||||
close(s.values)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,34 +9,5 @@ type T(type P) struct {}
|
|||
func (_ T(P)) m1() T(P)
|
||||
|
||||
func (m T(P)) m2() {
|
||||
// TODO(gri) this should be valid
|
||||
var _ T(P) = m /* ERROR cannot use */ .m1()
|
||||
var _ T(P) = m.m1()
|
||||
}
|
||||
|
||||
/*
|
||||
type Pair(type K) struct {
|
||||
key K
|
||||
}
|
||||
|
||||
type Receiver(type T) struct {
|
||||
values T
|
||||
}
|
||||
|
||||
type Iterator(type K) struct {
|
||||
r Receiver(Pair(K))
|
||||
}
|
||||
|
||||
func (r Receiver(T)) Values() T {
|
||||
return r.values
|
||||
}
|
||||
|
||||
func (it Iterator(K)) Next() K {
|
||||
//x := it.r.Values()
|
||||
// it : Iterator(K)
|
||||
// it.r : Receiver(Pair(K))
|
||||
// it.r.Values: Pair(K)
|
||||
//var r Receiver(Pair(K))
|
||||
var x Pair(K) //= (r.Values)()
|
||||
return x.key
|
||||
}
|
||||
*/
|
||||
|
|
@ -129,3 +129,38 @@ type T struct {}
|
|||
func (T) m1() {}
|
||||
func (T) m2( /* ERROR method must have no type parameters */ type)() {}
|
||||
func (T) m3( /* ERROR method must have no type parameters */ type P)() {}
|
||||
|
||||
// type inference across parameterized types
|
||||
|
||||
type S1(type P) struct { f P }
|
||||
|
||||
func f9(type P)(x S1(P))
|
||||
|
||||
func _() {
|
||||
f9(int)(S1(int){42})
|
||||
f9(S1(int){42})
|
||||
}
|
||||
|
||||
type S2(type A, B, C) struct{}
|
||||
|
||||
func f10(type X, Y, Z)(a S2(X, int, Z), b S2(X, Y, bool))
|
||||
|
||||
func _(type P)() {
|
||||
f10(int, float32, string)(S2(int, int, string){}, S2(int, float32, bool){})
|
||||
f10(S2(int, int, string){}, S2(int, float32, bool){})
|
||||
f10(S2(P, int, P){}, S2(P, float32, bool){})
|
||||
}
|
||||
|
||||
// method expressions
|
||||
|
||||
func (_ S1(P)) m()
|
||||
|
||||
func _() {
|
||||
m := S1(int).m
|
||||
m(struct { f int }{42})
|
||||
}
|
||||
|
||||
func _(type T) (x T) {
|
||||
m := S1(T).m
|
||||
m(S1(T){x})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -537,7 +537,7 @@ func (c *Contract) ifaceAt(index int) *Interface {
|
|||
|
||||
// A TypeParam represents a type parameter type.
|
||||
type TypeParam struct {
|
||||
id uint64 // unique id
|
||||
id uint64 // unique id (TODO should this be with the object? all objects?)
|
||||
obj *TypeName
|
||||
index int // parameter index
|
||||
bound Type // either an *Interface or a *Contract
|
||||
|
|
|
|||
Loading…
Reference in New Issue