mirror of
https://github.com/golang/go
synced 2024-11-17 21:54:49 -07:00
go/types, types2: consider methods when unifying type parameters and constraints
An inferred type argument must implement its type parameter's constraint's methods whether or not a core type exists. This allows us to infer type parameters used in method signatures. Fixes #51593. Change-Id: I1fddb05a71d442641b4311d8e30a13ea9bdb4db5 Reviewed-on: https://go-review.googlesource.com/c/go/+/472298 TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Robert Griesemer <gri@google.com> Run-TryBot: Robert Griesemer <gri@google.com> Reviewed-by: Robert Griesemer <gri@google.com> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
09852e75ac
commit
b44f2222b5
@ -326,7 +326,7 @@ func TestCheck(t *testing.T) {
|
|||||||
}
|
}
|
||||||
func TestSpec(t *testing.T) { testDirFiles(t, "../../../../internal/types/testdata/spec", 0, false) }
|
func TestSpec(t *testing.T) { testDirFiles(t, "../../../../internal/types/testdata/spec", 0, false) }
|
||||||
func TestExamples(t *testing.T) {
|
func TestExamples(t *testing.T) {
|
||||||
testDirFiles(t, "../../../../internal/types/testdata/examples", 60, false)
|
testDirFiles(t, "../../../../internal/types/testdata/examples", 125, false)
|
||||||
} // TODO(gri) narrow column tolerance
|
} // TODO(gri) narrow column tolerance
|
||||||
func TestFixedbugs(t *testing.T) {
|
func TestFixedbugs(t *testing.T) {
|
||||||
testDirFiles(t, "../../../../internal/types/testdata/fixedbugs", 100, false)
|
testDirFiles(t, "../../../../internal/types/testdata/fixedbugs", 100, false)
|
||||||
|
@ -186,12 +186,12 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
|
|||||||
u.tracef("type parameters: %s", tparams)
|
u.tracef("type parameters: %s", tparams)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeatedly apply constraint type inference as long as
|
// Unify type parameters with their constraints as long
|
||||||
// progress is being made.
|
// as progress is being made.
|
||||||
//
|
//
|
||||||
// This is an O(n^2) algorithm where n is the number of
|
// This is an O(n^2) algorithm where n is the number of
|
||||||
// type parameters: if there is progress, at least one
|
// type parameters: if there is progress, at least one
|
||||||
// type argument is inferred per iteration and we have
|
// type argument is inferred per iteration, and we have
|
||||||
// a doubly nested loop.
|
// a doubly nested loop.
|
||||||
//
|
//
|
||||||
// In practice this is not a problem because the number
|
// In practice this is not a problem because the number
|
||||||
@ -205,6 +205,11 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
|
|||||||
nn := u.unknowns()
|
nn := u.unknowns()
|
||||||
|
|
||||||
for _, tpar := range tparams {
|
for _, tpar := range tparams {
|
||||||
|
tx := u.at(tpar)
|
||||||
|
if traceInference && tx != nil {
|
||||||
|
u.tracef("%s = %s", tpar, tx)
|
||||||
|
}
|
||||||
|
|
||||||
// If there is a core term (i.e., a core type with tilde information)
|
// If there is a core term (i.e., a core type with tilde information)
|
||||||
// unify the type parameter with the core type.
|
// unify the type parameter with the core type.
|
||||||
if core, single := coreTerm(tpar); core != nil {
|
if core, single := coreTerm(tpar); core != nil {
|
||||||
@ -212,7 +217,6 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
|
|||||||
u.tracef("core(%s) = %s (single = %v)", tpar, core, single)
|
u.tracef("core(%s) = %s (single = %v)", tpar, core, single)
|
||||||
}
|
}
|
||||||
// A type parameter can be unified with its core type in two cases.
|
// A type parameter can be unified with its core type in two cases.
|
||||||
tx := u.at(tpar)
|
|
||||||
switch {
|
switch {
|
||||||
case tx != nil:
|
case tx != nil:
|
||||||
// The corresponding type argument tx is known. There are 2 cases:
|
// The corresponding type argument tx is known. There are 2 cases:
|
||||||
@ -239,6 +243,17 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
|
|||||||
if traceInference {
|
if traceInference {
|
||||||
u.tracef("core(%s) = nil", tpar)
|
u.tracef("core(%s) = nil", tpar)
|
||||||
}
|
}
|
||||||
|
if tx != nil {
|
||||||
|
// We don't have a core type, but the type argument tx is known.
|
||||||
|
// It must have (at least) all the methods of the type constraint,
|
||||||
|
// and the method signatures must unify; otherwise tx cannot satisfy
|
||||||
|
// the constraint.
|
||||||
|
constraint := tpar.iface()
|
||||||
|
if m, wrong := check.missingMethod(tx, constraint, true, u.unify); m != nil {
|
||||||
|
check.errorf(pos, CannotInferTypeArgs, "%s does not satisfy %s %s", tx, constraint, check.missingMethodCause(tx, constraint, m, wrong))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +288,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
|
|||||||
j++
|
j++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// untyped[:j] are the undices of parameters without a type yet
|
// untyped[:j] are the indices of parameters without a type yet
|
||||||
for _, i := range untyped[:j] {
|
for _, i := range untyped[:j] {
|
||||||
tpar := params.At(i).typ.(*TypeParam)
|
tpar := params.At(i).typ.(*TypeParam)
|
||||||
arg := args[i]
|
arg := args[i]
|
||||||
|
@ -188,12 +188,12 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
|
|||||||
u.tracef("type parameters: %s", tparams)
|
u.tracef("type parameters: %s", tparams)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeatedly apply constraint type inference as long as
|
// Unify type parameters with their constraints as long
|
||||||
// progress is being made.
|
// as progress is being made.
|
||||||
//
|
//
|
||||||
// This is an O(n^2) algorithm where n is the number of
|
// This is an O(n^2) algorithm where n is the number of
|
||||||
// type parameters: if there is progress, at least one
|
// type parameters: if there is progress, at least one
|
||||||
// type argument is inferred per iteration and we have
|
// type argument is inferred per iteration, and we have
|
||||||
// a doubly nested loop.
|
// a doubly nested loop.
|
||||||
//
|
//
|
||||||
// In practice this is not a problem because the number
|
// In practice this is not a problem because the number
|
||||||
@ -207,6 +207,11 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
|
|||||||
nn := u.unknowns()
|
nn := u.unknowns()
|
||||||
|
|
||||||
for _, tpar := range tparams {
|
for _, tpar := range tparams {
|
||||||
|
tx := u.at(tpar)
|
||||||
|
if traceInference && tx != nil {
|
||||||
|
u.tracef("%s = %s", tpar, tx)
|
||||||
|
}
|
||||||
|
|
||||||
// If there is a core term (i.e., a core type with tilde information)
|
// If there is a core term (i.e., a core type with tilde information)
|
||||||
// unify the type parameter with the core type.
|
// unify the type parameter with the core type.
|
||||||
if core, single := coreTerm(tpar); core != nil {
|
if core, single := coreTerm(tpar); core != nil {
|
||||||
@ -214,7 +219,6 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
|
|||||||
u.tracef("core(%s) = %s (single = %v)", tpar, core, single)
|
u.tracef("core(%s) = %s (single = %v)", tpar, core, single)
|
||||||
}
|
}
|
||||||
// A type parameter can be unified with its core type in two cases.
|
// A type parameter can be unified with its core type in two cases.
|
||||||
tx := u.at(tpar)
|
|
||||||
switch {
|
switch {
|
||||||
case tx != nil:
|
case tx != nil:
|
||||||
// The corresponding type argument tx is known. There are 2 cases:
|
// The corresponding type argument tx is known. There are 2 cases:
|
||||||
@ -241,6 +245,17 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
|
|||||||
if traceInference {
|
if traceInference {
|
||||||
u.tracef("core(%s) = nil", tpar)
|
u.tracef("core(%s) = nil", tpar)
|
||||||
}
|
}
|
||||||
|
if tx != nil {
|
||||||
|
// We don't have a core type, but the type argument tx is known.
|
||||||
|
// It must have (at least) all the methods of the type constraint,
|
||||||
|
// and the method signatures must unify; otherwise tx cannot satisfy
|
||||||
|
// the constraint.
|
||||||
|
constraint := tpar.iface()
|
||||||
|
if m, wrong := check.missingMethod(tx, constraint, true, u.unify); m != nil {
|
||||||
|
check.errorf(posn, CannotInferTypeArgs, "%s does not satisfy %s %s", tx, constraint, check.missingMethodCause(tx, constraint, m, wrong))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +290,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
|
|||||||
j++
|
j++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// untyped[:j] are the undices of parameters without a type yet
|
// untyped[:j] are the indices of parameters without a type yet
|
||||||
for _, i := range untyped[:j] {
|
for _, i := range untyped[:j] {
|
||||||
tpar := params.At(i).typ.(*TypeParam)
|
tpar := params.At(i).typ.(*TypeParam)
|
||||||
arg := args[i]
|
arg := args[i]
|
||||||
|
@ -114,3 +114,38 @@ func _() {
|
|||||||
// List[Elem].
|
// List[Elem].
|
||||||
related3 /* ERROR "cannot infer Slice" */ [int]()
|
related3 /* ERROR "cannot infer Slice" */ [int]()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wantsMethods[P interface{ m1(Q); m2() R }, Q, R any](P) {}
|
||||||
|
|
||||||
|
type hasMethods1 struct{}
|
||||||
|
|
||||||
|
func (hasMethods1) m1(int)
|
||||||
|
func (hasMethods1) m2() string
|
||||||
|
|
||||||
|
type hasMethods2 struct{}
|
||||||
|
|
||||||
|
func (*hasMethods2) m1(int)
|
||||||
|
func (*hasMethods2) m2() string
|
||||||
|
|
||||||
|
type hasMethods3 interface{
|
||||||
|
m1(float64)
|
||||||
|
m2() complex128
|
||||||
|
}
|
||||||
|
|
||||||
|
type hasMethods4 interface{
|
||||||
|
m1()
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// wantsMethod can be called with arguments that have the relevant methods
|
||||||
|
// and wantsMethod's type arguments are inferred from those types' method
|
||||||
|
// signatures.
|
||||||
|
wantsMethods(hasMethods1{})
|
||||||
|
wantsMethods(&hasMethods1{})
|
||||||
|
// TODO(gri) improve error message (the cause is ptr vs non-pointer receiver)
|
||||||
|
wantsMethods /* ERROR "hasMethods2 does not satisfy interface{m1(Q); m2() R} (wrong type for method m1)" */ (hasMethods2{})
|
||||||
|
wantsMethods(&hasMethods2{})
|
||||||
|
wantsMethods(hasMethods3(nil))
|
||||||
|
wantsMethods /* ERROR "any does not satisfy interface{m1(Q); m2() R} (missing method m1)" */ (any(nil))
|
||||||
|
wantsMethods /* ERROR "hasMethods4 does not satisfy interface{m1(Q); m2() R} (wrong type for method m1)" */ (hasMethods4(nil))
|
||||||
|
}
|
||||||
|
@ -9,5 +9,5 @@ func f[P interface{ m(R) }, R any]() {}
|
|||||||
type T = interface { m(int) }
|
type T = interface { m(int) }
|
||||||
|
|
||||||
func _() {
|
func _() {
|
||||||
_ = f /* ERROR "cannot infer R" */ [T] // don't crash in type inference
|
_ = f[T]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user