Skip to content

Commit 3efed75

Browse files
committed
Fix scala#2941: Avoid infinite loop in "avoid".
1 parent fc71ee6 commit 3efed75

File tree

2 files changed

+40
-9
lines changed

2 files changed

+40
-9
lines changed

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,29 +116,43 @@ trait TypeAssigner {
116116
val lo = ctx.typeComparer.instanceType(tp.origin, fromBelow = variance >= 0)
117117
val lo1 = apply(lo)
118118
if (lo1 ne lo) lo1 else tp
119+
case tp: LazyRef =>
120+
mapOver(tp.ref)
119121
case _ =>
120122
mapOver(tp)
121123
}
122124

125+
private[this] var widenings: Set[Type] = Set()
126+
123127
/** Three deviations from standard derivedSelect:
124128
* 1. We first try a widening conversion to the type's info with
125129
* the original prefix. Since the original prefix is known to
126130
* be a subtype of the returned prefix, this can improve results.
131+
* However, we never try this more than once for a type in order to
132+
* avoid looping, see pos/i2941.scala.
127133
* 2. Then, if the approximation result is a singleton reference C#x.type, we
128134
* replace by the widened type, which is usually more natural.
129135
* 3. Finally, we need to handle the case where the prefix type does not have a member
130136
* named `tp.name` anymmore. In that case, we need to fall back to Bot..Top.
131137
*/
132138
override def derivedSelect(tp: NamedType, pre: Type) =
133-
if (pre eq tp.prefix)
134-
tp
135-
else tryWiden(tp, tp.prefix).orElse {
136-
if (tp.isTerm && variance > 0 && !pre.isSingleton)
137-
apply(tp.info.widenExpr)
138-
else if (upper(pre).member(tp.name).exists)
139-
super.derivedSelect(tp, pre)
140-
else
141-
range(tp.bottomType, tp.topType)
139+
if (pre eq tp.prefix) tp
140+
else {
141+
def derived =
142+
if (tp.isTerm && variance > 0 && !pre.isSingleton)
143+
apply(tp.info.widenExpr)
144+
else if (upper(pre).member(tp.name).exists)
145+
super.derivedSelect(tp, pre)
146+
else
147+
range(tp.bottomType, tp.topType)
148+
if (widenings.contains(tp))
149+
derived
150+
else {
151+
widenings += tp
152+
val widened = tryWiden(tp, tp.prefix)
153+
widenings -= tp
154+
if (widened.exists) widened else derived
155+
}
142156
}
143157
}
144158

tests/pos/i2941.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
trait FooBase {
2+
type Bar >: Null <: BarBase { type This <: FooBase.this.Bar }
3+
type This >: this.type <: FooBase { type This <: FooBase.this.This }
4+
5+
def derived(bar: Bar): This = ???
6+
}
7+
8+
trait BarBase {
9+
type This >: Null <: BarBase { type This <: BarBase.this.This }
10+
}
11+
12+
object Test {
13+
def bad(foo: FooBase) = foo match {
14+
case foo: FooBase =>
15+
foo.derived(???) // Triggers infinite loop in TypeAssigner.avoid()
16+
}
17+
}

0 commit comments

Comments
 (0)