From b3364db33ff2ee2d57b4d0eaed03632099244f63 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 20 Jun 2014 15:04:03 +0200 Subject: Avoid caching values that depend on typevar state. TypeVars flip from the initial state, where underlying == origin to the final state where underlying == inst. This flip can invalidate information that depends on the underlying type of a TypeVar. Since we do not know when the flip occurs, we need to avoid keeping any such information in a cache. The commit makes three caches depend on a new value: typerState.ephemeral. The value is set to `true` each time we follow the underlying type of a TypeVar, and this disables cached information to be retained. A test case for this commit is t2693.scala. This test passes typechecking with the previous commit, but fails in -Ycheck:front because of stale cache info in an "#Apply" typeref. The present commit fixes that. --- src/dotty/tools/dotc/core/TyperState.scala | 12 +++++++- src/dotty/tools/dotc/core/Types.scala | 48 +++++++++++++++++++----------- src/dotty/tools/dotc/typer/Implicits.scala | 33 +++++++++++++++----- 3 files changed, 66 insertions(+), 27 deletions(-) (limited to 'src/dotty/tools/dotc') diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala index 8c742edab..fd8a534d4 100644 --- a/src/dotty/tools/dotc/core/TyperState.scala +++ b/src/dotty/tools/dotc/core/TyperState.scala @@ -23,6 +23,10 @@ class TyperState(r: Reporter) extends DotClass with Showable { /** The uninstantiated variables */ def uninstVars = constraint.uninstVars + /** The ephemeral flag */ + def ephemeral: Boolean = false + def ephemeral_=(x: Boolean): Unit = () + /** Gives for each instantiated type var that does not yet have its `inst` field * set, the instance value stored in the constraint. Storing instances in constraints * is done only in a temporary way for contexts that may be retracted @@ -76,6 +80,12 @@ extends TyperState(r) { override def constraint = myConstraint override def constraint_=(c: Constraint) = myConstraint = c + private var myEphemeral: Boolean = previous.ephemeral + + override def ephemeral = myEphemeral + override def ephemeral_=(x: Boolean): Unit = { myEphemeral = x } + + override def fresh(isCommittable: Boolean): TyperState = new MutableTyperState(this, new StoreReporter, isCommittable) @@ -96,11 +106,11 @@ extends TyperState(r) { val targetState = ctx.typerState assert(isCommittable) targetState.constraint = constraint - constraint foreachTypeVar { tvar => if (tvar.owningState eq this) tvar.owningState = targetState } + targetState.ephemeral = ephemeral targetState.gc() reporter.flush() } diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index f1cdfe54b..f3e10c5b0 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1075,24 +1075,32 @@ object Types { /** A second fallback to recompute the denotation if necessary */ private def computeDenot(implicit ctx: Context): Denotation = { - val d = lastDenotation match { - case null => - val sym = lastSymbol - if (sym == null) loadDenot else denotOfSym(sym) - case d: SymDenotation => - if (d.validFor.runId == ctx.runId || ctx.stillValid(d)) d.current - else { - val newd = loadDenot - if (newd.exists) newd else d.staleSymbolError - } - case d => - if (d.validFor.runId == ctx.period.runId) d.current - else loadDenot + val savedEphemeral = ctx.typerState.ephemeral + ctx.typerState.ephemeral = false + try { + val d = lastDenotation match { + case null => + val sym = lastSymbol + if (sym == null) loadDenot else denotOfSym(sym) + case d: SymDenotation => + if (d.validFor.runId == ctx.runId || ctx.stillValid(d)) d.current + else { + val newd = loadDenot + if (newd.exists) newd else d.staleSymbolError + } + case d => + if (d.validFor.runId == ctx.period.runId) d.current + else loadDenot + } + if (ctx.typerState.ephemeral) record("ephemeral cache miss: loadDenot") + else { + lastDenotation = d + lastSymbol = d.symbol + checkedPeriod = ctx.period + } + d } - lastDenotation = d - lastSymbol = d.symbol - checkedPeriod = ctx.period - d + finally ctx.typerState.ephemeral |= savedEphemeral } private def denotOfSym(sym: Symbol)(implicit ctx: Context): Denotation = { @@ -1974,7 +1982,11 @@ object Types { /** If the variable is instantiated, its instance, otherwise its origin */ override def underlying(implicit ctx: Context): Type = { val inst = instanceOpt - if (inst.exists) inst else origin + if (inst.exists) inst + else { + ctx.typerState.ephemeral = true + origin + } } override def computeHash: Int = identityHash diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index 86d513fff..da1492d61 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -142,9 +142,15 @@ object Implicits { if (monitored) record(s"elided eligible refs", elided(this)) eligibles case None => - val eligibles = computeEligible(tp) - eligibleCache(tp) = eligibles - eligibles + val savedEphemeral = ctx.typerState.ephemeral + ctx.typerState.ephemeral = false + try { + val result = computeEligible(tp) + if (ctx.typerState.ephemeral) record("ephemeral cache miss: eligible") + else eligibleCache(tp) = result + result + } + finally ctx.typerState.ephemeral |= savedEphemeral } } @@ -334,11 +340,22 @@ trait ImplicitRunInfo { self: RunInfo => def iscope(tp: Type, isLifted: Boolean = false): OfTypeImplicits = if (tp.hash == NotCached || !Config.cacheImplicitScopes) ofTypeImplicits(collectCompanions(tp)) - else implicitScopeCache.getOrElseUpdate(tp, { - val liftedTp = if (isLifted) tp else liftToClasses(tp) - if (liftedTp ne tp) iscope(liftedTp, isLifted = true) - else ofTypeImplicits(collectCompanions(tp)) - }) + else implicitScopeCache get tp match { + case Some(is) => is + case None => + val savedEphemeral = ctx.typerState.ephemeral + ctx.typerState.ephemeral = false + try { + val liftedTp = if (isLifted) tp else liftToClasses(tp) + val result = + if (liftedTp ne tp) iscope(liftedTp, isLifted = true) + else ofTypeImplicits(collectCompanions(tp)) + if (ctx.typerState.ephemeral) record("ephemeral cache miss: implicitScope") + else implicitScopeCache(tp) = result + result + } + finally ctx.typerState.ephemeral |= savedEphemeral + } iscope(tp) } -- cgit v1.2.3