]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: better error message for invalid == on type parameters
authorRobert Griesemer <gri@golang.org>
Thu, 16 Dec 2021 05:25:50 +0000 (21:25 -0800)
committerRobert Griesemer <gri@golang.org>
Sun, 9 Jan 2022 18:43:51 +0000 (18:43 +0000)
Fixes #48712.

Change-Id: I6f214cdfdd1815493f2a04828e8f0097f1d8c124
Reviewed-on: https://go-review.googlesource.com/c/go/+/372734
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>
src/cmd/compile/internal/types2/expr.go
src/cmd/compile/internal/types2/testdata/fixedbugs/issue48712.go2 [new file with mode: 0644]
src/go/types/expr.go
src/go/types/testdata/fixedbugs/issue48712.go2 [new file with mode: 0644]

index 3e3104abb65fb06a056ffe2d79cca0b993998e6e..0147e2adfd8db5edf7ef1cbbc9170e84c6851c01 100644 (file)
@@ -770,10 +770,12 @@ func (check *Checker) comparison(x, y *operand, op syntax.Operator) {
        xok, _ := x.assignableTo(check, y.typ, nil)
        yok, _ := y.assignableTo(check, x.typ, nil)
        if xok || yok {
+               equality := false
                defined := false
                switch op {
                case syntax.Eql, syntax.Neq:
                        // spec: "The equality operators == and != apply to operands that are comparable."
+                       equality = true
                        defined = Comparable(x.typ) && Comparable(y.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ)
                case syntax.Lss, syntax.Leq, syntax.Gtr, syntax.Geq:
                        // spec: The ordering operators <, <=, >, and >= apply to operands that are ordered."
@@ -782,11 +784,19 @@ func (check *Checker) comparison(x, y *operand, op syntax.Operator) {
                        unreachable()
                }
                if !defined {
-                       typ := x.typ
-                       if x.isNil() {
-                               typ = y.typ
+                       if equality && (isTypeParam(x.typ) || isTypeParam(y.typ)) {
+                               typ := x.typ
+                               if isTypeParam(y.typ) {
+                                       typ = y.typ
+                               }
+                               err = check.sprintf("%s is not comparable", typ)
+                       } else {
+                               typ := x.typ
+                               if x.isNil() {
+                                       typ = y.typ
+                               }
+                               err = check.sprintf("operator %s not defined on %s", op, typ)
                        }
-                       err = check.sprintf("operator %s not defined on %s", op, typ)
                }
        } else {
                err = check.sprintf("mismatched types %s and %s", x.typ, y.typ)
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48712.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48712.go2
new file mode 100644 (file)
index 0000000..bad8712
--- /dev/null
@@ -0,0 +1,41 @@
+// 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 _[P comparable](x, y P) {
+       _ = x == x
+       _ = x == y
+       _ = y == x
+       _ = y == y
+
+       _ = x /* ERROR operator < not defined on P */ < y
+}
+
+func _[P comparable](x P, y any) {
+       _ = x == x
+       _ = x == y
+       _ = y == x
+       _ = y == y
+
+       _ = x /* ERROR operator < not defined on P */ < y
+}
+
+func _[P any](x, y P) {
+       _ = x /* ERROR P is not comparable */ == x
+       _ = x /* ERROR P is not comparable */ == y
+       _ = y /* ERROR P is not comparable */ == x
+       _ = y /* ERROR P is not comparable */ == y
+
+       _ = x /* ERROR operator < not defined on P */ < y
+}
+
+func _[P any](x P, y any) {
+       _ = x /* ERROR P is not comparable */ == x
+       _ = x /* ERROR P is not comparable */ == y
+       _ = y /* ERROR P is not comparable */ == x
+       _ = y == y
+
+       _ = x /* ERROR operator < not defined on P */ < y
+}
index 8ddfb8de7e1b0caf1ae5ea57da5f45401af3ba64..73b01f4aa44d5d3560d9a241d00a5273da1a7ffa 100644 (file)
@@ -729,10 +729,12 @@ func (check *Checker) comparison(x, y *operand, op token.Token) {
        xok, _ := x.assignableTo(check, y.typ, nil)
        yok, _ := y.assignableTo(check, x.typ, nil)
        if xok || yok {
+               equality := false
                defined := false
                switch op {
                case token.EQL, token.NEQ:
                        // spec: "The equality operators == and != apply to operands that are comparable."
+                       equality = true
                        defined = Comparable(x.typ) && Comparable(y.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ)
                case token.LSS, token.LEQ, token.GTR, token.GEQ:
                        // spec: The ordering operators <, <=, >, and >= apply to operands that are ordered."
@@ -741,11 +743,19 @@ func (check *Checker) comparison(x, y *operand, op token.Token) {
                        unreachable()
                }
                if !defined {
-                       typ := x.typ
-                       if x.isNil() {
-                               typ = y.typ
+                       if equality && (isTypeParam(x.typ) || isTypeParam(y.typ)) {
+                               typ := x.typ
+                               if isTypeParam(y.typ) {
+                                       typ = y.typ
+                               }
+                               err = check.sprintf("%s is not comparable", typ)
+                       } else {
+                               typ := x.typ
+                               if x.isNil() {
+                                       typ = y.typ
+                               }
+                               err = check.sprintf("operator %s not defined on %s", op, typ)
                        }
-                       err = check.sprintf("operator %s not defined on %s", op, typ)
                        code = _UndefinedOp
                }
        } else {
diff --git a/src/go/types/testdata/fixedbugs/issue48712.go2 b/src/go/types/testdata/fixedbugs/issue48712.go2
new file mode 100644 (file)
index 0000000..bad8712
--- /dev/null
@@ -0,0 +1,41 @@
+// 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 _[P comparable](x, y P) {
+       _ = x == x
+       _ = x == y
+       _ = y == x
+       _ = y == y
+
+       _ = x /* ERROR operator < not defined on P */ < y
+}
+
+func _[P comparable](x P, y any) {
+       _ = x == x
+       _ = x == y
+       _ = y == x
+       _ = y == y
+
+       _ = x /* ERROR operator < not defined on P */ < y
+}
+
+func _[P any](x, y P) {
+       _ = x /* ERROR P is not comparable */ == x
+       _ = x /* ERROR P is not comparable */ == y
+       _ = y /* ERROR P is not comparable */ == x
+       _ = y /* ERROR P is not comparable */ == y
+
+       _ = x /* ERROR operator < not defined on P */ < y
+}
+
+func _[P any](x P, y any) {
+       _ = x /* ERROR P is not comparable */ == x
+       _ = x /* ERROR P is not comparable */ == y
+       _ = y /* ERROR P is not comparable */ == x
+       _ = y == y
+
+       _ = x /* ERROR operator < not defined on P */ < y
+}