Skip to content

Commit

Permalink
Alternative scheme that does not require a size limit
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Mar 22, 2024
1 parent c5c94f0 commit 612ba92
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 27 deletions.
33 changes: 16 additions & 17 deletions compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
Original file line number Diff line number Diff line change
Expand Up @@ -648,16 +648,23 @@ trait ConstraintHandling {
* as those could leak the annotation to users (see run/inferred-repeated-result).
*/
def widenInferred(inst: Type, bound: Type, widenUnions: Boolean)(using Context): Type =
def typeSize(tp: Type): Int = tp match
case tp: AndOrType => typeSize(tp.tp1) + typeSize(tp.tp2)
case _ => 1

def widenOr(tp: Type) =
val tpw = tp.widenUnion
if (tpw ne tp) && !tpw.isTransparent() && (tpw <:< bound) then tpw else tp
if widenUnions then
val tpw = tp.widenUnion
if tpw ne tp then
if tpw.isTransparent() then
// Now also widen singletons of soft unions. Before these were skipped
// since we widenUnion on soft unions is independent of whether singletons
// are widened or not. This avoids an expensive subtype check in widenSingle,
// see 19907_*.scala for test cases.
tp.widenSingletons()
else if tpw <:< bound then tpw
else tp
else tp
else tp.hardenUnions

def widenSingle(tp: Type) =
val tpw = tp.widenSingletons
val tpw = tp.widenSingletons(skipSoftUnions = widenUnions)
if (tpw ne tp) && (tpw <:< bound) then tpw else tp

def isSingleton(tp: Type): Boolean = tp match
Expand All @@ -667,16 +674,8 @@ trait ConstraintHandling {
val wideInst =
if isSingleton(bound) then inst
else
val widenedFromUnion =
if widenUnions && typeSize(inst) > 64 then
// If the inferred type `inst` is too large, the subtype check for `bound` in `widenSingle`
// can be expensive due to comparisons between large union types, so we avoid it by
// `widenUnion` directly here.
// See issue #19907.
widenOr(inst)
else
val widenedFromSingle = widenSingle(inst)
if widenUnions then widenOr(widenedFromSingle) else widenedFromSingle.hardenUnions
val widenedFromSingle = widenSingle(inst)
val widenedFromUnion = widenOr(widenedFromSingle)
val widened = dropTransparentTraits(widenedFromUnion, bound)
widenIrreducible(widened)

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
def widenOK =
(tp2.widenSingletons eq tp2)
&& (tp1.widenSingletons ne tp1)
&& inFrozenGadtAndConstraint(recur(tp1.widenSingletons, tp2))
&& inFrozenGadtAndConstraint(recur(tp1.widenSingletons(), tp2))

def joinOK = tp2.dealiasKeepRefiningAnnots match {
case tp2: AppliedType if !tp2.tycon.typeSymbol.isClass =>
Expand Down
20 changes: 11 additions & 9 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1377,15 +1377,15 @@ object Types extends TypeUtils {
* and going to the operands of & and |.
* Overridden and cached in OrType.
*/
def widenSingletons(using Context): Type = dealias match {
def widenSingletons(skipSoftUnions: Boolean = false)(using Context): Type = dealias match {
case tp: SingletonType =>
tp.widen
case tp: OrType =>
val tp1w = tp.widenSingletons
val tp1w = tp.widenSingletons(skipSoftUnions)
if (tp1w eq tp) this else tp1w
case tp: AndType =>
val tp1w = tp.tp1.widenSingletons
val tp2w = tp.tp2.widenSingletons
val tp1w = tp.tp1.widenSingletons(skipSoftUnions)
val tp2w = tp.tp2.widenSingletons(skipSoftUnions)
if ((tp.tp1 eq tp1w) && (tp.tp2 eq tp2w)) this else tp1w & tp2w
case _ =>
this
Expand Down Expand Up @@ -3619,8 +3619,8 @@ object Types extends TypeUtils {
else tp1n.atoms | tp2n.atoms

private def computeWidenSingletons()(using Context): Type =
val tp1w = tp1.widenSingletons
val tp2w = tp2.widenSingletons
val tp1w = tp1.widenSingletons()
val tp2w = tp2.widenSingletons()
if ((tp1 eq tp1w) && (tp2 eq tp2w)) this else TypeComparer.lub(tp1w, tp2w, isSoft = isSoft)

private def ensureAtomsComputed()(using Context): Unit =
Expand All @@ -3633,9 +3633,11 @@ object Types extends TypeUtils {
ensureAtomsComputed()
myAtoms

override def widenSingletons(using Context): Type =
ensureAtomsComputed()
myWidened
override def widenSingletons(skipSoftUnions: Boolean)(using Context): Type =
if isSoft && skipSoftUnions then this
else
ensureAtomsComputed()
myWidened

def derivedOrType(tp1: Type, tp2: Type, soft: Boolean = isSoft)(using Context): Type =
if ((tp1 eq this.tp1) && (tp2 eq this.tp2) && soft == isSoft) this
Expand Down

0 comments on commit 612ba92

Please sign in to comment.