@@ -93,7 +93,7 @@ object Implicits:
93
93
if (initctx eq NoContext ) initctx else initctx.retractMode(Mode .ImplicitsEnabled )
94
94
protected given Context = irefCtx
95
95
96
- /** The nesting level of this context. Non-zero only in ContextialImplicits */
96
+ /** The nesting level of this context. Non-zero only in ContextualImplicits */
97
97
def level : Int = 0
98
98
99
99
/** The implicit references */
@@ -408,6 +408,13 @@ object Implicits:
408
408
}
409
409
}
410
410
411
+ /** Search mode to use for possibly avoiding looping givens */
412
+ enum SearchMode :
413
+ case Old , // up to 3.3, old mode w/o protection
414
+ CompareWarn , // from 3.4, old mode, warn if new mode would change result
415
+ CompareErr , // from 3.5, old mode, error if new mode would change result
416
+ New // from future, new mode where looping givens are avoided
417
+
411
418
/** The result of an implicit search */
412
419
sealed abstract class SearchResult extends Showable {
413
420
def tree : Tree
@@ -1553,18 +1560,18 @@ trait Implicits:
1553
1560
/** Search implicit in context `ctxImplicits` or else in implicit scope
1554
1561
* of expected type if `ctxImplicits == null`.
1555
1562
*/
1556
- private def searchImplicit (ctxImplicits : ContextualImplicits | Null ): SearchResult =
1563
+ private def searchImplicit (ctxImplicits : ContextualImplicits | Null , mode : SearchMode ): SearchResult =
1557
1564
if isUnderspecified(wildProto) then
1558
1565
SearchFailure (TooUnspecific (pt), span)
1559
1566
else
1560
1567
val contextual = ctxImplicits != null
1561
1568
val preEligible = // the eligible candidates, ignoring positions
1562
- if contextual then
1569
+ if ctxImplicits != null then
1563
1570
if ctx.gadt.isNarrowing then
1564
1571
withoutMode(Mode .ImplicitsEnabled ) {
1565
- ctx.implicits .uncachedEligible(wildProto)
1572
+ ctxImplicits .uncachedEligible(wildProto)
1566
1573
}
1567
- else ctx.implicits .eligible(wildProto)
1574
+ else ctxImplicits .eligible(wildProto)
1568
1575
else implicitScope(wildProto).eligible
1569
1576
1570
1577
/** Does candidate `cand` come too late for it to be considered as an
@@ -1589,16 +1596,13 @@ trait Implicits:
1589
1596
end comesTooLate
1590
1597
1591
1598
val eligible = // the eligible candidates that come before the search point
1592
- if contextual && sourceVersion.isAtLeast( SourceVersion .`3.4`)
1599
+ if contextual && mode != SearchMode . Old
1593
1600
then preEligible.filterNot(comesTooLate)
1594
1601
else preEligible
1595
1602
1596
1603
def checkResolutionChange (result : SearchResult ) =
1597
- if (eligible ne preEligible)
1598
- && ! sourceVersion.isAtLeast(SourceVersion .future)
1599
- then
1600
- val prevResult = searchImplicit(preEligible, contextual)
1601
- prevResult match
1604
+ if (eligible ne preEligible) && mode != SearchMode .New then
1605
+ searchImplicit(preEligible, contextual) match
1602
1606
case prevResult : SearchSuccess =>
1603
1607
def remedy = pt match
1604
1608
case _ : SelectionProto =>
@@ -1628,41 +1632,38 @@ trait Implicits:
1628
1632
| - use a `given ... with` clause as the enclosing given,
1629
1633
| - rearrange definitions so that ${showResult(prevResult)} comes earlier,
1630
1634
| - use an explicit $remedy. """
1631
- if sourceVersion.isAtLeast( SourceVersion .`3.5`)
1635
+ if mode == SearchMode . CompareErr
1632
1636
then report.error(msg, srcPos)
1633
1637
else report.warning(msg.append(" \n This will be an error in Scala 3.5 and later." ), srcPos)
1634
- case _ =>
1635
- prevResult
1638
+ prevResult
1639
+ case prevResult : SearchFailure => result
1636
1640
else result
1637
1641
end checkResolutionChange
1638
1642
1639
- val result = searchImplicit(eligible, contextual)
1640
- result match
1641
- case result : SearchSuccess =>
1642
- checkResolutionChange(result)
1643
- case failure : SearchFailure =>
1644
- failure.reason match
1645
- case _ : AmbiguousImplicits => failure
1646
- case reason =>
1647
- if contextual then
1648
- // If we filtered out some candidates for being too late, we should
1649
- // do another contextual search further out, since the dropped candidates
1650
- // might have shadowed an eligible candidate in an outer level.
1651
- // Otherwise, proceed with a search of the implicit scope.
1652
- val newCtxImplicits =
1653
- if eligible eq preEligible then null
1654
- else ctxImplicits.nn.outerImplicits: ContextualImplicits | Null
1655
- // !!! Dotty problem: without the ContextualImplicits | Null type ascription
1656
- // we get a Ycheck failure after arrayConstructors due to "Types differ"
1657
- checkResolutionChange :
1658
- searchImplicit(newCtxImplicits).recoverWith:
1643
+ checkResolutionChange :
1644
+ searchImplicit(eligible, contextual).recoverWith:
1645
+ case failure : SearchFailure =>
1646
+ failure.reason match
1647
+ case _ : AmbiguousImplicits => failure
1648
+ case reason =>
1649
+ if contextual then
1650
+ // If we filtered out some candidates for being too late, we should
1651
+ // do another contextual search further out, since the dropped candidates
1652
+ // might have shadowed an eligible candidate in an outer level.
1653
+ // Otherwise, proceed with a search of the implicit scope.
1654
+ val newCtxImplicits =
1655
+ if eligible eq preEligible then null
1656
+ else ctxImplicits.nn.outerImplicits: ContextualImplicits | Null
1657
+ // !!! Dotty problem: without the ContextualImplicits | Null type ascription
1658
+ // we get a Ycheck failure after arrayConstructors due to "Types differ"
1659
+ searchImplicit(newCtxImplicits, SearchMode .New ).recoverWith:
1659
1660
failure2 => failure2.reason match
1660
1661
case _ : AmbiguousImplicits => failure2
1661
1662
case _ =>
1662
1663
reason match
1663
1664
case (_ : DivergingImplicit ) => failure
1664
1665
case _ => List (failure, failure2).maxBy(_.tree.treeSize)
1665
- else failure
1666
+ else failure
1666
1667
end searchImplicit
1667
1668
1668
1669
/** Find a unique best implicit reference */
@@ -1679,7 +1680,11 @@ trait Implicits:
1679
1680
case ref : TermRef =>
1680
1681
SearchSuccess (tpd.ref(ref).withSpan(span.startPos), ref, 0 )(ctx.typerState, ctx.gadt)
1681
1682
case _ =>
1682
- searchImplicit(ctx.implicits)
1683
+ searchImplicit(ctx.implicits,
1684
+ if sourceVersion.isAtLeast(SourceVersion .future) then SearchMode .New
1685
+ else if sourceVersion.isAtLeast(SourceVersion .`3.5`) then SearchMode .CompareErr
1686
+ else if sourceVersion.isAtLeast(SourceVersion .`3.4`) then SearchMode .CompareWarn
1687
+ else SearchMode .Old )
1683
1688
end bestImplicit
1684
1689
1685
1690
def implicitScope (tp : Type ): OfTypeImplicits = ctx.run.nn.implicitScope(tp)
0 commit comments