]> Cypherpunks.ru repositories - gostls13.git/commitdiff
[release-branch.go1.21] go/types, types2: remove order dependency in inference involv...
authorRobert Griesemer <gri@golang.org>
Mon, 21 Aug 2023 22:50:18 +0000 (15:50 -0700)
committerGopher Robot <gobot@golang.org>
Wed, 30 Aug 2023 21:35:21 +0000 (21:35 +0000)
In inexact unification, when a named type matches against an inferred
unnamed type, we change the previously inferred type to the named type.
This preserves the type name and assignability.

We have to do the same thing when encountering a directional channel:
a bidirectional channel can always be assigned to a directional channel
but not the other way around. Thus, if we see a directional channel, we
must choose the directional channel.

This CL extends the previously existing logic for named types to
directional channels and also makes the code conditional on inexact
unification. The latter is an optimization - if unification is exact,
type differences don't exist and updating an already inferred type has
no effect.

Fixes #62205.

Change-Id: I807e3b9f9ab363f9ed848bdb18b2577b1d680ea7
Reviewed-on: https://go-review.googlesource.com/c/go/+/524256
Run-TryBot: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/types2/unify.go
src/go/types/unify.go
src/internal/types/testdata/fixedbugs/issue62157.go [new file with mode: 0644]

index 4f041323532a0508343abceba09b0b75ae0ba3c7..4e9c77161584f4c15785fe9df3b234074fb630bb 100644 (file)
@@ -401,18 +401,40 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
                                        // Therefore, we must fail unification (go.dev/issue/60933).
                                        return false
                                }
-                               // If y is a defined type, make sure we record that type
-                               // for type parameter x, which may have until now only
-                               // recorded an underlying type (go.dev/issue/43056).
-                               // Either both types are interfaces, or neither type is.
-                               // If both are interfaces, they have the same methods.
+                               // If we have inexact unification and one of x or y is a defined type, select the
+                               // defined type. This ensures that in a series of types, all matching against the
+                               // same type parameter, we infer a defined type if there is one, independent of
+                               // order. Type inference or assignment may fail, which is ok.
+                               // Selecting a defined type, if any, ensures that we don't lose the type name;
+                               // and since we have inexact unification, a value of equally named or matching
+                               // undefined type remains assignable (go.dev/issue/43056).
                                //
-                               // Note: Changing the recorded type for a type parameter to
-                               // a defined type is only ok when unification is inexact.
-                               // But in exact unification, if we have a match, x and y must
-                               // be identical, so changing the recorded type for x is a no-op.
-                               if yn {
-                                       u.set(px, y)
+                               // Similarly, if we have inexact unification and there are no defined types but
+                               // channel types, select a directed channel, if any. This ensures that in a series
+                               // of unnamed types, all matching against the same type parameter, we infer the
+                               // directed channel if there is one, independent of order.
+                               // Selecting a directional channel, if any, ensures that a value of another
+                               // inexactly unifying channel type remains assignable (go.dev/issue/62157).
+                               //
+                               // If we have multiple defined channel types, they are either identical or we
+                               // have assignment conflicts, so we can ignore directionality in this case.
+                               //
+                               // If we have defined and literal channel types, a defined type wins to avoid
+                               // order dependencies.
+                               if mode&exact == 0 {
+                                       switch {
+                                       case xn:
+                                               // x is a defined type: nothing to do.
+                                       case yn:
+                                               // x is not a defined type and y is a defined type: select y.
+                                               u.set(px, y)
+                                       default:
+                                               // Neither x nor y are defined types.
+                                               if yc, _ := under(y).(*Chan); yc != nil && yc.dir != SendRecv {
+                                                       // y is a directed channel type: select y.
+                                                       u.set(px, y)
+                                               }
+                                       }
                                }
                                return true
                        }
index 646eb3e3390796c28ced4122960c81243b33d175..6680d97b86e49a2d5b689b29b64da6917c11a73e 100644 (file)
@@ -403,18 +403,40 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
                                        // Therefore, we must fail unification (go.dev/issue/60933).
                                        return false
                                }
-                               // If y is a defined type, make sure we record that type
-                               // for type parameter x, which may have until now only
-                               // recorded an underlying type (go.dev/issue/43056).
-                               // Either both types are interfaces, or neither type is.
-                               // If both are interfaces, they have the same methods.
+                               // If we have inexact unification and one of x or y is a defined type, select the
+                               // defined type. This ensures that in a series of types, all matching against the
+                               // same type parameter, we infer a defined type if there is one, independent of
+                               // order. Type inference or assignment may fail, which is ok.
+                               // Selecting a defined type, if any, ensures that we don't lose the type name;
+                               // and since we have inexact unification, a value of equally named or matching
+                               // undefined type remains assignable (go.dev/issue/43056).
                                //
