Skip to content

More efficient garbage collection of type variables #4447

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Constraint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
49 changes: 29 additions & 20 deletions compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]) = {
Expand All @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TyperState.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
127 changes: 127 additions & 0 deletions tests/pos-deep-subtype/inductive-implicits-bench.scala
Original file line number Diff line number Diff line change
@@ -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
}