Skip to content

Commit 1facb3e

Browse files
committed
Fix implicit scope
The previous implicit scope was hard to understand, and did not conform to spec. The new implementation is simpler to understand. It also does not conform to spec as written but there's a chance that we might change the spec to conform to it.
1 parent d3ebb98 commit 1facb3e

File tree

1 file changed

+49
-38
lines changed

1 file changed

+49
-38
lines changed

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -483,28 +483,31 @@ trait ImplicitRunInfo { self: Run =>
483483
val seen: mutable.Set[Type] = mutable.Set()
484484
val incomplete: mutable.Set[Type] = mutable.Set()
485485

486-
/** Replace every typeref that does not refer to a class by a conjunction of class types
486+
/** Is `sym` an anchor type for which givens may exist? Anchor types are classes,
487+
* opaque type aliases, and abstract types, but not type parameters
488+
*/
489+
def isAnchor(sym: Symbol) = sym.isClass && !sym.is(Package) || sym.isOpaqueAlias
490+
491+
def anchors(tp: Type): List[Type] = tp match {
492+
case tp: NamedType if isAnchor(tp.symbol) => tp :: Nil
493+
case tp: TypeProxy => anchors(tp.superType)
494+
case tp: AndOrType => anchors(tp.tp1) ++ anchors(tp.tp2)
495+
case _ => Nil
496+
}
497+
498+
/** Replace every typeref that does not refer to a class by a conjunction of anchor types
487499
* that has the same implicit scope as the original typeref. The motivation for applying
488500
* this map is that it reduces the total number of types for which we need to
489501
* compute and cache the implicit scope; all variations wrt type parameters or
490502
* abstract types are eliminated.
491503
*/
492-
object liftToClasses extends TypeMap {
504+
object liftToAnchors extends TypeMap {
493505
override implicit protected val ctx: Context = liftingCtx
494506
override def stopAtStatic = true
495507

496-
private def isLiftTarget(sym: Symbol) = sym.isClass || sym.isOpaqueAlias
497-
498508
def apply(tp: Type) = tp match {
499509
case tp: TypeRef =>
500-
if (isLiftTarget(tp.symbol)) tp
501-
else {
502-
val pre = tp.prefix
503-
def joinClass(tp: Type, cls: Symbol) =
504-
AndType.make(tp, cls.typeRef.asSeenFrom(pre, cls.owner))
505-
val lead = if (pre eq NoPrefix) defn.AnyType else apply(pre)
506-
(lead /: tp.parentSymbols(isLiftTarget))(joinClass)
507-
}
510+
((defn.AnyType: Type) /: anchors(tp))(AndType.make(_, _))
508511
case tp: TypeVar =>
509512
apply(tp.underlying)
510513
case tp: AppliedType if !tp.tycon.typeSymbol.isClass =>
@@ -521,7 +524,6 @@ trait ImplicitRunInfo { self: Run =>
521524
}
522525
}
523526

524-
// todo: compute implicits directly, without going via companionRefs?
525527
def collectCompanions(tp: Type): TermRefSet = track("computeImplicitScope") {
526528
trace(i"collectCompanions($tp)", implicitsDetailed) {
527529

@@ -541,32 +543,41 @@ trait ImplicitRunInfo { self: Run =>
541543
}
542544

543545
val comps = new TermRefSet
546+
def addCompanion(pre: Type, companion: Symbol) =
547+
if (companion.exists && !companion.isAbsent) comps += TermRef(pre, companion)
548+
def addCompanionNamed(pre: Type, name: TermName) =
549+
addCompanion(pre, pre.member(name).suchThat(_.is(Module)).symbol)
550+
551+
def addPath(pre: Type): Unit = pre.dealias match {
552+
case pre: ThisType if pre.cls.is(Module) && pre.cls.isStaticOwner =>
553+
addPath(pre.cls.sourceModule.termRef)
554+
case pre: TermRef =>
555+
if (pre.symbol.is(Package)) {
556+
if (ctx.scala2Mode) {
557+
addCompanionNamed(pre, nme.PACKAGE)
558+
addPath(pre.prefix)
559+
}
560+
}
561+
else {
562+
comps += pre
563+
addPath(pre.prefix)
564+
}
565+
case _ =>
566+
}
544567
tp match {
545-
case tp: NamedType =>
546-
if (!tp.symbol.is(Package) || ctx.scala2Mode) {
547-
// Don't consider implicits in package prefixes unless under -language:Scala2
568+
case tp: TermRef =>
569+
addPath(tp.prefix)
570+
case tp: TypeRef =>
571+
val sym = tp.symbol
572+
if (isAnchor(sym)) {
548573
val pre = tp.prefix
549-
comps ++= iscopeRefs(pre)
550-
def addRef(companion: TermRef): Unit = {
551-
val compSym = companion.symbol
552-
if (compSym is Package) {
553-
assert(ctx.scala2Mode)
554-
addRef(companion.select(nme.PACKAGE))
555-
}
556-
else if (compSym.exists)
557-
comps += companion.asSeenFrom(pre, compSym.owner).asInstanceOf[TermRef]
558-
}
559-
def addCompanionOf(sym: Symbol) = {
560-
val companion = sym.companionModule
561-
if (companion.exists) addRef(companion.termRef)
562-
}
563-
def addClassScope(cls: ClassSymbol): Unit = {
564-
addCompanionOf(cls)
565-
for (parent <- cls.classParents; ref <- iscopeRefs(tp.baseType(parent.classSymbol)))
566-
addRef(ref)
567-
}
568-
tp.classSymbols(liftingCtx).foreach(addClassScope)
574+
addPath(pre)
575+
if (sym.is(Module)) addCompanion(pre, sym.sourceModule)
576+
else if (sym.isClass) addCompanion(pre, sym.companionModule)
577+
else addCompanionNamed(pre, sym.name.stripModuleClassSuffix.toTermName)
569578
}
579+
val superAnchors = if (sym.isClass) tp.parents else anchors(tp.superType)
580+
for (anchor <- superAnchors) comps ++= iscopeRefs(anchor)
570581
case _ =>
571582
for (part <- tp.namedPartsWith(_.isType)) comps ++= iscopeRefs(part)
572583
}
@@ -575,12 +586,12 @@ trait ImplicitRunInfo { self: Run =>
575586
}
576587

577588
/** The implicit scope of type `tp`
578-
* @param isLifted Type `tp` is the result of a `liftToClasses` application
589+
* @param isLifted Type `tp` is the result of a `liftToAnchors` application
579590
*/
580591
def iscope(tp: Type, isLifted: Boolean = false): OfTypeImplicits = {
581592
val canCache = Config.cacheImplicitScopes && tp.hash != NotCached && !tp.isProvisional
582593
def computeIScope() = {
583-
val liftedTp = if (isLifted) tp else liftToClasses(tp)
594+
val liftedTp = if (isLifted) tp else liftToAnchors(tp)
584595
val refs =
585596
if (liftedTp ne tp) {
586597
implicitsDetailed.println(i"lifted of $tp = $liftedTp")

0 commit comments

Comments
 (0)