Skip to content

Commit 7bf005b

Browse files
committed
Better support for LazyRefs in & and |
1 parent e79a67a commit 7bf005b

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
@@ -1727,10 +1727,14 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
17271727
else if tp1.isAny && !tp2.isLambdaSub || tp1.isAnyKind || tp2.isRef(NothingClass) then tp2
17281728
else if tp2.isAny && !tp1.isLambdaSub || tp2.isAnyKind || tp1.isRef(NothingClass) then tp1
17291729
else tp2 match { // normalize to disjunctive normal form if possible.
1730+
case tp2: LazyRef =>
1731+
glb(tp1, tp2.ref)
17301732
case OrType(tp21, tp22) =>
17311733
tp1 & tp21 | tp1 & tp22
17321734
case _ =>
17331735
tp1 match {
1736+
case tp1: LazyRef =>
1737+
glb(tp1.ref, tp2)
17341738
case OrType(tp11, tp12) =>
17351739
tp11 & tp2 | tp12 & tp2
17361740
case _ =>
@@ -1777,31 +1781,34 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
17771781
else if (!tp2.exists) tp2
17781782
else if tp1.isAny && !tp2.isLambdaSub || tp1.isAnyKind || tp2.isRef(NothingClass) then tp1
17791783
else if tp2.isAny && !tp1.isLambdaSub || tp2.isAnyKind || tp1.isRef(NothingClass) then tp2
1780-
else {
1781-
def mergedLub: Type = {
1782-
val atoms1 = tp1.atoms(widenOK = true)
1783-
if (atoms1.nonEmpty && !widenInUnions) {
1784-
val atoms2 = tp2.atoms(widenOK = true)
1785-
if (atoms2.nonEmpty) {
1786-
if (atoms1.subsetOf(atoms2)) return tp2
1787-
if (atoms2.subsetOf(atoms1)) return tp1
1788-
if ((atoms1 & atoms2).isEmpty) return orType(tp1, tp2)
1789-
}
1790-
}
1791-
val t1 = mergeIfSuper(tp1, tp2, canConstrain)
1792-
if (t1.exists) return t1
1784+
else tp1 match
1785+
case tp1: LazyRef => lub(tp1.ref, tp2)
1786+
case _ => tp2 match
1787+
case tp2: LazyRef => lub(tp1, tp2.ref)
1788+
case _ =>
1789+
def mergedLub: Type = {
1790+
val atoms1 = tp1.atoms(widenOK = true)
1791+
if (atoms1.nonEmpty && !widenInUnions) {
1792+
val atoms2 = tp2.atoms(widenOK = true)
1793+
if (atoms2.nonEmpty) {
1794+
if (atoms1.subsetOf(atoms2)) return tp2
1795+
if (atoms2.subsetOf(atoms1)) return tp1
1796+
if ((atoms1 & atoms2).isEmpty) return orType(tp1, tp2)
1797+
}
1798+
}
1799+
val t1 = mergeIfSuper(tp1, tp2, canConstrain)
1800+
if (t1.exists) return t1
17931801

1794-
val t2 = mergeIfSuper(tp2, tp1, canConstrain)
1795-
if (t2.exists) return t2
1802+
val t2 = mergeIfSuper(tp2, tp1, canConstrain)
1803+
if (t2.exists) return t2
17961804

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

18071814
/** 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.
@@ -2566,15 +2571,15 @@ object Types {
25662571
}
25672572
}
25682573

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

0 commit comments

Comments
 (0)