From c83112809bb58da290b000446c7f405b373cf6a1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 6 Sep 2017 10:57:05 +0200 Subject: [PATCH 1/2] 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 --- compiler/src/dotty/tools/dotc/Run.scala | 2 +- .../src/dotty/tools/dotc/core/Contexts.scala | 8 +- .../dotty/tools/dotc/core/TyperState.scala | 152 +++++++++--------- .../tools/dotc/reporting/StoreReporter.scala | 2 + .../dotty/tools/dotc/typer/Applications.scala | 57 +++---- .../dotty/tools/dotc/typer/Implicits.scala | 26 +-- .../dotty/tools/dotc/typer/Inferencing.scala | 2 +- .../dotty/tools/dotc/typer/ProtoTypes.scala | 8 +- .../src/dotty/tools/dotc/typer/Typer.scala | 4 +- 9 files changed, 124 insertions(+), 137 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index ebf16bb9eb14..28b74b1b38c6 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -49,7 +49,7 @@ class Run(comp: Compiler, ictx: Context) { .setOwner(defn.RootClass) .setTyper(new Typer) .addMode(Mode.ImplicitsEnabled) - .setTyperState(new MutableTyperState(ctx.typerState, ctx.typerState.reporter, isCommittable = true)) + .setTyperState(new TyperState(ctx.typerState)) .setFreshNames(new FreshNameCreator.Default) ctx.initialize()(start) // re-initialize the base context with start def addImport(ctx: Context, refFn: () => TermRef) = diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index df330c4da0b5..f430a083c7c2 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -459,9 +459,9 @@ object Contexts { def setCompilerCallback(callback: CompilerCallback): this.type = { this.compilerCallback = callback; this } def setSbtCallback(callback: AnalysisCallback): this.type = { this.sbtCallback = callback; this } def setTyperState(typerState: TyperState): this.type = { this.typerState = typerState; this } - def setReporter(reporter: Reporter): this.type = setTyperState(typerState.withReporter(reporter)) - def setNewTyperState: this.type = setTyperState(typerState.fresh(isCommittable = true)) - def setExploreTyperState: this.type = setTyperState(typerState.fresh(isCommittable = false)) + def setReporter(reporter: Reporter): this.type = setTyperState(typerState.fresh().setReporter(reporter)) + def setNewTyperState(): this.type = setTyperState(typerState.fresh().setCommittable(true)) + def setExploreTyperState(): this.type = setTyperState(typerState.fresh().setCommittable(false)) def setPrinterFn(printer: Context => Printer): this.type = { this.printerFn = printer; this } def setOwner(owner: Symbol): this.type = { assert(owner != NoSymbol); this.owner = owner; this } def setSettings(sstate: SettingsState): this.type = { this.sstate = sstate; this } @@ -520,7 +520,7 @@ object Contexts { outer = NoContext period = InitialPeriod mode = Mode.None - typerState = new TyperState(new ConsoleReporter()) + typerState = new TyperState(null) printerFn = new RefinedPrinter(_) owner = NoSymbol sstate = settings.defaultState diff --git a/compiler/src/dotty/tools/dotc/core/TyperState.scala b/compiler/src/dotty/tools/dotc/core/TyperState.scala index 051fb20ee02c..df7c9e087423 100644 --- a/compiler/src/dotty/tools/dotc/core/TyperState.scala +++ b/compiler/src/dotty/tools/dotc/core/TyperState.scala @@ -13,18 +13,31 @@ import config.Config import collection.mutable import java.lang.ref.WeakReference -class TyperState(r: Reporter) extends DotClass with Showable { +class TyperState(previous: TyperState /* | Null */) extends DotClass with Showable { - /** The current reporter */ - def reporter = r + private var myReporter = + if (previous == null) new ConsoleReporter() else previous.reporter - /** The current constraint set */ - def constraint: Constraint = - new OrderingConstraint(SimpleMap.Empty, SimpleMap.Empty, SimpleMap.Empty) - def constraint_=(c: Constraint)(implicit ctx: Context): Unit = {} + def reporter: Reporter = myReporter - /** The uninstantiated variables */ - def uninstVars = constraint.uninstVars + /** A fresh type state with the same constraint as this one and the given reporter */ + def setReporter(reporter: Reporter): this.type = { myReporter = reporter; this } + + private var myConstraint: Constraint = + if (previous == null) new OrderingConstraint(SimpleMap.Empty, SimpleMap.Empty, SimpleMap.Empty) + else previous.constraint + + def constraint = myConstraint + def constraint_=(c: Constraint)(implicit ctx: Context) = { + if (Config.debugCheckConstraintsClosed && isGlobalCommittable) c.checkClosed() + myConstraint = c + } + + private val previousConstraint = + if (previous == null) constraint else previous.constraint + + private var myEphemeral: Boolean = + if (previous == null) false else previous.ephemeral /** The ephemeral flag is set as a side effect if an operation accesses * 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 { * check the ephemeral flag; If the flag is set during an operation, the result * of that operation should not be cached. */ - def ephemeral: Boolean = false - def ephemeral_=(x: Boolean): Unit = () + def ephemeral = myEphemeral + def ephemeral_=(x: Boolean): Unit = { myEphemeral = x } + + private var myIsCommittable = true + + def isCommittable = myIsCommittable + + def setCommittable(committable: Boolean): this.type = { this.myIsCommittable = committable; this } + + def isGlobalCommittable: Boolean = + isCommittable && (previous == null || previous.isGlobalCommittable) + + private var isCommitted = false + + /** A fresh typer state with the same constraint as this one. */ + def fresh(): TyperState = + new TyperState(this).setReporter(new StoreReporter(reporter)).setCommittable(isCommittable) + + /** The uninstantiated variables */ + def uninstVars = constraint.uninstVars /** 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 @@ -49,76 +80,36 @@ class TyperState(r: Reporter) extends DotClass with Showable { case tp => tp } - /** A fresh typer state with the same constraint as this one. - * @param isCommittable The constraint can be committed to an enclosing context. - */ - def fresh(isCommittable: Boolean): TyperState = this - - /** A fresh type state with the same constraint as this one and the given reporter */ - def withReporter(reporter: Reporter) = new TyperState(reporter) - - /** Commit state so that it gets propagated to enclosing context */ - def commit()(implicit ctx: Context): Unit = unsupported("commit") - /** The closest ancestor of this typer state (including possibly this typer state itself) * which is not yet committed, or which does not have a parent. */ - def uncommittedAncestor: TyperState = this - - /** Make type variable instances permanent by assigning to `inst` field if - * type variable instantiation cannot be retracted anymore. Then, remove - * no-longer needed constraint entries. - */ - def gc()(implicit ctx: Context): Unit = () - - /** Is it allowed to commit this state? */ - def isCommittable: Boolean = false - - /** Can this state be transitively committed until the top-level? */ - def isGlobalCommittable: Boolean = false - - override def toText(printer: Printer): Text = "ImmutableTyperState" - - /** A string showing the hashes of all nested mutable typerstates */ - def hashesStr: String = "" -} - -class MutableTyperState(previous: TyperState, r: Reporter, override val isCommittable: Boolean) -extends TyperState(r) { - - private var myReporter = r - - override def reporter = myReporter - - private val previousConstraint = previous.constraint - private var myConstraint: Constraint = previousConstraint + def uncommittedAncestor: TyperState = + if (isCommitted) previous.uncommittedAncestor else this - override def constraint = myConstraint - override def constraint_=(c: Constraint)(implicit ctx: Context) = { - if (Config.debugCheckConstraintsClosed && isGlobalCommittable) c.checkClosed() - myConstraint = c + private var testReporter: StoreReporter = null + + /** Test using `op`, restoring typerState to previous state afterwards */ + def test(op: => Boolean): Boolean = { + val savedReporter = myReporter + val savedConstraint = myConstraint + val savedCommittable = myIsCommittable + val savedCommitted = isCommitted + myIsCommittable = false + myReporter = + if (testReporter == null) new StoreReporter(reporter) + else { + testReporter.reset() + testReporter + } + try op + finally { + myReporter = savedReporter + myConstraint = savedConstraint + myIsCommittable = savedCommittable + isCommitted = savedCommitted + } } - 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(reporter), isCommittable) - - override def withReporter(reporter: Reporter) = - new MutableTyperState(this, reporter, isCommittable) - - override val isGlobalCommittable = - isCommittable && - (!previous.isInstanceOf[MutableTyperState] || previous.isGlobalCommittable) - - private var isCommitted = false - - override def uncommittedAncestor: TyperState = - if (isCommitted) previous.uncommittedAncestor else this - /** Commit typer state so that its information is copied into current typer state * In addition (1) the owning state of undetermined or temporarily instantiated * type variables changes from this typer state to the current one. (2) Variables @@ -137,7 +128,7 @@ extends TyperState(r) { * isApplicableSafe but also for (e.g. erased-lubs.scala) as well as * many parts of dotty itself. */ - override def commit()(implicit ctx: Context) = { + def commit()(implicit ctx: Context) = { val targetState = ctx.typerState assert(isCommittable) targetState.constraint = @@ -152,7 +143,11 @@ extends TyperState(r) { isCommitted = true } - override def gc()(implicit ctx: Context): Unit = { + /** Make type variable instances permanent by assigning to `inst` field if + * type variable instantiation cannot be retracted anymore. Then, remove + * no-longer needed constraint entries. + */ + def gc()(implicit ctx: Context): Unit = { val toCollect = new mutable.ListBuffer[TypeLambda] constraint foreachTypeVar { tvar => if (!tvar.inst.exists) { @@ -170,6 +165,5 @@ extends TyperState(r) { override def toText(printer: Printer): Text = constraint.toText(printer) - override def hashesStr: String = hashCode.toString + " -> " + previous.hashesStr - + def hashesStr: String = hashCode.toString + " -> " + previous.hashesStr } diff --git a/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala b/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala index 343e6a517712..ac60124ae86e 100644 --- a/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala @@ -22,6 +22,8 @@ class StoreReporter(outer: Reporter) extends Reporter { private var infos: mutable.ListBuffer[MessageContainer] = null + def reset() = infos = null + def doReport(m: MessageContainer)(implicit ctx: Context): Unit = { typr.println(s">>>> StoredError: ${m.message}") // !!! DEBUG if (infos == null) infos = new mutable.ListBuffer diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 934c2236b014..4bb4b7820c98 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -847,7 +847,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => def followTypeAlias(tree: untpd.Tree): untpd.Tree = { tree match { case tree: untpd.RefTree => - val nestedCtx = ctx.fresh.setNewTyperState + val nestedCtx = ctx.fresh.setNewTyperState() val ttree = typedType(untpd.rename(tree, tree.name.toTypeName))(nestedCtx) ttree.tpe match { @@ -1002,26 +1002,20 @@ trait Applications extends Compatibility { self: Typer with Dynamic => /** Is given method reference applicable to type arguments `targs` and argument trees `args`? * @param resultType The expected result type of the application */ - def isApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean = { - val nestedContext = ctx.fresh.setExploreTyperState - new ApplicableToTrees(methRef, targs, args, resultType)(nestedContext).success - } + def isApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean = + ctx.typerState.test(new ApplicableToTrees(methRef, targs, args, resultType).success) /** Is given method reference applicable to type arguments `targs` and argument trees `args` without inferring views? * @param resultType The expected result type of the application */ - def isDirectlyApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean = { - val nestedContext = ctx.fresh.setExploreTyperState - new ApplicableToTreesDirectly(methRef, targs, args, resultType)(nestedContext).success - } + def isDirectlyApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean = + ctx.typerState.test(new ApplicableToTreesDirectly(methRef, targs, args, resultType).success) /** Is given method reference applicable to argument types `args`? * @param resultType The expected result type of the application */ - def isApplicable(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context): Boolean = { - val nestedContext = ctx.fresh.setExploreTyperState - new ApplicableToTypes(methRef, args, resultType)(nestedContext).success - } + def isApplicable(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context): Boolean = + ctx.typerState.test(new ApplicableToTypes(methRef, args, resultType).success) /** Is given type applicable to type arguments `targs` and argument trees `args`, * possibly after inserting an `apply`? @@ -1102,12 +1096,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case tp2: MethodType => true // (3a) case tp2: PolyType if tp2.resultType.isInstanceOf[MethodType] => true // (3a) case tp2: PolyType => // (3b) - val nestedCtx = ctx.fresh.setExploreTyperState - - { - implicit val ctx = nestedCtx - isAsSpecificValueType(tp1, constrained(tp2).resultType) - } + ctx.typerState.test(isAsSpecificValueType(tp1, constrained(tp2).resultType)) case _ => // (3b) isAsSpecificValueType(tp1, tp2) } @@ -1257,22 +1246,20 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * probability of pruning the search. result type comparisons are neither cheap nor * do they prune much, on average. */ - def adaptByResult(chosen: TermRef) = { - def nestedCtx = ctx.fresh.setExploreTyperState - pt match { - case pt: FunProto if !resultConforms(chosen, pt.resultType)(nestedCtx) => - alts.filter(alt => - (alt ne chosen) && resultConforms(alt, pt.resultType)(nestedCtx)) match { - case Nil => chosen - case alt2 :: Nil => alt2 - case alts2 => - resolveOverloaded(alts2, pt) match { - case alt2 :: Nil => alt2 - case _ => chosen - } - } - case _ => chosen - } + def adaptByResult(chosen: TermRef) = pt match { + case pt: FunProto if !ctx.typerState.test(resultConforms(chosen, pt.resultType)) => + val conformingAlts = alts.filter(alt => + (alt ne chosen) && ctx.typerState.test(resultConforms(alt, pt.resultType))) + conformingAlts match { + case Nil => chosen + case alt2 :: Nil => alt2 + case alts2 => + resolveOverloaded(alts2, pt) match { + case alt2 :: Nil => alt2 + case _ => chosen + } + } + case _ => chosen } var found = resolveOverloaded(alts, pt, Nil)(ctx.retractMode(Mode.ImplicitsEnabled)) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 11ac5b3b98fa..c75b1897e5ad 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -67,7 +67,7 @@ object Implicits { case mt: MethodType => mt.isImplicit || mt.paramInfos.length != 1 || - !(argType relaxed_<:< mt.paramInfos.head)(ctx.fresh.setExploreTyperState) + !ctx.typerState.test(argType relaxed_<:< mt.paramInfos.head) case poly: PolyType => // We do not need to call ProtoTypes#constrained on `poly` because // `refMatches` is always called with mode TypevarsMissContext enabled. @@ -75,7 +75,7 @@ object Implicits { case mt: MethodType => mt.isImplicit || mt.paramInfos.length != 1 || - !(argType relaxed_<:< wildApprox(mt.paramInfos.head, null, Set.empty)(ctx.fresh.setExploreTyperState)) + !ctx.typerState.test(argType relaxed_<:< wildApprox(mt.paramInfos.head, null, Set.empty)) case rtp => discardForView(wildApprox(rtp, null, Set.empty), argType) } @@ -132,8 +132,12 @@ object Implicits { } if (refs.isEmpty) Nil - else refs.filter(refMatches(_)(ctx.fresh.addMode(Mode.TypevarsMissContext).setExploreTyperState)) // create a defensive copy of ctx to avoid constraint pollution - .map(Candidate(_, level)) + else { + val nestedCtx = ctx.fresh.addMode(Mode.TypevarsMissContext) + refs + .filter(ref => nestedCtx.typerState.test(refMatches(ref)(nestedCtx))) + .map(Candidate(_, level)) + } } } @@ -503,8 +507,9 @@ trait Implicits { self: Typer => && from.isValueType && ( from.isValueSubType(to) || inferView(dummyTreeOfType(from), to) - (ctx.fresh.addMode(Mode.ImplicitExploration).setExploreTyperState) + (ctx.fresh.addMode(Mode.ImplicitExploration).setExploreTyperState()) .isInstanceOf[SearchSuccess] + // TODO: investigate why we can't TyperState#test here ) ) @@ -576,7 +581,7 @@ trait Implicits { self: Typer => formal.argTypes match { case args @ (arg1 :: arg2 :: Nil) if !ctx.featureEnabled(defn.LanguageModuleClass, nme.strictEquality) && - validEqAnyArgs(arg1, arg2)(ctx.fresh.setExploreTyperState) => + ctx.typerState.test(validEqAnyArgs(arg1, arg2)) => ref(defn.Eq_eqAny).appliedToTypes(args).withPos(pos) case _ => EmptyTree @@ -782,7 +787,7 @@ trait Implicits { self: Typer => val generated1 = adapt(generated, pt) lazy val shadowing = typed(untpd.Ident(ref.name) withPos pos.toSynthetic, funProto)( - nestedContext.addMode(Mode.ImplicitShadowing).setExploreTyperState) + nestedContext.addMode(Mode.ImplicitShadowing).setExploreTyperState()) def refSameAs(shadowing: Tree): Boolean = ref.symbol == closureBody(shadowing).symbol || { shadowing match { @@ -814,7 +819,7 @@ trait Implicits { self: Typer => val history = ctx.searchHistory nest wildProto val result = if (history eq ctx.searchHistory) divergingImplicit(cand.ref) - else typedImplicit(cand)(nestedContext.setNewTyperState.setSearchHistory(history)) + else typedImplicit(cand)(nestedContext.setNewTyperState().setSearchHistory(history)) result match { case fail: SearchFailure => rankImplicits(pending1, acc) @@ -822,7 +827,7 @@ trait Implicits { self: Typer => if (ctx.mode.is(Mode.ImplicitExploration) || isCoherent) best :: Nil else { val newPending = pending1.filter(cand1 => - isAsGood(cand1.ref, best.ref, cand1.level, best.level)(nestedContext.setExploreTyperState)) + ctx.typerState.test(isAsGood(cand1.ref, best.ref, cand1.level, best.level)(nestedContext))) rankImplicits(newPending, best :: acc) } } @@ -851,7 +856,8 @@ trait Implicits { self: Typer => /** Convert a (possibly empty) list of search successes into a single search result */ def condense(hits: List[SearchSuccess]): SearchResult = hits match { case best :: alts => - alts find (alt => isAsGood(alt.ref, best.ref, alt.level, best.level)(ctx.fresh.setExploreTyperState)) match { + alts.find(alt => + ctx.typerState.test(isAsGood(alt.ref, best.ref, alt.level, best.level))) match { case Some(alt) => typr.println(i"ambiguous implicits for $pt: ${best.ref} @ ${best.level}, ${alt.ref} @ ${alt.level}") /* !!! DEBUG diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 01f4dd7fb895..ee5995d3f0ee 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -31,7 +31,7 @@ object Inferencing { * Variables that are successfully minimized do not count as uninstantiated. */ def isFullyDefined(tp: Type, force: ForceDegree.Value)(implicit ctx: Context): Boolean = { - val nestedCtx = ctx.fresh.setNewTyperState + val nestedCtx = ctx.fresh.setNewTyperState() val result = new IsFullyDefinedAccumulator(force)(nestedCtx).process(tp) if (result) nestedCtx.typerState.commit() result diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 9f36fbed9d34..e7fe53bff0c6 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -39,11 +39,9 @@ object ProtoTypes { (tp.widenExpr relaxed_<:< pt.widenExpr) || viewExists(tp, pt) /** Test compatibility after normalization in a fresh typerstate. */ - def normalizedCompatible(tp: Type, pt: Type)(implicit ctx: Context) = { - val nestedCtx = ctx.fresh.setExploreTyperState - val normTp = normalize(tp, pt)(nestedCtx) - isCompatible(normTp, pt)(nestedCtx) || - pt.isRef(defn.UnitClass) && normTp.isParameterless + def normalizedCompatible(tp: Type, pt: Type)(implicit ctx: Context) = ctx.typerState.test { + val normTp = normalize(tp, pt) + isCompatible(normTp, pt) || pt.isRef(defn.UnitClass) && normTp.isParameterless } private def disregardProto(pt: Type)(implicit ctx: Context): Boolean = pt.dealias match { diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index ab63b6f51580..a4fe66b932b2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1751,7 +1751,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit typed(tree, selType)(ctx addMode Mode.Pattern) def tryEither[T](op: Context => T)(fallBack: (T, TyperState) => T)(implicit ctx: Context) = { - val nestedCtx = ctx.fresh.setNewTyperState + val nestedCtx = ctx.fresh.setNewTyperState() val result = op(nestedCtx) if (nestedCtx.reporter.hasErrors) fallBack(result, nestedCtx.typerState) @@ -1972,7 +1972,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val constraint = ctx.typerState.constraint def inst(tp: Type): Type = tp match { case TypeBounds(lo, hi) - if (lo eq hi) || (hi <:< lo)(ctx.fresh.setExploreTyperState) => + if (lo eq hi) || ctx.typerState.test(hi <:< lo) => inst(lo) case tp: TypeParamRef => constraint.typeVarOfParam(tp).orElse(tp) From 67e578124e7d58346962ee6658d2dfae4686e767 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 27 Sep 2017 18:51:43 +0200 Subject: [PATCH 2/2] Fix merge breakage --- compiler/src/dotty/tools/dotc/transform/patmat/Space.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index da53a106edad..1a4424af8a99 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -542,7 +542,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val childTp = if (child.isTerm) child.termRef else child.typeRef - val resTp = instantiate(childTp, parent)(ctx.fresh.setNewTyperState) + val resTp = instantiate(childTp, parent)(ctx.fresh.setNewTyperState()) if (!resTp.exists) { debug.println(s"[refine] unqualified child ousted: ${childTp.show} !< ${parent.show}")