mirror of
https://github.com/golang/go
synced 2024-09-30 03:24:39 -06: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 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
|
||||
func TestFixedbugs(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
|
||||
// Repeatedly apply constraint type inference as long as
|
||||
// progress is being made.
|
||||
// Unify type parameters with their constraints as long
|
||||
// as progress is being made.
|
||||
//
|
||||
// This is an O(n^2) algorithm where n is the number of
|
||||
// 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.
|
||||
//
|
||||
// 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()
|
||||
|
||||
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)
|
||||
// unify the type parameter with the core type.
|
||||
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)
|
||||
}
|
||||
// A type parameter can be unified with its core type in two cases.
|
||||
tx := u.at(tpar)
|
||||
switch {
|
||||
case tx != nil:
|
||||
// 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 {
|
||||
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++
|
||||
}
|
||||
}
|
||||
// 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] {
|
||||
tpar := params.At(i).typ.(*TypeParam)
|
||||
arg := args[i]
|
||||
|
@ -188,12 +188,12 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
|
||||
u.tracef("type parameters: %s", tparams)
|
||||
}
|
||||
|
||||
// Repeatedly apply constraint type inference as long as
|
||||
// progress is being made.
|
||||
// Unify type parameters with their constraints as long
|
||||
// as progress is being made.
|
||||
//
|
||||
// This is an O(n^2) algorithm where n is the number of
|
||||
// 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.
|
||||
//
|
||||
// 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()
|
||||
|
||||
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)
|
||||
// unify the type parameter with the core type.
|
||||
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)
|
||||
}
|
||||
// A type parameter can be unified with its core type in two cases.
|
||||
tx := u.at(tpar)
|
||||
switch {
|
||||
case tx != nil:
|
||||
// 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 {
|
||||
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++
|
||||
}
|
||||
}
|
||||
// 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] {
|
||||
tpar := params.At(i).typ.(*TypeParam)
|
||||
arg := args[i]
|
||||
|
@ -114,3 +114,38 @@ func _() {
|
||||
// List[Elem].
|
||||
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) }
|
||||
|
||||
func _() {
|
||||
_ = f /* ERROR "cannot infer R" */ [T] // don't crash in type inference
|
||||
_ = f[T]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user