From 812b9365b19005592f037ca9ba7e323614437d49 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Aug 2020 14:02:54 +0200 Subject: [PATCH] Drop ConstrainResult mode bit Use a TypeComparer variable instead. This saves ~60K fresh contexts on typer/*.scala, which is roughtly 10%. It's also cleaner since it avoids a global mode bit. --- compiler/src/dotty/tools/dotc/core/Mode.scala | 3 -- .../dotty/tools/dotc/core/TypeComparer.scala | 13 +++++- .../dotty/tools/dotc/typer/ProtoTypes.scala | 41 +++++++++++-------- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Mode.scala b/compiler/src/dotty/tools/dotc/core/Mode.scala index d236513b7caa..219099adcfb1 100644 --- a/compiler/src/dotty/tools/dotc/core/Mode.scala +++ b/compiler/src/dotty/tools/dotc/core/Mode.scala @@ -60,9 +60,6 @@ object Mode { */ val Printing: Mode = newMode(10, "Printing") - /** We are constraining a method based on its expected type. */ - val ConstrainResult: Mode = newMode(11, "ConstrainResult") - /** We are currently in a `viewExists` check. In that case, ambiguous * implicits checks are disabled and we succeed with the first implicit * found. diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index caf903371967..f0721fd648bd 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -63,6 +63,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling private var myInstance: TypeComparer = this def currentInstance: TypeComparer = myInstance + private var useNecessaryEither = false + /** Is a subtype check in progress? In that case we may not * permanently instantiate type variables, because the corresponding * constraint might still be retracted and the instantiation should @@ -129,6 +131,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling } } + def necessarySubType(tp1: Type, tp2: Type): Boolean = + val saved = useNecessaryEither + useNecessaryEither = true + try topLevelSubType(tp1, tp2) + finally useNecessaryEither = saved + def testSubType(tp1: Type, tp2: Type): CompareResult = GADTused = false if !topLevelSubType(tp1, tp2) then CompareResult.Fail @@ -1481,7 +1489,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling * @see [[sufficientEither]] for the normal case */ protected def either(op1: => Boolean, op2: => Boolean): Boolean = - if ctx.mode.is(Mode.GadtConstraintInference) || ctx.mode.is(Mode.ConstrainResult) then + if ctx.mode.is(Mode.GadtConstraintInference) || useNecessaryEither then necessaryEither(op1, op2) else sufficientEither(op1, op2) @@ -2528,6 +2536,9 @@ object TypeComparer { def topLevelSubType(tp1: Type, tp2: Type)(using Context): Boolean = comparing(_.topLevelSubType(tp1, tp2)) + def necessarySubType(tp1: Type, tp2: Type)(using Context): Boolean = + comparing(_.necessarySubType(tp1, tp2)) + def isSubType(tp1: Type, tp2: Type)(using Context): Boolean = comparing(_.isSubType(tp1, tp2)) diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 27b8b2914de6..78c9267d8700 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -14,6 +14,7 @@ import Uniques._ import config.Printers.typr import util.SourceFile import util.Property +import TypeComparer.necessarySubType import scala.annotation.internal.sharable @@ -37,6 +38,14 @@ object ProtoTypes { def isCompatible(tp: Type, pt: Type)(using Context): Boolean = (tp.widenExpr relaxed_<:< pt.widenExpr) || viewExists(tp, pt) + /** Like isCompatibe, but using a subtype comparison with necessary eithers + * that don't unnecessarily truncate the constraint space, returning false instead. + */ + def necessarilyCompatible(tp: Type, pt: Type)(using Context): Boolean = + val tpw = tp.widenExpr + val ptw = pt.widenExpr + necessarySubType(tpw, ptw) || tpw.isValueSubType(ptw) || viewExists(tp, pt) + /** Test compatibility after normalization. * Do this in a fresh typerstate unless `keepConstraint` is true. */ @@ -67,24 +76,22 @@ object ProtoTypes { * fits the given expected result type. */ def constrainResult(mt: Type, pt: Type)(using Context): Boolean = - withMode(Mode.ConstrainResult) { - val savedConstraint = ctx.typerState.constraint - val res = pt.widenExpr match { - case pt: FunProto => - mt match { - case mt: MethodType => constrainResult(resultTypeApprox(mt), pt.resultType) - case _ => true - } - case _: ValueTypeOrProto if !disregardProto(pt) => - isCompatible(normalize(mt, pt), pt) - case pt: WildcardType if pt.optBounds.exists => - isCompatible(normalize(mt, pt), pt) - case _ => - true - } - if !res then ctx.typerState.constraint = savedConstraint - res + val savedConstraint = ctx.typerState.constraint + val res = pt.widenExpr match { + case pt: FunProto => + mt match { + case mt: MethodType => constrainResult(resultTypeApprox(mt), pt.resultType) + case _ => true + } + case _: ValueTypeOrProto if !disregardProto(pt) => + necessarilyCompatible(normalize(mt, pt), pt) + case pt: WildcardType if pt.optBounds.exists => + necessarilyCompatible(normalize(mt, pt), pt) + case _ => + true } + if !res then ctx.typerState.constraint = savedConstraint + res /** Constrain result with special case if `meth` is an inlineable method in an inlineable context. * In that case, we should always succeed and not constrain type parameters in the expected type,