go/types, types2: better error when trying to use ~ as bitwise operation

When coming from C, the bitwise integer complement (bitwise negation)
operator is ~, but in Go it is ^. Report an error mentioning ^ when
~ is used with an integer operand.

Background: Some articles on the web claim that Go doesn't have a
bitwise complement operator.

Change-Id: I41185cae4a70d528754e44f42c13c013ed91bf27
Reviewed-on: https://go-review.googlesource.com/c/go/+/463747
Auto-Submit: Robert Griesemer <gri@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go
index 0433f8a..472e30a 100644
--- a/src/cmd/compile/internal/types2/expr.go
+++ b/src/cmd/compile/internal/types2/expr.go
@@ -178,7 +178,8 @@
 		return
 	}
 
-	switch e.Op {
+	op := e.Op
+	switch op {
 	case syntax.And:
 		// spec: "As an exception to the addressability
 		// requirement x may also be a composite literal."
@@ -215,13 +216,17 @@
 		return
 
 	case syntax.Tilde:
-		// Provide a better error position and message than what check.op below could do.
-		check.error(e, UndefinedOp, "cannot use ~ outside of interface or type constraint")
-		x.mode = invalid
-		return
+		// Provide a better error position and message than what check.op below would do.
+		if !allInteger(x.typ) {
+			check.error(e, UndefinedOp, "cannot use ~ outside of interface or type constraint")
+			x.mode = invalid
+			return
+		}
+		check.error(e, UndefinedOp, "cannot use ~ outside of interface or type constraint (use ^ for bitwise complement)")
+		op = syntax.Xor
 	}
 
-	if !check.op(unaryOpPredicates, x, e.Op) {
+	if !check.op(unaryOpPredicates, x, op) {
 		x.mode = invalid
 		return
 	}
@@ -235,7 +240,7 @@
 		if isUnsigned(x.typ) {
 			prec = uint(check.conf.sizeof(x.typ) * 8)
 		}
-		x.val = constant.UnaryOp(op2tok[e.Op], x.val, prec)
+		x.val = constant.UnaryOp(op2tok[op], x.val, prec)
 		x.expr = e
 		check.overflow(x)
 		return
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
index b85a2c7..f09a29b 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -164,7 +164,9 @@
 	if x.mode == invalid {
 		return
 	}
-	switch e.Op {
+
+	op := e.Op
+	switch op {
 	case token.AND:
 		// spec: "As an exception to the addressability
 		// requirement x may also be a composite literal."
@@ -202,13 +204,17 @@
 		return
 
 	case token.TILDE:
-		// Provide a better error position and message than what check.op below could do.
-		check.error(e, UndefinedOp, "cannot use ~ outside of interface or type constraint")
-		x.mode = invalid
-		return
+		// Provide a better error position and message than what check.op below would do.
+		if !allInteger(x.typ) {
+			check.error(e, UndefinedOp, "cannot use ~ outside of interface or type constraint")
+			x.mode = invalid
+			return
+		}
+		check.error(e, UndefinedOp, "cannot use ~ outside of interface or type constraint (use ^ for bitwise complement)")
+		op = token.XOR
 	}
 
-	if !check.op(unaryOpPredicates, x, e.Op) {
+	if !check.op(unaryOpPredicates, x, op) {
 		x.mode = invalid
 		return
 	}
@@ -222,7 +228,7 @@
 		if isUnsigned(x.typ) {
 			prec = uint(check.conf.sizeof(x.typ) * 8)
 		}
-		x.val = constant.UnaryOp(e.Op, x.val, prec)
+		x.val = constant.UnaryOp(op, x.val, prec)
 		x.expr = e
 		check.overflow(x, x.Pos())
 		return
diff --git a/src/internal/types/testdata/check/expr0.go b/src/internal/types/testdata/check/expr0.go
index 552bd8f..eba991e 100644
--- a/src/internal/types/testdata/check/expr0.go
+++ b/src/internal/types/testdata/check/expr0.go
@@ -24,12 +24,14 @@
 	b11 = &b0
 	b12 = <-b0 /* ERROR "cannot receive" */
 	b13 = & & /* ERROR "cannot take address" */ b0
+	b14 = ~ /* ERROR "cannot use ~ outside of interface or type constraint" */ b0
 
 	// byte
 	_ = byte(0)
 	_ = byte(- /* ERROR "cannot convert" */ 1)
 	_ = - /* ERROR "-byte(1) (constant -1 of type byte) overflows byte" */ byte(1) // test for issue 11367
 	_ = byte /* ERROR "overflows byte" */ (0) - byte(1)
+	_ = ~ /* ERROR "cannot use ~ outside of interface or type constraint (use ^ for bitwise complement)" */ byte(0)
 
 	// int
 	i0 = 1
@@ -51,6 +53,7 @@
 	i16 = &i0
 	i17 = *i16
 	i18 = <-i16 /* ERROR "cannot receive" */
+	i19 = ~ /* ERROR "cannot use ~ outside of interface or type constraint (use ^ for bitwise complement)" */ i0
 
 	// uint
 	u0 = uint(1)
@@ -73,6 +76,7 @@
 	u17 = *u16
 	u18 = <-u16 /* ERROR "cannot receive" */
 	u19 = ^uint(0)
+	u20 = ~ /* ERROR "cannot use ~ outside of interface or type constraint (use ^ for bitwise complement)" */ u0
 
 	// float64
 	f0 = float64(1)
@@ -94,6 +98,7 @@
 	f16 = &f0
 	f17 = *u16
 	f18 = <-u16 /* ERROR "cannot receive" */
+	f19 = ~ /* ERROR "cannot use ~ outside of interface or type constraint" */ f0
 
 	// complex128
 	c0 = complex128(1)
@@ -115,6 +120,7 @@
 	c16 = &c0
 	c17 = *u16
 	c18 = <-u16 /* ERROR "cannot receive" */
+	c19 = ~ /* ERROR "cannot use ~ outside of interface or type constraint" */ c0
 
 	// string
 	s0 = "foo"
@@ -126,6 +132,7 @@
 	s6 = &s4
 	s7 = *s6
 	s8 = <-s7
+	s9 = ~ /* ERROR "cannot use ~ outside of interface or type constraint" */ s0
 
 	// channel
 	ch chan int
@@ -145,6 +152,8 @@
 	// ok is of type bool
 	ch11, myok = <-ch
 	_ mybool = myok /* ERRORx `cannot use .* in variable declaration` */
+	ch12 = ~ /* ERROR "cannot use ~ outside of interface or type constraint" */ ch
+
 )
 
 // address of composite literals