From 36fb21d438847b3c01a557b03ad4b75a3e78e687 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 3 Jan 2021 11:47:21 +0100 Subject: [PATCH] Fix onwer comparison when sorting eligible implicits We used the wrong symbols to compare, which in effect meant that we sorted eligible implicits wuthout taking owner subtyping into account. This way, we might consider canidates first that would then later be pruned by another sccesful candidate in a subclass, so the search tree would be larger. Fixes #10964 --- .../dotty/tools/dotc/typer/Implicits.scala | 2 +- tests/pos/i10964.scala | 48 +++++++++++++ tests/pos/i10964a.scala | 68 +++++++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i10964.scala create mode 100644 tests/pos/i10964a.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index d9bff78ff9a3..58d009708e36 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1250,7 +1250,7 @@ trait Implicits: val arity2 = sym2.info.firstParamTypes.length if arity1 < arity2 then return true if arity1 > arity2 then return false - compareOwner(sym1, sym2) == 1 + compareOwner(sym1.owner, sym2.owner) == 1 /** Sort list of implicit references according to `prefer`. * This is just an optimization that aims at reducing the average diff --git a/tests/pos/i10964.scala b/tests/pos/i10964.scala new file mode 100644 index 000000000000..368659e12510 --- /dev/null +++ b/tests/pos/i10964.scala @@ -0,0 +1,48 @@ +trait Applicative[F[_]] + +trait FooT[F[_], A] +trait BarT[F[_], A] +trait QuxT[F[_], A] +trait BazT[F[_], A] +trait ZepT[F[_], A] +trait JazT[F[_], A] +trait LafT[F[_], A] +trait PogT[F[_], A] + +trait Sync[F[_]] +object Sync { + implicit def syncForFooT[F[_]](implicit F0: Sync[F]): Sync[[X] =>> FooT[F, X]] = ??? + implicit def syncForBarT[F[_]](implicit F0: Sync[F]): Sync[[X] =>> BarT[F, X]] = ??? + implicit def syncForQuxT[F[_]](implicit F0: Sync[F]): Sync[[X] =>> QuxT[F, X]] = ??? + implicit def syncForBazT[F[_]](implicit F0: Sync[F]): Sync[[X] =>> BazT[F, X]] = ??? + implicit def syncForZepT[F[_]](implicit F0: Sync[F]): Sync[[X] =>> ZepT[F, X]] = ??? + implicit def syncForJazT[F[_]](implicit F0: Sync[F]): Sync[[X] =>> JazT[F, X]] = ??? + // defining additional implicits beyond the 6 above seems to result in hang/OOM + implicit def syncForLafT[F[_]](implicit F0: Sync[F]): Sync[[X] =>> LafT[F, X]] = ??? + implicit def syncForPogT[F[_]](implicit F0: Sync[F]): Sync[[X] =>> PogT[F, X]] = ??? +} + +trait Ref[F[_], A] +object Ref { + trait Make[F[_]] + object Make extends MakeInstances + + trait MakeInstances extends MakeLowPriorityInstances { + implicit def applicativeInstance[F[_]](implicit F: Applicative[F]): Make[F] = ??? + } + + trait MakeLowPriorityInstances { + implicit def syncInstance[F[_]](implicit F: Sync[F]): Make[F] = ??? + } + + def of[F[_], A](a: A)(implicit mk: Make[F]): F[Ref[F, A]] = ??? +} + +class Resource[F[_], A] { + implicit def syncForResource[F[_]](implicit F0: Sync[F]): Sync[[X] =>> Resource[F, X]] = ??? + + def foo(x: F[Unit])(implicit F: Applicative[F]) = { + Ref.of /*[F, (F[Unit], F[Unit])]*/ ((x, x)) + () + } +} \ No newline at end of file diff --git a/tests/pos/i10964a.scala b/tests/pos/i10964a.scala new file mode 100644 index 000000000000..55ba1a47d266 --- /dev/null +++ b/tests/pos/i10964a.scala @@ -0,0 +1,68 @@ +trait Monoid[A] +trait Semigroup[A] +trait Applicative[F[_]] + +trait OptionT[F[_], A] +trait EitherT[F[_], A, B] +trait IorT[F[_], A, B] +trait WriterT[F[_], L, V] +trait Kleisli[F[_], A, B] + +final class ApplicativeIdOps[A](private val a: A) extends AnyVal { + def pure[F[_]](implicit F: Applicative[F]): F[A] = ??? +} + +object ApplicativeSyntax { + implicit final def syntaxApplicativeId[A](a: A): ApplicativeIdOps[A] = new ApplicativeIdOps[A](a) +} + +trait Sync[F[_]] + +object Sync { + implicit def syncForOptionT[F[_]](implicit F0: Sync[F]): Sync[[X] =>> OptionT[F, X]] = ??? + implicit def syncForEitherT[F[_], E](implicit F0: Sync[F]): Sync[[X] =>> EitherT[F, E, X]] = ??? + implicit def syncForIorT[F[_], L](implicit F0: Sync[F], L0: Semigroup[L]): Sync[[X] =>> IorT[F, L, X]] = ??? + implicit def syncForWriterT[F[_], L](implicit F0: Sync[F], L0: Monoid[L]): Sync[[X] =>> WriterT[F, L, X]] = ??? + implicit def syncForKleisli[F[_], R](implicit F0: Sync[F]): Sync[[X] =>> Kleisli[F, R, X]] = ??? +} + +trait Async[F[_]] extends Sync[F] + +object Async { + implicit def asyncForOptionT[F[_]](implicit F0: Async[F]): Async[[X] =>> OptionT[F, X]] = ??? + implicit def asyncForEitherT[F[_], E](implicit F0: Async[F]): Async[[X] =>> EitherT[F, E, X]] = ??? + implicit def asyncForIorT[F[_], L](implicit F0: Async[F], L0: Semigroup[L]): Async[[X] =>> IorT[F, L, X]] = ??? + implicit def asyncForWriterT[F[_], L](implicit F0: Async[F], L0: Monoid[L]): Async[[X] =>> WriterT[F, L, X]] = ??? + implicit def asyncForKleisli[F[_], R](implicit F0: Async[F]): Async[[X] =>> Kleisli[F, R, X]] = ??? +} + +trait Concurrent[F[_], E] extends Applicative[F] + +trait Ref[F[_], A] + +object Ref { + trait Make[F[_]] + object Make extends MakeInstances + + trait MakeInstances extends MakeLowPriorityInstances { + implicit def concurrentInstance[F[_]](implicit F: Concurrent[F, _]): Make[F] = ??? + } + + trait MakeLowPriorityInstances { + implicit def syncInstance[F[_]](implicit F: Sync[F]): Make[F] = ??? + } + + def of[F[_], A](a: A)(implicit mk: Make[F]): F[Ref[F, A]] = ??? +} + + +class Resource[F[_], A] { + import ApplicativeSyntax._ + + implicit def asyncForResource[F[_]](implicit F0: Async[F]): Async[[X] =>> Resource[F, X]] = ??? + + def parZip(implicit F: Concurrent[F, Throwable]) = { + Ref.of /*[F, (F[Unit], F[Unit])]*/ (().pure[F] -> ().pure[F]) + () + } +} \ No newline at end of file