mirror of
https://github.com/golang/go
synced 2024-11-16 19:14:43 -07:00
go/types: implement Checker.implements
This is a port of CL 363837 from types2 to go/types. As usual, test error messages had to be repositioned on the operand. Change-Id: I2b53fae7aa30f9147f8d05f75b0ab252338320bb Reviewed-on: https://go-review.googlesource.com/c/go/+/364934 Trust: Robert Findley <rfindley@google.com> 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
14c3f749be
commit
9ea939be60
@ -133,34 +133,6 @@ func (check *Checker) validateTArgLen(pos token.Pos, ntparams, ntargs int) bool
|
||||
}
|
||||
|
||||
func (check *Checker) verify(pos token.Pos, tparams []*TypeParam, targs []Type) (int, error) {
|
||||
smap := makeSubstMap(tparams, targs)
|
||||
for i, tpar := range tparams {
|
||||
// stop checking bounds after the first failure
|
||||
if err := check.satisfies(pos, targs[i], tpar, smap); err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// satisfies reports whether the type argument targ satisfies the constraint of type parameter
|
||||
// parameter tpar (after any of its type parameters have been substituted through smap).
|
||||
// A suitable error is reported if the result is false.
|
||||
// TODO(gri) This should be a method of interfaces or type sets.
|
||||
func (check *Checker) satisfies(pos token.Pos, targ Type, tpar *TypeParam, smap substMap) error {
|
||||
iface := tpar.iface()
|
||||
|
||||
// Every type argument satisfies interface{}.
|
||||
if iface.Empty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// A type argument that is a type parameter with an empty type set satisfies any constraint.
|
||||
// (The empty set is a subset of any set.)
|
||||
if targ, _ := targ.(*TypeParam); targ != nil && targ.iface().typeSet().IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(rfindley): it would be great if users could pass in a qualifier here,
|
||||
// rather than falling back to verbose qualification. Maybe this can be part
|
||||
// of the shared context.
|
||||
@ -168,78 +140,115 @@ func (check *Checker) satisfies(pos token.Pos, targ Type, tpar *TypeParam, smap
|
||||
if check != nil {
|
||||
qf = check.qualifier
|
||||
}
|
||||
|
||||
smap := makeSubstMap(tparams, targs)
|
||||
for i, tpar := range tparams {
|
||||
// The type parameter bound is parameterized with the same type parameters
|
||||
// as the instantiated type; before we can use it for bounds checking we
|
||||
// need to instantiate it with the type arguments with which we instantiated
|
||||
// the parameterized type.
|
||||
bound := check.subst(pos, tpar.bound, smap, nil)
|
||||
if err := check.implements(targs[i], bound, qf); err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// implements checks if V implements T and reports an error if it doesn't.
|
||||
// If a qualifier is provided, it is used in error formatting.
|
||||
func (check *Checker) implements(V, T Type, qf Qualifier) error {
|
||||
Vu := under(V)
|
||||
Tu := under(T)
|
||||
if Vu == Typ[Invalid] || Tu == Typ[Invalid] {
|
||||
return nil
|
||||
}
|
||||
|
||||
errorf := func(format string, args ...interface{}) error {
|
||||
return errors.New(sprintf(nil, qf, false, format, args...))
|
||||
}
|
||||
|
||||
// No type argument with non-empty type set satisfies the empty type set.
|
||||
if iface.typeSet().IsEmpty() {
|
||||
return errorf("%s does not satisfy %s (constraint type set is empty)", targ, tpar.bound)
|
||||
Ti, _ := Tu.(*Interface)
|
||||
if Ti == nil {
|
||||
return errorf("%s is not an interface", T)
|
||||
}
|
||||
|
||||
// The type parameter bound is parameterized with the same type parameters
|
||||
// as the instantiated type; before we can use it for bounds checking we
|
||||
// need to instantiate it with the type arguments with which we instantiate
|
||||
// the parameterized type.
|
||||
iface = check.subst(pos, iface, smap, nil).(*Interface)
|
||||
// Every type satisfies the empty interface.
|
||||
if Ti.Empty() {
|
||||
return nil
|
||||
}
|
||||
// T is not the empty interface (i.e., the type set of T is restricted)
|
||||
|
||||
// if iface is comparable, targ must be comparable
|
||||
// An interface V with an empty type set satisfies any interface.
|
||||
// (The empty set is a subset of any set.)
|
||||
Vi, _ := Vu.(*Interface)
|
||||
if Vi != nil && Vi.typeSet().IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
// type set of V is not empty
|
||||
|
||||
// No type with non-empty type set satisfies the empty type set.
|
||||
// TODO(gri) should use "implements" rather than "satisfies" throughout
|
||||
if Ti.typeSet().IsEmpty() {
|
||||
return errorf("%s does not satisfy %s (constraint type set is empty)", V, T)
|
||||
}
|
||||
|
||||
// If T is comparable, V must be comparable.
|
||||
// TODO(gri) the error messages needs to be better, here
|
||||
if iface.IsComparable() && !Comparable(targ) {
|
||||
if tpar, _ := targ.(*TypeParam); tpar != nil && tpar.iface().typeSet().IsAll() {
|
||||
return errorf("%s has no constraints", targ)
|
||||
if Ti.IsComparable() && !Comparable(V) {
|
||||
if Vi != nil && Vi.typeSet().IsAll() {
|
||||
return errorf("%s has no constraints", V)
|
||||
}
|
||||
return errorf("%s does not satisfy comparable", targ)
|
||||
return errorf("%s does not satisfy comparable", V)
|
||||
}
|
||||
|
||||
// targ must implement iface (methods)
|
||||
// V must implement T (methods)
|
||||
// - check only if we have methods
|
||||
if iface.NumMethods() > 0 {
|
||||
if Ti.NumMethods() > 0 {
|
||||
// If the type argument is a pointer to a type parameter, the type argument's
|
||||
// method set is empty.
|
||||
// TODO(gri) is this what we want? (spec question)
|
||||
if base, isPtr := deref(targ); isPtr && isTypeParam(base) {
|
||||
return errorf("%s has no methods", targ)
|
||||
if base, isPtr := deref(V); isPtr && isTypeParam(base) {
|
||||
return errorf("%s has no methods", V)
|
||||
}
|
||||
if m, wrong := check.missingMethod(targ, iface, true); m != nil {
|
||||
if m, wrong := check.missingMethod(V, Ti, true); m != nil {
|
||||
// TODO(gri) needs to print updated name to avoid major confusion in error message!
|
||||
// (print warning for now)
|
||||
// Old warning:
|
||||
// check.softErrorf(pos, "%s does not satisfy %s (warning: name not updated) = %s (missing method %s)", targ, tpar.bound, iface, m)
|
||||
// check.softErrorf(pos, "%s does not satisfy %s (warning: name not updated) = %s (missing method %s)", V, T, Ti, m)
|
||||
if wrong != nil {
|
||||
// TODO(gri) This can still report uninstantiated types which makes the error message
|
||||
// more difficult to read then necessary.
|
||||
// TODO(rFindley) should this use parentheses rather than ':' for qualification?
|
||||
return errorf("%s does not satisfy %s: wrong method signature\n\tgot %s\n\twant %s",
|
||||
targ, tpar.bound, wrong, m,
|
||||
V, T, wrong, m,
|
||||
)
|
||||
}
|
||||
return errorf("%s does not satisfy %s (missing method %s)", targ, tpar.bound, m.name)
|
||||
return errorf("%s does not satisfy %s (missing method %s)", V, T, m.name)
|
||||
}
|
||||
}
|
||||
|
||||
// targ must also be in the set of types of iface, if any.
|
||||
// V must also be in the set of types of T, if any.
|
||||
// Constraints with empty type sets were already excluded above.
|
||||
if !iface.typeSet().hasTerms() {
|
||||
if !Ti.typeSet().hasTerms() {
|
||||
return nil // nothing to do
|
||||
}
|
||||
|
||||
// If targ is itself a type parameter, each of its possible types must be in the set
|
||||
// of iface types (i.e., the targ type set must be a subset of the iface type set).
|
||||
// Type arguments with empty type sets were already excluded above.
|
||||
if targ, _ := targ.(*TypeParam); targ != nil {
|
||||
targBound := targ.iface()
|
||||
if !targBound.typeSet().subsetOf(iface.typeSet()) {
|
||||
// If V is itself an interface, each of its possible types must be in the set
|
||||
// of T types (i.e., the V type set must be a subset of the T type set).
|
||||
// Interfaces V with empty type sets were already excluded above.
|
||||
if Vi != nil {
|
||||
if !Vi.typeSet().subsetOf(Ti.typeSet()) {
|
||||
// TODO(gri) report which type is missing
|
||||
return errorf("%s does not satisfy %s", targ, tpar.bound)
|
||||
return errorf("%s does not satisfy %s", V, T)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Otherwise, targ's type must be included in the iface type set.
|
||||
if !iface.typeSet().includes(targ) {
|
||||
// Otherwise, V's type must be included in the iface type set.
|
||||
if !Ti.typeSet().includes(V) {
|
||||
// TODO(gri) report which type is missing
|
||||
return errorf("%s does not satisfy %s", targ, tpar.bound)
|
||||
return errorf("%s does not satisfy %s", V, T)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -8,10 +8,10 @@ func f1[T any, C chan T | <-chan T](ch C) {}
|
||||
|
||||
func _(ch chan int) { f1(ch) }
|
||||
func _(ch <-chan int) { f1(ch) }
|
||||
func _(ch chan<- int) { f1 /* ERROR chan<- int does not satisfy chan T\|<-chan T */ (ch) }
|
||||
func _(ch chan<- int) { f1 /* ERROR chan<- int does not satisfy chan int\|<-chan int */ (ch) }
|
||||
|
||||
func f2[T any, C chan T | chan<- T](ch C) {}
|
||||
|
||||
func _(ch chan int) { f2(ch) }
|
||||
func _(ch <-chan int) { f2 /* ERROR <-chan int does not satisfy chan T\|chan<- T */ (ch)}
|
||||
func _(ch <-chan int) { f2 /* ERROR <-chan int does not satisfy chan int\|chan<- int */ (ch) }
|
||||
func _(ch chan<- int) { f2(ch) }
|
||||
|
Loading…
Reference in New Issue
Block a user