diff --git a/compiler/src/dotty/tools/dotc/core/Constraint.scala b/compiler/src/dotty/tools/dotc/core/Constraint.scala index 037cffa3256b..7431a5e48d28 100644 --- a/compiler/src/dotty/tools/dotc/core/Constraint.scala +++ b/compiler/src/dotty/tools/dotc/core/Constraint.scala @@ -135,8 +135,13 @@ abstract class Constraint extends Showable { */ def uninstVars: collection.Seq[TypeVar] - /** The weakest constraint that subsumes both this constraint and `other` */ - def & (other: Constraint)(implicit ctx: Context): Constraint + /** The weakest constraint that subsumes both this constraint and `other`. + * + * @param otherHasErrors If true, handle incompatible constraints by + * returning an approximate constraint, instead of + * failing with an exception + */ + def & (other: Constraint, otherHasErrors: Boolean)(implicit ctx: Context): Constraint /** Check that no constrained parameter contains itself as a bound */ def checkNonCyclic()(implicit ctx: Context): Unit diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 1eb13bab1f6a..cfb11cde9fc0 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -63,29 +63,38 @@ trait ConstraintHandling { } if (Config.checkConstraintsSeparated) assert(!occursIn(bound), s"$param occurs in $bound") - val newBound = narrowedBound(param, bound, isUpper) - val c1 = constraint.updateEntry(param, newBound) - (c1 eq constraint) || { - constraint = c1 - val TypeBounds(lo, hi) = constraint.entry(param) - isSubType(lo, hi) + + val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param) + val equalBounds = isUpper && (lo eq bound) || !isUpper && (bound eq hi) + if (equalBounds && !bound.existsPart(_.isInstanceOf[WildcardType])) { + // The narrowed bounds are equal and do not contain wildcards, + // so we can remove `param` from the constraint. + // (Handling wildcards requires choosing a bound, but we don't know which + // bound to choose here, this is handled in `ConstraintHandling#approximation`) + constraint = constraint.replace(param, bound) + true + } + else { + // Narrow one of the bounds of type parameter `param` + // If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure + // that `param >: bound`. + val narrowedBounds = { + val saved = homogenizeArgs + homogenizeArgs = Config.alignArgsInAnd + try + if (isUpper) oldBounds.derivedTypeBounds(lo, hi & bound) + else oldBounds.derivedTypeBounds(lo | bound, hi) + finally homogenizeArgs = saved + } + val c1 = constraint.updateEntry(param, narrowedBounds) + (c1 eq constraint) || { + constraint = c1 + val TypeBounds(lo, hi) = constraint.entry(param) + isSubType(lo, hi) + } } } - /** Narrow one of the bounds of type parameter `param` - * If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure - * that `param >: bound`. - */ - def narrowedBound(param: TypeParamRef, bound: Type, isUpper: Boolean)(implicit ctx: Context): TypeBounds = { - val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param) - val saved = homogenizeArgs - homogenizeArgs = Config.alignArgsInAnd - try - if (isUpper) oldBounds.derivedTypeBounds(lo, hi & bound) - else oldBounds.derivedTypeBounds(lo | bound, hi) - finally homogenizeArgs = saved - } - private def location(implicit ctx: Context) = "" // i"in ${ctx.typerState.stateChainStr}" // use for debugging protected def addUpperBound(param: TypeParamRef, bound: Type): Boolean = { diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index 47700da1090b..0eeb99e66928 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -501,7 +501,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } } - def & (other: Constraint)(implicit ctx: Context) = { + def & (other: Constraint, otherHasErrors: Boolean)(implicit ctx: Context) = { def merge[T](m1: ArrayValuedMap[T], m2: ArrayValuedMap[T], join: (T, T) => T): ArrayValuedMap[T] = { var merged = m1 def mergeArrays(xs1: Array[T], xs2: Array[T]) = { @@ -528,7 +528,10 @@ class OrderingConstraint(private val boundsMap: ParamBounds, case (_, e2: TypeBounds) if e2 contains e1 => e1 case (tv1: TypeVar, tv2: TypeVar) if tv1.instanceOpt eq tv2.instanceOpt => e1 case _ => - throw new AssertionError(i"cannot merge $this with $other, mergeEntries($e1, $e2) failed") + if (otherHasErrors) + e1 + else + throw new AssertionError(i"cannot merge $this with $other, mergeEntries($e1, $e2) failed") } val that = other.asInstanceOf[OrderingConstraint] diff --git a/compiler/src/dotty/tools/dotc/core/TyperState.scala b/compiler/src/dotty/tools/dotc/core/TyperState.scala index 10378131be63..6ae682ee5e2e 100644 --- a/compiler/src/dotty/tools/dotc/core/TyperState.scala +++ b/compiler/src/dotty/tools/dotc/core/TyperState.scala @@ -157,7 +157,7 @@ class TyperState(previous: TyperState /* | Null */) { assert(isCommittable) targetState.constraint = if (targetState.constraint eq previousConstraint) constraint - else targetState.constraint & constraint + else targetState.constraint & (constraint, otherHasErrors = reporter.errorsReported) constraint foreachTypeVar { tvar => if (tvar.owningState.get eq this) tvar.owningState = new WeakReference(targetState) } diff --git a/tests/pos-deep-subtype/inductive-implicits-bench.scala b/tests/pos-deep-subtype/inductive-implicits-bench.scala new file mode 100644 index 000000000000..0df40e8cc286 --- /dev/null +++ b/tests/pos-deep-subtype/inductive-implicits-bench.scala @@ -0,0 +1,127 @@ +// Adapted from https://github.com/scala/compiler-benchmark/blob/master/corpus/induction/latest/inductive-implicits-bench.scala + +// With polymorphic implicit pruning: +// set resolvers in compilation += "pr-scala snapshots" at "https://scala-ci.typesafe.com/artifactory/scala-pr-validation-snapshots/" +// set scalaVersion in compilation := "2.13.0-pre-765b3ed-SNAPSHOT" +// +// Without polymorphic implicit pruning: +// set resolvers in compilation += "scala-integration" at "https://scala-ci.typesafe.com/artifactory/scala-integration/" +// set scalaVersion in compilation := "2.13.0-pre-1c56f0a" +// +// Then: +// cold -psource=induction -jvmArgs -Xss4M -jvmArgs -Xmx2G +// +// Nb. this is *very* slow without the pruning (> 400s). +// With the pruning: 10-20s on reasonable hardware. + +package shapeless { + sealed trait HList extends Product with Serializable + + final case class ::[+H, +T <: HList](head : H, tail : T) extends HList { + def ::[HH](h : HH) : HH :: H :: T = shapeless.::(h, this) + + override def toString = head match { + case _: ::[_, _] => "("+head.toString+") :: "+tail.toString + case _ => head.toString+" :: "+tail.toString + } + } + + sealed trait HNil extends HList { + def ::[H](h : H) = shapeless.::(h, this) + override def toString = "HNil" + } + + case object HNil extends HNil + + //@annotation.inductive + trait Selector[L <: HList, U] { + def apply(l: L): U + } + + object Selector { + def apply[L <: HList, U](implicit selector: Selector[L, U]): Selector[L, U] = selector + + implicit def inHead[H, T <: HList]: Selector[H :: T, H] = + new Selector[H :: T, H] { + def apply(l : H :: T) = l.head + } + + implicit def inTail[H, T <: HList, U] + (implicit st : Selector[T, U]): Selector[H :: T, U] = + new Selector[H :: T, U] { + def apply(l : H :: T) = st(l.tail) + } + } +} + +import shapeless._ + +object Test extends App { + val sel = Selector[L, Boolean] + + type L = + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + // + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + // + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + /* + // + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + // + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + // + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + // + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + // + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + // + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + // + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + // + */ + Boolean :: + HNil +}