Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reflect: factor out special channel assignability rule from haveIdenticalUnderlyingType #29739

Closed
wants to merge 11 commits into from
45 changes: 41 additions & 4 deletions src/reflect/all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3379,6 +3379,13 @@ type MyRunes []int32
type MyFunc func()
type MyByte byte

type IntChan chan int
type IntChanRecv <-chan int
type IntChanSend chan<- int
type BytesChan chan []byte
type BytesChanRecv <-chan []byte
type BytesChanSend chan<- []byte

var convertTests = []struct {
in Value
out Value
Expand Down Expand Up @@ -3740,10 +3747,6 @@ var convertTests = []struct {
{V((***byte)(nil)), V((***byte)(nil))},
{V((***int32)(nil)), V((***int32)(nil))},
{V((***int64)(nil)), V((***int64)(nil))},
{V((chan int)(nil)), V((<-chan int)(nil))},
{V((chan int)(nil)), V((chan<- int)(nil))},
{V((chan string)(nil)), V((<-chan string)(nil))},
{V((chan string)(nil)), V((chan<- string)(nil))},
{V((chan byte)(nil)), V((chan byte)(nil))},
{V((chan MyByte)(nil)), V((chan MyByte)(nil))},
{V((map[int]bool)(nil)), V((map[int]bool)(nil))},
Expand All @@ -3755,6 +3758,40 @@ var convertTests = []struct {
{V(new(io.Reader)), V(new(io.Reader))},
{V(new(io.Writer)), V(new(io.Writer))},

// channels
{V(IntChan(nil)), V((chan<- int)(nil))},
{V(IntChan(nil)), V((<-chan int)(nil))},
{V((chan int)(nil)), V(IntChanRecv(nil))},
{V((chan int)(nil)), V(IntChanSend(nil))},
{V(IntChanRecv(nil)), V((<-chan int)(nil))},
{V((<-chan int)(nil)), V(IntChanRecv(nil))},
{V(IntChanSend(nil)), V((chan<- int)(nil))},
{V((chan<- int)(nil)), V(IntChanSend(nil))},
{V(IntChan(nil)), V((chan int)(nil))},
{V((chan int)(nil)), V(IntChan(nil))},
{V((chan int)(nil)), V((<-chan int)(nil))},
{V((chan int)(nil)), V((chan<- int)(nil))},
{V(BytesChan(nil)), V((chan<- []byte)(nil))},
{V(BytesChan(nil)), V((<-chan []byte)(nil))},
{V((chan []byte)(nil)), V(BytesChanRecv(nil))},
{V((chan []byte)(nil)), V(BytesChanSend(nil))},
{V(BytesChanRecv(nil)), V((<-chan []byte)(nil))},
{V((<-chan []byte)(nil)), V(BytesChanRecv(nil))},
{V(BytesChanSend(nil)), V((chan<- []byte)(nil))},
{V((chan<- []byte)(nil)), V(BytesChanSend(nil))},
{V(BytesChan(nil)), V((chan []byte)(nil))},
{V((chan []byte)(nil)), V(BytesChan(nil))},
{V((chan []byte)(nil)), V((<-chan []byte)(nil))},
{V((chan []byte)(nil)), V((chan<- []byte)(nil))},

// cannot convert other instances (channels)
{V(IntChan(nil)), V(IntChan(nil))},
{V(IntChanRecv(nil)), V(IntChanRecv(nil))},
{V(IntChanSend(nil)), V(IntChanSend(nil))},
{V(BytesChan(nil)), V(BytesChan(nil))},
{V(BytesChanRecv(nil)), V(BytesChanRecv(nil))},
{V(BytesChanSend(nil)), V(BytesChanSend(nil))},

// interfaces
{V(int(1)), EmptyInterfaceV(int(1))},
{V(string("hello")), EmptyInterfaceV(string("hello"))},
Expand Down
26 changes: 17 additions & 9 deletions src/reflect/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,18 @@ func implements(T, V *rtype) bool {
return false
}

// specialChannelAssignability reports whether a value x of channel type V
// can be directly assigned (using memmove) to another channel type T.
// https://golang.org/doc/go_spec.html#Assignability
// T and V must be both of Chan kind.
func specialChannelAssignability(T, V *rtype) bool {
// Special case:
// 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.
return V.ChanDir() == BothDir && (T.Name() == "" || V.Name() == "") && haveIdenticalType(T.Elem(), V.Elem(), true)
}

// directlyAssignable reports whether a value x of type V can be directly
// assigned (using memmove) to a value of type T.
// https://golang.org/doc/go_spec.html#Assignability
Expand All @@ -1568,7 +1580,11 @@ func directlyAssignable(T, V *rtype) bool {
return false
}

// x's type T and V must have identical underlying types.
if T.Kind() == Chan && specialChannelAssignability(T, V) {
return true
}

// x's type T and V must have identical underlying types.
return haveIdenticalUnderlyingType(T, V, true)
}

Expand Down Expand Up @@ -1606,14 +1622,6 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
return T.Len() == V.Len() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)

case Chan:
// Special case:
// x is a bidirectional channel value, T is a channel type,
// and x's type V and T have identical element types.
if V.ChanDir() == BothDir && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) {
return true
}

// Otherwise continue test for identical underlying type.
return V.ChanDir() == T.ChanDir() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)

case Func:
Expand Down
5 changes: 5 additions & 0 deletions src/reflect/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -2411,6 +2411,11 @@ func convertOp(dst, src *rtype) func(Value, Type) Value {
return cvtRunesString
}
}

case Chan:
if dst.Kind() == Chan && specialChannelAssignability(dst, src) {
return cvtDirect
}
}

// dst and src have same underlying type.
Expand Down