Skip to content

Commit 5975a06

Browse files
Fix derivesFrom false negative in provablyDisjointClasses
Before the addition of `SymDenotation#mayDeriveFrom`, tests/neg/i17132.min.scala was unsoundly accepted without a type error because `R[T]` is reduced to `Any` where `T <: P[Any]` by testing `provablyDisjointClasses` before `P` is added as a baseClass of `Q`. Now, a recursion overflows occurs because: - reducing `R[T] := T match case Q[t] => R[t]; case ...` requires - proving `T` disjoint from `Q[t]` where `T <: P[Any]`, which asks - whether existsCommonBaseTypeWithDisjointArguments, which requires - normalizing the type arg to the base class P for the class Q, i.e. R[t] - ... In short, despite the use of the pending set in provablyDisjoint, diverging is still possible when there is "cycle" in the type arguments in the base classes, (and where the pending set is thus reset for a separate normalization problem). One could attempt to add some more logic to detect these loops s.t. they are considered "stuck", as opposed to reporting an error. But it isn't clear that there are any concrete motivations for this.
1 parent ee2f641 commit 5975a06

File tree

4 files changed

+52
-1
lines changed

4 files changed

+52
-1
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,9 +865,20 @@ object SymDenotations {
865865
* and is the denoting symbol also different from `Null` or `Nothing`?
866866
* @note erroneous classes are assumed to derive from all other classes
867867
* and all classes derive from them.
868+
* @note may return a false negative when `this.info.isInstanceOf[TempClassInfo]`.
868869
*/
869870
def derivesFrom(base: Symbol)(using Context): Boolean = false
870871

872+
/** Could `this` derive from `base` now or in the future.
873+
* For concistency with derivesFrom, The info is only forced when this is a ClassDenotation.
874+
* If the info is a TempClassInfo then the baseClassSet may be temporarily approximated as empty.
875+
* This is problematic when stability of `!derivesFrom(base)` is assumed for soundness,
876+
* e.g., in `TypeComparer#provablyDisjointClasses`.
877+
* @note may return a false positive when `this.info.isInstanceOf[TempClassInfo]`.
878+
*/
879+
final def mayDeriveFrom(base: Symbol)(using Context): Boolean =
880+
this.isInstanceOf[ClassDenotation] && (info.isInstanceOf[TempClassInfo] || derivesFrom(base))
881+
871882
/** Is this a Scala or Java annotation ? */
872883
def isAnnotation(using Context): Boolean =
873884
isClass && (derivesFrom(defn.AnnotationClass) || is(JavaAnnotation))

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3230,7 +3230,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
32303230
.filter(child => child.exists && child != cls)
32313231

32323232
def eitherDerivesFromOther(cls1: Symbol, cls2: Symbol): Boolean =
3233-
cls1.derivesFrom(cls2) || cls2.derivesFrom(cls1)
3233+
cls1.mayDeriveFrom(cls2) || cls2.mayDeriveFrom(cls1)
32343234

32353235
def smallestNonTraitBase(cls: Symbol): Symbol =
32363236
val classes = if cls.isClass then cls.asClass.baseClasses else cls.info.classSymbols

tests/neg/i17132.min.check

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
-- Error: tests/neg/i17132.min.scala:4:7 -------------------------------------------------------------------------------
2+
4 |class Q[T <: P[Any]] extends P[R[T]] // error
3+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
| Recursion limit exceeded.
5+
| Maybe there is an illegal cyclic reference?
6+
| If that's not the case, you could also try to increase the stacksize using the -Xss JVM option.
7+
| For the unprocessed stack trace, compile with -Xno-enrich-error-messages.
8+
| A recurring operation is (inner to outer):
9+
|
10+
| reduce type t match ...
11+
| reduce type t match ...
12+
| reduce type t match ...
13+
| reduce type t match ...
14+
| reduce type t match ...
15+
| reduce type t match ...
16+
| reduce type t match ...
17+
| reduce type t match ...
18+
| reduce type t match ...
19+
| reduce type t match ...
20+
| ...
21+
|
22+
| reduce type t match ...
23+
| reduce type t match ...
24+
| reduce type t match ...
25+
| reduce type t match ...
26+
| reduce type t match ...
27+
| reduce type t match ...
28+
| reduce type t match ...
29+
| reduce type t match ...
30+
| reduce type t match ...
31+
| reduce type T match ...

tests/neg/i17132.min.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
class P[T]
3+
//class Q[T] extends P[R[T]] // ok
4+
class Q[T <: P[Any]] extends P[R[T]] // error
5+
//type Q[T <: P[Any]] <: P[R[T]] // ok
6+
7+
type R[U] = U match
8+
case Q[t] => R[t]
9+
case P[t] => t

0 commit comments

Comments
 (0)