From: Robert Findley Date: Tue, 16 Nov 2021 03:42:41 +0000 (-0500) Subject: go/types: slightly relax notion of structural type X-Git-Tag: go1.18beta1~296 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=289c930750fccac6aca578c675694b612532fd24;p=gostls13.git go/types: slightly relax notion of structural type This is a port of CL 363075 from types2 to go/types, adjusted for the different error reporting API, and to adjust positions of error messages in tests. Change-Id: Ic6bfedf1152eff94bad20725b56e6ba804b2e3e8 Reviewed-on: https://go-review.googlesource.com/c/go/+/363991 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 0edaf63db0..6eeb431b73 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -174,29 +174,26 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) { return case token.ARROW: - 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, _InvalidReceive, "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.invalidOp(x, _InvalidReceive, "cannot receive from %s: no structural type", x) x.mode = invalid return } + ch, _ := u.(*Chan) + if ch == nil { + check.invalidOp(x, _InvalidReceive, "cannot receive from non-channel %s", x) + x.mode = invalid + return + } + if ch.dir == SendOnly { + check.invalidOp(x, _InvalidReceive, "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 } diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index e7514f19ae..363ea35acf 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -417,27 +417,21 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { 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.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-channel %s", &ch) - return false - } - if uch.dir == RecvOnly { - check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to receive-only channel %s", &ch) - return false - } - if elem != nil && !Identical(uch.elem, elem) { - check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "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.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to %s: no structural type", &ch) return } - check.assignment(&val, elem, "send") + uch, _ := u.(*Chan) + if uch == nil { + check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-channel %s", &ch) + return + } + if uch.dir == RecvOnly { + check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to receive-only channel %s", &ch) + return + } + check.assignment(&val, uch.elem, "send") case *ast.IncDecStmt: var op token.Token diff --git a/src/go/types/testdata/check/typeparams.go2 b/src/go/types/testdata/check/typeparams.go2 index 6bf303af90..9e2bffb539 100644 --- a/src/go/types/testdata/check/typeparams.go2 +++ b/src/go/types/testdata/check/typeparams.go2 @@ -210,7 +210,7 @@ func _[ 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 */ {} diff --git a/src/go/types/testdata/fixedbugs/issue43671.go2 b/src/go/types/testdata/fixedbugs/issue43671.go2 index 6cc3801cc9..46ac51ebdd 100644 --- a/src/go/types/testdata/fixedbugs/issue43671.go2 +++ b/src/go/types/testdata/fixedbugs/issue43671.go2 @@ -12,11 +12,11 @@ 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 + <-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) { @@ -28,7 +28,7 @@ func _[T C2](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) { diff --git a/src/go/types/testdata/fixedbugs/issue45920.go2 b/src/go/types/testdata/fixedbugs/issue45920.go2 new file mode 100644 index 0000000000..f659f3a0db --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue45920.go2 @@ -0,0 +1,17 @@ +// 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) } diff --git a/src/go/types/testdata/fixedbugs/issue47115.go2 b/src/go/types/testdata/fixedbugs/issue47115.go2 index 6694219b54..f71e06c9b2 100644 --- a/src/go/types/testdata/fixedbugs/issue47115.go2 +++ b/src/go/types/testdata/fixedbugs/issue47115.go2 @@ -12,7 +12,7 @@ type C4 interface{ chan int | chan<- int } 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) { @@ -28,7 +28,7 @@ func _[T C2](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) { diff --git a/src/go/types/type.go b/src/go/types/type.go index 26a605444d..8f23fb530d 100644 --- a/src/go/types/type.go +++ b/src/go/types/type.go @@ -27,17 +27,51 @@ func under(t Type) Type { 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. +// 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(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 }) { @@ -55,10 +89,13 @@ func structuralString(typ Type) Type { 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 }) {