From 034207bf9068bfe3d78be3208348a4b86e885217 Mon Sep 17 00:00:00 2001
From: Eugene Flesselle <eugene@flesselle.net>
Date: Mon, 29 Apr 2024 12:07:54 +0200
Subject: [PATCH] Refine the bounds of the `Tuple.Filter` type lambda predicate
 ..

to only require it be defined on the elements of the tuple.

This is one of the ongoing proposed tuple improvements, addressing #19175.

As carefully pointed out by @sjrd, this _is_ a potential breaking change.
See tests/neg/tuple-filter-compat.scala for an example.

This is not an unprecedented change however,
the analogous improvements were made to `Tuple.{Map, FlatMap}` in 28a695ef.
---
 library/src/scala/Tuple.scala       |  2 +-
 tests/neg/tuple-filter-compat.scala | 12 ++++++++++++
 tests/pos/tuple-filter.scala        |  6 ++++++
 3 files changed, 19 insertions(+), 1 deletion(-)
 create mode 100644 tests/neg/tuple-filter-compat.scala

diff --git a/library/src/scala/Tuple.scala b/library/src/scala/Tuple.scala
index 8074fe3664e5..060f2b5492b8 100644
--- a/library/src/scala/Tuple.scala
+++ b/library/src/scala/Tuple.scala
@@ -166,7 +166,7 @@ object Tuple {
    *  ```
    *  @syntax markdown
    */
-  type Filter[Tup <: Tuple, P[_] <: Boolean] <: Tuple = Tup match {
+  type Filter[Tup <: Tuple, P[_ <: Union[Tup]] <: Boolean] <: Tuple = Tup match {
     case EmptyTuple => EmptyTuple
     case h *: t => P[h] match {
       case true => h *: Filter[t, P]
diff --git a/tests/neg/tuple-filter-compat.scala b/tests/neg/tuple-filter-compat.scala
new file mode 100644
index 000000000000..f50837fc1d4b
--- /dev/null
+++ b/tests/neg/tuple-filter-compat.scala
@@ -0,0 +1,12 @@
+
+type OldFilter[Tup <: Tuple, P[_] <: Boolean] = Nothing
+type NewFilter[Tup <: Tuple, P[_ <: Tuple.Union[Tup]] <: Boolean] = Nothing
+
+trait A:
+  type X >: OldFilter <: OldFilter
+
+trait B1 extends A:
+  type X = OldFilter // ok
+
+trait B2 extends A:
+  type X = NewFilter // error: breaking change
diff --git a/tests/pos/tuple-filter.scala b/tests/pos/tuple-filter.scala
index 2c9638b2e47b..f67518f171f0 100644
--- a/tests/pos/tuple-filter.scala
+++ b/tests/pos/tuple-filter.scala
@@ -1,10 +1,16 @@
+import scala.compiletime.ops.int.<
+
 type P[x] <: Boolean = x match {
   case 3 => false
   case _ => true
 }
+
 type RejectAll[x] = false
 
+type Pos[X <: Int] = 0 < X
+
 def Test =
   summon[Tuple.Filter[(1, 2, 3, 4), P] =:= (1, 2, 4)]
   summon[Tuple.Filter[(1, 2, 3, 4), RejectAll] =:= EmptyTuple]
   summon[Tuple.Filter[EmptyTuple, P] =:= EmptyTuple]
+  summon[Tuple.Filter[(1, -2, 3, -4), Pos] =:= (1, 3)]