diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go index bd35417c64..d4425a2bfd 100644 --- a/src/cmd/compile/internal/types2/expr.go +++ b/src/cmd/compile/internal/types2/expr.go @@ -157,6 +157,14 @@ var op2str2 = [...]string{ syntax.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) +} + func (check *Checker) unary(x *operand, e *syntax.Operation) { check.expr(x, e.X) if x.mode == invalid { @@ -177,19 +185,29 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) { return case syntax.Recv: - typ := asChan(x.typ) - if typ == nil { - check.errorf(x, invalidOp+"cannot receive from non-channel %s", x) - x.mode = invalid - return - } - if typ.dir == SendOnly { - check.errorf(x, invalidOp+"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.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 + }) { x.mode = invalid return } x.mode = commaok - x.typ = typ.elem + x.typ = elem check.hasCallOrRecv = true return } diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43671.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43671.go2 new file mode 100644 index 0000000000..6cc3801cc9 --- /dev/null +++ b/src/cmd/compile/internal/types2/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 +}