// 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
}
// 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
}
--- /dev/null
+// 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(""))
+}