@@ -24,6 +24,7 @@ import Constants._
24
24
import Applications ._
25
25
import ProtoTypes ._
26
26
import ErrorReporting ._
27
+ import reporting .diagnostic .MessageContainer
27
28
import Inferencing .fullyDefinedType
28
29
import Trees ._
29
30
import Hashable ._
@@ -212,6 +213,8 @@ object Implicits {
212
213
/** A "no matching implicit found" failure */
213
214
case object NoImplicitMatches extends SearchFailure
214
215
216
+ case object DivergingImplicit extends SearchFailure
217
+
215
218
/** A search failure that can show information about the cause */
216
219
abstract class ExplainedSearchFailure extends SearchFailure {
217
220
protected def pt : Type
@@ -233,9 +236,35 @@ object Implicits {
233
236
" \n " + explanation
234
237
}
235
238
236
- class NonMatchingImplicit (ref : TermRef , val pt : Type , val argument : tpd.Tree ) extends ExplainedSearchFailure {
237
- def explanation (implicit ctx : Context ): String =
238
- em " ${err.refStr(ref)} does not $qualify"
239
+ class NonMatchingImplicit (ref : TermRef ,
240
+ val pt : Type ,
241
+ val argument : tpd.Tree ,
242
+ trail : List [MessageContainer ]) extends ExplainedSearchFailure {
243
+ private val separator = " \n **** because ****\n "
244
+
245
+ /** Replace repeated parts beginning with `separator` by ... */
246
+ private def elideRepeated (str : String ): String = {
247
+ val startIdx = str.indexOfSlice(separator)
248
+ val nextIdx = str.indexOfSlice(separator, startIdx + separator.length)
249
+ if (nextIdx < 0 ) str
250
+ else {
251
+ val prefix = str.take(startIdx)
252
+ val first = str.slice(startIdx, nextIdx)
253
+ var rest = str.drop(nextIdx)
254
+ if (rest.startsWith(first)) {
255
+ rest = rest.drop(first.length)
256
+ val dots = " \n\n ...\n "
257
+ if (! rest.startsWith(dots)) rest = dots ++ rest
258
+ }
259
+ prefix ++ first ++ rest
260
+ }
261
+ }
262
+
263
+ def explanation (implicit ctx : Context ): String = {
264
+ val headMsg = em " ${err.refStr(ref)} does not $qualify"
265
+ val trailMsg = trail.map(mc => i " $separator ${mc.message}" ).mkString
266
+ elideRepeated(headMsg ++ trailMsg)
267
+ }
239
268
}
240
269
241
270
class ShadowedImplicit (ref : TermRef , shadowing : Type , val pt : Type , val argument : tpd.Tree ) extends ExplainedSearchFailure {
@@ -273,9 +302,10 @@ trait ImplicitRunInfo { self: RunInfo =>
273
302
* a type variable, we need the current context, the current
274
303
* runinfo context does not do.
275
304
*/
276
- def implicitScope (tp : Type , liftingCtx : Context ): OfTypeImplicits = {
305
+ def implicitScope (rootTp : Type , liftingCtx : Context ): OfTypeImplicits = {
277
306
278
307
val seen : mutable.Set [Type ] = mutable.Set ()
308
+ val incomplete : mutable.Set [Type ] = mutable.Set ()
279
309
280
310
/** Replace every typeref that does not refer to a class by a conjunction of class types
281
311
* that has the same implicit scope as the original typeref. The motivation for applying
@@ -309,16 +339,23 @@ trait ImplicitRunInfo { self: RunInfo =>
309
339
}
310
340
}
311
341
312
- def iscopeRefs (tp : Type ): TermRefSet =
313
- if (seen contains tp) EmptyTermRefSet
314
- else {
315
- seen += tp
316
- iscope(tp).companionRefs
317
- }
318
-
319
342
// todo: compute implicits directly, without going via companionRefs?
320
343
def collectCompanions (tp : Type ): TermRefSet = track(" computeImplicitScope" ) {
321
344
ctx.traceIndented(i " collectCompanions( $tp) " , implicits) {
345
+
346
+ def iscopeRefs (t : Type ): TermRefSet = implicitScopeCache.get(t) match {
347
+ case Some (is) =>
348
+ is.companionRefs
349
+ case None =>
350
+ if (seen contains t) {
351
+ incomplete += tp // all references to rootTo will be accounted for in `seen` so we return `EmptySet`.
352
+ EmptyTermRefSet // on the other hand, the refs of `tp` are now not accurate, so `tp` is marked incomplete.
353
+ } else {
354
+ seen += t
355
+ iscope(t).companionRefs
356
+ }
357
+ }
358
+
322
359
val comps = new TermRefSet
323
360
tp match {
324
361
case tp : NamedType =>
@@ -356,7 +393,8 @@ trait ImplicitRunInfo { self: RunInfo =>
356
393
* @param isLifted Type `tp` is the result of a `liftToClasses` application
357
394
*/
358
395
def iscope (tp : Type , isLifted : Boolean = false ): OfTypeImplicits = {
359
- def computeIScope (cacheResult : Boolean ) = {
396
+ val canCache = Config .cacheImplicitScopes && tp.hash != NotCached
397
+ def computeIScope () = {
360
398
val savedEphemeral = ctx.typerState.ephemeral
361
399
ctx.typerState.ephemeral = false
362
400
try {
@@ -367,33 +405,23 @@ trait ImplicitRunInfo { self: RunInfo =>
367
405
else
368
406
collectCompanions(tp)
369
407
val result = new OfTypeImplicits (tp, refs)(ctx)
370
- if (ctx.typerState.ephemeral) record(" ephemeral cache miss: implicitScope" )
371
- else if (cacheResult) implicitScopeCache(tp) = result
408
+ if (ctx.typerState.ephemeral)
409
+ record(" ephemeral cache miss: implicitScope" )
410
+ else if (canCache &&
411
+ ((tp eq rootTp) || // first type traversed is always cached
412
+ ! incomplete.contains(tp) && // other types are cached if they are not incomplete
413
+ result.companionRefs.forall( // and all their companion refs are cached
414
+ implicitScopeCache.contains)))
415
+ implicitScopeCache(tp) = result
372
416
result
373
417
}
374
418
finally ctx.typerState.ephemeral |= savedEphemeral
375
419
}
376
-
377
- if (tp.hash == NotCached || ! Config .cacheImplicitScopes)
378
- computeIScope(cacheResult = false )
379
- else implicitScopeCache get tp match {
380
- case Some (is) => is
381
- case None =>
382
- // Implicit scopes are tricky to cache because of loops. For example
383
- // in `tests/pos/implicit-scope-loop.scala`, the scope of B contains
384
- // the scope of A which contains the scope of B. We break the loop
385
- // by returning EmptyTermRefSet in `collectCompanions` for types
386
- // that we have already seen, but this means that we cannot cache
387
- // the computed scope of A, it is incomplete.
388
- // Keeping track of exactly where these loops happen would require a
389
- // lot of book-keeping, instead we choose to be conservative and only
390
- // cache scopes before any type has been seen. This is unfortunate
391
- // because loops are very common for types in scala.collection.
392
- computeIScope(cacheResult = seen.isEmpty)
393
- }
420
+ if (canCache) implicitScopeCache.getOrElse(tp, computeIScope())
421
+ else computeIScope()
394
422
}
395
423
396
- iscope(tp )
424
+ iscope(rootTp )
397
425
}
398
426
399
427
/** A map that counts the number of times an implicit ref was picked */
@@ -587,7 +615,7 @@ trait Implicits { self: Typer =>
587
615
val wildProto = implicitProto(pt, wildApprox(_))
588
616
589
617
/** Search failures; overridden in ExplainedImplicitSearch */
590
- protected def nonMatchingImplicit (ref : TermRef ): SearchFailure = NoImplicitMatches
618
+ protected def nonMatchingImplicit (ref : TermRef , trail : List [ MessageContainer ] ): SearchFailure = NoImplicitMatches
591
619
protected def divergingImplicit (ref : TermRef ): SearchFailure = NoImplicitMatches
592
620
protected def shadowedImplicit (ref : TermRef , shadowing : Type ): SearchFailure = NoImplicitMatches
593
621
protected def failedSearch : SearchFailure = NoImplicitMatches
@@ -628,7 +656,7 @@ trait Implicits { self: Typer =>
628
656
{ implicits.println(i " invalid eqAny[ $tp1, $tp2] " ); false }
629
657
}
630
658
if (ctx.reporter.hasErrors)
631
- nonMatchingImplicit(ref)
659
+ nonMatchingImplicit(ref, ctx.reporter.removeBufferedMessages )
632
660
else if (contextual && ! ctx.mode.is(Mode .ImplicitShadowing ) &&
633
661
! shadowing.tpe.isError && ! refMatches(shadowing)) {
634
662
implicits.println(i " SHADOWING $ref in ${ref.termSymbol.owner} is shadowed by $shadowing in ${shadowing.symbol.owner}" )
@@ -637,7 +665,7 @@ trait Implicits { self: Typer =>
637
665
else generated1 match {
638
666
case TypeApply (fn, targs @ (arg1 :: arg2 :: Nil ))
639
667
if fn.symbol == defn.Predef_eqAny && ! validEqAnyArgs(arg1.tpe, arg2.tpe) =>
640
- nonMatchingImplicit(ref)
668
+ nonMatchingImplicit(ref, Nil )
641
669
case _ =>
642
670
SearchSuccess (generated1, ref, ctx.typerState)
643
671
}
@@ -743,8 +771,8 @@ trait Implicits { self: Typer =>
743
771
fail
744
772
}
745
773
def failures = myFailures.toList
746
- override def nonMatchingImplicit (ref : TermRef ) =
747
- record(new NonMatchingImplicit (ref, pt, argument))
774
+ override def nonMatchingImplicit (ref : TermRef , trail : List [ MessageContainer ] ) =
775
+ record(new NonMatchingImplicit (ref, pt, argument, trail ))
748
776
override def divergingImplicit (ref : TermRef ) =
749
777
record(new DivergingImplicit (ref, pt, argument))
750
778
override def shadowedImplicit (ref : TermRef , shadowing : Type ): SearchFailure =
0 commit comments