From e2fdcb2da79b2a0782c6f7442e344b00ba7886bc Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 12 Jan 2021 09:44:35 +0100 Subject: [PATCH 1/2] Special case comparisons between LazyRefs and bottom/top types Fixes #11064 When comparing a LazyRef with some other type, we need to force the LazyRef which can potentially lead to a cycle (as observed in #11064). However, in the situations ``` Nothing <: LazyRef(...) LazyRef(...) <: Any ``` we can skip that since the comparison is always true. This scheme fixes #11064, but it breaks down in similar situations if additional bounds are introduced (see neg/i11064.scala). So it is quite fragile. But it's probably the best we can do. The example in #11064 looks harmless, but type-theoretically it's actually a pretty explosive mix of F-bounded types and existential types (in Scala 2). In Scala 2, we need to force more for F-bounded types and we need to approximate existential types by dependent types, so not everything works the same way. My long term advice would be: get rid of F-bounds. Model them with intersections at the use site. --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 7 ++++--- tests/neg/i11064.scala | 9 +++++++++ tests/pos/i11064.scala | 9 +++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 tests/neg/i11064.scala create mode 100644 tests/pos/i11064.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 7bb942e9ab6a..c1d0d2faaeb2 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -306,7 +306,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling } compareWild case tp2: LazyRef => - !tp2.evaluating && recur(tp1, tp2.ref) + isBottom(tp1) || !tp2.evaluating && recur(tp1, tp2.ref) case tp2: AnnotatedType if !tp2.isRefining => recur(tp1, tp2.parent) case tp2: ThisType => @@ -373,7 +373,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling if (recur(info1.alias, tp2)) return true if (tp1.prefix.isStable) return tryLiftedToThis1 case _ => - if (tp1 eq NothingType) return true + if (tp1 eq NothingType) || isBottom(tp1) then return true } thirdTry case tp1: TypeParamRef => @@ -420,7 +420,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling // If `tp1` is in train of being evaluated, don't force it // because that would cause an assertionError. Return false instead. // See i859.scala for an example where we hit this case. - !tp1.evaluating && recur(tp1.ref, tp2) + tp2.isRef(AnyClass, skipRefined = false) + || !tp1.evaluating && recur(tp1.ref, tp2) case tp1: AnnotatedType if !tp1.isRefining => recur(tp1.parent, tp2) case AndType(tp11, tp12) => diff --git a/tests/neg/i11064.scala b/tests/neg/i11064.scala new file mode 100644 index 000000000000..7cfab64d0e04 --- /dev/null +++ b/tests/neg/i11064.scala @@ -0,0 +1,9 @@ +trait TypedArray[T, Repr] + +trait Ops[T <: TypedArray[_, T]] { + def typedArray(): T +} + +object Test { + def test(ops: Ops[_ <: TypedArray[_ <: AnyRef, _]]) = ops.typedArray() // error: Recursion limit exceeded. +} \ No newline at end of file diff --git a/tests/pos/i11064.scala b/tests/pos/i11064.scala new file mode 100644 index 000000000000..286b3d8dcc07 --- /dev/null +++ b/tests/pos/i11064.scala @@ -0,0 +1,9 @@ +trait TypedArray[T, Repr] + +trait Ops[T <: TypedArray[_, T]] { + def typedArray(): T +} + +object Test { + def test(ops: Ops[_ <: TypedArray[_, _]]) = ops.typedArray() +} \ No newline at end of file From 4e009c6d2010639522bcb232af15f1aced93a0c9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 12 Jan 2021 11:55:56 +0100 Subject: [PATCH 2/2] Fix tests --- .../{neg => neg-custom-args/allow-deep-subtypes}/i11064.scala | 0 tests/neg/i6225.scala | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename tests/{neg => neg-custom-args/allow-deep-subtypes}/i11064.scala (100%) diff --git a/tests/neg/i11064.scala b/tests/neg-custom-args/allow-deep-subtypes/i11064.scala similarity index 100% rename from tests/neg/i11064.scala rename to tests/neg-custom-args/allow-deep-subtypes/i11064.scala diff --git a/tests/neg/i6225.scala b/tests/neg/i6225.scala index 6d2f7943a394..148a484fd0f1 100644 --- a/tests/neg/i6225.scala +++ b/tests/neg/i6225.scala @@ -1,11 +1,11 @@ -object O1 { // error: cannot be instantiated +object O1 { type A[X] = X opaque type T = A // error: opaque type alias must be fully applied } object O2 { opaque type A[X] = X - object A { // error: cannot be instantiated + object A { opaque type T = A // error: opaque type alias must be fully applied } }