-                               // Note: Changing the recorded type for a type parameter to
-                               // a defined type is only ok when unification is inexact.
-                               // But in exact unification, if we have a match, x and y must
-                               // be identical, so changing the recorded type for x is a no-op.
-                               if yn {
-                                       u.set(px, y)
+                               // Similarly, if we have inexact unification and there are no defined types but
+                               // channel types, select a directed channel, if any. This ensures that in a series
+                               // of unnamed types, all matching against the same type parameter, we infer the
+                               // directed channel if there is one, independent of order.
+                               // Selecting a directional channel, if any, ensures that a value of another
+                               // inexactly unifying channel type remains assignable (go.dev/issue/62157).
+                               //
+                               // If we have multiple defined channel types, they are either identical or we
+                               // have assignment conflicts, so we can ignore directionality in this case.
+                               //
+                               // If we have defined and literal channel types, a defined type wins to avoid
+                               // order dependencies.
+                               if mode&exact == 0 {
+                                       switch {
+                                       case xn:
+                                               // x is a defined type: nothing to do.
+                                       case yn:
+                                               // x is not a defined type and y is a defined type: select y.
+                                               u.set(px, y)
+                                       default:
+                                               // Neither x nor y are defined types.
+                                               if yc, _ := under(y).(*Chan); yc != nil && yc.dir != SendRecv {
+                                                       // y is a directed channel type: select y.
+                                                       u.set(px, y)
+                                               }
+                                       }
                                }
                                return true
                        }
diff --git a/src/internal/types/testdata/fixedbugs/issue62157.go b/src/internal/types/testdata/fixedbugs/issue62157.go
new file mode 100644 (file)
index 0000000..c44f921
--- /dev/null
@@ -0,0 +1,128 @@
+// Copyright 2023 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 f[T any](...T) T { var x T; return x }
+
+// Test case 1
+
+func _() {
+       var a chan string
+       var b <-chan string
+       f(a, b)
+       f(b, a)
+}
+
+// Test case 2
+
+type F[T any] func(T) bool
+
+func g[T any](T) F[<-chan T] { return nil }
+
+func f1[T any](T, F[T]) {}
+func f2[T any](F[T], T) {}
+
+func _() {
+       var ch chan string
+       f1(ch, g(""))
+       f2(g(""), ch)
+}
+
+// Test case 3: named and directional types combined
+
+func _() {
+       type namedA chan int
+       type namedB chan<- int
+
+       var a chan int
+       var A namedA
+       var b chan<- int
+       var B namedB
+
+       // Defined types win over channel types irrespective of channel direction.
+       f(A, b /* ERROR "cannot use b (variable of type chan<- int) as namedA value in argument to f" */)
+       f(b /* ERROR "cannot use b (variable of type chan<- int) as namedA value in argument to f" */, A)
+
+       f(a, b /* ERROR "cannot use b (variable of type chan<- int) as namedA value in argument to f" */, A)
+       f(a, A, b /* ERROR "cannot use b (variable of type chan<- int) as namedA value in argument to f" */)
+       f(b /* ERROR "cannot use b (variable of type chan<- int) as namedA value in argument to f" */, A, a)
+       f(b /* ERROR "cannot use b (variable of type chan<- int) as namedA value in argument to f" */, a, A)
+       f(A, a, b /* ERROR "cannot use b (variable of type chan<- int) as namedA value in argument to f" */)
+       f(A, b /* ERROR "cannot use b (variable of type chan<- int) as namedA value in argument to f" */, a)
+
+       // Unnamed directed channels win over bidirectional channels.
+       b = f(a, b)
+       b = f(b, a)
+
+       // Defined directed channels win over defined bidirectional channels.
+       A = f(A, a)
+       A = f(a, A)
+       B = f(B, b)
+       B = f(b, B)
+
+       f(a, b, B)
+       f(a, B, b)
+       f(b, B, a)
+       f(b, a, B)
+       f(B, a, b)
+       f(B, b, a)
+
+       // Differently named channel types conflict irrespective of channel direction.
+       f(A, B /* ERROR "type namedB of B does not match inferred type namedA for T" */)
+       f(B, A /* ERROR "type namedA of A does not match inferred type namedB for T" */)
+
+       // Ensure that all combinations of directional and
+       // bidirectional channels with a named directional
+       // channel lead to the correct (named) directional
+       // channel.
+       B = f(a, b)
+       B = f(a, B)
+       B = f(b, a)
+       B = f(B, a)
+
+       B = f(a, b, B)
+       B = f(a, B, b)
+       B = f(b, B, a)
+       B = f(b, a, B)
+       B = f(B, a, b)
+       B = f(B, b, a)
+
+       // verify type error
+       A = f /* ERROR "cannot use f(B, b, a) (value of type namedB) as namedA value in assignment" */ (B, b, a)
+}
+
+// Test case 4: some more combinations
+
+func _() {
+       type A chan int
+       type B chan int
+       type C = chan int
+       type D = chan<- int
+
+       var a A
+       var b B
+       var c C
+       var d D
+
+       f(a, b /* ERROR "type B of b does not match inferred type A for T" */, c)
+       f(c, a, b /* ERROR "type B of b does not match inferred type A for T" */)
+       f(a, b /* ERROR "type B of b does not match inferred type A for T" */, d)
+       f(d, a, b /* ERROR "type B of b does not match inferred type A for T" */)
+}
+
+// Simplified test case from issue
+
+type Matcher[T any] func(T) bool
+
+func Produces[T any](T) Matcher[<-chan T] { return nil }
+
+func Assert1[T any](Matcher[T], T) {}
+func Assert2[T any](T, Matcher[T]) {}
+
+func _() {
+       var ch chan string
+       Assert1(Produces(""), ch)
+       Assert2(ch, Produces(""))
+}