diff --git a/library/src/scala/CanEqual.scala b/library/src/scala/CanEqual.scala index 39e5389df989..e65c8e3909b5 100644 --- a/library/src/scala/CanEqual.scala +++ b/library/src/scala/CanEqual.scala @@ -26,9 +26,16 @@ object CanEqual { given canEqualNumber: CanEqual[Number, Number] = derived given canEqualString: CanEqual[String, String] = derived - // The next three definitions can go into the companion objects of classes - // Seq and Set. For now they are here in order not to have to touch the + // The next five definitions can go into the companion objects of their corresponding + // classes. For now they are here in order not to have to touch the // source code of these classes given canEqualSeq[T, U](using eq: CanEqual[T, U]): CanEqual[Seq[T], Seq[U]] = derived given canEqualSet[T, U](using eq: CanEqual[T, U]): CanEqual[Set[T], Set[U]] = derived + + given canEqualOptions[T, U](using eq: CanEqual[T, U]): CanEqual[Option[T], Option[U]] = derived + given canEqualOption[T](using eq: CanEqual[T, T]): CanEqual[Option[T], Option[T]] = derived // for `case None` in pattern matching + + given canEqualEither[L1, R1, L2, R2]( + using eqL: CanEqual[L1, L2], eqR: CanEqual[R1, R2] + ): CanEqual[Either[L1, R1], Either[L2, R2]] = derived } diff --git a/library/src/scala/Tuple.scala b/library/src/scala/Tuple.scala index c6db4d0b83bc..c5c2115811cb 100644 --- a/library/src/scala/Tuple.scala +++ b/library/src/scala/Tuple.scala @@ -232,6 +232,11 @@ object Tuple { def fromProductTyped[P <: Product](p: P)(using m: scala.deriving.Mirror.ProductOf[P]): m.MirroredElemTypes = runtime.Tuples.fromProduct(p).asInstanceOf[m.MirroredElemTypes] + + given canEqualEmptyTuple: CanEqual[EmptyTuple, EmptyTuple] = CanEqual.derived + given canEqualTuple[H1, T1 <: Tuple, H2, T2 <: Tuple]( + using eqHead: CanEqual[H1, H2], eqTail: CanEqual[T1, T2] + ): CanEqual[H1 *: T1, H2 *: T2] = CanEqual.derived } /** A tuple of 0 elements */ diff --git a/tests/neg/equality1.scala b/tests/neg/equality1.scala index 77ddb371051a..1e7b76908d79 100644 --- a/tests/neg/equality1.scala +++ b/tests/neg/equality1.scala @@ -3,4 +3,118 @@ object equality1 { class A class B new A == new B // error: cannot compare + + case class Foo(n: Int) derives CanEqual + + sealed trait Status derives CanEqual + object Status { + case class Active(since: Int) extends Status + case object Pending extends Status + case object Inactive extends Status + } + + enum Color derives CanEqual { + case Red + case Green + case Blue + } + + val option1a: Option[Int] = Some(1) + val option1b: Option[Int] = Some(1) + option1a == option1b + + option1a match { + case Some(1) => + println("1") + case Some(n) => + println("Not 1") + case None => // This None case doesn't work without CanEqual.canEqualOption[T] + println("None") + } + + 1 == '1' + val option2a: Option[Int] = Some(1) + val option2b: Option[Char] = Some('1') + option2a == option2b + + val option3a: Option[Foo] = Some(Foo(1)) + val option3b: Option[Foo] = Some(Foo(1)) + option3a == option3b + + val option4a: Option[Status] = Some(Status.Active(2020)) + val option4b: Option[Status] = Some(Status.Pending) + val option4c: Option[Status] = Some(Status.Inactive) + option4a == option4b + option4b == option4c + + val option5a: Option[Color] = Some(Color.Red) + val option5b: Option[Color] = Some(Color.Green) + val option5c: Option[Color] = Some(Color.Blue) + option5a == option5b + option5b == option5c + + val optionError1a: Option[Int] = Some(1) + val optionError1b: Option[String] = Some("1") + optionError1a == optionError1b // error: cannot compare + + val optionError2a: Option[Char] = Some('a') + val optionError2b: Option[String] = Some("a") + optionError2a == optionError2b // error: cannot compare + + val optionTuple1a: Option[(Int, String)] = Some((1, "OK")) + val optionTuple1b: Option[(Int, String)] = Some((1, "OK")) + optionTuple1a == optionTuple1b + + 'a' == 97 + val optionTuple2a: Option[(Int, Char)] = Some((1, 'a')) + val optionTuple2b: Option[(Int, Int)] = Some((1, 97)) + optionTuple2a == optionTuple2b + + val optionTupleError1a: Option[(Int, String)] = Some((1, "OK")) + val optionTupleError1b: Option[(String, Int)] = Some(("OK", 1)) + optionTupleError1a == optionTupleError1b // error: cannot compare + + val eitherL1a: Either[String, Int] = Left("Error") + val eitherL1b: Either[String, Int] = Left("Error") + eitherL1a == eitherL1b + + val eitherR1a: Either[String, Int] = Right(999) + val eitherR1b: Either[String, Int] = Right(999) + eitherR1a == eitherR1b + + val eitherErrorL1a: Either[String, Int] = Left("Error") + val eitherErrorL1b: Either[Char, Int] = Left('E') + eitherErrorL1a == eitherErrorL1b // error: cannot compare + + val eitherErrorR1a: Either[String, Int] = Right(999) + val eitherErrorR1b: Either[String, String] = Right("999") + eitherErrorR1a == eitherErrorR1b // error: cannot compare + + + val eitherTupleL1a: Either[(String, Long), (Int, Boolean)] = Left(("Error", 123L)) + val eitherTupleL1b: Either[(String, Long), (Int, Boolean)] = Left(("Error", 123L)) + eitherTupleL1a == eitherTupleL1b + + val eitherTupleR1a: Either[(String, Long), (Int, Boolean)] = Right((999, true)) + val eitherTupleR1b: Either[(String, Long), (Int, Boolean)] = Right((999, true)) + eitherTupleR1a == eitherTupleR1b + + val eitherTupleErrorL1a: Either[(String, Long), (Int, Boolean)] = Left(("Error", 123L)) + val eitherTupleErrorL1b: Either[(Long, String), (Int, Boolean)] = Left((123L, "Error")) + eitherTupleErrorL1a == eitherTupleErrorL1b // error: cannot compare + + val eitherTupleErrorR1a: Either[(String, Long), (Int, Boolean)] = Right((999, true)) + val eitherTupleErrorR1b: Either[(String, Long), (Boolean, Int)] = Right((true, 999)) + eitherTupleErrorR1a == eitherTupleErrorR1b // error: cannot compare + + (1, "a") == (1, "a") + (1, "a", true) == (1, "a", true) + (1, "a", true, 't') == (1, "a", true, 't') + (1, "a", true, 't', 10L) == (1, "a", true, 't', 10L) + + (1, "a") == (1, 'a') // error: cannot compare + (1, "a") == ("a", 1) // error: cannot compare + (1, "a") == (1, "a", true) // error: cannot compare + (1, "a", true, 't', 10L) == (1, "a", 1.5D, 't', 10L) // error: cannot compare + }