Skip to content

Commit 7dd576c

Browse files
committed
Fuse successive SubstBindings maps and filters
1 parent 7ee4ccc commit 7dd576c

File tree

9 files changed

+123
-16
lines changed

9 files changed

+123
-16
lines changed

compiler/src/dotty/tools/dotc/Run.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ import dotty.tools.dotc.util.chaining.*
4242
import java.util.{Timer, TimerTask}
4343

4444
/** A compiler run. Exports various methods to compile source files */
45-
class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with ConstraintRunInfo {
45+
class Run(comp: Compiler, ictx: Context)
46+
extends ImplicitRunInfo, ConstraintRunInfo, cc.CaptureRunInfo {
4647

4748
/** Default timeout to stop looking for further implicit suggestions, in ms.
4849
* This is usually for the first import suggestion; subsequent suggestions
@@ -519,6 +520,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
519520
/** Print summary of warnings and errors encountered */
520521
def printSummary(): Unit = {
521522
printMaxConstraint()
523+
printMaxPath()
522524
val r = runContext.reporter
523525
if !r.errorsReported then
524526
profile.printSummary()
@@ -529,6 +531,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
529531
override def reset(): Unit = {
530532
super[ImplicitRunInfo].reset()
531533
super[ConstraintRunInfo].reset()
534+
super[CaptureRunInfo].reset()
532535
myCtx = null
533536
myUnits = Nil
534537
myUnitsCached = Nil
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dotty.tools.dotc
2+
package cc
3+
4+
import core.Contexts.{Context, ctx}
5+
import config.Printers.capt
6+
7+
trait CaptureRunInfo:
8+
self: Run =>
9+
private var maxSize = 0
10+
private var maxPath: List[CaptureSet.DerivedVar] = Nil
11+
12+
def recordPath(size: Int, path: => List[CaptureSet.DerivedVar]): Unit =
13+
if size > maxSize then
14+
maxSize = size
15+
maxPath = path
16+
17+
def printMaxPath()(using Context): Unit =
18+
if maxSize > 0 then
19+
println(s"max derived capture set path length: $maxSize")
20+
println(s"max derived capture set path: ${maxPath.map(_.summarize).reverse}")
21+
22+
protected def reset(): Unit =
23+
maxSize = 0
24+
maxPath = Nil
25+
end CaptureRunInfo

compiler/src/dotty/tools/dotc/cc/CaptureSet.scala

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,10 @@ sealed abstract class CaptureSet extends Showable:
296296
val elems1 = elems.filter(p)
297297
if elems1 == elems then this
298298
else Const(elems.filter(p))
299-
else Filtered(asVar, p)
299+
else
300+
this match
301+
case self: Filtered => Filtered(self.source, ref => self.p(ref) && p(ref))
302+
case _ => Filtered(asVar, p)
300303

301304
/** Capture set obtained by applying `tm` to all elements of the current capture set
302305
* and joining the results. If the current capture set is a variable, the same
@@ -326,7 +329,13 @@ sealed abstract class CaptureSet extends Showable:
326329
if isConst then
327330
if mappedElems == elems then this
328331
else Const(mappedElems)
329-
else BiMapped(asVar, tm, mappedElems)
332+
else
333+
def unfused = BiMapped(asVar, tm, mappedElems)
334+
this match
335+
case self: BiMapped => self.bimap.fuse(tm) match
336+
case Some(fused: BiTypeMap) => BiMapped(self.source, fused, mappedElems)
337+
case _ => unfused
338+
case _ => unfused
330339
case tm: IdentityCaptRefMap =>
331340
this
332341
case tm: AvoidMap if this.isInstanceOf[HiddenSet] =>
@@ -749,6 +758,25 @@ object CaptureSet:
749758

750759
override def propagateSolved(provisional: Boolean)(using Context) =
751760
if source.isConst && !isConst then markSolved(provisional)
761+
762+
// ----------- Longest path recording -------------------------
763+
764+
/** Summarize for set displaying in a path */
765+
def summarize: String = getClass.toString
766+
767+
/** The length of the path of DerivedVars ending in this set */
768+
def pathLength: Int = source match
769+
case source: DerivedVar => source.pathLength + 1
770+
case _ => 1
771+
772+
/** The path of DerivedVars ending in this set */
773+
def path: List[DerivedVar] = source match
774+
case source: DerivedVar => this :: source.path
775+
case _ => this :: Nil
776+
777+
if ctx.settings.YccLog.value || util.Stats.enabled then
778+
ctx.run.nn.recordPath(pathLength, path)
779+
752780
end DerivedVar
753781

754782
/** A variable that changes when `source` changes, where all additional new elements are mapped
@@ -852,7 +880,7 @@ object CaptureSet:
852880
* Parameters as in Mapped.
853881
*/
854882
final class BiMapped private[CaptureSet]
855-
(val source: Var, bimap: BiTypeMap, initialElems: Refs)(using @constructorOnly ctx: Context)
883+
(val source: Var, val bimap: BiTypeMap, initialElems: Refs)(using @constructorOnly ctx: Context)
856884
extends DerivedVar(source.owner, initialElems):
857885

858886
override def tryInclude(elem: CaptureRef, origin: CaptureSet)(using Context, VarState): CompareResult =
@@ -881,11 +909,12 @@ object CaptureSet:
881909

882910
override def isMaybeSet: Boolean = bimap.isInstanceOf[MaybeMap]
883911
override def toString = s"BiMapped$id($source, elems = $elems)"
912+
override def summarize = bimap.getClass.toString
884913
end BiMapped
885914

886915
/** A variable with elements given at any time as { x <- source.elems | p(x) } */
887916
class Filtered private[CaptureSet]
888-
(val source: Var, p: Context ?=> CaptureRef => Boolean)(using @constructorOnly ctx: Context)
917+
(val source: Var, val p: Context ?=> CaptureRef => Boolean)(using @constructorOnly ctx: Context)
889918
extends DerivedVar(source.owner, source.elems.filter(p)):
890919

891920
override def tryInclude(elem: CaptureRef, origin: CaptureSet)(using Context, VarState): CompareResult =
@@ -1298,10 +1327,21 @@ object CaptureSet:
12981327
case t: CaptureRef if t.isTrackableRef => mapRef(t)
12991328
case _ => mapOver(t)
13001329

1301-
lazy val inverse = new BiTypeMap:
1330+
override def fuse(next: BiTypeMap)(using Context) = next match
1331+
case next: Inverse if next.inverse.getClass == getClass => assert(false); Some(IdentityTypeMap)
1332+
case next: NarrowingCapabilityMap if next.getClass == getClass => assert(false)
1333+
case _ => None
1334+
1335+
class Inverse extends BiTypeMap:
13021336
def apply(t: Type) = t // since f(c) <: c, this is the best inverse
13031337
def inverse = NarrowingCapabilityMap.this
13041338
override def toString = NarrowingCapabilityMap.this.toString ++ ".inverse"
1339+
override def fuse(next: BiTypeMap)(using Context) = next match
1340+
case next: NarrowingCapabilityMap if next.inverse.getClass == getClass => assert(false); Some(IdentityTypeMap)
1341+
case next: NarrowingCapabilityMap if next.getClass == getClass => assert(false)
1342+
case _ => None
1343+
1344+
lazy val inverse = Inverse()
13051345
end NarrowingCapabilityMap
13061346

13071347
/** Maps `x` to `x?` */

compiler/src/dotty/tools/dotc/cc/root.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,17 +186,27 @@ object root:
186186
case _ =>
187187
mapFollowingAliases(t)
188188

189+
override def fuse(next: BiTypeMap)(using Context) = next match
190+
case next: Inverse => assert(false); Some(IdentityTypeMap)
191+
case _ => None
192+
189193
override def toString = "CapToFresh"
190194

191-
object inverse extends BiTypeMap, FollowAliasesMap:
195+
class Inverse extends BiTypeMap, FollowAliasesMap:
192196
def apply(t: Type): Type = t match
193197
case t @ Fresh(_) => cap
194198
case t @ CapturingType(_, refs) => mapOver(t)
195199
case _ => mapFollowingAliases(t)
196200

201+
override def fuse(next: BiTypeMap)(using Context) = next match
202+
case next: CapToFresh => assert(false); Some(IdentityTypeMap)
203+
case _ => None
204+
197205
def inverse = thisMap
198206
override def toString = thisMap.toString + ".inverse"
199207

208+
lazy val inverse = Inverse()
209+
200210
end CapToFresh
201211

202212
/** Maps cap to fresh */

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,35 @@ object Substituters:
164164
}
165165

166166
final class SubstBindingMap[BT <: BindingType](val from: BT, val to: BT)(using Context) extends DeepTypeMap, BiTypeMap {
167+
override def fuse(next: BiTypeMap)(using Context) = next match
168+
case next: SubstBindingMap[_] =>
169+
if next.from eq to then Some(SubstBindingMap(from, next.to))
170+
else Some(SubstBindingsMap(Array(from, next.from), Array(to, next.to)))
171+
case _ => None
167172
def apply(tp: Type): Type = subst(tp, from, to, this)(using mapCtx)
168173
def inverse = SubstBindingMap(to, from)
169174
}
170175

176+
final class SubstBindingsMap(val from: Array[BindingType], val to: Array[BindingType])(using Context) extends DeepTypeMap, BiTypeMap {
177+
override def fuse(next: BiTypeMap)(using Context) = next match
178+
case next: SubstBindingMap[_] =>
179+
var i = 0
180+
while i < from.length && (to(i) ne next.from) do i += 1
181+
if i < from.length then Some(SubstBindingsMap(from, to.updated(i, next.to)))
182+
else Some(SubstBindingsMap(from :+ next.from, to :+ next.to))
183+
case _ => None
184+
185+
def apply(tp: Type): Type = tp match
186+
case tp: BoundType =>
187+
var i = 0
188+
while i < from.length && (from(i) ne tp.binder) do i += 1
189+
if i < from.length then tp.copyBoundType(to(i).asInstanceOf[tp.BT]) else tp
190+
case _ =>
191+
mapOver(tp)
192+
193+
def inverse = SubstBindingsMap(to, from)
194+
}
195+
171196
final class Subst1Map(from: Symbol, to: Type)(using Context) extends DeepTypeMap {
172197
def apply(tp: Type): Type = subst1(tp, from, to, this)(using mapCtx)
173198
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6097,6 +6097,10 @@ object Types extends TypeUtils {
60976097
/** A restriction of the inverse to a function on tracked CaptureRefs */
60986098
def backward(ref: CaptureRef): CaptureRef = inverse(ref) match
60996099
case result: CaptureRef if result.isTrackableRef => result
6100+
6101+
/** Fuse with another map */
6102+
def fuse(next: BiTypeMap)(using Context): Option[TypeMap] = None
6103+
61006104
end BiTypeMap
61016105

61026106
abstract class TypeMap(implicit protected var mapCtx: Context)

tests/neg-custom-args/captures/heal-tparam-cs.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
| Required: (c: Capp^{io}) -> () ->{io} Unit
2727
|
2828
| Note that the existential capture root in () ->? Unit
29-
| cannot subsume the capability <cap of (c1: Capp^?): () ->{c1} Unit>
29+
| cannot subsume the capability <cap of (x$0: Capp^?): () ->{x$0} Unit>
3030
21 | (c1: Capp^{io}) => () => { c1.use() }
3131
22 | }
3232
|
@@ -38,7 +38,7 @@
3838
| Required: (c: Capp^{io}) -> () ->{net} Unit
3939
|
4040
| Note that the existential capture root in () ->? Unit
41-
| cannot subsume the capability <cap of (c1: Capp^?): () ->{c1} Unit>
41+
| cannot subsume the capability <cap of (x$0: Capp^?): () ->{x$0} Unit>
4242
26 | (c1: Capp^{io}) => () => { c1.use() }
4343
27 | }
4444
|
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21920.scala:34:34 ---------------------------------------
2-
34 | val cell: Cell[File] = File.open(f => Cell(Seq(f))) // error
3-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4-
| Found: Cell[box File^{f, f²}]{val head: () ?->? IterableOnce[box File^{f²}]^?}^?
5-
| Required: Cell[File]
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21920.scala:34:35 ---------------------------------------
2+
34 | val cell: Cell[File] = File.open(f => Cell(() => Seq(f))) // error
3+
| ^^^^^^^^^^^^^^^^^^^^^^^
4+
| Found: (f: File^?) ->? box Cell[box File^?]{val head: () ->? IterableOnce[box File^?]^?}^?
5+
| Required: (f: File^) ->{fresh} box Cell[box File^?]{val head: () ->? IterableOnce[box File^?]^?}^?
66
|
7-
| where: f is a reference to a value parameter
8-
| f² is a reference to a value parameter
7+
| Note that the universal capability `cap`
8+
| cannot be included in capture set ?
99
|
1010
| longer explanation available when compiling with `-explain`

0 commit comments

Comments
 (0)