Skip to content

Commit 4e8d270

Browse files
TapirLiuianlancetaylor
authored andcommitted
reflect: factor out special channel assignability rule from haveIdenticalUnderlyingType
Go specification says: A value x is assignable to a variable of type T if x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a defined type. However, the current reflection implementation is incorrect which makes "x is assignable to T" even if type V and T are both defined type. The current reflection implementation also mistakes the base types of two non-defined pointer types share the same underlying type when the two base types satisfy the above mentioned special channel assignability rule. Fixes #29469 Change-Id: Ia4b9c4ac47dc8e76a11faef422b2e5c5726b78b3 GitHub-Last-Rev: 487c20a GitHub-Pull-Request: #29739 Reviewed-on: https://go-review.googlesource.com/c/go/+/157822 Run-TryBot: Ian Lance Taylor <iant@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
1 parent dd8bbc7 commit 4e8d270

File tree

3 files changed

+63
-13
lines changed

3 files changed

+63
-13
lines changed

src/reflect/all_test.go

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3634,6 +3634,13 @@ type MyRunes []int32
36343634
type MyFunc func()
36353635
type MyByte byte
36363636

3637+
type IntChan chan int
3638+
type IntChanRecv <-chan int
3639+
type IntChanSend chan<- int
3640+
type BytesChan chan []byte
3641+
type BytesChanRecv <-chan []byte
3642+
type BytesChanSend chan<- []byte
3643+
36373644
var convertTests = []struct {
36383645
in Value
36393646
out Value
@@ -3995,10 +4002,6 @@ var convertTests = []struct {
39954002
{V((***byte)(nil)), V((***byte)(nil))},
39964003
{V((***int32)(nil)), V((***int32)(nil))},
39974004
{V((***int64)(nil)), V((***int64)(nil))},
3998-
{V((chan int)(nil)), V((<-chan int)(nil))},
3999-
{V((chan int)(nil)), V((chan<- int)(nil))},
4000-
{V((chan string)(nil)), V((<-chan string)(nil))},
4001-
{V((chan string)(nil)), V((chan<- string)(nil))},
40024005
{V((chan byte)(nil)), V((chan byte)(nil))},
40034006
{V((chan MyByte)(nil)), V((chan MyByte)(nil))},
40044007
{V((map[int]bool)(nil)), V((map[int]bool)(nil))},
@@ -4010,6 +4013,40 @@ var convertTests = []struct {
40104013
{V(new(io.Reader)), V(new(io.Reader))},
40114014
{V(new(io.Writer)), V(new(io.Writer))},
40124015

4016+
// channels
4017+
{V(IntChan(nil)), V((chan<- int)(nil))},
4018+
{V(IntChan(nil)), V((<-chan int)(nil))},
4019+
{V((chan int)(nil)), V(IntChanRecv(nil))},
4020+
{V((chan int)(nil)), V(IntChanSend(nil))},
4021+
{V(IntChanRecv(nil)), V((<-chan int)(nil))},
4022+
{V((<-chan int)(nil)), V(IntChanRecv(nil))},
4023+
{V(IntChanSend(nil)), V((chan<- int)(nil))},
4024+
{V((chan<- int)(nil)), V(IntChanSend(nil))},
4025+
{V(IntChan(nil)), V((chan int)(nil))},
4026+
{V((chan int)(nil)), V(IntChan(nil))},
4027+
{V((chan int)(nil)), V((<-chan int)(nil))},
4028+
{V((chan int)(nil)), V((chan<- int)(nil))},
4029+
{V(BytesChan(nil)), V((chan<- []byte)(nil))},
4030+
{V(BytesChan(nil)), V((<-chan []byte)(nil))},
4031+
{V((chan []byte)(nil)), V(BytesChanRecv(nil))},
4032+
{V((chan []byte)(nil)), V(BytesChanSend(nil))},
4033+
{V(BytesChanRecv(nil)), V((<-chan []byte)(nil))},
4034+
{V((<-chan []byte)(nil)), V(BytesChanRecv(nil))},
4035+
{V(BytesChanSend(nil)), V((chan<- []byte)(nil))},
4036+
{V((chan<- []byte)(nil)), V(BytesChanSend(nil))},
4037+
{V(BytesChan(nil)), V((chan []byte)(nil))},
4038+
{V((chan []byte)(nil)), V(BytesChan(nil))},
4039+
{V((chan []byte)(nil)), V((<-chan []byte)(nil))},
4040+
{V((chan []byte)(nil)), V((chan<- []byte)(nil))},
4041+
4042+
// cannot convert other instances (channels)
4043+
{V(IntChan(nil)), V(IntChan(nil))},
4044+
{V(IntChanRecv(nil)), V(IntChanRecv(nil))},
4045+
{V(IntChanSend(nil)), V(IntChanSend(nil))},
4046+
{V(BytesChan(nil)), V(BytesChan(nil))},
4047+
{V(BytesChanRecv(nil)), V(BytesChanRecv(nil))},
4048+
{V(BytesChanSend(nil)), V(BytesChanSend(nil))},
4049+
40134050
// interfaces
40144051
{V(int(1)), EmptyInterfaceV(int(1))},
40154052
{V(string("hello")), EmptyInterfaceV(string("hello"))},

src/reflect/type.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,6 +1542,18 @@ func implements(T, V *rtype) bool {
15421542
return false
15431543
}
15441544

1545+
// specialChannelAssignability reports whether a value x of channel type V
1546+
// can be directly assigned (using memmove) to another channel type T.
1547+
// https://golang.org/doc/go_spec.html#Assignability
1548+
// T and V must be both of Chan kind.
1549+
func specialChannelAssignability(T, V *rtype) bool {
1550+
// Special case:
1551+
// x is a bidirectional channel value, T is a channel type,
1552+
// x's type V and T have identical element types,
1553+
// and at least one of V or T is not a defined type.
1554+
return V.ChanDir() == BothDir && (T.Name() == "" || V.Name() == "") && haveIdenticalType(T.Elem(), V.Elem(), true)
1555+
}
1556+
15451557
// directlyAssignable reports whether a value x of type V can be directly
15461558
// assigned (using memmove) to a value of type T.
15471559
// https://golang.org/doc/go_spec.html#Assignability
@@ -1559,7 +1571,11 @@ func directlyAssignable(T, V *rtype) bool {
15591571
return false
15601572
}
15611573

1562-
// x's type T and V must have identical underlying types.
1574+
if T.Kind() == Chan && specialChannelAssignability(T, V) {
1575+
return true
1576+
}
1577+
1578+
// x's type T and V must have identical underlying types.
15631579
return haveIdenticalUnderlyingType(T, V, true)
15641580
}
15651581

@@ -1597,14 +1613,6 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
15971613
return T.Len() == V.Len() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
15981614

15991615
case Chan:
1600-
// Special case:
1601-
// x is a bidirectional channel value, T is a channel type,
1602-
// and x's type V and T have identical element types.
1603-
if V.ChanDir() == BothDir && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) {
1604-
return true
1605-
}
1606-
1607-
// Otherwise continue test for identical underlying type.
16081616
return V.ChanDir() == T.ChanDir() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
16091617

16101618
case Func:

src/reflect/value.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2476,6 +2476,11 @@ func convertOp(dst, src *rtype) func(Value, Type) Value {
24762476
return cvtRunesString
24772477
}
24782478
}
2479+
2480+
case Chan:
2481+
if dst.Kind() == Chan && specialChannelAssignability(dst, src) {
2482+
return cvtDirect
2483+
}
24792484
}
24802485

24812486
// dst and src have same underlying type.

0 commit comments

Comments
 (0)