[dev.go2go] go/types: prevent comparable interface from being used outside constraints

It was already not possible to implement an interface that was or
embedded the predeclared interface "comparable" in ordinary (non-
constraint) use, but trying to do so would lead to confusing errors
(missing method "==").

Simply disallow the use of such interfaces outside constraints,
as we do for interfaces containing type lists.

Change-Id: I15ccf1b77226a50baf16df46192e90144208f9dd
Reviewed-on: https://go-review.googlesource.com/c/go/+/238300
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Robert Griesemer 2020-06-16 17:37:54 -07:00
parent 4ba19b0188
commit c2b328710b
3 changed files with 32 additions and 7 deletions

View File

@ -249,7 +249,7 @@ func _(type A, B Adder, C Adder(A))() {
}
// The type of variables (incl. parameters and return values) cannot
// be an interface with type constraints.
// be an interface with type constraints or be/embed comparable.
type I interface {
type int
}
@ -266,3 +266,17 @@ func _() I /* ERROR cannot contain type constraints */
func _() {
var _ I /* ERROR cannot contain type constraints */
}
type C interface {
comparable
}
var _ comparable /* ERROR comparable */
var _ C /* ERROR comparable */
func _(_ comparable /* ERROR comparable */ , _ C /* ERROR comparable */ )
func _() {
var _ comparable /* ERROR comparable */
var _ C /* ERROR comparable */
}

View File

@ -82,6 +82,17 @@ func IsInterface(typ Type) bool {
return typ.Interface() != nil
}
// isComparableInterface reports whether typ is an interface
// that is or embeds that predeclared interface "comparable".
func isComparableInterface(typ Type) bool {
// If the magic method == exists, the type parameter is comparable.
if t := typ.Interface(); t != nil {
_, m := lookupMethod(t.allMethods, nil, "==")
return m != nil
}
return false
}
// Comparable reports whether values of type T are comparable.
func Comparable(T Type) bool {
// If T is a type parameter not constraint by any type
@ -93,9 +104,7 @@ func Comparable(T Type) bool {
//
// is not comparable because []byte is not comparable.
if t := T.TypeParam(); t != nil && optype(t) == theTop {
// If the magic method == exists, the type parameter is comparable.
_, m := lookupMethod(t.Bound().allMethods, nil, "==")
return m != nil
return isComparableInterface(t.Bound())
}
switch t := optype(T.Under()).(type) {
@ -117,9 +126,7 @@ func Comparable(T Type) bool {
case *Sum:
return t.is(Comparable)
case *TypeParam:
// If the magic method == exists, the type parameter is comparable.
_, m := lookupMethod(t.Bound().allMethods, nil, "==")
return m != nil
return isComparableInterface(t.Bound())
}
return false
}

View File

@ -136,6 +136,10 @@ func (check *Checker) varType(e ast.Expr) Type {
check.completeInterface(e.Pos(), t) // TODO(gri) is this the correct position?
if t.allTypes != nil {
check.softErrorf(e.Pos(), "interface type for variable cannot contain type constraints (%s)", t.allTypes)
return
}
if isComparableInterface(t) {
check.softErrorf(e.Pos(), "interface type for variable cannot be (or embed) comparable")
}
})
}