return u
}
-// If t is a type parameter, StructuralType returns the single underlying
-// type of all types in the type parameter's type constraint if it exists,
-// or nil otherwise. If t is not a type parameter, StructuralType returns
-// the underlying type of t.
+// If typ is a type parameter, structuralType returns the single underlying
+// type of all types in the corresponding type constraint if it exists, or
+// nil otherwise. If the type set contains only unrestricted and restricted
+// channel types (with identical element types), the single underlying type
+// is the restricted channel type if the restrictions are always the same.
+// If typ is not a type parameter, structuralType returns the underlying type.
func StructuralType(t Type) Type {
return structuralType(t)
}
return
case syntax.Recv:
- var elem Type
- if !underIs(x.typ, func(u Type) bool {
- ch, _ := u.(*Chan)
- if ch == nil {
- check.errorf(x, invalidOp+"cannot receive from non-channel %s", x)
- return false
- }
- if ch.dir == SendOnly {
- check.errorf(x, invalidOp+"cannot receive from send-only channel %s", x)
- return false
- }
- if elem != nil && !Identical(ch.elem, elem) {
- check.errorf(x, invalidOp+"channels of %s must have the same element type", x)
- return false
- }
- elem = ch.elem
- return true
- }) {
+ u := structuralType(x.typ)
+ if u == nil {
+ check.errorf(x, invalidOp+"cannot receive from %s: no structural type", x)
+ x.mode = invalid
+ return
+ }
+ ch, _ := u.(*Chan)
+ if ch == nil {
+ check.errorf(x, invalidOp+"cannot receive from non-channel %s", x)
+ x.mode = invalid
+ return
+ }
+ if ch.dir == SendOnly {
+ check.errorf(x, invalidOp+"cannot receive from send-only channel %s", x)
x.mode = invalid
return
}
x.mode = commaok
- x.typ = elem
+ x.typ = ch.elem
check.hasCallOrRecv = true
return
}
if ch.mode == invalid || val.mode == invalid {
return
}
- var elem Type
- if !underIs(ch.typ, func(u Type) bool {
- uch, _ := u.(*Chan)
- if uch == nil {
- check.errorf(s, invalidOp+"cannot send to non-channel %s", &ch)
- return false
- }
- if uch.dir == RecvOnly {
- check.errorf(s, invalidOp+"cannot send to receive-only channel %s", &ch)
- return false
- }
- if elem != nil && !Identical(uch.elem, elem) {
- check.errorf(s, invalidOp+"channels of %s must have the same element type", &ch)
- return false
- }
- elem = uch.elem
- return true
- }) {
+ u := structuralType(ch.typ)
+ if u == nil {
+ check.errorf(s, invalidOp+"cannot send to %s: no structural type", &ch)
return
}
- check.assignment(&val, elem, "send")
+ uch, _ := u.(*Chan)
+ if uch == nil {
+ check.errorf(s, invalidOp+"cannot send to non-channel %s", &ch)
+ return
+ }
+ if uch.dir == RecvOnly {
+ check.errorf(s, invalidOp+"cannot send to receive-only channel %s", &ch)
+ return
+ }
+ check.assignment(&val, uch.elem, "send")
case *syntax.AssignStmt:
lhs := unpackExpr(s.Lhs)
for _, _ /* ERROR permits only one iteration variable */ = range c1 {}
var c2 C2
- for range c2 /* ERROR cannot range over c2.*no structural type */ {}
+ for range c2 {}
var c3 C3
for range c3 /* ERROR receive from send-only channel */ {}
type C5[T any] interface{ ~chan T | <-chan T }
func _[T any](ch T) {
- <-ch // ERROR cannot receive from non-channel
+ <-ch // ERROR cannot receive from ch .* no structural type
}
func _[T C0](ch T) {
- <-ch // ERROR cannot receive from non-channel
+ <-ch // ERROR cannot receive from non-channel ch
}
func _[T C1](ch T) {
}
func _[T C3](ch T) {
- <-ch // ERROR channels of ch .* must have the same element type
+ <-ch // ERROR cannot receive from ch .* no structural type
}
func _[T C4](ch T) {
--- /dev/null
+// Copyright 2021 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 f1[T any, C chan T | <-chan T](ch C) {}
+
+func _(ch chan int) { f1(ch) }
+func _(ch <-chan int) { f1(ch) }
+func _(ch chan<- int) { f1( /* ERROR chan<- int does not satisfy chan T\|<-chan T */ ch) }
+
+func f2[T any, C chan T | chan<- T](ch C) {}
+
+func _(ch chan int) { f2(ch) }
+func _(ch <-chan int) { f2( /* ERROR <-chan int does not satisfy chan T\|chan<- T */ ch) }
+func _(ch chan<- int) { f2(ch) }
type C5[T any] interface{ ~chan T | chan<- T }
func _[T any](ch T) {
- ch /* ERROR cannot send to non-channel */ <- 0
+ ch /* ERROR cannot send to ch .* no structural type */ <- 0
}
func _[T C0](ch T) {
}
func _[T C3](ch T) {
- ch /* ERROR channels of ch .* must have the same element type */ <- 0
+ ch /* ERROR cannot send to ch .* no structural type */ <- 0
}
func _[T C4](ch T) {
return t
}
+// If x and y are identical, match returns x.
+// If x and y are identical channels but for their direction
+// and one of them is unrestricted, match returns the channel
+// with the restricted direction.
+// In all other cases, match returns nil.
+func match(x, y Type) Type {
+ // Common case: we don't have channels.
+ if Identical(x, y) {
+ return x
+ }
+
+ // We may have channels that differ in direction only.
+ if x, _ := x.(*Chan); x != nil {
+ if y, _ := y.(*Chan); y != nil && Identical(x.elem, y.elem) {
+ // We have channels that differ in direction only.
+ // If there's an unrestricted channel, select the restricted one.
+ switch {
+ case x.dir == SendRecv:
+ return y
+ case y.dir == SendRecv:
+ return x
+ }
+ }
+ }
+
+ // types are different
+ return nil
+}
+
// If typ is a type parameter, structuralType returns the single underlying
// type of all types in the corresponding type constraint if it exists, or
-// nil otherwise. If typ is not a type parameter, structuralType returns
-// the underlying type.
+// nil otherwise. If the type set contains only unrestricted and restricted
+// channel types (with identical element types), the single underlying type
+// is the restricted channel type if the restrictions are always the same.
+// If typ is not a type parameter, structuralType returns the underlying type.
func structuralType(typ Type) Type {
var su Type
if underIs(typ, func(u Type) bool {
- if su != nil && !Identical(su, u) {
- return false
+ if su != nil {
+ u = match(su, u)
+ if u == nil {
+ return false
+ }
}
- // su == nil || Identical(su, u)
+ // su == nil || match(su, u) != nil
su = u
return true
}) {
if isString(u) {
u = NewSlice(universeByte)
}
- if su != nil && !Identical(su, u) {
- return false
+ if su != nil {
+ u = match(su, u)
+ if u == nil {
+ return false
+ }
}
- // su == nil || Identical(su, u)
+ // su == nil || match(su, u) != nil
su = u
return true
}) {