Skip to content

Commit 21b585d

Browse files
committed
Introduce a new implementation of implicit shadowing
Avoid building up a set of all in-scope implicits during each implicit search. Instead, do the filtering of shadowed implicits in a second pass.
1 parent c35194d commit 21b585d

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

src/compiler/scala/tools/nsc/typechecker/Implicits.scala

+76
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ trait Implicits {
257257

258258
var useCountArg: Int = 0
259259
var useCountView: Int = 0
260+
def useCount(isView: Boolean): Int = if (isView) useCountView else useCountArg
260261

261262
/** Does type `tp` contain an Error type as parameter or result?
262263
*/
@@ -995,8 +996,83 @@ trait Implicits {
995996
// most frequent one first
996997
matches sortBy (x => if (isView) -x.useCountView else -x.useCountArg)
997998
}
999+
1000+
/** Sorted list of eligible implicits.
1001+
*/
1002+
private def eligibleNew = {
1003+
final case class Candidate(info: ImplicitInfo, level: Int)
1004+
var matches: java.util.ArrayList[Candidate] = null
1005+
var matchesNames: java.util.HashSet[Name] = null
1006+
1007+
var maxCandidateLevel = 0
1008+
1009+
{
1010+
var i = 0
1011+
// Collect candidates, the level at which each was found and build a set of their names
1012+
var iss = this.iss
1013+
while (!iss.isEmpty) {
1014+
var is = iss.head
1015+
while (!is.isEmpty) {
1016+
val info = is.head
1017+
if (checkValid(info.sym) && survives(info, NoShadower)) {
1018+
if (matches == null) {
1019+
matches = new java.util.ArrayList(16)
1020+
matchesNames = new java.util.HashSet(16)
1021+
}
1022+
matches.add(Candidate(info, i))
1023+
matchesNames.add(info.name)
1024+
maxCandidateLevel = i
1025+
}
1026+
is = is.tail
1027+
}
1028+
iss = iss.tail
1029+
i += 1
1030+
}
1031+
}
1032+
1033+
if (matches == null)
1034+
Nil // OPT common case: no candidates
1035+
else {
1036+
if (isLocalToCallsite) {
1037+
// A second pass to filter out results that are shadowed by implicits in inner scopes.
1038+
var i = 0
1039+
var removed = false
1040+
var iss = this.iss
1041+
while (!iss.isEmpty && i < maxCandidateLevel) {
1042+
var is = iss.head
1043+
while (!is.isEmpty) {
1044+
val info = is.head
1045+
if (matchesNames.contains(info.name)) {
1046+
var j = 0
1047+
val numMatches = matches.size()
1048+
while (j < numMatches) {
1049+
val matchInfo = matches.get(j)
1050+
if (matchInfo != null && matchInfo.info.name == info.name && matchInfo.level > i) {
1051+
// Shadowed. For now set to null, so as not to mess up the indexing our current loop.
1052+
matches.set(j, null)
1053+
removed = true
1054+
}
1055+
j += 1
1056+
}
1057+
}
1058+
is = is.tail
1059+
}
1060+
iss = iss.tail
1061+
i += 1
1062+
}
1063+
if (removed) matches.removeIf(_ == null) // remove for real now.
1064+
}
1065+
// most frequent one first. Sort in-place.
1066+
matches.sort(((x, y) => java.lang.Integer.compare(y.info.useCount(isView), x.info.useCount(isView))))
1067+
val result = new ListBuffer[ImplicitInfo]
1068+
matches.forEach(x => result += x.info)
1069+
result.toList
1070+
}
1071+
}
1072+
9981073
if (eligible.nonEmpty)
9991074
printTyping(tree, eligible.size + s" eligible for pt=$pt at ${fullSiteString(context)}")
1075+
assert(eligibleNew == eligible, (eligibleNew, eligible))
10001076

10011077
/** Faster implicit search. Overall idea:
10021078
* - prune aggressively

0 commit comments

Comments
 (0)