// If DisableUnusedImportCheck is set, packages are not checked
// for unused imports.
DisableUnusedImportCheck bool
+
+ // If AltComparableSemantics is set, ordinary (non-type parameter)
+ // interfaces satisfy the comparable constraint.
+ AltComparableSemantics bool
}
func srcimporter_setUsesCgo(conf *Config) {
if V.Underlying() == Typ[Invalid] {
return false
}
- return (*Checker)(nil).implements(V, T, nil)
+ return (*Checker)(nil).implements(V, T, false, nil)
}
// Identical reports whether x and y are identical types.
flags := flag.NewFlagSet("", flag.PanicOnError)
flags.StringVar(&conf.GoVersion, "lang", "", "")
flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "")
+ flags.BoolVar(&conf.AltComparableSemantics, "altComparableSemantics", false, "")
if err := parseFlags(filenames[0], nil, flags); err != nil {
t.Fatal(err)
}
// the parameterized type.
bound := check.subst(pos, tpar.bound, smap, nil, ctxt)
var cause string
- if !check.implements(targs[i], bound, &cause) {
+ if !check.implements(targs[i], bound, true, &cause) {
return i, errors.New(cause)
}
}
}
// implements checks if V implements T. The receiver may be nil if implements
-// is called through an exported API call such as AssignableTo.
+// is called through an exported API call such as AssignableTo. If constraint
+// is set, T is a type constraint.
//
// If the provided cause is non-nil, it may be set to an error string
// explaining why V does not implement T.
-func (check *Checker) implements(V, T Type, cause *string) bool {
+func (check *Checker) implements(V, T Type, constraint bool, cause *string) bool {
Vu := under(V)
Tu := under(T)
if Vu == Typ[Invalid] || Tu == Typ[Invalid] {
// Only check comparability if we don't have a more specific error.
checkComparability := func() bool {
// If T is comparable, V must be comparable.
- if Ti.IsComparable() && !comparable(V, false, nil, nil) {
+ // For constraint satisfaction, use dynamic comparability for the
+ // alternative comparable semantics such that ordinary, non-type
+ // parameter interfaces implement comparable.
+ dynamic := constraint && check != nil && check.conf.AltComparableSemantics
+ if Ti.IsComparable() && !comparable(V, dynamic, nil, nil) {
if cause != nil {
*cause = check.sprintf("%s does not implement comparable", V)
}
if IsInterface(T) {
return true
}
- return check.implements(T, V, nil)
+ return check.implements(T, V, false, nil)
}
// deref dereferences typ if it is a *Pointer and returns its base and true.
// T is an interface type and x implements T and T is not a type parameter.
// Also handle the case where T is a pointer to an interface.
if _, ok := Tu.(*Interface); ok && Tp == nil || isInterfacePtr(Tu) {
- if !check.implements(V, T, cause) {
+ if !check.implements(V, T, false, cause) {
return false, InvalidIfaceAssign
}
return true, 0
// If V is an interface, check if a missing type assertion is the problem.
if Vi, _ := Vu.(*Interface); Vi != nil && Vp == nil {
- if check.implements(T, V, nil) {
+ if check.implements(T, V, false, nil) {
// T implements V, so give hint about type assertion.
if cause != nil {
*cause = "need type assertion"
// If DisableUnusedImportCheck is set, packages are not checked
// for unused imports.
DisableUnusedImportCheck bool
+
+ // If altComparableSemantics is set, ordinary (non-type parameter)
+ // interfaces satisfy the comparable constraint.
+ altComparableSemantics bool
}
func srcimporter_setUsesCgo(conf *Config) {
if V.Underlying() == Typ[Invalid] {
return false
}
- return (*Checker)(nil).implements(V, T, nil)
+ return (*Checker)(nil).implements(V, T, false, nil)
}
// Identical reports whether x and y are identical types.
flags := flag.NewFlagSet("", flag.PanicOnError)
flags.StringVar(&conf.GoVersion, "lang", "", "")
flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "")
+ flags.BoolVar(addrAltComparableSemantics(&conf), "altComparableSemantics", false, "")
if err := parseFlags(filenames[0], srcs[0], flags); err != nil {
t.Fatal(err)
}
return int(v.FieldByName("go116code").Int())
}
+// addrAltComparableSemantics(conf) returns &conf.altComparableSemantics (unexported field).
+func addrAltComparableSemantics(conf *Config) *bool {
+ v := reflect.Indirect(reflect.ValueOf(conf))
+ return (*bool)(v.FieldByName("altComparableSemantics").Addr().UnsafePointer())
+}
+
// TestManual is for manual testing of a package - either provided
// as a list of filenames belonging to the package, or a directory
// name containing the package files - after the test arguments
// the parameterized type.
bound := check.subst(pos, tpar.bound, smap, nil, ctxt)
var cause string
- if !check.implements(targs[i], bound, &cause) {
+ if !check.implements(targs[i], bound, true, &cause) {
return i, errors.New(cause)
}
}
}
// implements checks if V implements T. The receiver may be nil if implements
-// is called through an exported API call such as AssignableTo.
+// is called through an exported API call such as AssignableTo. If constraint
+// is set, T is a type constraint.
//
// If the provided cause is non-nil, it may be set to an error string
// explaining why V does not implement T.
-func (check *Checker) implements(V, T Type, cause *string) bool {
+func (check *Checker) implements(V, T Type, constraint bool, cause *string) bool {
Vu := under(V)
Tu := under(T)
if Vu == Typ[Invalid] || Tu == Typ[Invalid] {
// Only check comparability if we don't have a more specific error.
checkComparability := func() bool {
// If T is comparable, V must be comparable.
- if Ti.IsComparable() && !comparable(V, false, nil, nil) {
+ // For constraint satisfaction, use dynamic comparability for the
+ // alternative comparable semantics such that ordinary, non-type
+ // parameter interfaces implement comparable.
+ dynamic := constraint && check != nil && check.conf.altComparableSemantics
+ if Ti.IsComparable() && !comparable(V, dynamic, nil, nil) {
if cause != nil {
*cause = check.sprintf("%s does not implement comparable", V)
}
if IsInterface(T) {
return true
}
- return check.implements(T, V, nil)
+ return check.implements(T, V, false, nil)
}
// deref dereferences typ if it is a *Pointer and returns its base and true.
// T is an interface type and x implements T and T is not a type parameter.
// Also handle the case where T is a pointer to an interface.
if _, ok := Tu.(*Interface); ok && Tp == nil || isInterfacePtr(Tu) {
- if !check.implements(V, T, cause) {
+ if !check.implements(V, T, false, cause) {
return false, InvalidIfaceAssign
}
return true, 0
// If V is an interface, check if a missing type assertion is the problem.
if Vi, _ := Vu.(*Interface); Vi != nil && Vp == nil {
- if check.implements(T, V, nil) {
+ if check.implements(T, V, false, nil) {
// T implements V, so give hint about type assertion.
if cause != nil {
*cause = "need type assertion"
--- /dev/null
+// -altComparableSemantics
+
+// 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 f1[_ comparable]() {}
+func f2[_ interface{ comparable }]() {}
+
+type T interface{ m() }
+
+func _[P comparable, Q ~int, R any]() {
+ _ = f1[int]
+ _ = f1[T /* T does implement comparable */]
+ _ = f1[any /* any does implement comparable */]
+ _ = f1[P]
+ _ = f1[Q]
+ _ = f1[R /* ERROR R does not implement comparable */]
+
+ _ = f2[int]
+ _ = f2[T /* T does implement comparable */]
+ _ = f2[any /* any does implement comparable */]
+ _ = f2[P]
+ _ = f2[Q]
+ _ = f2[R /* ERROR R does not implement comparable */]
+}