Skip to content

Commit 207c81c

Browse files
committed
Fix #9103: Add additional config to NamedParts accumulator
The NamedParts accumulator always searched the underlying types of TermRef, but this is the right thing to do only for avoidance. In other situations, notably implicit scope construction we should only count the TermRef itself, but not its underlying type.
1 parent b852e14 commit 207c81c

File tree

7 files changed

+67
-25
lines changed

7 files changed

+67
-25
lines changed

compiler/src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,8 @@ object TypeOps:
385385
val widenMap = new ApproximatingTypeMap {
386386
@threadUnsafe lazy val forbidden = symsToAvoid.toSet
387387
def toAvoid(sym: Symbol) = !sym.isStatic && forbidden.contains(sym)
388-
def partsToAvoid = new NamedPartsAccumulator(tp => toAvoid(tp.symbol))
388+
def partsToAvoid =
389+
new NamedPartsAccumulator(tp => toAvoid(tp.symbol), widenTermRefs = true)
389390
def apply(tp: Type): Type = tp match {
390391
case tp: TermRef
391392
if toAvoid(tp.symbol) || partsToAvoid(mutable.Set.empty, tp.info).nonEmpty =>

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -385,20 +385,18 @@ object Types {
385385
final def foreachPart(p: Type => Unit, stopAtStatic: Boolean = false)(implicit ctx: Context): Unit =
386386
new ForeachAccumulator(p, stopAtStatic).apply((), this)
387387

388-
/** The parts of this type which are type or term refs */
389-
final def namedParts(implicit ctx: Context): collection.Set[NamedType] =
390-
namedPartsWith(alwaysTrue)
391-
392388
/** The parts of this type which are type or term refs and which
393389
* satisfy predicate `p`.
394390
*
395391
* @param p The predicate to satisfy
396392
* @param excludeLowerBounds If set to true, the lower bounds of abstract
397393
* types will be ignored.
398394
*/
399-
def namedPartsWith(p: NamedType => Boolean, excludeLowerBounds: Boolean = false)
400-
(implicit ctx: Context): collection.Set[NamedType] =
401-
new NamedPartsAccumulator(p, excludeLowerBounds).apply(mutable.LinkedHashSet(), this)
395+
def namedPartsWith(p: NamedType => Boolean,
396+
widenTermRefs: Boolean = false,
397+
excludeLowerBounds: Boolean = false)
398+
(implicit ctx: Context): collection.Set[NamedType] =
399+
new NamedPartsAccumulator(p, widenTermRefs, excludeLowerBounds).apply(mutable.LinkedHashSet(), this)
402400

403401
/** Map function `f` over elements of an AndType, rebuilding with function `g` */
404402
def mapReduceAnd[T](f: Type => T)(g: (T, T) => T)(implicit ctx: Context): T = stripTypeVar match {
@@ -5453,8 +5451,10 @@ object Types {
54535451
def apply(x: Unit, tp: Type): Unit = foldOver(p(tp), tp)
54545452
}
54555453

5456-
class NamedPartsAccumulator(p: NamedType => Boolean, excludeLowerBounds: Boolean = false)
5457-
(implicit ctx: Context) extends TypeAccumulator[mutable.Set[NamedType]] {
5454+
class NamedPartsAccumulator(p: NamedType => Boolean,
5455+
widenTermRefs: Boolean = false,
5456+
excludeLowerBounds: Boolean = false)
5457+
(implicit ctx: Context) extends TypeAccumulator[mutable.Set[NamedType]] {
54585458
override def stopAtStatic: Boolean = false
54595459
def maybeAdd(x: mutable.Set[NamedType], tp: NamedType): mutable.Set[NamedType] = if (p(tp)) x += tp else x
54605460
val seen: util.HashSet[Type] = new util.HashSet[Type](64) {
@@ -5468,12 +5468,13 @@ object Types {
54685468
tp match {
54695469
case tp: TypeRef =>
54705470
foldOver(maybeAdd(x, tp), tp)
5471+
case tp: TermRef =>
5472+
val x1 = foldOver(maybeAdd(x, tp), tp)
5473+
if widenTermRefs then apply(x1, tp.underlying) else x1
54715474
case tp: ThisType =>
54725475
apply(x, tp.tref)
54735476
case NoPrefix =>
54745477
foldOver(x, tp)
5475-
case tp: TermRef =>
5476-
apply(foldOver(maybeAdd(x, tp), tp), tp.underlying)
54775478
case tp: AppliedType =>
54785479
foldOver(x, tp)
54795480
case TypeBounds(lo, hi) =>

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

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ object Implicits {
244244
*/
245245
class OfTypeImplicits(tp: Type, val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) {
246246
assert(initctx.typer != null)
247-
implicits.println(i"implicits of type $tp = ${companionRefs.toList}%, %")
247+
implicits.println(i"implicit scope of type $tp = ${companionRefs.toList}%, %")
248248
@threadUnsafe lazy val refs: List[ImplicitRef] = {
249249
val buf = new mutable.ListBuffer[TermRef]
250250
for (companion <- companionRefs) buf ++= companion.implicitMembers
@@ -604,7 +604,7 @@ trait ImplicitRunInfo {
604604
val superAnchors = if (sym.isClass) tp.parents else anchors(tp.superType)
605605
for (anchor <- superAnchors) comps ++= iscopeRefs(anchor)
606606
case tp =>
607-
for (part <- tp.namedPartsWith(_.isType)) comps ++= iscopeRefs(part)
607+
for part <- tp.namedPartsWith(_.isType) do comps ++= iscopeRefs(part)
608608
}
609609
comps
610610
}
@@ -1153,28 +1153,27 @@ trait Implicits { self: Typer =>
11531153
|Consider using the scala.implicits.Not class to implement similar functionality.""",
11541154
ctx.source.atSpan(span))
11551155

1156-
/** A relation that imfluences the order in which implicits are tried.
1156+
/** A relation that influences the order in which implicits are tried.
11571157
* We prefer (in order of importance)
11581158
* 1. more deeply nested definitions
1159-
* 2. definitions in subclasses
1160-
* 3. definitions with fewer implicit parameters
1161-
* The reason for (3) is that we want to fail fast if the search type
1159+
* 2. definitions with fewer implicit parameters
1160+
* 3. definitions in subclasses
1161+
* The reason for (2) is that we want to fail fast if the search type
11621162
* is underconstrained. So we look for "small" goals first, because that
11631163
* will give an ambiguity quickly.
11641164
*/
1165-
def prefer(cand1: Candidate, cand2: Candidate): Boolean = {
1165+
def prefer(cand1: Candidate, cand2: Candidate): Boolean =
11661166
val level1 = cand1.level
11671167
val level2 = cand2.level
1168-
if (level1 > level2) return true
1169-
if (level1 < level2) return false
1168+
if level1 > level2 then return true
1169+
if level1 < level2 then return false
11701170
val sym1 = cand1.ref.symbol
11711171
val sym2 = cand2.ref.symbol
11721172
val arity1 = sym1.info.firstParamTypes.length
11731173
val arity2 = sym2.info.firstParamTypes.length
1174-
if (arity1 < arity2) return true
1175-
if (arity1 > arity2) return false
1174+
if arity1 < arity2 then return true
1175+
if arity1 > arity2 then return false
11761176
compareOwner(sym1, sym2) == 1
1177-
}
11781177

11791178
/** Sort list of implicit references according to `prefer`.
11801179
* This is just an optimization that aims at reducing the average

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,9 @@ class Typer extends Namer
860860

861861
def escapingRefs(block: Tree, localSyms: => List[Symbol])(using Context): collection.Set[NamedType] = {
862862
lazy val locals = localSyms.toSet
863-
block.tpe.namedPartsWith(tp => locals.contains(tp.symbol) && !tp.isErroneous)
863+
block.tpe.namedPartsWith(
864+
tp => locals.contains(tp.symbol) && !tp.isErroneous,
865+
widenTermRefs = true)
864866
}
865867

866868
/** Ensure that an expression's type can be expressed without references to locally defined

tests/neg/i9103.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
trait Show[T] {def show(a: T): String}
2+
3+
object S extends LowPriorityInstances {
4+
class Permissions
5+
}
6+
7+
sealed trait LowPriorityInstances
8+
object LowPriorityInstances {
9+
given S.Permissions = ???
10+
given Show[S.Permissions] = _ => "perms"
11+
}
12+
13+
@main def test =
14+
println(implicitly[S.Permissions]) // error
15+
println(implicitly[Show[S.Permissions]]) // error
16+

tests/neg/implicit-scope-2.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class A {
2+
class B
3+
}
4+
5+
trait T
6+
object T {
7+
implicit def b: AA.a.B = ???
8+
}
9+
10+
object AA {
11+
val a = new A with T {}
12+
}
13+
14+
object test {
15+
implicitly[AA.a.B] // error
16+
}

tests/pos/i9103.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object a:
2+
type Foo[T]
3+
given as Foo[Unit] = ???
4+
5+
val b = a
6+
7+
def Test = summon[b.Foo[Unit]]

0 commit comments

Comments
 (0)