Skip to content

Commit

Permalink
Refine checking for underspecified implicit queries
Browse files Browse the repository at this point in the history
 - Use the wildcard approximation instead of the original type
   since that one determined what is eligible, and the goal
   is to refuse the search if everything is eligible.
 - Also refuse underspecified implicit parameters, not just
   conversions.
 - Treat wildcard types as underspecified.

Two tests had to be reclassified. But the original tests were not meant to compile
anyway. They were bout misleading error messages (no longer the case) and crashers.
  • Loading branch information
odersky committed Nov 9, 2021
1 parent 807743a commit db5956b
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 64 deletions.
68 changes: 38 additions & 30 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Expand Up @@ -795,16 +795,8 @@ trait Implicits:
*/
def inferView(from: Tree, to: Type)(using Context): SearchResult = {
record("inferView")
val wfromtp = from.tpe.widen
if to.isAny
|| to.isAnyRef
|| to.isRef(defn.UnitClass)
|| wfromtp.isRef(defn.NothingClass)
|| wfromtp.isRef(defn.NullClass)
|| !ctx.mode.is(Mode.ImplicitsEnabled)
|| from.isInstanceOf[Super]
|| (wfromtp eq NoPrefix)
then NoMatchingImplicitsFailure
if !ctx.mode.is(Mode.ImplicitsEnabled) || from.isInstanceOf[Super] then
NoMatchingImplicitsFailure
else {
def adjust(to: Type) = to.stripTypeVar.widenExpr match {
case SelectionProto(name, memberProto, compat, true) =>
Expand Down Expand Up @@ -1434,27 +1426,43 @@ trait Implicits:
rank(sort(eligible), NoMatchingImplicitsFailure, Nil)
end searchImplicit

def isUnderSpecifiedArgument(tp: Type): Boolean =
tp.isRef(defn.NothingClass) || tp.isRef(defn.NullClass) || (tp eq NoPrefix)

private def isUnderspecified(tp: Type): Boolean = tp.stripTypeVar match
case tp: WildcardType =>
!tp.optBounds.exists || isUnderspecified(tp.optBounds.hiBound)
case tp: ViewProto =>
isUnderspecified(tp.resType)
|| tp.resType.isRef(defn.UnitClass)
|| isUnderSpecifiedArgument(tp.argType.widen)
case _ =>
tp.isAny || tp.isAnyRef

private def searchImplicit(contextual: Boolean): SearchResult =
val eligible =
if contextual then ctx.implicits.eligible(wildProto)
else implicitScope(wildProto).eligible
searchImplicit(eligible, contextual) match
case result: SearchSuccess =>
result
case failure: SearchFailure =>
failure.reason match
case _: AmbiguousImplicits => failure
case reason =>
if contextual then
searchImplicit(contextual = false).recoverWith {
failure2 => failure2.reason match
case _: AmbiguousImplicits => failure2
case _ =>
reason match
case (_: DivergingImplicit) => failure
case _ => List(failure, failure2).maxBy(_.tree.treeSize)
}
else failure
if isUnderspecified(wildProto) then
NoMatchingImplicitsFailure
else
val eligible =
if contextual then ctx.implicits.eligible(wildProto)
else implicitScope(wildProto).eligible
searchImplicit(eligible, contextual) match
case result: SearchSuccess =>
result
case failure: SearchFailure =>
failure.reason match
case _: AmbiguousImplicits => failure
case reason =>
if contextual then
searchImplicit(contextual = false).recoverWith {
failure2 => failure2.reason match
case _: AmbiguousImplicits => failure2
case _ =>
reason match
case (_: DivergingImplicit) => failure
case _ => List(failure, failure2).maxBy(_.tree.treeSize)
}
else failure
end searchImplicit

/** Find a unique best implicit reference */
Expand Down
1 change: 0 additions & 1 deletion compiler/test/dotty/tools/dotc/CompilationTests.scala
Expand Up @@ -183,7 +183,6 @@ class CompilationTests {
compileFile("tests/neg-custom-args/feature-shadowing.scala", defaultOptions.and("-Xfatal-warnings", "-feature")),
compileDir("tests/neg-custom-args/hidden-type-errors", defaultOptions.and("-explain")),
compileFile("tests/neg-custom-args/i13026.scala", defaultOptions.and("-print-lines")),
compileFile("tests/neg-custom-args/i13838.scala", defaultOptions.and("-Ximplicit-search-limit", "1000")),
).checkExpectedErrors()
}

Expand Down
32 changes: 0 additions & 32 deletions tests/neg-custom-args/i13838.check

This file was deleted.

File renamed without changes.
File renamed without changes.
47 changes: 47 additions & 0 deletions tests/neg/i13838a.scala
@@ -0,0 +1,47 @@
object TooSlow {
trait EqSyntax {
implicit def catsSyntaxEq[A: Eq](a: A): EqOps[A] = ???
}

final class EqOps[A]

object eq extends EqSyntax

import eq._

sealed abstract class Foo[A]
object Foo {
implicit def eqFoo[A: Eq]: Eq[Foo[A]] = ???
}

type FooT[F[_], A] = F[Foo[A]]
object FooT {
def liftF[F[_], A](fa: F[A]): F[Foo[A]] =
map(fa)(???) // error

def map[F[_], A, B](ffa: F[Foo[A]])(f: A => B): F[Foo[B]] =
???
}

trait Order[A] extends Eq[A]

trait Eq[A]

object Eq {
implicit def catsKernelOrderForTuple14[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13](implicit A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10], A11: Order[A11], A12: Order[A12], A13: Order[A13]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)] = ???
implicit def catsKernelOrderForTuple13[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12](implicit A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10], A11: Order[A11], A12: Order[A12]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)] = ???
implicit def catsKernelOrderForTuple12[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11](implicit A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10], A11: Order[A11]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)] = ???
implicit def catsKernelOrderForTuple11[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10](implicit A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)] = ???
implicit def catsKernelOrderForTuple10[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9](implicit A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)] = ???
implicit def catsKernelOrderForTuple9[A0, A1, A2, A3, A4, A5, A6, A7, A8](implicit A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8)] = ???
implicit def catsKernelOrderForTuple8[A0, A1, A2, A3, A4, A5, A6, A7](implicit A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7]): Order[(A0, A1, A2, A3, A4, A5, A6, A7)] = ???
implicit def catsKernelOrderForTuple7[A0, A1, A2, A3, A4, A5, A6](implicit A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6]): Order[(A0, A1, A2, A3, A4, A5, A6)] = ???
implicit def catsKernelOrderForTuple6[A0, A1, A2, A3, A4, A5](implicit A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5]): Order[(A0, A1, A2, A3, A4, A5)] = ???
implicit def catsKernelOrderForTuple5[A0, A1, A2, A3, A4](implicit A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4]): Order[(A0, A1, A2, A3, A4)] = ???
implicit def catsKernelOrderForTuple4[A0, A1, A2, A3](implicit A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3]): Order[(A0, A1, A2, A3)] = ???
implicit def catsKernelOrderForTuple3[A0, A1, A2](implicit A0: Order[A0], A1: Order[A1], A2: Order[A2]): Order[(A0, A1, A2)] = ???
implicit def catsKernelOrderForTuple2[A0, A1](implicit A0: Order[A0], A1: Order[A1]): Order[(A0, A1)] = ???
implicit def catsKernelOrderForTuple1[A0](implicit A0: Order[A0]): Order[Tuple1[A0]] = ???
}

}
2 changes: 1 addition & 1 deletion tests/pos/i7745.scala → tests/neg/i7745.scala
@@ -1,3 +1,3 @@
trait F[x]
implicit def foo[f[_], y, x <: f[y]](implicit ev: F[y]): F[x] = ???
val test = implicitly
val test = implicitly // error

0 comments on commit db5956b

Please sign in to comment.