Ordinary interface types now satisfy comparable constraints.
This change makes the new comparable semantics the default behavior,
depending on the Go -lang version.
It also renames the flag types2.Config.AltComparableSemantics to
types2.Config.OldComparableSemantics and inverts its meaning
(or types.Config.oldComparableSemantics respectively).
Adjust some existing tests by setting -oldComparableSemantics
and add some new tests that verify version-dependent behavior.
The compiler flag -oldcomparable may be used to temporarily
switch back to the Go 1.18/1.19 behavior should this change
cause problems, or to identify that a problem is unrelated
to this change. The flag will be removed for Go 1.21.
For #52509.
For #56548.
Change-Id: Ic2b22db9433a8dd81dc1ed9d30835f0395fb7205
Reviewed-on: https://go-review.googlesource.com/c/go/+/453978
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Robert Griesemer <gri@google.com>
SymABIs string "help:\"read symbol ABIs from `file`\""
TraceProfile string "help:\"write an execution trace to `file`\""
TrimPath string "help:\"remove `prefix` from recorded source file paths\""
- WB bool "help:\"enable write barrier\"" // TODO: remove
- AltComparable bool "help:\"enable alternative comparable semantics\"" // experiment - remove eventually
+ WB bool "help:\"enable write barrier\"" // TODO: remove
+ OldComparable bool "help:\"enable old comparable semantics\"" // TODO: remove for Go 1.21
PgoProfile string "help:\"read profile from `file`\""
// Configuration derived from flags; not a flag itself.
},
Importer: &importer,
Sizes: &gcSizes{},
- AltComparableSemantics: base.Flag.AltComparable, // experiment - remove eventually
+ OldComparableSemantics: base.Flag.OldComparable, // default is new comparable semantics
}
info := &types2.Info{
StoreTypesInSyntax: true,
// for unused imports.
DisableUnusedImportCheck bool
- // If AltComparableSemantics is set, ordinary (non-type parameter)
- // interfaces satisfy the comparable constraint.
- AltComparableSemantics bool
+ // If OldComparableSemantics is set, ordinary (non-type parameter)
+ // interfaces do not satisfy the comparable constraint.
+ // TODO(gri) remove this flag for Go 1.21
+ OldComparableSemantics bool
}
func srcimporter_setUsesCgo(conf *Config) {
flags := flag.NewFlagSet("", flag.PanicOnError)
flags.StringVar(&conf.GoVersion, "lang", "", "")
flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "")
- flags.BoolVar(&conf.AltComparableSemantics, "altComparableSemantics", false, "")
+ flags.BoolVar(&conf.OldComparableSemantics, "oldComparableSemantics", false, "")
if err := parseFlags(filenames[0], nil, flags); err != nil {
t.Fatal(err)
}
// Only check comparability if we don't have a more specific error.
checkComparability := func() bool {
+ if !Ti.IsComparable() {
+ return true
+ }
// If T is comparable, V must be comparable.
- // 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 V is strictly comparable, we're done.
+ if comparable(V, false /* strict comparability */, nil, nil) {
+ return true
+ }
+ // If check.conf.OldComparableSemantics is set (by the compiler or
+ // a test), we only consider strict comparability and we're done.
+ // TODO(gri) remove this check for Go 1.21
+ if check != nil && check.conf.OldComparableSemantics {
if cause != nil {
*cause = check.sprintf("%s does not implement comparable", V)
}
return false
}
- return true
+ // For constraint satisfaction, use dynamic (spec) comparability
+ // so that ordinary, non-type parameter interfaces implement comparable.
+ if constraint && comparable(V, true /* spec comparability */, nil, nil) {
+ // V is comparable if we are at Go 1.20 or higher.
+ if check == nil || check.allowVersion(check.pkg, 1, 20) {
+ return true
+ }
+ if cause != nil {
+ *cause = check.sprintf("%s to implement comparable requires go1.20 or later", V)
+ }
+ return false
+ }
+ if cause != nil {
+ *cause = check.sprintf("%s does not implement comparable", V)
+ }
+ return false
}
// V must also be in the set of types of T, if any.
// for unused imports.
DisableUnusedImportCheck bool
- // If altComparableSemantics is set, ordinary (non-type parameter)
- // interfaces satisfy the comparable constraint.
- altComparableSemantics bool
+ // If oldComparableSemantics is set, ordinary (non-type parameter)
+ // interfaces do not satisfy the comparable constraint.
+ // TODO(gri) remove this flag for Go 1.21
+ oldComparableSemantics bool
}
func srcimporter_setUsesCgo(conf *Config) {
flags := flag.NewFlagSet("", flag.PanicOnError)
flags.StringVar(&conf.GoVersion, "lang", "", "")
flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "")
- flags.BoolVar(addrAltComparableSemantics(&conf), "altComparableSemantics", false, "")
+ flags.BoolVar(addrOldComparableSemantics(&conf), "oldComparableSemantics", 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 {
+// addrOldComparableSemantics(conf) returns &conf.oldComparableSemantics (unexported field).
+func addrOldComparableSemantics(conf *Config) *bool {
v := reflect.Indirect(reflect.ValueOf(conf))
- return (*bool)(v.FieldByName("altComparableSemantics").Addr().UnsafePointer())
+ return (*bool)(v.FieldByName("oldComparableSemantics").Addr().UnsafePointer())
}
// TestManual is for manual testing of a package - either provided
// Only check comparability if we don't have a more specific error.
checkComparability := func() bool {
+ if !Ti.IsComparable() {
+ return true
+ }
// If T is comparable, V must be comparable.
- // 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 V is strictly comparable, we're done.
+ if comparable(V, false /* strict comparability */, nil, nil) {
+ return true
+ }
+ // If check.conf.OldComparableSemantics is set (by the compiler or
+ // a test), we only consider strict comparability and we're done.
+ // TODO(gri) remove this check for Go 1.21
+ if check != nil && check.conf.oldComparableSemantics {
if cause != nil {
*cause = check.sprintf("%s does not implement comparable", V)
}
return false
}
- return true
+ // For constraint satisfaction, use dynamic (spec) comparability
+ // so that ordinary, non-type parameter interfaces implement comparable.
+ if constraint && comparable(V, true /* spec comparability */, nil, nil) {
+ // V is comparable if we are at Go 1.20 or higher.
+ if check == nil || check.allowVersion(check.pkg, 1, 20) {
+ return true
+ }
+ if cause != nil {
+ *cause = check.sprintf("%s to implement comparable requires go1.20 or later", V)
+ }
+ return false
+ }
+ if cause != nil {
+ *cause = check.sprintf("%s does not implement comparable", V)
+ }
+ return false
}
// V must also be in the set of types of T, if any.
+// -oldComparableSemantics
+
// Copyright 2020 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.
+// -oldComparableSemantics
+
// 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.
+// -oldComparableSemantics
+
// 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.
-// -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.
--- /dev/null
+// -lang=go1.19
+
+// 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 /* ERROR T to implement comparable requires go1\.20 or later */]
+ _ = f1[any /* ERROR any to implement comparable requires go1\.20 or later */]
+ _ = f1[P]
+ _ = f1[Q]
+ _ = f1[R /* ERROR R does not implement comparable */]
+
+ _ = f2[int]
+ _ = f2[T /* ERROR T to implement comparable requires go1\.20 or later */]
+ _ = f2[any /* ERROR any to implement comparable requires go1\.20 or later */]
+ _ = f2[P]
+ _ = f2[Q]
+ _ = f2[R /* ERROR R does not implement comparable */]
+}
--- /dev/null
+// -oldComparableSemantics
+
+// 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 /* 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 /* 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 */]
+}