mirror of
https://github.com/golang/go
synced 2024-11-08 04:36:11 -07:00
go/types: use correct recv for parameterized embedded methods
This is a direct port of CL 298129 to go/types. Fixes #44688 Change-Id: I950992ea7beea5b9c8bea0c296b5ce03b2aa9b12 Reviewed-on: https://go-review.googlesource.com/c/go/+/298349 Trust: Robert Findley <rfindley@google.com> Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
6db80d7420
commit
12bb256cb3
@ -592,31 +592,42 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||||||
// methods may not have a fully set up signature yet
|
// methods may not have a fully set up signature yet
|
||||||
if m, _ := obj.(*Func); m != nil {
|
if m, _ := obj.(*Func); m != nil {
|
||||||
check.objDecl(m, nil)
|
check.objDecl(m, nil)
|
||||||
// If m has a parameterized receiver type, infer the type parameter
|
// If m has a parameterized receiver type, infer the type arguments from
|
||||||
// values from the actual receiver provided and then substitute the
|
// the actual receiver provided and then substitute the type parameters in
|
||||||
// type parameters in the signature accordingly.
|
// the signature accordingly.
|
||||||
// TODO(gri) factor this code out
|
// TODO(gri) factor this code out
|
||||||
sig := m.typ.(*Signature)
|
sig := m.typ.(*Signature)
|
||||||
if len(sig.rparams) > 0 {
|
if len(sig.rparams) > 0 {
|
||||||
|
// For inference to work, we must use the receiver type
|
||||||
|
// matching the receiver in the actual method declaration.
|
||||||
|
// If the method is embedded, the matching receiver is the
|
||||||
|
// embedded struct or interface that declared the method.
|
||||||
|
// Traverse the embedding to find that type (issue #44688).
|
||||||
|
recv := x.typ
|
||||||
|
for i := 0; i < len(index)-1; i++ {
|
||||||
|
// The embedded type is either a struct or a pointer to
|
||||||
|
// a struct except for the last one (which we don't need).
|
||||||
|
recv = asStruct(derefStructPtr(recv)).Field(index[i]).typ
|
||||||
|
}
|
||||||
|
|
||||||
// The method may have a pointer receiver, but the actually provided receiver
|
// 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
|
// may be a (hopefully addressable) non-pointer value, or vice versa. Here we
|
||||||
// only care about inferring receiver type parameters; to make the inference
|
// only care about inferring receiver type parameters; to make the inference
|
||||||
// work, match up pointer-ness of receiver and argument.
|
// work, match up pointer-ness of receiver and argument.
|
||||||
arg := x
|
if ptrRecv := isPointer(sig.recv.typ); ptrRecv != isPointer(recv) {
|
||||||
if ptrRecv := isPointer(sig.recv.typ); ptrRecv != isPointer(arg.typ) {
|
|
||||||
copy := *arg
|
|
||||||
if ptrRecv {
|
if ptrRecv {
|
||||||
copy.typ = NewPointer(arg.typ)
|
recv = NewPointer(recv)
|
||||||
} else {
|
} else {
|
||||||
copy.typ = arg.typ.(*Pointer).base
|
recv = recv.(*Pointer).base
|
||||||
}
|
}
|
||||||
arg = ©
|
|
||||||
}
|
}
|
||||||
targs, failed := check.infer(sig.rparams, NewTuple(sig.recv), []*operand{arg})
|
arg := operand{mode: variable, expr: x.expr, typ: recv}
|
||||||
|
targs, failed := check.infer(sig.rparams, NewTuple(sig.recv), []*operand{&arg})
|
||||||
if failed >= 0 {
|
if failed >= 0 {
|
||||||
// We may reach here if there were other errors (see issue #40056).
|
// We may reach here if there were other errors (see issue #40056).
|
||||||
// check.infer will report a follow-up error.
|
// check.infer will report a follow-up error.
|
||||||
// TODO(gri) avoid the follow-up error or provide better explanation.
|
// TODO(gri) avoid the follow-up error as it is confusing
|
||||||
|
// (there's no inference in the source code)
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
// Don't modify m. Instead - for now - make a copy of m and use that instead.
|
// Don't modify m. Instead - for now - make a copy of m and use that instead.
|
||||||
|
83
src/go/types/fixedbugs/issue44688.go2
Normal file
83
src/go/types/fixedbugs/issue44688.go2
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// Copyright 2021 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 P
|
||||||
|
|
||||||
|
type A1[T any] struct{}
|
||||||
|
|
||||||
|
func (*A1[T]) m1(T) {}
|
||||||
|
|
||||||
|
type A2[T any] interface {
|
||||||
|
m2(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
type B1[T any] struct {
|
||||||
|
filler int
|
||||||
|
*A1[T]
|
||||||
|
A2[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
type B2[T any] interface {
|
||||||
|
A2[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
type C[T any] struct {
|
||||||
|
filler1 int
|
||||||
|
filler2 int
|
||||||
|
B1[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
type D[T any] struct {
|
||||||
|
filler1 int
|
||||||
|
filler2 int
|
||||||
|
filler3 int
|
||||||
|
C[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// calling embedded methods
|
||||||
|
var b1 B1[string]
|
||||||
|
|
||||||
|
b1.A1.m1("")
|
||||||
|
b1.m1("")
|
||||||
|
|
||||||
|
b1.A2.m2("")
|
||||||
|
b1.m2("")
|
||||||
|
|
||||||
|
var b2 B2[string]
|
||||||
|
b2.m2("")
|
||||||
|
|
||||||
|
// a deeper nesting
|
||||||
|
var d D[string]
|
||||||
|
d.m1("")
|
||||||
|
d.m2("")
|
||||||
|
|
||||||
|
// calling method expressions
|
||||||
|
m1x := B1[string].m1
|
||||||
|
m1x(b1, "")
|
||||||
|
m2x := B2[string].m2
|
||||||
|
m2x(b2, "")
|
||||||
|
|
||||||
|
// calling method values
|
||||||
|
m1v := b1.m1
|
||||||
|
m1v("")
|
||||||
|
m2v := b1.m2
|
||||||
|
m2v("")
|
||||||
|
b2v := b2.m2
|
||||||
|
b2v("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// actual test case from issue
|
||||||
|
|
||||||
|
type A[T any] struct{}
|
||||||
|
|
||||||
|
func (*A[T]) f(T) {}
|
||||||
|
|
||||||
|
type B[T any] struct{ A[T] }
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var b B[string]
|
||||||
|
b.A.f("")
|
||||||
|
b.f("")
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user