mirror of
https://github.com/golang/go
synced 2024-11-14 17:30:29 -07:00
go/types, types2: always try inference over methods when possible
During type inference, when comparing type parameters against their constraints, if a type argument is completely known it must implement its constraint. In this case, always unify the type argument's methods against the constraint methods, if any. Before this CL, this step was only attempted if the constraint had no core type. That left information unused which led to type inference failures where it should have succeeded. Fixes #66751. Change-Id: I71e96b71258624212186cf17ec47e67a589817b9 Reviewed-on: https://go-review.googlesource.com/c/go/+/617896 Reviewed-by: Tim King <taking@google.com> Reviewed-by: Robert Griesemer <gri@google.com> Auto-Submit: Robert Griesemer <gri@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
7703db647c
commit
726d898c92
@ -236,10 +236,10 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
|
||||
u.tracef("-- type parameter %s = %s: core(%s) = %s, single = %v", tpar, tx, tpar, core, single)
|
||||
}
|
||||
|
||||
// If there is a core term (i.e., a core type with tilde information)
|
||||
// unify the type parameter with the core type.
|
||||
// If the type parameter's constraint has a core term (i.e., a core type with tilde information)
|
||||
// try to unify the type parameter with that core type.
|
||||
if core != nil {
|
||||
// A type parameter can be unified with its core type in two cases.
|
||||
// A type parameter can be unified with its constraint's core type in two cases.
|
||||
switch {
|
||||
case tx != nil:
|
||||
if traceInference {
|
||||
@ -266,33 +266,42 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
|
||||
if traceInference {
|
||||
u.tracef("-> set type parameter %s to constraint core type %s", tpar, core.typ)
|
||||
}
|
||||
// The corresponding type argument tx is unknown and there's a single
|
||||
// specific type and no tilde.
|
||||
// The corresponding type argument tx is unknown and the core term
|
||||
// describes a single specific type and no tilde.
|
||||
// In this case the type argument must be that single type; set it.
|
||||
u.set(tpar, core.typ)
|
||||
}
|
||||
} else {
|
||||
if tx != nil {
|
||||
if traceInference {
|
||||
u.tracef("-> unify type parameter %s (type %s) methods with constraint methods", tpar, tx)
|
||||
}
|
||||
// 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.
|
||||
// TODO(gri) Now that unification handles interfaces, this code can
|
||||
// be reduced to calling u.unify(tx, tpar.iface(), assign)
|
||||
// (which will compare signatures exactly as we do below).
|
||||
// We leave it as is for now because missingMethod provides
|
||||
// a failure cause which allows for a better error message.
|
||||
// Eventually, unify should return an error with cause.
|
||||
var cause string
|
||||
constraint := tpar.iface()
|
||||
if m, _ := check.missingMethod(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, exact) }, &cause); m != nil {
|
||||
// TODO(gri) better error message (see TODO above)
|
||||
err.addf(pos, "%s (type %s) does not satisfy %s %s", tpar, tx, tpar.Constraint(), cause)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Independent of whether there is a core term, if the type argument tx is known
|
||||
// it must implement the methods of the type constraint, possibly after unification
|
||||
// of the relevant method signatures, otherwise tx cannot satisfy the constraint.
|
||||
// This unification step may provide additional type arguments.
|
||||
//
|
||||
// Note: The type argument tx may be known but contain references to other type
|
||||
// parameters (i.e., tx may still be parameterized).
|
||||
// In this case the methods of tx don't correctly reflect the final method set
|
||||
// and we may get a missing method error below. Skip this step in this case.
|
||||
//
|
||||
// TODO(gri) We should be able continue even with a parameterized tx if we add
|
||||
// a simplify step beforehand (see below). This will require factoring out the
|
||||
// simplify phase so we can call it from here.
|
||||
if tx != nil && !isParameterized(tparams, tx) {
|
||||
if traceInference {
|
||||
u.tracef("-> unify type parameter %s (type %s) methods with constraint methods", tpar, tx)
|
||||
}
|
||||
// TODO(gri) Now that unification handles interfaces, this code can
|
||||
// be reduced to calling u.unify(tx, tpar.iface(), assign)
|
||||
// (which will compare signatures exactly as we do below).
|
||||
// We leave it as is for now because missingMethod provides
|
||||
// a failure cause which allows for a better error message.
|
||||
// Eventually, unify should return an error with cause.
|
||||
var cause string
|
||||
constraint := tpar.iface()
|
||||
if m, _ := check.missingMethod(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, exact) }, &cause); m != nil {
|
||||
// TODO(gri) better error message (see TODO above)
|
||||
err.addf(pos, "%s (type %s) does not satisfy %s %s", tpar, tx, tpar.Constraint(), cause)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -239,10 +239,10 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
|
||||
u.tracef("-- type parameter %s = %s: core(%s) = %s, single = %v", tpar, tx, tpar, core, single)
|
||||
}
|
||||
|
||||
// If there is a core term (i.e., a core type with tilde information)
|
||||
// unify the type parameter with the core type.
|
||||
// If the type parameter's constraint has a core term (i.e., a core type with tilde information)
|
||||
// try to unify the type parameter with that core type.
|
||||
if core != nil {
|
||||
// A type parameter can be unified with its core type in two cases.
|
||||
// A type parameter can be unified with its constraint's core type in two cases.
|
||||
switch {
|
||||
case tx != nil:
|
||||
if traceInference {
|
||||
@ -269,33 +269,42 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
|
||||
if traceInference {
|
||||
u.tracef("-> set type parameter %s to constraint core type %s", tpar, core.typ)
|
||||
}
|
||||
// The corresponding type argument tx is unknown and there's a single
|
||||
// specific type and no tilde.
|
||||
// The corresponding type argument tx is unknown and the core term
|
||||
// describes a single specific type and no tilde.
|
||||
// In this case the type argument must be that single type; set it.
|
||||
u.set(tpar, core.typ)
|
||||
}
|
||||
} else {
|
||||
if tx != nil {
|
||||
if traceInference {
|
||||
u.tracef("-> unify type parameter %s (type %s) methods with constraint methods", tpar, tx)
|
||||
}
|
||||
// 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.
|
||||
// TODO(gri) Now that unification handles interfaces, this code can
|
||||
// be reduced to calling u.unify(tx, tpar.iface(), assign)
|
||||
// (which will compare signatures exactly as we do below).
|
||||
// We leave it as is for now because missingMethod provides
|
||||
// a failure cause which allows for a better error message.
|
||||
// Eventually, unify should return an error with cause.
|
||||
var cause string
|
||||
constraint := tpar.iface()
|
||||
if m, _ := check.missingMethod(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, exact) }, &cause); m != nil {
|
||||
// TODO(gri) better error message (see TODO above)
|
||||
err.addf(posn, "%s (type %s) does not satisfy %s %s", tpar, tx, tpar.Constraint(), cause)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Independent of whether there is a core term, if the type argument tx is known
|
||||
// it must implement the methods of the type constraint, possibly after unification
|
||||
// of the relevant method signatures, otherwise tx cannot satisfy the constraint.
|
||||
// This unification step may provide additional type arguments.
|
||||
//
|
||||
// Note: The type argument tx may be known but contain references to other type
|
||||
// parameters (i.e., tx may still be parameterized).
|
||||
// In this case the methods of tx don't correctly reflect the final method set
|
||||
// and we may get a missing method error below. Skip this step in this case.
|
||||
//
|
||||
// TODO(gri) We should be able continue even with a parameterized tx if we add
|
||||
// a simplify step beforehand (see below). This will require factoring out the
|
||||
// simplify phase so we can call it from here.
|
||||
if tx != nil && !isParameterized(tparams, tx) {
|
||||
if traceInference {
|
||||
u.tracef("-> unify type parameter %s (type %s) methods with constraint methods", tpar, tx)
|
||||
}
|
||||
// TODO(gri) Now that unification handles interfaces, this code can
|
||||
// be reduced to calling u.unify(tx, tpar.iface(), assign)
|
||||
// (which will compare signatures exactly as we do below).
|
||||
// We leave it as is for now because missingMethod provides
|
||||
// a failure cause which allows for a better error message.
|
||||
// Eventually, unify should return an error with cause.
|
||||
var cause string
|
||||
constraint := tpar.iface()
|
||||
if m, _ := check.missingMethod(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, exact) }, &cause); m != nil {
|
||||
// TODO(gri) better error message (see TODO above)
|
||||
err.addf(posn, "%s (type %s) does not satisfy %s %s", tpar, tx, tpar.Constraint(), cause)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
62
src/internal/types/testdata/fixedbugs/issue66751.go
vendored
Normal file
62
src/internal/types/testdata/fixedbugs/issue66751.go
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright 2024 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 S struct{}
|
||||
|
||||
func (*S) m(int) {}
|
||||
|
||||
func f[A interface {
|
||||
~*B
|
||||
m(C)
|
||||
}, B, C any]() {
|
||||
}
|
||||
|
||||
var _ = f[*S] // must be able to infer all remaining type arguments
|
||||
|
||||
// original test case from issue
|
||||
|
||||
type ptrTo[A any] interface{ ~*A }
|
||||
type hasFoo[A any] interface{ foo(A) }
|
||||
type both[A, B any] interface {
|
||||
ptrTo[A]
|
||||
hasFoo[B]
|
||||
}
|
||||
|
||||
type fooer[A any] struct{}
|
||||
|
||||
func (f *fooer[A]) foo(A) {}
|
||||
|
||||
func withPtr[A ptrTo[B], B any]() {}
|
||||
func withFoo[A hasFoo[B], B any]() {}
|
||||
func withBoth[A both[B, C], B, C any]() {}
|
||||
|
||||
func _() {
|
||||
withPtr[*fooer[int]]() // ok
|
||||
withFoo[*fooer[int]]() // ok
|
||||
withBoth[*fooer[int]]() // should be able to infer C
|
||||
}
|
||||
|
||||
// related test case reported in issue
|
||||
|
||||
type X struct{}
|
||||
|
||||
func (x X) M() int { return 42 }
|
||||
|
||||
func CallM1[T interface{ M() R }, R any](t T) R {
|
||||
return t.M()
|
||||
}
|
||||
|
||||
func CallM2[T interface {
|
||||
X
|
||||
M() R
|
||||
}, R any](t T) R {
|
||||
return t.M()
|
||||
}
|
||||
|
||||
func _() {
|
||||
CallM1(X{}) // ok
|
||||
CallM2(X{}) // should be able to infer R
|
||||
}
|
Loading…
Reference in New Issue
Block a user