go/types: fix receiver type parameter inference

Make sure receiver type and actual receiver argument
match in "pointer-ness" before attempting to infer
any receiver type parameters.

Change-Id: Ie28004ce42ecef46e348424140b8d0c65e37cdaa
This commit is contained in:
Robert Griesemer 2020-01-31 11:02:58 -08:00
parent 5a2e660efa
commit 1bcfb0add1
3 changed files with 62 additions and 7 deletions

View File

@ -489,14 +489,35 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
// 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.
// TODO(gri) factor this code out
sig := m.typ.(*Signature)
if len(sig.rparams) > 0 {
// check.dump("### recv typ = %s", x.typ)
// check.dump("### method = %s tparams = %s", m, m.rparams)
targs := check.infer(sig.recv.pos, sig.rparams, NewTuple(sig.recv), []*operand{x})
// check.dump("### inferred targs = %s", targs)
//check.dump("### recv typ = %s", x.typ)
//check.dump("### method = %s rparams = %s tparams = %s", m, sig.rparams, sig.tparams)
// The method may have a pointer receiver, but the actually provided receiver
// may be a (hopefully addressable) non-pointer value, or vice versa. Here we
// only care about inferring receiver type parameters; to make the inferrence
// work, match up pointer-ness of reveiver and argument.
arg := x
if ptrRecv := isPointer(sig.recv.typ); ptrRecv != isPointer(arg.typ) {
copy := *arg
if ptrRecv {
copy.typ = NewPointer(arg.typ)
} else {
copy.typ = arg.typ.(*Pointer).base
}
arg = &copy
}
targs := check.infer(sig.recv.pos, sig.rparams, NewTuple(sig.recv), []*operand{arg})
//check.dump("### inferred targs = %s", targs)
if len(targs) == 0 {
// TODO(gri) Provide explanation as to why we can't possibly
// reach here (consider invalid receivers, etc.).
panic("internal error: receiver type parameter inference failed")
}
// 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.)
// TODO(gri) investigate and provide a correct explanation here
copy := *m
copy.typ = check.subst(e.Pos(), m.typ, sig.rparams, targs)
obj = &copy

View File

@ -33,9 +33,12 @@ func (check *Checker) infer(pos token.Pos, tparams []*TypeName, params *Tuple, a
}
if isTyped(arg.typ) {
if !check.identical0(par.typ, arg.typ, true, nil, targs) {
check.errorf(arg.pos(), "type %s for %s does not match %s = %s",
arg.typ, arg.expr, par.typ, check.subst(pos, par.typ, tparams, targs),
)
// Calling subst for an error message can cause problems.
// TODO(gri) Determine best approach here.
// check.errorf(arg.pos(), "type %s for %s does not match %s = %s",
// arg.typ, arg.expr, par.typ, check.subst(pos, par.typ, tparams, targs),
// )
check.errorf(arg.pos(), "type %s for %s does not match %s", arg.typ, arg.expr, par.typ)
return nil
}
} else {

View File

@ -116,3 +116,34 @@ type A(type P) struct{ x P }
func (_ A(P)) a() {}
var _ T5(A(int), int)
// Invoking methods with parameterized receiver types uses
// type inference to determine the actual type arguments matching
// the receiver type parameters from the actual receiver argument.
// Go does implicit address-taking and dereferenciation depending
// on the actual receiver and the method's receiver type. To make
// type inference work, the type-checker matches "pointer-ness"
// of the actual receiver and the method's receiver type.
// The following tests verify test this mechanism.
type R1(type A) struct{}
func (_ R1(A)) vm()
func (_ *R1(A)) pm()
func _(type T)(r R1(T), p *R1(T)) {
r.vm()
r.pm()
p.vm()
p.pm()
}
type R2(type A, B) struct{}
func (_ R2(A, B)) vm()
func (_ *R2(A, B)) pm()
func _(type T)(r R2(T, int), p *R2(string, T)) {
r.vm()
r.pm()
p.vm()
p.pm()
}