Skip to content

Commit d0e0c4e

Browse files
committed
Better support for LazyRefs in & and |
1 parent 092083e commit d0e0c4e

File tree

2 files changed

+38
-26
lines changed

2 files changed

+38
-26
lines changed

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,10 +1725,14 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
17251725
else if tp1.isAny && !tp2.isLambdaSub || tp1.isAnyKind || tp2.isRef(NothingClass) then tp2
17261726
else if tp2.isAny && !tp1.isLambdaSub || tp2.isAnyKind || tp1.isRef(NothingClass) then tp1
17271727
else tp2 match { // normalize to disjunctive normal form if possible.
1728+
case tp2: LazyRef =>
1729+
glb(tp1, tp2.ref)
17281730
case OrType(tp21, tp22) =>
17291731
tp1 & tp21 | tp1 & tp22
17301732
case _ =>
17311733
tp1 match {
1734+
case tp1: LazyRef =>
1735+
glb(tp1.ref, tp2)
17321736
case OrType(tp11, tp12) =>
17331737
tp11 & tp2 | tp12 & tp2
17341738
case _ =>
@@ -1775,31 +1779,34 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
17751779
else if (!tp2.exists) tp2
17761780
else if tp1.isAny && !tp2.isLambdaSub || tp1.isAnyKind || tp2.isRef(NothingClass) then tp1
17771781
else if tp2.isAny && !tp1.isLambdaSub || tp2.isAnyKind || tp1.isRef(NothingClass) then tp2
1778-
else {
1779-
def mergedLub: Type = {
1780-
val atoms1 = tp1.atoms
1781-
if (atoms1.nonEmpty && !widenInUnions) {
1782-
val atoms2 = tp2.atoms
1783-
if (atoms2.nonEmpty) {
1784-
if (atoms1.subsetOf(atoms2)) return tp2
1785-
if (atoms2.subsetOf(atoms1)) return tp1
1786-
if ((atoms1 & atoms2).isEmpty) return orType(tp1, tp2)
1787-
}
1788-
}
1789-
val t1 = mergeIfSuper(tp1, tp2, canConstrain)
1790-
if (t1.exists) return t1
1782+
else tp1 match
1783+
case tp1: LazyRef => lub(tp1.ref, tp2)
1784+
case _ => tp2 match
1785+
case tp2: LazyRef => lub(tp1, tp2.ref)
1786+
case _ =>
1787+
def mergedLub: Type = {
1788+
val atoms1 = tp1.atoms
1789+
if (atoms1.nonEmpty && !widenInUnions) {
1790+
val atoms2 = tp2.atoms
1791+
if (atoms2.nonEmpty) {
1792+
if (atoms1.subsetOf(atoms2)) return tp2
1793+
if (atoms2.subsetOf(atoms1)) return tp1
1794+
if ((atoms1 & atoms2).isEmpty) return orType(tp1, tp2)
1795+
}
1796+
}
1797+
val t1 = mergeIfSuper(tp1, tp2, canConstrain)
1798+
if (t1.exists) return t1
17911799

1792-
val t2 = mergeIfSuper(tp2, tp1, canConstrain)
1793-
if (t2.exists) return t2
1800+
val t2 = mergeIfSuper(tp2, tp1, canConstrain)
1801+
if (t2.exists) return t2
17941802

1795-
def widen(tp: Type) = if (widenInUnions) tp.widen else tp.widenIfUnstable
1796-
val tp1w = widen(tp1)
1797-
val tp2w = widen(tp2)
1798-
if ((tp1 ne tp1w) || (tp2 ne tp2w)) lub(tp1w, tp2w)
1799-
else orType(tp1w, tp2w) // no need to check subtypes again
1800-
}
1801-
mergedLub
1802-
}
1803+
def widen(tp: Type) = if (widenInUnions) tp.widen else tp.widenIfUnstable
1804+
val tp1w = widen(tp1)
1805+
val tp2w = widen(tp2)
1806+
if ((tp1 ne tp1w) || (tp2 ne tp2w)) lub(tp1w, tp2w)
1807+
else orType(tp1w, tp2w) // no need to check subtypes again
1808+
}
1809+
mergedLub
18031810
}
18041811

18051812
/** The least upper bound of a list of types */

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,8 @@ object Types {
10071007
* pos/i536 demonstrates that the infinite loop can also involve lower bounds.
10081008
*/
10091009
def safe_& (that: Type)(implicit ctx: Context): Type = (this, that) match {
1010-
case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => TypeBounds(OrType(lo1, lo2), AndType(hi1, hi2))
1010+
case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) =>
1011+
TypeBounds(OrType(lo1.stripLazyRef, lo2.stripLazyRef), AndType(hi1.stripLazyRef, hi2.stripLazyRef))
10111012
case _ => this & that
10121013
}
10131014

@@ -1049,6 +1050,10 @@ object Types {
10491050
case _ => this
10501051
}
10511052

1053+
def stripLazyRef(given Context): Type = this match
1054+
case lzy: LazyRef => lzy.ref
1055+
case _ => this
1056+
10521057
/** Widen from singleton type to its underlying non-singleton
10531058
* base type by applying one or more `underlying` dereferences,
10541059
* Also go from => T to T.
@@ -2563,15 +2568,15 @@ object Types {
25632568
}
25642569
}
25652570

2566-
case class LazyRef(private var refFn: Context => Type) extends UncachedProxyType with ValueType {
2571+
case class LazyRef(private var refFn: Context => Type, reportCycles: Boolean = false) extends UncachedProxyType with ValueType {
25672572
private var myRef: Type = null
25682573
private var computed = false
25692574
def ref(implicit ctx: Context): Type = {
25702575
if (computed) {
25712576
if (myRef == null) {
25722577
// if errors were reported previously handle this by throwing a CyclicReference
25732578
// instead of crashing immediately. A test case is neg/i6057.scala.
2574-
assert(ctx.reporter.errorsReported)
2579+
assert(reportCycles || ctx.reporter.errorsReported)
25752580
throw CyclicReference(NoDenotation)
25762581
}
25772582
}

0 commit comments

Comments
 (0)