Skip to content

Commit ba9ad1e

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.
1 parent a60b9cd commit ba9ad1e

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
}
@@ -1253,22 +1242,20 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
12531242
* probability of pruning the search. result type comparisons are neither cheap nor
12541243
* do they prune much, on average.
12551244
*/
1256-
def adaptByResult(chosen: TermRef) = {
1257-
def nestedCtx = ctx.fresh.setExploreTyperState
1258-
pt match {
1259-
case pt: FunProto if !resultConforms(chosen, pt.resultType)(nestedCtx) =>
1260-
alts.filter(alt =>
1261-
(alt ne chosen) && resultConforms(alt, pt.resultType)(nestedCtx)) match {
1262-
case Nil => chosen
1263-
case alt2 :: Nil => alt2
1264-
case alts2 =>
1265-
resolveOverloaded(alts2, pt) match {
1266-
case alt2 :: Nil => alt2
1267-
case _ => chosen
1268-
}
1269-
}
1270-
case _ => chosen
1271-
}
1245+
def adaptByResult(chosen: TermRef) = pt match {
1246+
case pt: FunProto if !ctx.typerState.test(resultConforms(chosen, pt.resultType)) =>
1247+
val conformingAlts = alts.filter(alt =>
1248+
(alt ne chosen) && ctx.typerState.test(resultConforms(alt, pt.resultType)))
1249+
conformingAlts match {
1250+
case Nil => chosen
1251+
case alt2 :: Nil => alt2
1252+
case alts2 =>
1253+
resolveOverloaded(alts2, pt) match {
1254+
case alt2 :: Nil => alt2
1255+
case _ => chosen
1256+
}
1257+
}
1258+
case _ => chosen
12721259
}
12731260

12741261
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
}
@@ -131,8 +131,12 @@ object Implicits {
131131
}
132132

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

@@ -507,6 +511,7 @@ trait Implicits { self: Typer =>
507511
|| inferView(dummyTreeOfType(from), to)
508512
(ctx.fresh.addMode(Mode.ImplicitExploration).setExploreTyperState)
509513
.isInstanceOf[SearchSuccess]
514+
// TODO: investigate why we can't TyperState#test here
510515
)
511516
)
512517

@@ -578,7 +583,7 @@ trait Implicits { self: Typer =>
578583
formal.argTypes match {
579584
case args @ (arg1 :: arg2 :: Nil)
580585
if !ctx.featureEnabled(defn.LanguageModuleClass, nme.strictEquality) &&
581-
validEqAnyArgs(arg1, arg2)(ctx.fresh.setExploreTyperState) =>
586+
ctx.typerState.test(validEqAnyArgs(arg1, arg2)) =>
582587
ref(defn.Eq_eqAny).appliedToTypes(args).withPos(pos)
583588
case _ =>
584589
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
@@ -1977,7 +1977,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
19771977
val constraint = ctx.typerState.constraint
19781978
def inst(tp: Type): Type = tp match {
19791979
case TypeBounds(lo, hi)
1980-
if (lo eq hi) || (hi <:< lo)(ctx.fresh.setExploreTyperState) =>
1980+
if (lo eq hi) || ctx.typerState.test(hi <:< lo) =>
19811981
inst(lo)
19821982
case tp: TypeParamRef =>
19831983
constraint.typeVarOfParam(tp).orElse(tp)

0 commit comments

Comments
 (0)