@@ -26,8 +26,8 @@ import Scopes.newScope
2626import Typer .BindingPrec , BindingPrec .*
2727import Hashable .*
2828import util .{EqHashMap , Stats }
29- import config .{Config , Feature }
30- import Feature .migrateTo3
29+ import config .{Config , Feature , SourceVersion }
30+ import Feature .{ migrateTo3 , sourceVersion }
3131import config .Printers .{implicits , implicitsDetailed }
3232import collection .mutable
3333import reporting .*
@@ -324,7 +324,7 @@ object Implicits:
324324 /** Is this the outermost implicits? This is the case if it either the implicits
325325 * of NoContext, or the last one before it.
326326 */
327- private def isOuterMost = {
327+ private def isOutermost = {
328328 val finalImplicits = NoContext .implicits
329329 (this eq finalImplicits) || (outerImplicits eqn finalImplicits)
330330 }
@@ -356,7 +356,7 @@ object Implicits:
356356 Stats .record(" uncached eligible" )
357357 if monitored then record(s " check uncached eligible refs in irefCtx " , refs.length)
358358 val ownEligible = filterMatching(tp)
359- if isOuterMost then ownEligible
359+ if isOutermost then ownEligible
360360 else combineEligibles(ownEligible, outerImplicits.nn.uncachedEligible(tp))
361361
362362 /** The implicit references that are eligible for type `tp`. */
@@ -383,7 +383,7 @@ object Implicits:
383383 private def computeEligible (tp : Type ): List [Candidate ] = /* >|>*/ trace(i " computeEligible $tp in $refs%, % " , implicitsDetailed) /* <|<*/ {
384384 if (monitored) record(s " check eligible refs in irefCtx " , refs.length)
385385 val ownEligible = filterMatching(tp)
386- if isOuterMost then ownEligible
386+ if isOutermost then ownEligible
387387 else combineEligibles(ownEligible, outerImplicits.nn.eligible(tp))
388388 }
389389
@@ -392,7 +392,7 @@ object Implicits:
392392
393393 override def toString : String = {
394394 val own = i " (implicits: $refs%, %) "
395- if (isOuterMost ) own else own + " \n " + outerImplicits
395+ if (isOutermost ) own else own + " \n " + outerImplicits
396396 }
397397
398398 /** This context, or a copy, ensuring root import from symbol `root`
@@ -1550,34 +1550,117 @@ trait Implicits:
15501550 case _ =>
15511551 tp.isAny || tp.isAnyRef
15521552
1553- private def searchImplicit (contextual : Boolean ): SearchResult =
1553+ /** Search implicit in context `ctxImplicits` or else in implicit scope
1554+ * of expected type if `ctxImplicits == null`.
1555+ */
1556+ private def searchImplicit (ctxImplicits : ContextualImplicits | Null ): SearchResult =
15541557 if isUnderspecified(wildProto) then
15551558 SearchFailure (TooUnspecific (pt), span)
15561559 else
1557- val eligible =
1560+ val contextual = ctxImplicits != null
1561+ val preEligible = // the eligible candidates, ignoring positions
15581562 if contextual then
15591563 if ctx.gadt.isNarrowing then
15601564 withoutMode(Mode .ImplicitsEnabled ) {
15611565 ctx.implicits.uncachedEligible(wildProto)
15621566 }
15631567 else ctx.implicits.eligible(wildProto)
15641568 else implicitScope(wildProto).eligible
1565- searchImplicit(eligible, contextual) match
1569+
1570+ /** Does candidate `cand` come too late for it to be considered as an
1571+ * eligible candidate? This is the case if `cand` appears in the same
1572+ * scope as a given definition of the form `given ... = ...` that
1573+ * encloses the search point and `cand` comes later in the source or
1574+ * coincides with that given definition.
1575+ */
1576+ def comesTooLate (cand : Candidate ): Boolean =
1577+ val candSym = cand.ref.symbol
1578+ def candSucceedsGiven (sym : Symbol ): Boolean =
1579+ val owner = sym.owner
1580+ if owner == candSym.owner then
1581+ sym.is(GivenVal ) && sym.span.exists && sym.span.start <= candSym.span.start
1582+ else if owner.isClass then false
1583+ else candSucceedsGiven(owner)
1584+
1585+ ctx.isTyper
1586+ && ! candSym.isOneOf(TermParamOrAccessor | Synthetic )
1587+ && candSym.span.exists
1588+ && candSucceedsGiven(ctx.owner)
1589+ end comesTooLate
1590+
1591+ val eligible = // the eligible candidates that come before the search point
1592+ if contextual && sourceVersion.isAtLeast(SourceVersion .`3.4`)
1593+ then preEligible.filterNot(comesTooLate)
1594+ else preEligible
1595+
1596+ def checkResolutionChange (result : SearchResult ) =
1597+ if (eligible ne preEligible)
1598+ && ! Feature .enabled(Feature .givenLoopPrevention)
1599+ then
1600+ val prevResult = searchImplicit(preEligible, contextual)
1601+ prevResult match
1602+ case prevResult : SearchSuccess =>
1603+ def remedy = pt match
1604+ case _ : SelectionProto =>
1605+ " conversion,\n - use an import to get extension method into scope"
1606+ case _ : ViewProto =>
1607+ " conversion"
1608+ case _ =>
1609+ " argument"
1610+
1611+ def showResult (r : SearchResult ) = r match
1612+ case r : SearchSuccess => ctx.printer.toTextRef(r.ref).show
1613+ case r => r.show
1614+
1615+ result match
1616+ case result : SearchSuccess if prevResult.ref frozen_=:= result.ref =>
1617+ // OK
1618+ case _ =>
1619+ val msg =
1620+ em """ Result of implicit search for $pt will change.
1621+ |Current result ${showResult(prevResult)} will be no longer eligible
1622+ | because it is not defined before the search position.
1623+ |Result with new rules: ${showResult(result)}.
1624+ |To opt into the new rules, use the `experimental.givenLoopPrevention` language import.
1625+ |
1626+ |To fix the problem without the language import, you could try one of the following:
1627+ | - use a `given ... with` clause as the enclosing given,
1628+ | - rearrange definitions so that ${showResult(prevResult)} comes earlier,
1629+ | - use an explicit $remedy. """
1630+ if sourceVersion.isAtLeast(SourceVersion .`3.5`)
1631+ then report.error(msg, srcPos)
1632+ else report.warning(msg.append(" \n This will be an error in Scala 3.5 and later." ), srcPos)
1633+ case _ =>
1634+ prevResult
1635+ else result
1636+ end checkResolutionChange
1637+
1638+ val result = searchImplicit(eligible, contextual)
1639+ result match
15661640 case result : SearchSuccess =>
1567- result
1641+ checkResolutionChange( result)
15681642 case failure : SearchFailure =>
15691643 failure.reason match
15701644 case _ : AmbiguousImplicits => failure
15711645 case reason =>
15721646 if contextual then
1573- searchImplicit(contextual = false ).recoverWith {
1574- failure2 => failure2.reason match
1575- case _ : AmbiguousImplicits => failure2
1576- case _ =>
1577- reason match
1578- case (_ : DivergingImplicit ) => failure
1579- case _ => List (failure, failure2).maxBy(_.tree.treeSize)
1580- }
1647+ // If we filtered out some candidates for being too late, we should
1648+ // do another contextual search further out, since the dropped candidates
1649+ // might have shadowed an eligible candidate in an outer level.
1650+ // Otherwise, proceed with a search of the implicit scope.
1651+ val newCtxImplicits =
1652+ if eligible eq preEligible then null
1653+ else ctxImplicits.nn.outerImplicits: ContextualImplicits | Null
1654+ // !!! Dotty problem: without the ContextualImplicits | Null type ascription
1655+ // we get a Ycheck failure after arrayConstructors due to "Types differ"
1656+ checkResolutionChange :
1657+ searchImplicit(newCtxImplicits).recoverWith:
1658+ failure2 => failure2.reason match
1659+ case _ : AmbiguousImplicits => failure2
1660+ case _ =>
1661+ reason match
1662+ case (_ : DivergingImplicit ) => failure
1663+ case _ => List (failure, failure2).maxBy(_.tree.treeSize)
15811664 else failure
15821665 end searchImplicit
15831666
@@ -1595,7 +1678,7 @@ trait Implicits:
15951678 case ref : TermRef =>
15961679 SearchSuccess (tpd.ref(ref).withSpan(span.startPos), ref, 0 )(ctx.typerState, ctx.gadt)
15971680 case _ =>
1598- searchImplicit(contextual = true )
1681+ searchImplicit(ctx.implicits )
15991682 end bestImplicit
16001683
16011684 def implicitScope (tp : Type ): OfTypeImplicits = ctx.run.nn.implicitScope(tp)
0 commit comments