mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
go/types, types2: fix implements and identical predicates
- Use the correct predicate in Checker.implements: for interfaces we cannot use the API Comparable because it always returns true for all non-type parameter interface types: Comparable simply answers if == and != is permitted, and it's always been permitted for interfaces. Instead we must use Interface.IsComparable which looks at the type set of an interface. - When comparing interfaces for identity, we must also consider the whether the type sets have the comparable bit set. With this change, `any` doesn't implement `comparable` anymore. This only matters for generic functions and types, and the API functions. It does mean that for now (until we allow type-constrained interfaces for general non-constraint use, at some point in the future) a type parameter that needs to be comparable cannot be instantiated with an interface anymore. For #50646. Change-Id: I7e7f711bdcf94461f330c90509211ec0c2cf3633 Reviewed-on: https://go-review.googlesource.com/c/go/+/381254 Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
41f485b9a7
commit
360e1b8197
@ -221,7 +221,7 @@ func (check *Checker) implements(V, T Type) error {
|
||||
// If T is comparable, V must be comparable.
|
||||
// Remember as a pending error and report only if we don't have a more specific error.
|
||||
var pending error
|
||||
if Ti.IsComparable() && !Comparable(V) {
|
||||
if Ti.IsComparable() && ((Vi != nil && !Vi.IsComparable()) || (Vi == nil && !Comparable(V))) {
|
||||
pending = errorf("%s does not implement comparable", V)
|
||||
}
|
||||
|
||||
|
@ -623,16 +623,15 @@ func TestIssue50646(t *testing.T) {
|
||||
t.Errorf("comparable is not a comparable type")
|
||||
}
|
||||
|
||||
// TODO(gri) should comparable be an alias, like any? (see #50791)
|
||||
if !Implements(anyType, comparableType.Underlying().(*Interface)) {
|
||||
t.Errorf("any does not implement comparable")
|
||||
if Implements(anyType, comparableType.Underlying().(*Interface)) {
|
||||
t.Errorf("any implements comparable")
|
||||
}
|
||||
if !Implements(comparableType, anyType.(*Interface)) {
|
||||
t.Errorf("comparable does not implement any")
|
||||
}
|
||||
|
||||
if !AssignableTo(anyType, comparableType) {
|
||||
t.Errorf("any not assignable to comparable")
|
||||
if AssignableTo(anyType, comparableType) {
|
||||
t.Errorf("any assignable to comparable")
|
||||
}
|
||||
if !AssignableTo(comparableType, anyType) {
|
||||
t.Errorf("comparable not assignable to any")
|
||||
|
@ -306,6 +306,9 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
|
||||
if y, ok := y.(*Interface); ok {
|
||||
xset := x.typeSet()
|
||||
yset := y.typeSet()
|
||||
if xset.comparable != yset.comparable {
|
||||
return false
|
||||
}
|
||||
if !xset.terms.equal(yset.terms) {
|
||||
return false
|
||||
}
|
||||
|
@ -9,19 +9,18 @@ package p
|
||||
import "io"
|
||||
import "context"
|
||||
|
||||
// Interfaces are always comparable (though the comparison may panic at runtime).
|
||||
func eql[T comparable](x, y T) bool {
|
||||
return x == y
|
||||
}
|
||||
|
||||
func _() {
|
||||
var x interface{}
|
||||
var y interface{ m() }
|
||||
func _[X comparable, Y interface{comparable; m()}]() {
|
||||
var x X
|
||||
var y Y
|
||||
eql(x, y /* ERROR does not match */ ) // interfaces of different types
|
||||
eql(x, x)
|
||||
eql(y, y)
|
||||
eql(y, nil)
|
||||
eql[io.Reader](nil, nil)
|
||||
eql(y, nil /* ERROR cannot use nil as Y value in argument to eql */ )
|
||||
eql[io /* ERROR does not implement comparable */ .Reader](nil, nil)
|
||||
}
|
||||
|
||||
// If we have a receiver of pointer to type parameter type (below: *T)
|
||||
|
@ -4,9 +4,6 @@
|
||||
|
||||
package p
|
||||
|
||||
// Because we can use == and != with values of arbitrary
|
||||
// interfaces, all interfaces implement comparable.
|
||||
|
||||
func f1[_ comparable]() {}
|
||||
func f2[_ interface{ comparable }]() {}
|
||||
|
||||
@ -14,15 +11,15 @@ type T interface{ m() }
|
||||
|
||||
func _[P comparable, Q ~int, R any]() {
|
||||
_ = f1[int]
|
||||
_ = f1[T]
|
||||
_ = f1[any]
|
||||
_ = f1[T /* ERROR T does not implement comparable */ ]
|
||||
_ = f1[any /* ERROR any does not implement comparable */ ]
|
||||
_ = f1[P]
|
||||
_ = f1[Q]
|
||||
_ = f1[R /* ERROR R does not implement comparable */]
|
||||
|
||||
_ = f2[int]
|
||||
_ = f2[T]
|
||||
_ = f2[any]
|
||||
_ = f2[T /* ERROR T does not implement comparable */ ]
|
||||
_ = f2[any /* ERROR any does not implement comparable */ ]
|
||||
_ = f2[P]
|
||||
_ = f2[Q]
|
||||
_ = f2[R /* ERROR R does not implement comparable */]
|
||||
|
@ -387,6 +387,9 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
|
||||
if y, ok := y.(*Interface); ok {
|
||||
xset := x.typeSet()
|
||||
yset := y.typeSet()
|
||||
if xset.comparable != yset.comparable {
|
||||
return false
|
||||
}
|
||||
if !xset.terms.equal(yset.terms) {
|
||||
return false
|
||||
}
|
||||
|
@ -225,7 +225,7 @@ func (check *Checker) implements(V, T Type) error {
|
||||
// If T is comparable, V must be comparable.
|
||||
// Remember as a pending error and report only if we don't have a more specific error.
|
||||
var pending error
|
||||
if Ti.IsComparable() && !Comparable(V) {
|
||||
if Ti.IsComparable() && ((Vi != nil && !Vi.IsComparable()) || (Vi == nil && !Comparable(V))) {
|
||||
pending = errorf("%s does not implement comparable", V)
|
||||
}
|
||||
|
||||
|
@ -650,16 +650,15 @@ func TestIssue50646(t *testing.T) {
|
||||
t.Errorf("comparable is not a comparable type")
|
||||
}
|
||||
|
||||
// TODO(gri) should comparable be an alias, like any? (see #50791)
|
||||
if !Implements(anyType, comparableType.Underlying().(*Interface)) {
|
||||
t.Errorf("any does not implement comparable")
|
||||
if Implements(anyType, comparableType.Underlying().(*Interface)) {
|
||||
t.Errorf("any implements comparable")
|
||||
}
|
||||
if !Implements(comparableType, anyType.(*Interface)) {
|
||||
t.Errorf("comparable does not implement any")
|
||||
}
|
||||
|
||||
if !AssignableTo(anyType, comparableType) {
|
||||
t.Errorf("any not assignable to comparable")
|
||||
if AssignableTo(anyType, comparableType) {
|
||||
t.Errorf("any assignable to comparable")
|
||||
}
|
||||
if !AssignableTo(comparableType, anyType) {
|
||||
t.Errorf("comparable not assignable to any")
|
||||
|
@ -308,6 +308,9 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
|
||||
if y, ok := y.(*Interface); ok {
|
||||
xset := x.typeSet()
|
||||
yset := y.typeSet()
|
||||
if xset.comparable != yset.comparable {
|
||||
return false
|
||||
}
|
||||
if !xset.terms.equal(yset.terms) {
|
||||
return false
|
||||
}
|
||||
|
11
src/go/types/testdata/check/issues.go2
vendored
11
src/go/types/testdata/check/issues.go2
vendored
@ -9,19 +9,18 @@ package p
|
||||
import "io"
|
||||
import "context"
|
||||
|
||||
// Interfaces are always comparable (though the comparison may panic at runtime).
|
||||
func eql[T comparable](x, y T) bool {
|
||||
return x == y
|
||||
}
|
||||
|
||||
func _() {
|
||||
var x interface{}
|
||||
var y interface{ m() }
|
||||
func _[X comparable, Y interface{comparable; m()}]() {
|
||||
var x X
|
||||
var y Y
|
||||
eql(x, y /* ERROR does not match */ ) // interfaces of different types
|
||||
eql(x, x)
|
||||
eql(y, y)
|
||||
eql(y, nil)
|
||||
eql[io.Reader](nil, nil)
|
||||
eql(y, nil /* ERROR cannot use nil as Y value in argument to eql */ )
|
||||
eql[io /* ERROR does not implement comparable */ .Reader](nil, nil)
|
||||
}
|
||||
|
||||
// If we have a receiver of pointer to type parameter type (below: *T)
|
||||
|
11
src/go/types/testdata/fixedbugs/issue50646.go2
vendored
11
src/go/types/testdata/fixedbugs/issue50646.go2
vendored
@ -4,9 +4,6 @@
|
||||
|
||||
package p
|
||||
|
||||
// Because we can use == and != with values of arbitrary
|
||||
// interfaces, all interfaces implement comparable.
|
||||
|
||||
func f1[_ comparable]() {}
|
||||
func f2[_ interface{ comparable }]() {}
|
||||
|
||||
@ -14,15 +11,15 @@ type T interface{ m() }
|
||||
|
||||
func _[P comparable, Q ~int, R any]() {
|
||||
_ = f1[int]
|
||||
_ = f1[T]
|
||||
_ = f1[any]
|
||||
_ = f1[T /* ERROR T does not implement comparable */ ]
|
||||
_ = f1[any /* ERROR any does not implement comparable */ ]
|
||||
_ = f1[P]
|
||||
_ = f1[Q]
|
||||
_ = f1[R /* ERROR R does not implement comparable */]
|
||||
|
||||
_ = f2[int]
|
||||
_ = f2[T]
|
||||
_ = f2[any]
|
||||
_ = f2[T /* ERROR T does not implement comparable */ ]
|
||||
_ = f2[any /* ERROR any does not implement comparable */ ]
|
||||
_ = f2[P]
|
||||
_ = f2[Q]
|
||||
_ = f2[R /* ERROR R does not implement comparable */]
|
||||
|
@ -387,6 +387,9 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
|
||||
if y, ok := y.(*Interface); ok {
|
||||
xset := x.typeSet()
|
||||
yset := y.typeSet()
|
||||
if xset.comparable != yset.comparable {
|
||||
return false
|
||||
}
|
||||
if !xset.terms.equal(yset.terms) {
|
||||
return false
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ package main
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
IsZero[interface{}]("")
|
||||
IsZero[int](0)
|
||||
}
|
||||
|
||||
func IsZero[T comparable](val T) bool {
|
||||
|
@ -1 +1 @@
|
||||
<nil>:
|
||||
0:0
|
||||
|
@ -1,39 +0,0 @@
|
||||
// run -gcflags=-G=3
|
||||
|
||||
// Copyright 2022 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 main
|
||||
|
||||
func eql[P comparable](x, y P) {
|
||||
if x != y {
|
||||
panic("not equal")
|
||||
}
|
||||
}
|
||||
|
||||
func expectPanic(f func()) {
|
||||
defer func() {
|
||||
if recover() == nil {
|
||||
panic("function succeeded unexpectedly")
|
||||
}
|
||||
}()
|
||||
f()
|
||||
}
|
||||
|
||||
func main() {
|
||||
eql[int](1, 1)
|
||||
eql(1, 1)
|
||||
|
||||
// all interfaces implement comparable
|
||||
var x, y any = 2, 2
|
||||
eql[any](x, y)
|
||||
eql(x, y)
|
||||
|
||||
// but we may get runtime panics
|
||||
x, y = 1, 2 // x != y
|
||||
expectPanic(func() { eql(x, y) })
|
||||
|
||||
x, y = main, main // functions are not comparable
|
||||
expectPanic(func() { eql(x, y) })
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user