diff --git a/src/go/types/call.go b/src/go/types/call.go index 0a6535161f..a9a371b159 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -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 = © + } + 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 = © diff --git a/src/go/types/infer.go b/src/go/types/infer.go index ba0830df8b..405657c84f 100644 --- a/src/go/types/infer.go +++ b/src/go/types/infer.go @@ -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 { diff --git a/src/go/types/testdata/typeinst2.go2 b/src/go/types/testdata/typeinst2.go2 index 35f3cb25d6..1413f9d975 100644 --- a/src/go/types/testdata/typeinst2.go2 +++ b/src/go/types/testdata/typeinst2.go2 @@ -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() +}