reflect: add parentheses to properly bind <- in ChanOf’s string

Adds parentheses so as to properly bind <- to the right most
channel.
This meant that previously given:

   ChanOf(<-chan T)

it would mistakenly try to look up the type as

    chan <-chan T

instead of

    chan (<-chan T)

Fixes #39897

Change-Id: I8564916055f5fadde3382e41fe8820a1071e5f13
GitHub-Last-Rev: f8f2abe8d4c9e3d1414c89cadca8a16ce5cdeab9
GitHub-Pull-Request: golang/go#39898
Reviewed-on: https://go-review.googlesource.com/c/go/+/240280
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 63f6a92..6b31568 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -74,6 +74,10 @@
 	{struct{ x ([]int8) }{}, "[]int8"},
 	{struct{ x (map[string]int32) }{}, "map[string]int32"},
 	{struct{ x (chan<- string) }{}, "chan<- string"},
+	{struct{ x (chan<- chan string) }{}, "chan<- chan string"},
+	{struct{ x (chan<- <-chan string) }{}, "chan<- <-chan string"},
+	{struct{ x (<-chan <-chan string) }{}, "<-chan <-chan string"},
+	{struct{ x (chan (<-chan string)) }{}, "chan (<-chan string)"},
 	{struct {
 		x struct {
 			c chan *int32
@@ -5491,6 +5495,18 @@
 	// check that type already in binary is found
 	type T1 int
 	checkSameType(t, ChanOf(BothDir, TypeOf(T1(1))), (chan T1)(nil))
+
+	// Check arrow token association in undefined chan types.
+	var left chan<- chan T
+	var right chan (<-chan T)
+	tLeft := ChanOf(SendDir, ChanOf(BothDir, TypeOf(T(""))))
+	tRight := ChanOf(BothDir, ChanOf(RecvDir, TypeOf(T(""))))
+	if tLeft != TypeOf(left) {
+		t.Errorf("chan<-chan: have %s, want %T", tLeft, left)
+	}
+	if tRight != TypeOf(right) {
+		t.Errorf("chan<-chan: have %s, want %T", tRight, right)
+	}
 }
 
 func TestChanOfDir(t *testing.T) {
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 38b1283..44c96fe 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -1789,7 +1789,6 @@
 	}
 
 	// Look in known types.
-	// TODO: Precedence when constructing string.
 	var s string
 	switch dir {
 	default:
@@ -1799,7 +1798,16 @@
 	case RecvDir:
 		s = "<-chan " + typ.String()
 	case BothDir:
-		s = "chan " + typ.String()
+		typeStr := typ.String()
+		if typeStr[0] == '<' {
+			// typ is recv chan, need parentheses as "<-" associates with leftmost
+			// chan possible, see:
+			// * https://golang.org/ref/spec#Channel_types
+			// * https://github.com/golang/go/issues/39897
+			s = "chan (" + typeStr + ")"
+		} else {
+			s = "chan " + typeStr
+		}
 	}
 	for _, tt := range typesByString(s) {
 		ch := (*chanType)(unsafe.Pointer(tt))