]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: slightly relax notion of structural type
authorRobert Findley <rfindley@google.com>
Tue, 16 Nov 2021 03:42:41 +0000 (22:42 -0500)
committerRobert Findley <rfindley@google.com>
Tue, 16 Nov 2021 15:16:49 +0000 (15:16 +0000)
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 <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/go/types/expr.go
src/go/types/stmt.go
src/go/types/testdata/check/typeparams.go2
src/go/types/testdata/fixedbugs/issue43671.go2
src/go/types/testdata/fixedbugs/issue45920.go2 [new file with mode: 0644]
src/go/types/testdata/fixedbugs/issue47115.go2
src/go/types/type.go

index 0edaf63db015d1612fc0318a9177e42b8cebf3f0..6eeb431b736ad0112f8ac04325fb683b7c7f95fb 100644 (file)
@@ -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
        }
index e7514f19aec5cfdb5fbe369d2f7026e7107c2781..363ea35acf9515c82f5acca03eecc1e2888b6d53 100644 (file)
@@ -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
index 6bf303af905cf5b46eb1bc90cfcd1862f57989bc..9e2bffb539d448fc44330eddc8537c826406e1f4 100644 (file)
@@ -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 */ {}
index 6cc3801cc96b8b38ed8ef4c9ea6e4a8ad6c54081..46ac51ebdd2df27b2007ce66c953b460e693c4e8 100644 (file)
@@ -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 (file)
index 0000000..f659f3a
--- /dev/null
@@ -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) }
index 6694219b54934c3856d02c2faf7a0692dd752281..f71e06c9b23739599cc1eff6513a013f7aa27bae 100644 (file)
@@ -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) {
index 26a605444d0428198508ca48ba8aad8b0d90e389..8f23fb530d1b7bbdb1d740eadd59a0e662793fe1 100644 (file)
@@ -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
        }) {