@@ -40,7 +40,7 @@ import scala.annotation.internal.sharable
40
40
import scala .annotation .threadUnsafe
41
41
42
42
/** Implicit resolution */
43
- object Implicits {
43
+ object Implicits :
44
44
import tpd ._
45
45
46
46
/** An implicit definition `implicitRef` that is visible under a different name, `alias`.
@@ -499,7 +499,7 @@ object Implicits {
499
499
class FailedExtension (extApp : Tree , val expectedType : Type ) extends SearchFailureType :
500
500
def argument = EmptyTree
501
501
def explanation (using Context ) = em " $extApp does not $qualify"
502
- }
502
+ end Implicits
503
503
504
504
import Implicits ._
505
505
@@ -723,7 +723,8 @@ trait ImplicitRunInfo:
723
723
end ImplicitRunInfo
724
724
725
725
/** The implicit resolution part of type checking */
726
- trait Implicits { self : Typer =>
726
+ trait Implicits :
727
+ self : Typer =>
727
728
728
729
import tpd ._
729
730
@@ -1082,40 +1083,38 @@ trait Implicits { self: Typer =>
1082
1083
}
1083
1084
1084
1085
/** An implicit search; parameters as in `inferImplicit` */
1085
- class ImplicitSearch (protected val pt : Type , protected val argument : Tree , span : Span )(using Context ) {
1086
+ class ImplicitSearch (protected val pt : Type , protected val argument : Tree , span : Span )(using Context ):
1086
1087
assert(argument.isEmpty || argument.tpe.isValueType || argument.tpe.isInstanceOf [ExprType ],
1087
1088
em " found: $argument: ${argument.tpe}, expected: $pt" )
1088
1089
1089
1090
private def nestedContext () =
1090
1091
ctx.fresh.setMode(ctx.mode &~ Mode .ImplicitsEnabled )
1091
1092
1092
- private def implicitProto (resultType : Type , f : Type => Type ) =
1093
- if (argument.isEmpty) f(resultType) else ViewProto (f(argument.tpe.widen), f(resultType))
1094
- // Not clear whether we need to drop the `.widen` here. All tests pass with it in place, though.
1095
-
1096
1093
private def isCoherent = pt.isRef(defn.EqlClass )
1097
1094
1098
- /** The expected type for the searched implicit */
1099
- @ threadUnsafe lazy val fullProto : Type = implicitProto(pt, identity)
1095
+ val wideProto = pt.widenExpr
1100
1096
1101
1097
/** The expected type where parameters and uninstantiated typevars are replaced by wildcard types */
1102
- val wildProto : Type = implicitProto(pt, wildApprox(_))
1098
+ val wildProto : Type =
1099
+ if argument.isEmpty then wildApprox(pt)
1100
+ else ViewProto (wildApprox(argument.tpe.widen), wildApprox(pt))
1101
+ // Not clear whether we need to drop the `.widen` here. All tests pass with it in place, though.
1103
1102
1104
1103
val isNot : Boolean = wildProto.classSymbol == defn.NotClass
1105
1104
1106
1105
/** Try to type-check implicit reference, after checking that this is not
1107
1106
* a diverging search
1108
1107
*/
1109
1108
def tryImplicit (cand : Candidate , contextual : Boolean ): SearchResult =
1110
- if (ctx.searchHistory. checkDivergence(cand, pt))
1111
- SearchFailure (new DivergingImplicit (cand.ref, pt.widenExpr , argument))
1109
+ if checkDivergence(cand) then
1110
+ SearchFailure (new DivergingImplicit (cand.ref, wideProto , argument))
1112
1111
else {
1113
1112
val history = ctx.searchHistory.nest(cand, pt)
1114
1113
val result =
1115
1114
typedImplicit(cand, pt, argument, span)(using nestedContext().setNewTyperState().setFreshGADTBounds.setSearchHistory(history))
1116
1115
result match {
1117
1116
case res : SearchSuccess =>
1118
- ctx.searchHistory.defineBynameImplicit(pt.widenExpr , res)
1117
+ ctx.searchHistory.defineBynameImplicit(wideProto , res)
1119
1118
case _ =>
1120
1119
result
1121
1120
}
@@ -1306,7 +1305,7 @@ trait Implicits { self: Typer =>
1306
1305
// effectively in a more inner context than any other definition provided by
1307
1306
// explicit definitions. Consequently these terms have the highest priority and no
1308
1307
// other candidates need to be considered.
1309
- ctx.searchHistory. recursiveRef(pt) match {
1308
+ recursiveRef(pt) match {
1310
1309
case ref : TermRef =>
1311
1310
SearchSuccess (tpd.ref(ref).withSpan(span.startPos), ref, 0 )(ctx.typerState, ctx.gadt)
1312
1311
case _ =>
@@ -1346,8 +1345,101 @@ trait Implicits { self: Typer =>
1346
1345
case success : SearchSuccess => success.ref
1347
1346
}
1348
1347
}
1349
- }
1350
- }
1348
+
1349
+ /** Fields needed for divergence checking */
1350
+ @ threadUnsafe lazy val ptCoveringSet = wideProto.coveringSet
1351
+ @ threadUnsafe lazy val ptSize = wideProto.typeSize
1352
+ @ threadUnsafe lazy val wildPt = wildApprox(wideProto)
1353
+
1354
+ /**
1355
+ * Check if the supplied candidate implicit and target type indicate a diverging
1356
+ * implicit search.
1357
+ *
1358
+ * @param cand The candidate implicit to be explored.
1359
+ * @param pt The target type for the above candidate.
1360
+ * @result True if this candidate/pt are divergent, false otherwise.
1361
+ */
1362
+ def checkDivergence (cand : Candidate )(using Context ): Boolean =
1363
+ // For full details of the algorithm see the SIP:
1364
+ // https://docs.scala-lang.org/sips/byname-implicits.html
1365
+ util.Stats .record(" checkDivergence" )
1366
+
1367
+ // Unless we are able to tie a recursive knot, we report divergence if there is an
1368
+ // open implicit using the same candidate implicit definition which has a type which
1369
+ // is larger (see `typeSize`) and is constructed using the same set of types and type
1370
+ // constructors (see `coveringSet`).
1371
+ //
1372
+ // We are able to tie a recursive knot if there is compatible term already under
1373
+ // construction which is separated from this context by at least one by name argument
1374
+ // as we ascend the chain of open implicits to the outermost search context.
1375
+
1376
+ @ tailrec
1377
+ def loop (history : SearchHistory , belowByname : Boolean ): Boolean =
1378
+ history match
1379
+ case OpenSearch (cand1, tp, outer) =>
1380
+ if cand1.ref eq cand.ref then
1381
+ util.Stats .record(" checkDivergence for sure" )
1382
+ val wideTp = tp.widenExpr
1383
+ lazy val wildTp = wildApprox(wideTp)
1384
+ lazy val tpSize = wideTp.typeSize
1385
+ if belowByname && (wildTp <:< wildPt) then
1386
+ false
1387
+ else if tpSize > ptSize || wideTp.coveringSet != ptCoveringSet then
1388
+ loop(outer, tp.isByName || belowByname)
1389
+ else
1390
+ tpSize < ptSize
1391
+ || wildTp =:= wildPt
1392
+ || loop(outer, tp.isByName || belowByname)
1393
+ else loop(outer, tp.isByName || belowByname)
1394
+ case _ => false
1395
+
1396
+ loop(ctx.searchHistory, pt.isByName)
1397
+ end checkDivergence
1398
+
1399
+ /**
1400
+ * Return the reference, if any, to a term under construction or already constructed in
1401
+ * the current search history corresponding to the supplied target type.
1402
+ *
1403
+ * A term is eligible if its type is a subtype of the target type and either it has
1404
+ * already been constructed and is present in the current implicit dictionary, or it is
1405
+ * currently under construction and is separated from the current search context by at
1406
+ * least one by name argument position.
1407
+ *
1408
+ * Note that because any suitable term found is defined as part of this search it will
1409
+ * always be effectively in a more inner context than any other definition provided by
1410
+ * explicit definitions. Consequently these terms have the highest priority and no other
1411
+ * candidates need to be considered.
1412
+ *
1413
+ * @param pt The target type being searched for.
1414
+ * @result The corresponding dictionary reference if any, NoType otherwise.
1415
+ */
1416
+ def recursiveRef (pt : Type )(using Context ): Type =
1417
+ val widePt = pt.widenExpr
1418
+
1419
+ ctx.searchHistory.refBynameImplicit(widePt).orElse {
1420
+ val bynamePt = pt.isByName
1421
+ if (! ctx.searchHistory.byname && ! bynamePt) NoType // No recursion unless at least one open implicit is by name ...
1422
+ else {
1423
+ // We are able to tie a recursive knot if there is compatible term already under
1424
+ // construction which is separated from this context by at least one by name
1425
+ // argument as we ascend the chain of open implicits to the outermost search
1426
+ // context.
1427
+ @ tailrec
1428
+ def loop (history : SearchHistory , belowByname : Boolean ): Type =
1429
+ history match
1430
+ case OpenSearch (cand, tp, outer) =>
1431
+ if (belowByname || tp.isByName) && tp.widenExpr <:< widePt then tp
1432
+ else loop(outer, belowByname || tp.isByName)
1433
+ case _ => NoType
1434
+
1435
+ loop(ctx.searchHistory, bynamePt) match
1436
+ case NoType => NoType
1437
+ case tp => ctx.searchHistory.linkBynameImplicit(tp.widenExpr)
1438
+ }
1439
+ }
1440
+ end recursiveRef
1441
+ end ImplicitSearch
1442
+ end Implicits
1351
1443
1352
1444
/**
1353
1445
* Records the history of currently open implicit searches.
@@ -1378,97 +1470,6 @@ abstract class SearchHistory:
1378
1470
1379
1471
def isByname (tp : Type ): Boolean = tp.isInstanceOf [ExprType ]
1380
1472
1381
- /**
1382
- * Check if the supplied candidate implicit and target type indicate a diverging
1383
- * implicit search.
1384
- *
1385
- * @param cand The candidate implicit to be explored.
1386
- * @param pt The target type for the above candidate.
1387
- * @result True if this candidate/pt are divergent, false otherwise.
1388
- */
1389
- def checkDivergence (cand : Candidate , pt : Type )(using Context ): Boolean = {
1390
- // For full details of the algorithm see the SIP:
1391
- // https://docs.scala-lang.org/sips/byname-implicits.html
1392
-
1393
- val widePt = pt.widenExpr
1394
- lazy val ptCoveringSet = widePt.coveringSet
1395
- lazy val ptSize = widePt.typeSize
1396
- lazy val wildPt = wildApprox(widePt)
1397
-
1398
- // Unless we are able to tie a recursive knot, we report divergence if there is an
1399
- // open implicit using the same candidate implicit definition which has a type which
1400
- // is larger (see `typeSize`) and is constructed using the same set of types and type
1401
- // constructors (see `coveringSet`).
1402
- //
1403
- // We are able to tie a recursive knot if there is compatible term already under
1404
- // construction which is separated from this context by at least one by name argument
1405
- // as we ascend the chain of open implicits to the outermost search context.
1406
-
1407
- @ tailrec
1408
- def loop (history : SearchHistory , belowByname : Boolean ): Boolean =
1409
- history match
1410
- case _ : SearchRoot => false
1411
- case OpenSearch (cand1, tp, outer) =>
1412
- if cand1.ref == cand.ref then
1413
- val wideTp = tp.widenExpr
1414
- lazy val wildTp = wildApprox(wideTp)
1415
- lazy val tpSize = wideTp.typeSize
1416
- if belowByname && (wildTp <:< wildPt) then
1417
- false
1418
- else if tpSize > ptSize || wideTp.coveringSet != ptCoveringSet then
1419
- loop(outer, isByname(tp) || belowByname)
1420
- else
1421
- tpSize < ptSize
1422
- || wildTp =:= wildPt
1423
- || loop(outer, isByname(tp) || belowByname)
1424
- else loop(outer, isByname(tp) || belowByname)
1425
-
1426
- loop(this , isByname(pt))
1427
- }
1428
-
1429
- /**
1430
- * Return the reference, if any, to a term under construction or already constructed in
1431
- * the current search history corresponding to the supplied target type.
1432
- *
1433
- * A term is eligible if its type is a subtype of the target type and either it has
1434
- * already been constructed and is present in the current implicit dictionary, or it is
1435
- * currently under construction and is separated from the current search context by at
1436
- * least one by name argument position.
1437
- *
1438
- * Note that because any suitable term found is defined as part of this search it will
1439
- * always be effectively in a more inner context than any other definition provided by
1440
- * explicit definitions. Consequently these terms have the highest priority and no other
1441
- * candidates need to be considered.
1442
- *
1443
- * @param pt The target type being searched for.
1444
- * @result The corresponding dictionary reference if any, NoType otherwise.
1445
- */
1446
- def recursiveRef (pt : Type )(using Context ): Type = {
1447
- val widePt = pt.widenExpr
1448
-
1449
- refBynameImplicit(widePt).orElse {
1450
- val bynamePt = isByname(pt)
1451
- if (! byname && ! bynamePt) NoType // No recursion unless at least one open implicit is by name ...
1452
- else {
1453
- // We are able to tie a recursive knot if there is compatible term already under
1454
- // construction which is separated from this context by at least one by name
1455
- // argument as we ascend the chain of open implicits to the outermost search
1456
- // context.
1457
- @ tailrec
1458
- def loop (history : SearchHistory , belowByname : Boolean ): Type =
1459
- history match
1460
- case OpenSearch (cand, tp, outer) =>
1461
- if (belowByname || isByname(tp)) && tp.widenExpr <:< widePt then tp
1462
- else loop(outer, belowByname || isByname(tp))
1463
- case _ => NoType
1464
-
1465
- loop(this , bynamePt) match
1466
- case NoType => NoType
1467
- case tp => ctx.searchHistory.linkBynameImplicit(tp.widenExpr)
1468
- }
1469
- }
1470
- }
1471
-
1472
1473
// The following are delegated to the root of this search history.
1473
1474
def linkBynameImplicit (tpe : Type )(using Context ): TermRef =
1474
1475
root.linkBynameImplicit(tpe)
@@ -1498,12 +1499,11 @@ final class SearchRoot extends SearchHistory:
1498
1499
def open = Nil
1499
1500
1500
1501
/** The dictionary of recursive implicit types and corresponding terms for this search. */
1501
- var implicitDictionary0 : mutable.Map [Type , (TermRef , tpd.Tree )] = null
1502
- def implicitDictionary = {
1503
- if (implicitDictionary0 == null )
1504
- implicitDictionary0 = mutable.Map .empty[Type , (TermRef , tpd.Tree )]
1505
- implicitDictionary0
1506
- }
1502
+ var myImplicitDictionary : mutable.Map [Type , (TermRef , tpd.Tree )] = null
1503
+ private def implicitDictionary =
1504
+ if myImplicitDictionary == null then
1505
+ myImplicitDictionary = mutable.Map .empty[Type , (TermRef , tpd.Tree )]
1506
+ myImplicitDictionary
1507
1507
1508
1508
/**
1509
1509
* Link a reference to an under-construction implicit for the provided type to its
@@ -1593,7 +1593,7 @@ final class SearchRoot extends SearchHistory:
1593
1593
}
1594
1594
1595
1595
val pruned = prune(List (tree), implicitDictionary.map(_._2).toList, Nil )
1596
- implicitDictionary0 = null
1596
+ myImplicitDictionary = null
1597
1597
if (pruned.isEmpty) result
1598
1598
else if (pruned.exists(_._2 == EmptyTree )) NoMatchingImplicitsFailure
1599
1599
else {
0 commit comments