Skip to content

Commit 60d14c3

Browse files
authored
Merge pull request #3186 from dotty-staging/optimize-context
Avoid exploring TyperState creations
2 parents 2d572c5 + 67e5781 commit 60d14c3

File tree

10 files changed

+125
-138
lines changed

10 files changed

+125
-138
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/transform/patmat/Space.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
542542

543543
val childTp = if (child.isTerm) child.termRef else child.typeRef
544544

545-
val resTp = instantiate(childTp, parent)(ctx.fresh.setNewTyperState)
545+
val resTp = instantiate(childTp, parent)(ctx.fresh.setNewTyperState())
546546

547547
if (!resTp.exists) {
548548
debug.println(s"[refine] unqualified child ousted: ${childTp.show} !< ${parent.show}")

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)