Skip to content

Commit e5be3a1

Browse files
authored
Guard against recursive lower bounds in constraints (#21587)
We could get an indirect recursion going through a singleton type before. Fixes #21535
2 parents a4b162d + d0ea3b0 commit e5be3a1

File tree

3 files changed

+44
-4
lines changed

3 files changed

+44
-4
lines changed

Diff for: compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala

+17-4
Original file line numberDiff line numberDiff line change
@@ -295,11 +295,24 @@ trait ConstraintHandling {
295295
end legalBound
296296

297297
protected def addOneBound(param: TypeParamRef, rawBound: Type, isUpper: Boolean)(using Context): Boolean =
298+
299+
// Replace top-level occurrences of `param` in `bound` by `Nothing`
300+
def sanitize(bound: Type): Type =
301+
if bound.stripped eq param then defn.NothingType
302+
else bound match
303+
case bound: AndOrType =>
304+
bound.derivedAndOrType(sanitize(bound.tp1), sanitize(bound.tp2))
305+
case _ =>
306+
bound
307+
298308
if !constraint.contains(param) then true
299-
else if !isUpper && param.occursIn(rawBound) then
300-
// We don't allow recursive lower bounds when defining a type,
301-
// so we shouldn't allow them as constraints either.
302-
false
309+
else if !isUpper && param.occursIn(rawBound.widen) then
310+
val rawBound1 = sanitize(rawBound.widenDealias)
311+
if param.occursIn(rawBound1) then
312+
// We don't allow recursive lower bounds when defining a type,
313+
// so we shouldn't allow them as constraints either.
314+
false
315+
else addOneBound(param, rawBound1, isUpper)
303316
else
304317

305318
// Narrow one of the bounds of type parameter `param`

Diff for: tests/neg/i21535.check

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-- [E007] Type Mismatch Error: tests/neg/i21535.scala:7:4 --------------------------------------------------------------
2+
3 | (if (true) then
3+
4 | new A(66)
4+
5 | else
5+
6 | m1()
6+
7 | ).m2(p1 = p); // error
7+
| ^
8+
| Found: (Int | Short) @uncheckedVariance
9+
| Required: Int & Short
10+
|
11+
| longer explanation available when compiling with `-explain`

Diff for: tests/neg/i21535.scala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
def test() = {
2+
val p = 10.toShort
3+
(if (true) then
4+
new A(66)
5+
else
6+
m1()
7+
).m2(p1 = p); // error
8+
9+
}
10+
11+
def m1(): A[Short] = new A(10)
12+
13+
class A[D](var f: D) {
14+
15+
def m2(p1: D = f, p2: D = f): Unit = {}
16+
}

0 commit comments

Comments
 (0)