Skip to content

Commit c630d5a

Browse files
committed
Faster type avoidance
The probability that the type of a block refers to a local symbol is low: - type definitions are usually inside classes, objects or packages - avoidance is also called from TreeCopier after typer, it's even rare to refer to local symbols in such scenarios Therefore, we can have a fast check to see whether the type refers to any local definitions
1 parent 3dcc05c commit c630d5a

File tree

4 files changed

+42
-2
lines changed

4 files changed

+42
-2
lines changed

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5724,6 +5724,36 @@ object Types {
57245724
foldOver(xs, tp)
57255725
end NamedPartsAccumulator
57265726

5727+
class ExistsLocalRef(using Context) extends TypeAccumulator[Boolean]:
5728+
val locals = util.HashSet[Type]()
5729+
def apply(exists: Boolean, tp: Type): Boolean =
5730+
exists || tp.match {
5731+
case tp: NamedType =>
5732+
this(false, tp.prefix)
5733+
case NoPrefix =>
5734+
true
5735+
case tp: AppliedType =>
5736+
foldOver(false, tp)
5737+
case TypeBounds(lo, hi) =>
5738+
this(this(false, lo), hi)
5739+
case tp: ParamRef =>
5740+
!locals.contains(tp)
5741+
case tp: ConstantType =>
5742+
false
5743+
case tp: ThisType =>
5744+
false
5745+
case tl: HKTypeLambda =>
5746+
locals ++= tl.paramRefs
5747+
foldOver(false, tl)
5748+
case tp: RecThis =>
5749+
false
5750+
case tp: LazyRef =>
5751+
false
5752+
case _ =>
5753+
foldOver(false, tp)
5754+
}
5755+
end ExistsLocalRef
5756+
57275757
class isGroundAccumulator(using Context) extends TypeAccumulator[Boolean] {
57285758
def apply(x: Boolean, tp: Type): Boolean = x && {
57295759
tp match {

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ trait TypeAssigner {
4141
}
4242

4343
def avoidingType(expr: Tree, bindings: List[Tree])(using Context): Type =
44-
TypeOps.avoid(expr.tpe, localSyms(bindings).filterConserve(_.isTerm))
44+
val refersLocal = ExistsLocalRef()(false, expr.tpe)
45+
if refersLocal then
46+
TypeOps.avoid(expr.tpe, localSyms(bindings).filterConserve(_.isTerm))
47+
else
48+
expr.tpe
4549

4650
def avoidPrivateLeaks(sym: Symbol)(using Context): Type =
4751
if sym.owner.isClass && !sym.isOneOf(JavaOrPrivateOrSynthetic)

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,11 @@ class Typer extends Namer
10071007
case _ =>
10081008
Typed(tree, TypeTree(pt.simplified))
10091009
}
1010-
def noLeaks(t: Tree): Boolean = escapingRefs(t, localSyms).isEmpty
1010+
1011+
def noLeaks(t: Tree): Boolean =
1012+
val refersLocal = ExistsLocalRef()(false, tree.tpe)
1013+
!refersLocal || escapingRefs(t, localSyms).isEmpty
1014+
10111015
if (noLeaks(tree)) tree
10121016
else {
10131017
fullyDefinedType(tree.tpe, "block", tree.span)

compiler/test/dotc/pos-test-pickling.blacklist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,5 @@ i9793.scala
6161
# lazy_implicit symbol has different position after pickling
6262
i8182.scala
6363

64+
# trivial recursive type gets stripped
65+
i11464.scala

0 commit comments

Comments
 (0)