diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 46f6e33463..751a360890 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -144,6 +144,14 @@ var op2str2 = [...]string{ token.SHL: "shift", } +func underIs(typ Type, f func(Type) bool) bool { + u := under(typ) + if tpar, _ := u.(*TypeParam); tpar != nil { + return tpar.underIs(f) + } + return f(u) +} + // The unary expression e may be nil. It's passed in for better error messages only. func (check *Checker) unary(x *operand, e *ast.UnaryExpr) { check.expr(x, e.X) @@ -164,19 +172,29 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) { return case token.ARROW: - typ := asChan(x.typ) - if typ == nil { - check.invalidOp(x, _InvalidReceive, "cannot receive from non-channel %s", x) - x.mode = invalid - return - } - if typ.dir == SendOnly { - check.invalidOp(x, _InvalidReceive, "cannot receive from send-only channel %s", x) + var elem Type + if !underIs(x.typ, func(u Type) bool { + ch, _ := u.(*Chan) + if ch == nil { + check.invalidOp(x, _InvalidReceive, "cannot receive from non-channel %s", x) + return false + } + if ch.dir == SendOnly { + check.invalidOp(x, _InvalidReceive, "cannot receive from send-only channel %s", x) + return false + } + if elem != nil && !Identical(ch.elem, elem) { + check.invalidOp(x, _Todo, "channels of %s must have the same element type", x) + return false + } + elem = ch.elem + return true + }) { x.mode = invalid return } x.mode = commaok - x.typ = typ.elem + x.typ = elem check.hasCallOrRecv = true return } diff --git a/src/go/types/testdata/fixedbugs/issue43671.go2 b/src/go/types/testdata/fixedbugs/issue43671.go2 new file mode 100644 index 0000000000..6cc3801cc9 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue43671.go2 @@ -0,0 +1,58 @@ +// 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 + +type C0 interface{ int } +type C1 interface{ chan int } +type C2 interface{ chan int | <-chan int } +type C3 interface{ chan int | chan float32 } +type C4 interface{ chan int | chan<- int } +type C5[T any] interface{ ~chan T | <-chan T } + +func _[T any](ch T) { + <-ch // ERROR cannot receive from non-channel +} + +func _[T C0](ch T) { + <-ch // ERROR cannot receive from non-channel +} + +func _[T C1](ch T) { + <-ch +} + +func _[T C2](ch T) { + <-ch +} + +func _[T C3](ch T) { + <-ch // ERROR channels of ch .* must have the same element type +} + +func _[T C4](ch T) { + <-ch // ERROR cannot receive from send-only channel +} + +func _[T C5[X], X any](ch T, x X) { + x = <-ch +} + +// test case from issue, slightly modified +type RecvChan[T any] interface { + ~chan T | ~<-chan T +} + +func _[T any, C RecvChan[T]](ch C) T { + return <-ch +} + +func f[T any, C interface{ chan T }](ch C) T { + return <-ch +} + +func _(ch chan int) { + var x int = f(ch) // test constraint type inference for this case + _ = x +}