Skip to content

Commit c831128

Browse files
committed
Avoid exploring TyperState creations
Dotty bootstrap (from dotc directory) has 5924910 context creations. This commit brings that number down to 4307795. It also reduces the number of TyperStates created from 2'059'126 to 308'732. Simplify TyperState Have just one class instead of two. This will be important for inlining the `test` method. Also, fix test method - it needs to create a new StoreReporter. Fix ()'s in new/exploreTyperState More () fixes
1 parent ba01fba commit c831128

File tree

9 files changed

+124
-137
lines changed

9 files changed

+124
-137
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class Run(comp: Compiler, ictx: Context) {
4949
.setOwner(defn.RootClass)
5050
.setTyper(new Typer)
5151
.addMode(Mode.ImplicitsEnabled)
52-
.setTyperState(new MutableTyperState(ctx.typerState, ctx.typerState.reporter, isCommittable = true))
52+
.setTyperState(new TyperState(ctx.typerState))
5353
.setFreshNames(new FreshNameCreator.Default)
5454
ctx.initialize()(start) // re-initialize the base context with start
5555
def addImport(ctx: Context, refFn: () => TermRef) =

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -459,9 +459,9 @@ object Contexts {
459459
def setCompilerCallback(callback: CompilerCallback): this.type = { this.compilerCallback = callback; this }
460460
def setSbtCallback(callback: AnalysisCallback): this.type = { this.sbtCallback = callback; this }
461461
def setTyperState(typerState: TyperState): this.type = { this.typerState = typerState; this }
462-
def setReporter(reporter: Reporter): this.type = setTyperState(typerState.withReporter(reporter))
463-
def setNewTyperState: this.type = setTyperState(typerState.fresh(isCommittable = true))
464-
def setExploreTyperState: this.type = setTyperState(typerState.fresh(isCommittable = false))
462+
def setReporter(reporter: Reporter): this.type = setTyperState(typerState.fresh().setReporter(reporter))
463+
def setNewTyperState(): this.type = setTyperState(typerState.fresh().setCommittable(true))
464+
def setExploreTyperState(): this.type = setTyperState(typerState.fresh().setCommittable(false))
465465
def setPrinterFn(printer: Context => Printer): this.type = { this.printerFn = printer; this }
466466
def setOwner(owner: Symbol): this.type = { assert(owner != NoSymbol); this.owner = owner; this }
467467
def setSettings(sstate: SettingsState): this.type = { this.sstate = sstate; this }
@@ -520,7 +520,7 @@ object Contexts {
520520
outer = NoContext
521521
period = InitialPeriod
522522
mode = Mode.None
523-
typerState = new TyperState(new ConsoleReporter())
523+
typerState = new TyperState(null)
524524
printerFn = new RefinedPrinter(_)
525525
owner = NoSymbol
526526
sstate = settings.defaultState

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

Lines changed: 73 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,31 @@ import config.Config
1313
import collection.mutable
1414
import java.lang.ref.WeakReference
1515

16-
class TyperState(r: Reporter) extends DotClass with Showable {
16+
class TyperState(previous: TyperState /* | Null */) extends DotClass with Showable {
1717

18-
/** The current reporter */
19-
def reporter = r
18+
private var myReporter =
19+
if (previous == null) new ConsoleReporter() else previous.reporter
2020

21-
/** The current constraint set */
22-
def constraint: Constraint =
23-
new OrderingConstraint(SimpleMap.Empty, SimpleMap.Empty, SimpleMap.Empty)
24-
def constraint_=(c: Constraint)(implicit ctx: Context): Unit = {}
21+
def reporter: Reporter = myReporter
2522

26-
/** The uninstantiated variables */
27-
def uninstVars = constraint.uninstVars
23+
/** A fresh type state with the same constraint as this one and the given reporter */
24+
def setReporter(reporter: Reporter): this.type = { myReporter = reporter; this }
25+
26+
private var myConstraint: Constraint =
27+
if (previous == null) new OrderingConstraint(SimpleMap.Empty, SimpleMap.Empty, SimpleMap.Empty)
28+
else previous.constraint
29+
30+
def constraint = myConstraint
31+
def constraint_=(c: Constraint)(implicit ctx: Context) = {
32+
if (Config.debugCheckConstraintsClosed && isGlobalCommittable) c.checkClosed()
33+
myConstraint = c
34+
}
35+
36+
private val previousConstraint =
37+
if (previous == null) constraint else previous.constraint
38+
39+
private var myEphemeral: Boolean =
40+
if (previous == null) false else previous.ephemeral
2841

2942
/** The ephemeral flag is set as a side effect if an operation accesses
3043
* the underlying type of a type variable. The reason we need this flag is
@@ -33,8 +46,26 @@ class TyperState(r: Reporter) extends DotClass with Showable {
3346
* check the ephemeral flag; If the flag is set during an operation, the result
3447
* of that operation should not be cached.
3548
*/
36-
def ephemeral: Boolean = false
37-
def ephemeral_=(x: Boolean): Unit = ()
49+
def ephemeral = myEphemeral
50+
def ephemeral_=(x: Boolean): Unit = { myEphemeral = x }
51+
52+
private var myIsCommittable = true
53+
54+
def isCommittable = myIsCommittable
55+
56+
def setCommittable(committable: Boolean): this.type = { this.myIsCommittable = committable; this }
57+
58+
def isGlobalCommittable: Boolean =
59+
isCommittable && (previous == null || previous.isGlobalCommittable)
60+
61+
private var isCommitted = false
62+
63+
/** A fresh typer state with the same constraint as this one. */
64+
def fresh(): TyperState =
65+
new TyperState(this).setReporter(new StoreReporter(reporter)).setCommittable(isCommittable)
66+
67+
/** The uninstantiated variables */
68+
def uninstVars = constraint.uninstVars
3869

3970
/** Gives for each instantiated type var that does not yet have its `inst` field
4071
* set, the instance value stored in the constraint. Storing instances in constraints
@@ -49,76 +80,36 @@ class TyperState(r: Reporter) extends DotClass with Showable {
4980
case tp => tp
5081
}
5182

52-
/** A fresh typer state with the same constraint as this one.
53-
* @param isCommittable The constraint can be committed to an enclosing context.
54-
*/
55-
def fresh(isCommittable: Boolean): TyperState = this
56-
57-
/** A fresh type state with the same constraint as this one and the given reporter */
58-
def withReporter(reporter: Reporter) = new TyperState(reporter)
59-
60-
/** Commit state so that it gets propagated to enclosing context */
61-
def commit()(implicit ctx: Context): Unit = unsupported("commit")
62-
6383
/** The closest ancestor of this typer state (including possibly this typer state itself)
6484
* which is not yet committed, or which does not have a parent.
6585
*/
66-
def uncommittedAncestor: TyperState = this
67-
68-
/** Make type variable instances permanent by assigning to `inst` field if
69-
* type variable instantiation cannot be retracted anymore. Then, remove
70-
* no-longer needed constraint entries.
71-
*/
72-
def gc()(implicit ctx: Context): Unit = ()
73-
74-
/** Is it allowed to commit this state? */
75-
def isCommittable: Boolean = false
76-
77-
/** Can this state be transitively committed until the top-level? */
78-
def isGlobalCommittable: Boolean = false
79-
80-
override def toText(printer: Printer): Text = "ImmutableTyperState"
81-
82-
/** A string showing the hashes of all nested mutable typerstates */
83-
def hashesStr: String = ""
84-
}
85-
86-
class MutableTyperState(previous: TyperState, r: Reporter, override val isCommittable: Boolean)
87-
extends TyperState(r) {
88-
89-
private var myReporter = r
90-
91-
override def reporter = myReporter
92-
93-
private val previousConstraint = previous.constraint
94-
private var myConstraint: Constraint = previousConstraint
86+
def uncommittedAncestor: TyperState =
87+
if (isCommitted) previous.uncommittedAncestor else this
9588

96-
override def constraint = myConstraint
97-
override def constraint_=(c: Constraint)(implicit ctx: Context) = {
98-
if (Config.debugCheckConstraintsClosed && isGlobalCommittable) c.checkClosed()
99-
myConstraint = c
89+
private var testReporter: StoreReporter = null
90+
91+
/** Test using `op`, restoring typerState to previous state afterwards */
92+
def test(op: => Boolean): Boolean = {
93+
val savedReporter = myReporter
94+
val savedConstraint = myConstraint
95+
val savedCommittable = myIsCommittable
96+
val savedCommitted = isCommitted
97+
myIsCommittable = false
98+
myReporter =
99+
if (testReporter == null) new StoreReporter(reporter)
100+
else {
101+
testReporter.reset()
102+
testReporter
103+
}
104+
try op
105+
finally {
106+
myReporter = savedReporter
107+
myConstraint = savedConstraint
108+
myIsCommittable = savedCommittable
109+
isCommitted = savedCommitted
110+
}
100111
}
101112

102-
private var myEphemeral: Boolean = previous.ephemeral
103-
104-
override def ephemeral = myEphemeral
105-
override def ephemeral_=(x: Boolean): Unit = { myEphemeral = x }
106-
107-
override def fresh(isCommittable: Boolean): TyperState =
108-
new MutableTyperState(this, new StoreReporter(reporter), isCommittable)
109-
110-
override def withReporter(reporter: Reporter) =
111-
new MutableTyperState(this, reporter, isCommittable)
112-
113-
override val isGlobalCommittable =
114-
isCommittable &&
115-
(!previous.isInstanceOf[MutableTyperState] || previous.isGlobalCommittable)
116-
117-
private var isCommitted = false
118-
119-
override def uncommittedAncestor: TyperState =
120-
if (isCommitted) previous.uncommittedAncestor else this
121-
122113
/** Commit typer state so that its information is copied into current typer state
123114
* In addition (1) the owning state of undetermined or temporarily instantiated
124115
* type variables changes from this typer state to the current one. (2) Variables
@@ -137,7 +128,7 @@ extends TyperState(r) {
137128
* isApplicableSafe but also for (e.g. erased-lubs.scala) as well as
138129
* many parts of dotty itself.
139130
*/
140-
override def commit()(implicit ctx: Context) = {
131+
def commit()(implicit ctx: Context) = {
141132
val targetState = ctx.typerState
142133
assert(isCommittable)
143134
targetState.constraint =
@@ -152,7 +143,11 @@ extends TyperState(r) {
152143
isCommitted = true
153144
}
154145

155-
override def gc()(implicit ctx: Context): Unit = {
146+
/** Make type variable instances permanent by assigning to `inst` field if
147+
* type variable instantiation cannot be retracted anymore. Then, remove
148+
* no-longer needed constraint entries.
149+
*/
150+
def gc()(implicit ctx: Context): Unit = {
156151
val toCollect = new mutable.ListBuffer[TypeLambda]
157152
constraint foreachTypeVar { tvar =>
158153
if (!tvar.inst.exists) {
@@ -170,6 +165,5 @@ extends TyperState(r) {
170165

171166
override def toText(printer: Printer): Text = constraint.toText(printer)
172167

173-
override def hashesStr: String = hashCode.toString + " -> " + previous.hashesStr
174-
168+
def hashesStr: String = hashCode.toString + " -> " + previous.hashesStr
175169
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class StoreReporter(outer: Reporter) extends Reporter {
2222

2323
private var infos: mutable.ListBuffer[MessageContainer] = null
2424

25+
def reset() = infos = null
26+
2527
def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
2628
typr.println(s">>>> StoredError: ${m.message}") // !!! DEBUG
2729
if (infos == null) infos = new mutable.ListBuffer

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

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
847847
def followTypeAlias(tree: untpd.Tree): untpd.Tree = {
848848
tree match {
849849
case tree: untpd.RefTree =>
850-
val nestedCtx = ctx.fresh.setNewTyperState
850+
val nestedCtx = ctx.fresh.setNewTyperState()
851851
val ttree =
852852
typedType(untpd.rename(tree, tree.name.toTypeName))(nestedCtx)
853853
ttree.tpe match {
@@ -1002,26 +1002,20 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
10021002
/** Is given method reference applicable to type arguments `targs` and argument trees `args`?
10031003
* @param resultType The expected result type of the application
10041004
*/
1005-
def isApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean = {
1006-
val nestedContext = ctx.fresh.setExploreTyperState
1007-
new ApplicableToTrees(methRef, targs, args, resultType)(nestedContext).success
1008-
}
1005+
def isApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean =
1006+
ctx.typerState.test(new ApplicableToTrees(methRef, targs, args, resultType).success)
10091007

10101008
/** Is given method reference applicable to type arguments `targs` and argument trees `args` without inferring views?
10111009
* @param resultType The expected result type of the application
10121010
*/
1013-
def isDirectlyApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean = {
1014-
val nestedContext = ctx.fresh.setExploreTyperState
1015-
new ApplicableToTreesDirectly(methRef, targs, args, resultType)(nestedContext).success
1016-
}
1011+
def isDirectlyApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean =
1012+
ctx.typerState.test(new ApplicableToTreesDirectly(methRef, targs, args, resultType).success)
10171013

10181014
/** Is given method reference applicable to argument types `args`?
10191015
* @param resultType The expected result type of the application
10201016
*/
1021-
def isApplicable(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context): Boolean = {
1022-
val nestedContext = ctx.fresh.setExploreTyperState
1023-
new ApplicableToTypes(methRef, args, resultType)(nestedContext).success
1024-
}
1017+
def isApplicable(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context): Boolean =
1018+
ctx.typerState.test(new ApplicableToTypes(methRef, args, resultType).success)
10251019

10261020
/** Is given type applicable to type arguments `targs` and argument trees `args`,
10271021
* possibly after inserting an `apply`?
@@ -1102,12 +1096,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
11021096
case tp2: MethodType => true // (3a)
11031097
case tp2: PolyType if tp2.resultType.isInstanceOf[MethodType] => true // (3a)
11041098
case tp2: PolyType => // (3b)
1105-
val nestedCtx = ctx.fresh.setExploreTyperState
1106-
1107-
{
1108-
implicit val ctx = nestedCtx
1109-
isAsSpecificValueType(tp1, constrained(tp2).resultType)
1110-
}
1099+
ctx.typerState.test(isAsSpecificValueType(tp1, constrained(tp2).resultType))
11111100
case _ => // (3b)
11121101
isAsSpecificValueType(tp1, tp2)
11131102
}
@@ -1257,22 +1246,20 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
12571246
* probability of pruning the search. result type comparisons are neither cheap nor
12581247
* do they prune much, on average.
12591248
*/
1260-
def adaptByResult(chosen: TermRef) = {
1261-
def nestedCtx = ctx.fresh.setExploreTyperState
1262-
pt match {
1263-
case pt: FunProto if !resultConforms(chosen, pt.resultType)(nestedCtx) =>
1264-
alts.filter(alt =>
1265-
(alt ne chosen) && resultConforms(alt, pt.resultType)(nestedCtx)) match {
1266-
case Nil => chosen
1267-
case alt2 :: Nil => alt2
1268-
case alts2 =>
1269-
resolveOverloaded(alts2, pt) match {
1270-
case alt2 :: Nil => alt2
1271-
case _ => chosen
1272-
}
1273-
}
1274-
case _ => chosen
1275-
}
1249+
def adaptByResult(chosen: TermRef) = pt match {
1250+
case pt: FunProto if !ctx.typerState.test(resultConforms(chosen, pt.resultType)) =>
1251+
val conformingAlts = alts.filter(alt =>
1252+
(alt ne chosen) && ctx.typerState.test(resultConforms(alt, pt.resultType)))
1253+
conformingAlts match {
1254+
case Nil => chosen
1255+
case alt2 :: Nil => alt2
1256+
case alts2 =>
1257+
resolveOverloaded(alts2, pt) match {
1258+
case alt2 :: Nil => alt2
1259+
case _ => chosen
1260+
}
1261+
}
1262+
case _ => chosen
12761263
}
12771264

12781265
var found = resolveOverloaded(alts, pt, Nil)(ctx.retractMode(Mode.ImplicitsEnabled))

0 commit comments

Comments
 (0)