Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In OfTypeImplicits, only consider refs that don't appear in companionRefs as possible candidates #18691

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ object Implicits:
protected def isAccessible(ref: TermRef)(using Context): Boolean

/** Return those references in `refs` that are compatible with type `pt`. */
protected def filterMatching(pt: Type)(using Context): List[Candidate] = {
protected def filterMatching(pt: Type, extensionOnly: Boolean, forEach: ((ImplicitRef => Unit) => Unit))(using Context): List[Candidate] = {
record("filterMatching")

val considerExtension = pt match
Expand Down Expand Up @@ -244,24 +244,36 @@ object Implicits:
ckind
}

val candidates = new mutable.ListBuffer[Candidate]
def tryCandidate(ref: ImplicitRef) = trace.force(i"trying $ref as candidate (extensionOnly = $extensionOnly)"){
var ckind = exploreInFreshCtx { (ctx: FreshContext) ?=>
ctx.setMode(ctx.mode &~ Mode.SafeNulls | Mode.TypevarsMissContext)
candidateKind(ref.underlyingRef)
}
if extensionOnly then ckind &= Candidate.Extension
if ckind != Candidate.None then
// println(i"Getting implicit ref $ref (underlying = ${ref.underlyingRef})")
candidates += Candidate(ref, ckind, level)
true
else false
}
forEach(tryCandidate)
candidates.toList
}

protected def filterMatching(pt: Type)(using Context): List[Candidate] = {
val considerExtension = pt match
case ViewProto(_, _: SelectionProto) => true
case _ => false

if refs.isEmpty && (!considerExtension || companionRefs.isEmpty) then
Nil
else
val candidates = new mutable.ListBuffer[Candidate]
def tryCandidate(extensionOnly: Boolean)(ref: ImplicitRef) =
var ckind = exploreInFreshCtx { (ctx: FreshContext) ?=>
ctx.setMode(ctx.mode &~ Mode.SafeNulls | Mode.TypevarsMissContext)
candidateKind(ref.underlyingRef)
}
if extensionOnly then ckind &= Candidate.Extension
if ckind != Candidate.None then
candidates += Candidate(ref, ckind, level)

if considerExtension then
companionRefs.foreach(tryCandidate(extensionOnly = true))
candidates ++= filterMatching(pt, true, companionRefs.foreach)
if refs.nonEmpty then
refs.foreach(tryCandidate(extensionOnly = false))
candidates ++= filterMatching(pt, false, refs.foreach)
candidates.toList
}
}
Expand All @@ -272,17 +284,19 @@ object Implicits:
*/
class OfTypeImplicits(tp: Type, override val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) {
implicits.println(i"implicit scope of type $tp = ${companionRefs.showAsList}%, %")
@threadUnsafe lazy val refs: List[ImplicitRef] = {
@threadUnsafe private lazy val termRefs: List[TermRef] = {
val buf = new mutable.ListBuffer[TermRef]
for (companion <- companionRefs) buf ++= companion.implicitMembers
buf.toList
}

lazy val refs: List[ImplicitRef] = termRefs

/** The candidates that are eligible for expected type `tp` */
@threadUnsafe lazy val eligible: List[Candidate] =
trace(i"eligible($tp), companions = ${companionRefs.showAsList}%, %", implicitsDetailed, show = true) {
if (refs.nonEmpty && monitored) record(s"check eligible refs in tpe", refs.length)
filterMatching(tp)
filterMatching(tp, true, companionRefs.foreach) ++ filterMatching(tp, false, termRefs.filter(t => !companionRefs.contains(t)).foreach)
}

override def isAccessible(ref: TermRef)(using Context): Boolean =
Expand Down Expand Up @@ -1946,6 +1960,16 @@ sealed class TermRefSet(using Context):
case prefixes: List[Type] =>
if !prefixes.exists(_ =:= pre) then elems.put(sym, pre :: prefixes)

def contains(ref: TermRef): Boolean =
val pre = ref.prefix
if ref.symbol.exists then
val sym = ref.symbol.asTerm
elems.get(sym) match
case null => false
case prefix: Type => prefix =:= pre
case prefixes: List[Type] => prefixes.exists(_ =:= pre)
else false

def ++= (that: TermRefSet): Unit =
if !that.isEmpty then that.foreach(+=)

Expand Down
Loading