Skip to content

Commit 7b9462a

Browse files
oderskyallanrenucci
authored andcommitted
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.
1 parent 8d04b6b commit 7b9462a

File tree

4 files changed

+55
-43
lines changed

4 files changed

+55
-43
lines changed

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,21 @@ class TyperState(r: Reporter) extends DotClass with Showable {
7373

7474
/** Is it allowed to commit this state? */
7575
def isCommittable: Boolean = false
76+
def isCommittable_=(b: Boolean) = ()
7677

7778
/** Can this state be transitively committed until the top-level? */
7879
def isGlobalCommittable: Boolean = false
7980

81+
/** Test using `op`, restoring typerState to previous state afterwards */
82+
def test(op: => Boolean): Boolean = op
83+
8084
override def toText(printer: Printer): Text = "ImmutableTyperState"
8185

8286
/** A string showing the hashes of all nested mutable typerstates */
8387
def hashesStr: String = ""
8488
}
8589

86-
class MutableTyperState(previous: TyperState, r: Reporter, override val isCommittable: Boolean)
90+
class MutableTyperState(previous: TyperState, r: Reporter, override var isCommittable: Boolean)
8791
extends TyperState(r) {
8892

8993
private var myReporter = r
@@ -119,6 +123,21 @@ extends TyperState(r) {
119123
override def uncommittedAncestor: TyperState =
120124
if (isCommitted) previous.uncommittedAncestor else this
121125

126+
override def test(op: => Boolean): Boolean = {
127+
val savedReporter = myReporter
128+
val savedConstraint = myConstraint
129+
val savedCommitted = isCommitted
130+
val savedCommittable = isCommittable
131+
isCommittable = false
132+
try op
133+
finally {
134+
myReporter = savedReporter
135+
myConstraint = savedConstraint
136+
isCommitted = savedCommitted
137+
isCommittable = savedCommittable
138+
}
139+
}
140+
122141
/** Commit typer state so that its information is copied into current typer state
123142
* In addition (1) the owning state of undetermined or temporarily instantiated
124143
* type variables changes from this typer state to the current one. (2) Variables

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

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -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))

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

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,15 @@ object Implicits {
6767
case mt: MethodType =>
6868
mt.isImplicit ||
6969
mt.paramInfos.length != 1 ||
70-
!(argType relaxed_<:< mt.paramInfos.head)(ctx.fresh.setExploreTyperState)
70+
!ctx.typerState.test(argType relaxed_<:< mt.paramInfos.head)
7171
case poly: PolyType =>
7272
// We do not need to call ProtoTypes#constrained on `poly` because
7373
// `refMatches` is always called with mode TypevarsMissContext enabled.
7474
poly.resultType match {
7575
case mt: MethodType =>
7676
mt.isImplicit ||
7777
mt.paramInfos.length != 1 ||
78-
!(argType relaxed_<:< wildApprox(mt.paramInfos.head, null, Set.empty)(ctx.fresh.setExploreTyperState))
78+
!ctx.typerState.test(argType relaxed_<:< wildApprox(mt.paramInfos.head, null, Set.empty))
7979
case rtp =>
8080
discardForView(wildApprox(rtp, null, Set.empty), argType)
8181
}
@@ -132,8 +132,12 @@ object Implicits {
132132
}
133133

134134
if (refs.isEmpty) Nil
135-
else refs.filter(refMatches(_)(ctx.fresh.addMode(Mode.TypevarsMissContext).setExploreTyperState)) // create a defensive copy of ctx to avoid constraint pollution
136-
.map(Candidate(_, level))
135+
else {
136+
val nestedCtx = ctx.fresh.addMode(Mode.TypevarsMissContext)
137+
refs
138+
.filter(ref => nestedCtx.typerState.test(refMatches(ref)(nestedCtx)))
139+
.map(Candidate(_, level))
140+
}
137141
}
138142
}
139143

@@ -505,6 +509,7 @@ trait Implicits { self: Typer =>
505509
|| inferView(dummyTreeOfType(from), to)
506510
(ctx.fresh.addMode(Mode.ImplicitExploration).setExploreTyperState)
507511
.isInstanceOf[SearchSuccess]
512+
// TODO: investigate why we can't TyperState#test here
508513
)
509514
)
510515

@@ -576,7 +581,7 @@ trait Implicits { self: Typer =>
576581
formal.argTypes match {
577582
case args @ (arg1 :: arg2 :: Nil)
578583
if !ctx.featureEnabled(defn.LanguageModuleClass, nme.strictEquality) &&
579-
validEqAnyArgs(arg1, arg2)(ctx.fresh.setExploreTyperState) =>
584+
ctx.typerState.test(validEqAnyArgs(arg1, arg2)) =>
580585
ref(defn.Eq_eqAny).appliedToTypes(args).withPos(pos)
581586
case _ =>
582587
EmptyTree
@@ -822,7 +827,7 @@ trait Implicits { self: Typer =>
822827
if (ctx.mode.is(Mode.ImplicitExploration) || isCoherent) best :: Nil
823828
else {
824829
val newPending = pending1.filter(cand1 =>
825-
isAsGood(cand1.ref, best.ref, cand1.level, best.level)(nestedContext.setExploreTyperState))
830+
ctx.typerState.test(isAsGood(cand1.ref, best.ref, cand1.level, best.level)(nestedContext)))
826831
rankImplicits(newPending, best :: acc)
827832
}
828833
}
@@ -851,7 +856,8 @@ trait Implicits { self: Typer =>
851856
/** Convert a (possibly empty) list of search successes into a single search result */
852857
def condense(hits: List[SearchSuccess]): SearchResult = hits match {
853858
case best :: alts =>
854-
alts find (alt => isAsGood(alt.ref, best.ref, alt.level, best.level)(ctx.fresh.setExploreTyperState)) match {
859+
alts.find(alt =>
860+
ctx.typerState.test(isAsGood(alt.ref, best.ref, alt.level, best.level))) match {
855861
case Some(alt) =>
856862
typr.println(i"ambiguous implicits for $pt: ${best.ref} @ ${best.level}, ${alt.ref} @ ${alt.level}")
857863
/* !!! DEBUG

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1972,7 +1972,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
19721972
val constraint = ctx.typerState.constraint
19731973
def inst(tp: Type): Type = tp match {
19741974
case TypeBounds(lo, hi)
1975-
if (lo eq hi) || (hi <:< lo)(ctx.fresh.setExploreTyperState) =>
1975+
if (lo eq hi) || ctx.typerState.test(hi <:< lo) =>
19761976
inst(lo)
19771977
case tp: TypeParamRef =>
19781978
constraint.typeVarOfParam(tp).orElse(tp)

0 commit comments

Comments
 (0)