mirror of
https://github.com/golang/go
synced 2024-11-23 16:20:04 -07:00
go/types, types2: add "dynamic" flag to comparable predicate
A type implements a comparable interface only if the type is statically known to be comparable. Specifically, a type cannot contain (component) interfaces that are not statically known to be comparable. This CL adds a flag "dynamic" to the comparable predicate to control whether interfaces are always (dynamically) comparable. Set the flag to true when testing for (traditional) Go comparability; set the flag to false when testing whether a type implements the comparable interface. Fixes #51257. Change-Id: If22bc047ee59337deb2e7844b8f488d67e5c5530 Reviewed-on: https://go-review.googlesource.com/c/go/+/387055 Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
e534907f65
commit
163da6feb5
@ -899,7 +899,7 @@ func (check *Checker) incomparableCause(typ Type) string {
|
||||
}
|
||||
// see if we can extract a more specific error
|
||||
var cause string
|
||||
comparable(typ, nil, func(format string, args ...interface{}) {
|
||||
comparable(typ, true, nil, func(format string, args ...interface{}) {
|
||||
cause = check.sprintf(format, args...)
|
||||
})
|
||||
return cause
|
||||
|
@ -204,7 +204,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() && ((Vi != nil && !Vi.IsComparable()) || (Vi == nil && !Comparable(V))) {
|
||||
if Ti.IsComparable() && !comparable(V, false, nil, nil) {
|
||||
pending = errorf("%s does not implement comparable", V)
|
||||
}
|
||||
|
||||
|
@ -102,11 +102,12 @@ func isGeneric(t Type) bool {
|
||||
|
||||
// Comparable reports whether values of type T are comparable.
|
||||
func Comparable(T Type) bool {
|
||||
return comparable(T, nil, nil)
|
||||
return comparable(T, true, nil, nil)
|
||||
}
|
||||
|
||||
// If dynamic is set, non-type parameter interfaces are always comparable.
|
||||
// If reportf != nil, it may be used to report why T is not comparable.
|
||||
func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})) bool {
|
||||
func comparable(T Type, dynamic bool, seen map[Type]bool, reportf func(string, ...interface{})) bool {
|
||||
if seen[T] {
|
||||
return true
|
||||
}
|
||||
@ -124,7 +125,7 @@ func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})
|
||||
return true
|
||||
case *Struct:
|
||||
for _, f := range t.fields {
|
||||
if !comparable(f.typ, seen, nil) {
|
||||
if !comparable(f.typ, dynamic, seen, nil) {
|
||||
if reportf != nil {
|
||||
reportf("struct containing %s cannot be compared", f.typ)
|
||||
}
|
||||
@ -133,7 +134,7 @@ func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})
|
||||
}
|
||||
return true
|
||||
case *Array:
|
||||
if !comparable(t.elem, seen, nil) {
|
||||
if !comparable(t.elem, dynamic, seen, nil) {
|
||||
if reportf != nil {
|
||||
reportf("%s cannot be compared", t)
|
||||
}
|
||||
@ -141,7 +142,7 @@ func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})
|
||||
}
|
||||
return true
|
||||
case *Interface:
|
||||
return !isTypeParam(T) || t.typeSet().IsComparable(seen)
|
||||
return dynamic && !isTypeParam(T) || t.typeSet().IsComparable(seen)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
46
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51257.go2
vendored
Normal file
46
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51257.go2
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
// 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 p
|
||||
|
||||
func f[_ comparable]() {}
|
||||
|
||||
type S1 struct{ x int }
|
||||
type S2 struct{ x any }
|
||||
type S3 struct{ x [10]interface{ m() } }
|
||||
|
||||
func _[P1 comparable, P2 S2]() {
|
||||
_ = f[S1]
|
||||
_ = f[S2 /* ERROR S2 does not implement comparable */ ]
|
||||
_ = f[S3 /* ERROR S3 does not implement comparable */ ]
|
||||
|
||||
type L1 struct { x P1 }
|
||||
type L2 struct { x P2 }
|
||||
_ = f[L1]
|
||||
_ = f[L2 /* ERROR L2 does not implement comparable */ ]
|
||||
}
|
||||
|
||||
|
||||
// example from issue
|
||||
|
||||
type Set[T comparable] map[T]struct{}
|
||||
|
||||
func NewSetFromSlice[T comparable](items []T) *Set[T] {
|
||||
s := Set[T]{}
|
||||
|
||||
for _, item := range items {
|
||||
s[item] = struct{}{}
|
||||
}
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
type T struct{ x any }
|
||||
|
||||
func main() {
|
||||
NewSetFromSlice( /* ERROR T does not implement comparable */ []T{
|
||||
{"foo"},
|
||||
{5},
|
||||
})
|
||||
}
|
@ -39,7 +39,7 @@ func (s *_TypeSet) IsComparable(seen map[Type]bool) bool {
|
||||
return s.comparable
|
||||
}
|
||||
return s.is(func(t *term) bool {
|
||||
return t != nil && comparable(t.typ, seen, nil)
|
||||
return t != nil && comparable(t.typ, false, seen, nil)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -859,7 +859,7 @@ func (check *Checker) incomparableCause(typ Type) string {
|
||||
}
|
||||
// see if we can extract a more specific error
|
||||
var cause string
|
||||
comparable(typ, nil, func(format string, args ...interface{}) {
|
||||
comparable(typ, true, nil, func(format string, args ...interface{}) {
|
||||
cause = check.sprintf(format, args...)
|
||||
})
|
||||
return cause
|
||||
|
@ -204,7 +204,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() && ((Vi != nil && !Vi.IsComparable()) || (Vi == nil && !Comparable(V))) {
|
||||
if Ti.IsComparable() && !comparable(V, false, nil, nil) {
|
||||
pending = errorf("%s does not implement comparable", V)
|
||||
}
|
||||
|
||||
|
@ -104,11 +104,12 @@ func isGeneric(t Type) bool {
|
||||
|
||||
// Comparable reports whether values of type T are comparable.
|
||||
func Comparable(T Type) bool {
|
||||
return comparable(T, nil, nil)
|
||||
return comparable(T, true, nil, nil)
|
||||
}
|
||||
|
||||
// If dynamic is set, non-type parameter interfaces are always comparable.
|
||||
// If reportf != nil, it may be used to report why T is not comparable.
|
||||
func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})) bool {
|
||||
func comparable(T Type, dynamic bool, seen map[Type]bool, reportf func(string, ...interface{})) bool {
|
||||
if seen[T] {
|
||||
return true
|
||||
}
|
||||
@ -126,7 +127,7 @@ func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})
|
||||
return true
|
||||
case *Struct:
|
||||
for _, f := range t.fields {
|
||||
if !comparable(f.typ, seen, nil) {
|
||||
if !comparable(f.typ, dynamic, seen, nil) {
|
||||
if reportf != nil {
|
||||
reportf("struct containing %s cannot be compared", f.typ)
|
||||
}
|
||||
@ -135,7 +136,7 @@ func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})
|
||||
}
|
||||
return true
|
||||
case *Array:
|
||||
if !comparable(t.elem, seen, nil) {
|
||||
if !comparable(t.elem, dynamic, seen, nil) {
|
||||
if reportf != nil {
|
||||
reportf("%s cannot be compared", t)
|
||||
}
|
||||
@ -143,7 +144,7 @@ func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})
|
||||
}
|
||||
return true
|
||||
case *Interface:
|
||||
return !isTypeParam(T) || t.typeSet().IsComparable(seen)
|
||||
return dynamic && !isTypeParam(T) || t.typeSet().IsComparable(seen)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
46
src/go/types/testdata/fixedbugs/issue51257.go2
vendored
Normal file
46
src/go/types/testdata/fixedbugs/issue51257.go2
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
// 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 p
|
||||
|
||||
func f[_ comparable]() {}
|
||||
|
||||
type S1 struct{ x int }
|
||||
type S2 struct{ x any }
|
||||
type S3 struct{ x [10]interface{ m() } }
|
||||
|
||||
func _[P1 comparable, P2 S2]() {
|
||||
_ = f[S1]
|
||||
_ = f[S2 /* ERROR S2 does not implement comparable */ ]
|
||||
_ = f[S3 /* ERROR S3 does not implement comparable */ ]
|
||||
|
||||
type L1 struct { x P1 }
|
||||
type L2 struct { x P2 }
|
||||
_ = f[L1]
|
||||
_ = f[L2 /* ERROR L2 does not implement comparable */ ]
|
||||
}
|
||||
|
||||
|
||||
// example from issue
|
||||
|
||||
type Set[T comparable] map[T]struct{}
|
||||
|
||||
func NewSetFromSlice[T comparable](items []T) *Set[T] {
|
||||
s := Set[T]{}
|
||||
|
||||
for _, item := range items {
|
||||
s[item] = struct{}{}
|
||||
}
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
type T struct{ x any }
|
||||
|
||||
func main() {
|
||||
NewSetFromSlice /* ERROR T does not implement comparable */ ([]T{
|
||||
{"foo"},
|
||||
{5},
|
||||
})
|
||||
}
|
@ -37,7 +37,7 @@ func (s *_TypeSet) IsComparable(seen map[Type]bool) bool {
|
||||
return s.comparable
|
||||
}
|
||||
return s.is(func(t *term) bool {
|
||||
return t != nil && comparable(t.typ, seen, nil)
|
||||
return t != nil && comparable(t.typ, false, seen, nil)
|
||||
})
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user