Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prune polymorphic implicits more aggressively #6580

Merged
merged 2 commits into from Aug 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
54 changes: 53 additions & 1 deletion src/compiler/scala/tools/nsc/typechecker/Implicits.scala
Expand Up @@ -650,6 +650,43 @@ trait Implicits {
}
}

private def matchesPtInst(info: ImplicitInfo): Boolean = {
def isViewLike = pt match {
case Function1(_, _) => true
case _ => false
}

if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(matchesPtInstCalls)
info.tpe match {
case PolyType(tparams, restpe) =>
try {
val allUndetparams = (undetParams ++ tparams).distinct
val tvars = allUndetparams map freshVar
val tp = ApproximateDependentMap(restpe)
val tpInstantiated = tp.instantiateTypeParams(allUndetparams, tvars)
if(!matchesPt(tpInstantiated, wildPt, allUndetparams)) {
if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(matchesPtInstMismatch1)
false
} else if(!isView && !isViewLike) {
// we can't usefully prune views any further because we would need to type an application
// of the view to the term as is done in the computation of itree2 in typedImplicit1.
tvars.foreach(_.constr.stopWideningIfPrecluded)
val targs = solvedTypes(tvars, allUndetparams, allUndetparams map varianceInType(wildPt), upper = false, lubDepth(tpInstantiated :: wildPt :: Nil))
val AdjustedTypeArgs(okParams, okArgs) = adjustTypeArgs(allUndetparams, tvars, targs)
val remainingUndet = allUndetparams diff okParams
val tpSubst = deriveTypeWithWildcards(remainingUndet)(tp.instantiateTypeParams(okParams, okArgs))
if(!matchesPt(tpSubst, wildPt, remainingUndet)) {
if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(matchesPtInstMismatch2)
false
} else true
} else true
} catch {
case _: NoInstance => false
}
case _ => true
}
}

/** Capturing the overlap between isPlausiblyCompatible and normSubType.
* This is a faithful translation of the code which was there, but it
* seems likely the methods are intended to be even more similar than
Expand Down Expand Up @@ -1065,6 +1102,13 @@ trait Implicits {
* - find the most likely one
* - if it matches, forget about all others it improves upon
*/

// the pt for views can have embedded unification type variables, BoundedWildcardTypes or
// Nothings which can't be solved for. Rather than attempt to patch things up later we
// just skip those cases altogether.
lazy val wildPtNotInstantiable =
wildPt.exists { case _: BoundedWildcardType | _: TypeVar => true ; case tp if typeIsNothing(tp) => true; case _ => false }

@tailrec private def rankImplicits(pending: Infos, acc: List[(SearchResult, ImplicitInfo)]): List[(SearchResult, ImplicitInfo)] = pending match {
case Nil => acc
case firstPending :: otherPending =>
Expand All @@ -1080,7 +1124,9 @@ trait Implicits {

val savedInfos = undetParams.map(_.info)
val typedFirstPending = try {
typedImplicit(firstPending, ptChecked = true, isLocalToCallsite)
if(wildPtNotInstantiable || matchesPtInst(firstPending))
typedImplicit(firstPending, ptChecked = true, isLocalToCallsite)
else SearchFailure
} finally {
foreach2(undetParams, savedInfos){ (up, si) => up.setInfo(si) }
}
Expand Down Expand Up @@ -1804,4 +1850,10 @@ trait ImplicitsStats {
val matchesPtNanos = newSubTimer (" matchesPT", typerNanos)
val implicitCacheAccs = newCounter ("implicit cache accesses", "typer")
val implicitCacheHits = newSubCounter("implicit cache hits", implicitCacheAccs)

val matchesPtInstCalls = newCounter ("implicits instantiated for pruning")
val matchesPtInstMismatch1
= newSubCounter(" immediate mismatches", matchesPtInstCalls)
val matchesPtInstMismatch2
= newSubCounter(" instantiated mismatches", matchesPtInstCalls)
}
2 changes: 1 addition & 1 deletion test/files/neg/sortedImplicitNotFound.check
Expand Up @@ -53,7 +53,7 @@ sortedImplicitNotFound.scala:77: error: No implicit Ordering[Object] found to bu
es.flatMap(_ => List(o))
^
sortedImplicitNotFound.scala:80: error: diverging implicit expansion for type Ordering[(WeekDay.Value, Object)]
starting with method $conforms in object Predef
starting with method orderingToOrdered in object Ordered
es.zip(List(o)) // ah well...: diverging implicit expansion for type Ordering[(WeekDay.Value, Object)]
^
sortedImplicitNotFound.scala:83: error: No implicit Ordering[Object] found to build a SortedSet[Object]. You may want to upcast to a Set[Value] first by calling `unsorted`.
Expand Down
13 changes: 13 additions & 0 deletions test/files/pos/prune-poly-bound.scala
@@ -0,0 +1,13 @@
class Base[T0]
class Derived[T1] extends Base[T1]

class Foo[T2, U2]

object Foo {
implicit def mkFoo[T3, U3 <: Base[T3]](implicit ev: U3 <:< Base[T3]) : Foo[U3, Base[T3]] = ???
}

object Test {
def foo[T4, U4](t: T4)(implicit ftu: Foo[T4, U4]): U4 = ???
val bi: Base[Int] = foo(null.asInstanceOf[Derived[Int]])
}
19 changes: 19 additions & 0 deletions test/files/pos/prune-poly-f-bounded-view.scala
@@ -0,0 +1,19 @@
object Foo {
implicit def toBar[T <: Bar[T]](t: T): Baz = ???
}

import Foo._

trait Bar[T]

class Baz {
def wibble = 23
}

class Quux extends Bar[Quux] {
def blah = this.wibble
}

object Test {
(new Quux).blah
}
12 changes: 12 additions & 0 deletions test/files/pos/prune-poly-infer-nothing.scala
@@ -0,0 +1,12 @@
object Test {
trait Pure[+A]
trait Stream[+F[_], +O]
object Stream {
implicit def covaryPure[F[_], O, O2 >: O](s: Stream[Pure, O]): Stream[F, O2] = ???
def empty: Stream[Pure, Nothing] = ???
}

type EntityBody[+F[_]] = Stream[F, Byte]

val EmptyBody: EntityBody[Nothing] = Stream.empty
}
30 changes: 30 additions & 0 deletions test/files/pos/prune-poly-view.scala
@@ -0,0 +1,30 @@
object Test {
class Foo[T]
object Foo {
implicit def fromT[T](t: T): Foo[T] = ???
}

def bar[T](foo: Foo[T]) = ???

bar[Double](foo = 0)
}

object Test2 {
class Foo[T]
object Foo {
implicit def fromT[T](t: T): Foo[T] = ???
}

def bar[T](foo: Foo[T]) = ???

class C
object O extends C

bar[C](foo = O)
}

object Test3 {
implicit def toOption[T](v: T): Option[T] = Option(v)
val a: Int = 123
val b: Option[Long] = a // Works under 2.12.6 but not with the implicit-poly-prune-2.12.x PR
}