1
0
mirror of https://github.com/golang/go synced 2024-11-14 20:00:31 -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:
Robert Griesemer 2024-10-03 13:51:25 -07:00 committed by Gopher Robot
parent 7703db647c
commit 726d898c92
3 changed files with 134 additions and 54 deletions

View File

@ -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,20 +266,30 @@ 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 {
}
// 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)
}
// 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).
@ -295,7 +305,6 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
}
}
}
}
if u.unknowns() == nn {
break // no progress

View File

@ -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,20 +269,30 @@ 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 {
}
// 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)
}
// 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).
@ -298,7 +308,6 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
}
}
}
}
if u.unknowns() == nn {
break // no progress

View 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
}