Skip to content

Commit a3c81dd

Browse files
authored
Merge pull request #9742 from dotty-staging/cache-proto
Better caching of prototypes
2 parents a1deca9 + b0ce529 commit a3c81dd

File tree

3 files changed

+70
-83
lines changed

3 files changed

+70
-83
lines changed

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -342,12 +342,9 @@ abstract class SymbolLoader extends LazyType { self =>
342342
}
343343
try {
344344
val start = System.currentTimeMillis
345-
if (Config.tracingEnabled && ctx.settings.YdebugTrace.value)
346-
trace(s">>>> loading ${root.debugString}", _ => s"<<<< loaded ${root.debugString}") {
347-
doComplete(root)
348-
}
349-
else
345+
trace.onDebug("loading") {
350346
doComplete(root)
347+
}
351348
report.informTime("loaded " + description, start)
352349
}
353350
catch {

compiler/src/dotty/tools/dotc/reporting/trace.scala

Lines changed: 35 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -7,46 +7,28 @@ import config.Config
77
import config.Printers
88
import core.Mode
99

10-
/** Exposes the {{{ trace("question") { op } }}} syntax.
11-
*
12-
* Traced operations will print indented messages if enabled.
13-
* Tracing depends on [[Config.tracingEnabled]] and [[dotty.tools.dotc.config.ScalaSettings.Ylog]].
14-
* Tracing can be forced by replacing [[trace]] with [[trace.force]] (see below).
15-
*/
16-
object trace extends TraceSyntax {
17-
final val isForced = false
18-
19-
/** Forces a particular trace to be printed out regardless of tracing being enabled. */
20-
object force extends TraceSyntax {
21-
final val isForced = true
22-
}
23-
}
24-
25-
abstract class TraceSyntax {
26-
val isForced: Boolean
10+
/** This module is carefully optimized to give zero overhead if Config.tracingEnabled
11+
* is false. The `trace` operation is called in various hotspots, so every tiny bit
12+
* of overhead is unacceptable: boxing, closures, additional method calls are all out.
13+
*/
14+
object trace:
2715

2816
inline def onDebug[TD](inline question: String)(inline op: TD)(using Context): TD =
2917
conditionally(ctx.settings.YdebugTrace.value, question, false)(op)
3018

31-
inline def conditionally[TC](inline cond: Boolean, inline question: String, inline show: Boolean)(op: => TC)(using Context): TC =
32-
inline if (isForced || Config.tracingEnabled) {
33-
if (cond) apply[TC](question, Printers.default, show)(op)
34-
else op
35-
}
19+
inline def conditionally[TC](inline cond: Boolean, inline question: String, inline show: Boolean)(inline op: TC)(using Context): TC =
20+
if Config.tracingEnabled then
21+
apply(question, if cond then Printers.default else Printers.noPrinter, show)(op)
3622
else op
3723

38-
inline def apply[T](inline question: String, inline printer: Printers.Printer, inline showOp: Any => String)(op: => T)(using Context): T =
39-
inline if (isForced || Config.tracingEnabled) {
40-
if (!isForced && printer.eq(config.Printers.noPrinter)) op
41-
else doTrace[T](question, printer, showOp)(op)
42-
}
24+
inline def apply[T](inline question: String, inline printer: Printers.Printer, inline showOp: Any => String)(inline op: T)(using Context): T =
25+
if Config.tracingEnabled then
26+
doTrace[T](question, printer, showOp)(op)
4327
else op
4428

45-
inline def apply[T](inline question: String, inline printer: Printers.Printer, inline show: Boolean)(op: => T)(using Context): T =
46-
inline if (isForced || Config.tracingEnabled) {
47-
if (!isForced && printer.eq(config.Printers.noPrinter)) op
48-
else doTrace[T](question, printer, if (show) showShowable(_) else alwaysToString)(op)
49-
}
29+
inline def apply[T](inline question: String, inline printer: Printers.Printer, inline show: Boolean)(inline op: T)(using Context): T =
30+
if Config.tracingEnabled then
31+
doTrace[T](question, printer, if show then showShowable(_) else alwaysToString)(op)
5032
else op
5133

5234
inline def apply[T](inline question: String, inline printer: Printers.Printer)(inline op: T)(using Context): T =
@@ -56,55 +38,41 @@ abstract class TraceSyntax {
5638
apply[T](question, Printers.default, show)(op)
5739

5840
inline def apply[T](inline question: String)(inline op: T)(using Context): T =
59-
apply[T](question, Printers.default, false)(op)
41+
apply[T](question, false)(op)
6042

61-
private def showShowable(x: Any)(using Context) = x match {
43+
private def showShowable(x: Any)(using Context) = x match
6244
case x: printing.Showable => x.show
6345
case _ => String.valueOf(x)
64-
}
6546

6647
private val alwaysToString = (x: Any) => String.valueOf(x)
6748

6849
private def doTrace[T](question: => String,
6950
printer: Printers.Printer = Printers.default,
7051
showOp: Any => String = alwaysToString)
71-
(op: => T)(using Context): T = {
72-
// Avoid evaluating question multiple time, since each evaluation
73-
// may cause some extra logging output.
74-
lazy val q: String = question
75-
apply[T](s"==> $q?", (res: Any) => s"<== $q = ${showOp(res)}")(op)
76-
}
77-
78-
def apply[T](leading: => String, trailing: Any => String)(op: => T)(using Context): T = {
79-
val log: String => Unit = if (isForced) Console.println else {
80-
var logctx = ctx
81-
while (logctx.reporter.isInstanceOf[StoreReporter]) logctx = logctx.outer
82-
report.log(_)(using logctx)
83-
}
84-
doApply(leading, trailing, log)(op)
85-
}
86-
87-
def doApply[T](leading: => String, trailing: Any => String, log: String => Unit)(op: => T)(using Context): T =
88-
if (ctx.mode.is(Mode.Printing)) op
89-
else {
52+
(op: => T)(using Context): T =
53+
if ctx.mode.is(Mode.Printing) || (printer eq Printers.noPrinter) then op
54+
else
55+
// Avoid evaluating question multiple time, since each evaluation
56+
// may cause some extra logging output.
57+
val q = question
58+
val leading = s"==> $q?"
59+
val trailing = (res: Any) => s"<== $q = ${showOp(res)}"
9060
var finalized = false
61+
var logctx = ctx
62+
while logctx.reporter.isInstanceOf[StoreReporter] do logctx = logctx.outer
63+
def margin = ctx.base.indentTab * ctx.base.indent
9164
def finalize(result: Any, note: String) =
92-
if (!finalized) {
65+
if !finalized then
9366
ctx.base.indent -= 1
94-
log(s"${ctx.base.indentTab * ctx.base.indent}${trailing(result)}$note")
67+
report.log(s"$margin${trailing(result)}$note")
9568
finalized = true
96-
}
97-
try {
98-
log(s"${ctx.base.indentTab * ctx.base.indent}$leading")
69+
try
70+
report.log(s"$margin$leading")
9971
ctx.base.indent += 1
10072
val res = op
10173
finalize(res, "")
10274
res
103-
}
104-
catch {
105-
case ex: Throwable =>
106-
finalize("<missing>", s" (with exception $ex)")
107-
throw ex
108-
}
109-
}
110-
}
75+
catch case ex: Throwable =>
76+
finalize("<missing>", s" (with exception $ex)")
77+
throw ex
78+
end trace

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

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,25 @@ object ProtoTypes {
120120
}
121121

122122
/** A class marking ignored prototypes that can be revealed by `deepenProto` */
123-
case class IgnoredProto(ignored: Type) extends UncachedGroundType with MatchAlways:
123+
abstract case class IgnoredProto(ignored: Type) extends CachedGroundType with MatchAlways:
124124
override def revealIgnored = ignored
125125
override def deepenProto(using Context): Type = ignored
126126

127+
override def computeHash(bs: Hashable.Binders): Int = doHash(bs, ignored)
128+
129+
override def eql(that: Type): Boolean = that match
130+
case that: IgnoredProto => ignored eq that.ignored
131+
case _ => false
132+
133+
// equals comes from case class; no need to redefine
134+
end IgnoredProto
135+
136+
final class CachedIgnoredProto(ignored: Type) extends IgnoredProto(ignored)
137+
127138
object IgnoredProto:
128-
def apply(ignored: Type): IgnoredProto = ignored match
139+
def apply(ignored: Type)(using Context): IgnoredProto = ignored match
129140
case ignored: IgnoredProto => ignored
130-
case _ => new IgnoredProto(ignored)
141+
case _ => unique(CachedIgnoredProto(ignored))
131142

132143
/** A prototype for expressions [] that are part of a selection operation:
133144
*
@@ -185,22 +196,27 @@ object ProtoTypes {
185196
if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat)) this
186197
else SelectionProto(name, memberProto, compat, privateOK)
187198

188-
override def equals(that: Any): Boolean = that match {
189-
case that: SelectionProto =>
190-
(name eq that.name) && (memberProto == that.memberProto) && (compat eq that.compat) && (privateOK == that.privateOK)
191-
case _ =>
192-
false
193-
}
194-
195199
def map(tm: TypeMap)(using Context): SelectionProto = derivedSelectionProto(name, tm(memberProto), compat)
196200
def fold[T](x: T, ta: TypeAccumulator[T])(using Context): T = ta(x, memberProto)
197201

198202
override def deepenProto(using Context): SelectionProto = derivedSelectionProto(name, memberProto.deepenProto, compat)
199-
200203
override def computeHash(bs: Hashable.Binders): Int = {
201204
val delta = (if (compat eq NoViewsAllowed) 1 else 0) | (if (privateOK) 2 else 0)
202205
addDelta(doHash(bs, name, memberProto), delta)
203206
}
207+
208+
override def equals(that: Any): Boolean = that match
209+
case that: SelectionProto =>
210+
(name eq that.name) && memberProto.equals(that.memberProto) && (compat eq that.compat) && (privateOK == that.privateOK)
211+
case _ =>
212+
false
213+
214+
override def eql(that: Type): Boolean = that match {
215+
case that: SelectionProto =>
216+
(name eq that.name) && (memberProto eq that.memberProto) && (compat eq that.compat) && (privateOK == that.privateOK)
217+
case _ =>
218+
false
219+
}
204220
}
205221

206222
class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean)
@@ -450,6 +466,10 @@ object ProtoTypes {
450466

451467
class CachedViewProto(argType: Type, resultType: Type) extends ViewProto(argType, resultType) {
452468
override def computeHash(bs: Hashable.Binders): Int = doHash(bs, argType, resultType)
469+
override def eql(that: Type): Boolean = that match
470+
case that: ViewProto => (argType eq that.argType) && (resType eq that.resType)
471+
case _ => false
472+
// equals comes from case class; no need to redefine
453473
}
454474

455475
object ViewProto {
@@ -680,6 +700,8 @@ object ProtoTypes {
680700
tp.derivedViewProto(
681701
wildApprox(tp.argType, theMap, seen, internal),
682702
wildApprox(tp.resultType, theMap, seen, internal))
703+
case tp: IgnoredProto =>
704+
WildcardType
683705
case _: ThisType | _: BoundType => // default case, inlined for speed
684706
tp
685707
case tl: TypeLambda =>

0 commit comments

Comments
 (0)