From 3bfdd878e7e375726fb463dfb83bcfb1ef5f6e9c Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 13 Sep 2024 20:41:57 +0200 Subject: [PATCH] Guard against recursive lower bounds in constraints We could get an indirect recursion going through a singleton type before. Fixes #21535 [Cherry-picked d0ea3b0546bfae0c10e0f97937ff3a17a2de5860] --- .../tools/dotc/core/ConstraintHandling.scala | 21 +++++++++++++++---- tests/neg/i21535.check | 11 ++++++++++ tests/neg/i21535.scala | 16 ++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 tests/neg/i21535.check create mode 100644 tests/neg/i21535.scala diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 0ceaaac75063..67655cea8688 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -287,11 +287,24 @@ trait ConstraintHandling { end legalBound protected def addOneBound(param: TypeParamRef, rawBound: Type, isUpper: Boolean)(using Context): Boolean = + + // Replace top-level occurrences of `param` in `bound` by `Nothing` + def sanitize(bound: Type): Type = + if bound.stripped eq param then defn.NothingType + else bound match + case bound: AndOrType => + bound.derivedAndOrType(sanitize(bound.tp1), sanitize(bound.tp2)) + case _ => + bound + if !constraint.contains(param) then true - else if !isUpper && param.occursIn(rawBound) then - // We don't allow recursive lower bounds when defining a type, - // so we shouldn't allow them as constraints either. - false + else if !isUpper && param.occursIn(rawBound.widen) then + val rawBound1 = sanitize(rawBound.widenDealias) + if param.occursIn(rawBound1) then + // We don't allow recursive lower bounds when defining a type, + // so we shouldn't allow them as constraints either. + false + else addOneBound(param, rawBound1, isUpper) else // Narrow one of the bounds of type parameter `param` diff --git a/tests/neg/i21535.check b/tests/neg/i21535.check new file mode 100644 index 000000000000..7a24f2196ec8 --- /dev/null +++ b/tests/neg/i21535.check @@ -0,0 +1,11 @@ +-- [E007] Type Mismatch Error: tests/neg/i21535.scala:7:4 -------------------------------------------------------------- +3 | (if (true) then +4 | new A(66) +5 | else +6 | m1() +7 | ).m2(p1 = p); // error + | ^ + | Found: (Int | Short) @uncheckedVariance + | Required: Int & Short + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i21535.scala b/tests/neg/i21535.scala new file mode 100644 index 000000000000..f9573f823160 --- /dev/null +++ b/tests/neg/i21535.scala @@ -0,0 +1,16 @@ +def test() = { + val p = 10.toShort + (if (true) then + new A(66) + else + m1() + ).m2(p1 = p); // error + +} + +def m1(): A[Short] = new A(10) + +class A[D](var f: D) { + + def m2(p1: D = f, p2: D = f): Unit = {} +} \ No newline at end of file