Skip to content

Commit

Permalink
Simplify combineEligibles logic
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand committed Aug 8, 2023
1 parent 0908c3a commit d045a8b
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 34 deletions.
6 changes: 5 additions & 1 deletion compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,11 @@ class PlainPrinter(_ctx: Context) extends Printer {
else s"(no source file, offset = ${pos.span.point})"

def toText(cand: Candidate): Text =
"Candidate(" ~ toText(cand.ref) ~ ", " ~ Str("kind=" + cand.kind) ~ ", " ~ Str("lvl=" + cand.level) ~ ")"
"Cand("
~ toTextRef(cand.ref)
~ (if cand.isConversion then " conv" else "")
~ (if cand.isExtension then " ext" else "")
~ Str(" L" + cand.level) ~ ")"

def toText(result: SearchResult): Text = result match {
case result: SearchSuccess =>
Expand Down
48 changes: 18 additions & 30 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import ProtoTypes._
import ErrorReporting._
import Inferencing.{fullyDefinedType, isFullyDefined}
import Scopes.newScope
import Typer.BindingPrec, BindingPrec.*
import transform.TypeUtils._
import Hashable._
import util.{EqHashMap, Stats}
Expand All @@ -49,7 +50,7 @@ object Implicits:
}

/** Both search candidates and successes are references with a specific nesting level. */
sealed trait RefAndLevel {
sealed trait RefAndLevel extends Showable {
def ref: TermRef
def level: Int
}
Expand Down Expand Up @@ -328,41 +329,28 @@ object Implicits:
(this eq finalImplicits) || (outerImplicits eqn finalImplicits)
}

def bindingPrec: BindingPrec =
if isImport then if ctx.importInfo.uncheckedNN.isWildcardImport then WildImport else NamedImport else Definition

private def combineEligibles(ownEligible: List[Candidate], outerEligible: List[Candidate]): List[Candidate] =
if ownEligible.isEmpty then outerEligible
else if outerEligible.isEmpty then ownEligible
else
def filter(xs: List[Candidate], remove: List[Candidate]) =
// Drop candidates that are shadowed by candidates in "remove"
val shadowed = remove.map(_.ref.implicitName).toSet
xs.filterConserve(cand => !shadowed.contains(cand.ref.implicitName))

val ownNames = mutable.Set(ownEligible.map(_.ref.implicitName)*)
val outer = outerImplicits.uncheckedNN
def isWildcardImport(using Context) = ctx.importInfo.nn.isWildcardImport
def preferDefinitions = isImport && !outer.isImport
def preferNamedImport = isWildcardImport && !isWildcardImport(using outer.irefCtx)

if !migrateTo3(using irefCtx) && level == outer.level && (preferDefinitions || preferNamedImport) then
// special cases: definitions beat imports, and named imports beat
// wildcard imports, provided both are in contexts with same scope

// Using only the outer candidates at the same level as us,
// remove from our own eligibles any shadowed candidate.
// This removes locally imported candidates from shadowing local definitions, (foo's in i18316)
// but without a remotely imported candidate removing a more locally imported candidates (mkFoo's in i18183)
val ownEligible1 = filter(ownEligible, outerEligible.filter(_.level == level))

// Remove, from the outer eligibles, any candidate shadowed by one of our own candidates,
// provided that the outer eligibles aren't at the same level (so actually shadows).
// This complements the filtering of our own eligible candidates, by removing candidates in the outer candidates
// that are low-level priority and shadowed by our candidates. E.g. the outer import Imp.mkFoo in i18183.
val shadowed = ownEligible.map(_.ref.implicitName).toSet
val outerEligible1 =
outerEligible.filterConserve(cand => cand.level == level || !shadowed.contains(cand.ref.implicitName))

ownEligible1 ::: outerEligible1
if !migrateTo3(using irefCtx) && level == outer.level && outer.bindingPrec.beats(bindingPrec) then
val keptOuters = outerEligible.filterConserve: cand =>
if ownNames.contains(cand.ref.implicitName) then
val keepOuter = cand.level == level
if keepOuter then ownNames -= cand.ref.implicitName
keepOuter
else false
val keptOwn = ownEligible.filterConserve: cand =>
ownNames.contains(cand.ref.implicitName)
keptOwn ::: keptOuters
else
ownEligible ::: filter(outerEligible, ownEligible)
ownEligible ::: outerEligible.filterConserve: cand =>
!ownNames.contains(cand.ref.implicitName)

def uncachedEligible(tp: Type)(using Context): List[Candidate] =
Stats.record("uncached eligible")
Expand Down
9 changes: 6 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ object Typer {
case NothingBound, PackageClause, WildImport, NamedImport, Inheritance, Definition

def isImportPrec = this == NamedImport || this == WildImport

/** special cases: definitions beat imports, and named imports beat
* wildcard imports, provided both are in contexts with same scope */
def beats(prevPrec: BindingPrec): Boolean =
this == Definition || this == NamedImport && prevPrec == WildImport
}

/** Assert tree has a position, unless it is empty or a typed splice */
Expand Down Expand Up @@ -226,9 +231,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
def checkNewOrShadowed(found: Type, newPrec: BindingPrec, scala2pkg: Boolean = false)(using Context): Type =
if !previous.exists || TypeComparer.isSameRef(previous, found) then
found
else if (prevCtx.scope eq ctx.scope)
&& (newPrec == Definition || newPrec == NamedImport && prevPrec == WildImport)
then
else if (prevCtx.scope eq ctx.scope) && newPrec.beats(prevPrec) then
// special cases: definitions beat imports, and named imports beat
// wildcard imports, provided both are in contexts with same scope
found
Expand Down

0 comments on commit d045a8b

Please sign in to comment.