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