Fix #18763: Run type inference before implicit search#23020
Fix #18763: Run type inference before implicit search#23020Alex1005a wants to merge 70 commits intoscala:mainfrom
Conversation
|
The fact that type inference only happens when an "ambiguous implicit" error occurs can lead to weird things. import cats.effect.IO
import cats.implicits.*
import cats.effect.kernel.Async
import cats.Monad
case class SomeF[F[_], A]()
object SomeF {
implicit def asyncForSomeF[F[_]](implicit F: Async[F]): Async[[A] =>> SomeF[F, A]] = ???
implicit def monadForSomeF1[F[_]]: Monad[[A] =>> SomeF[F, A]] = ???
implicit def monadForSomeF2[F[_]]: Monad[[A] =>> SomeF[F, A]] = ???
def unit[F[_]]: SomeF[F, Unit] = SomeF()
}
val test: SomeF[IO, Unit] = toFunctorOps(SomeF.unit).as(???) // toFunctorOps can be omittedIn this code, for |
|
@Alex1005a, thanks for the contribution! The interaction of type inference and implicit search is tricky, since implicit search might be intended to instantiate some variables and inferring these variables beforehand might lose solutions. So a solution for one set of programs often causes failures in other programs. The problem is to find a heuristic that works uniformly better. @EugeneFlesselle, you worked on similar issues before, can you take a look at this? |
Yes, of course, I'll check it out |
# Conflicts: # compiler/src/dotty/tools/dotc/typer/Typer.scala
…matching" This reverts commit e5b7d00.
This reverts commit a0611d9.
This reverts commit f32a780.
This reverts commit 7ccb8be.
This reverts commit 817a63a.
This reverts commit a7e0680.
…ired implicit type" This reverts commit e948a02.
…r level" This reverts commit c676e30.
This reverts commit ec2bafd.
This reverts commit cf7538b.
|
@Alex1005a so 70 commits looks a bit crazy 😅 |
This PR will not be relevant and may be closed when the issue with diverging implicit search in another PR is resolved. |
Based on work by @Alex1005a in scala#23020. The compiler sometimes builds a FunProtoTyped (a function prototype where arguments are already typed), but later transformations (e.g. in wildApprox and FunProto.map) can turn it back into a FunProto. This is a problem because: - FunProto assumes arguments are not typed yet - FunProtoTyped assumes they already are Switching between these representations breaks internal assumptions and can lead to crashes, e.g. in `collectParts`: case t: TypeParamRef => assert(!ctx.typerState.constraint.contains(t), i"`wildApprox` failed to remove uninstantiated $t") This change preserve the FunProtoTyped representation: - wildApprox(FunProto) now always returns FunProtoTyped - FunProtoTyped.map now preserves the subtype via derivedFunProtoTyped Background: - scala#17305 (discussion around wildApprox) - scala#17471 by @andrzejressel (with review comments by @odersky) - scala#23020 by @Alex1005a (handling of FunProtoTyped) Signed-off-by: Piotr Paradzinski <dancingwithopenheart@gmail.com>
|
Hi @Alex1005a, thanks for your work on this
While investigating #24824, I ended up using your approach - I copied the way you handle I’m currently using #25662 as a small playground to validate that this is indeed the correct fix. If you’d like to extract a minimal fix for 24824 from your PR, |
|
Hi @dancewithheart. Glad to see a PR with the proposed fix! I wasn’t planning to work on this issue, so I’d be happy if you take care of it. |


Fixes #18763
It seems that the problem is that searching for an instance of
Functor[F]for the implicit conversiontoFunctorOpstries all possible variants, becauseFis not constrained in any way. After failure in the case ofAmbiguousImplicits, type inference occurs (Fis constrained toIO) and the required instance is found. Inferring types before searching for an instance will help to avoid such cases.