From 9cb35429fc2c52f5d243b9c9d29739df1ff5967a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 22 Oct 2015 13:19:14 +0200 Subject: [PATCH 1/2] Fix #830: Compiler hangs on implicit search with singleton &/| In fact we get a deep subtype recursion when compileing i830.scala. The problem goes away once we make use of the fact that the intersection of two singleton types which are not subtypes of each other is empty. --- src/dotty/tools/dotc/core/TypeComparer.scala | 16 +++++++++++++--- tests/pos/i830.scala | 6 ++++++ 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i830.scala diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 5fbffe6e95ab..f7fd145adabb 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -123,7 +123,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { pendingSubTypes = new mutable.HashSet[(Type, Type)] ctx.log(s"!!! deep subtype recursion involving ${tp1.show} <:< ${tp2.show}, constraint = ${state.constraint.show}") ctx.log(s"!!! constraint = ${constraint.show}") - assert(!ctx.settings.YnoDeepSubtypes.value) + if (ctx.settings.YnoDeepSubtypes.value) throw new Error("deep subtype") if (Config.traceDeepSubTypeRecursions && !this.isInstanceOf[ExplainingTypeComparer]) ctx.log(TypeComparer.explained(implicit ctx => ctx.typeComparer.isSubType(tp1, tp2))) } @@ -779,8 +779,18 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { else { val t2 = mergeIfSub(tp2, tp1) if (t2.exists) t2 - else andType(tp1, tp2) - } + else tp1 match { + case tp1: SingletonType => + tp2 match { + case tp2: SingletonType => + // Make use of the fact that the intersection of two singleton + // types which are not subtypes of each other is empty. + defn.NothingType + case _ => andType(tp1, tp2) + } + case _ => andType(tp1, tp2) + } + } } } } diff --git a/tests/pos/i830.scala b/tests/pos/i830.scala new file mode 100644 index 000000000000..8fcb29f366fd --- /dev/null +++ b/tests/pos/i830.scala @@ -0,0 +1,6 @@ +object C { + trait X[T] + implicit def u[A, B]: X[A | B] = new X[A | B] {} + def y[T](implicit x: X[T]): T = ??? + val x: 1 & 2 | 2 & 3 = y +} From 0a386a85da9c6df97bf2cb6627e1f905af17c123 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 23 Oct 2015 10:58:06 +0200 Subject: [PATCH 2/2] Only replace intersections of constants with Nothing --- src/dotty/tools/dotc/core/TypeComparer.scala | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index f7fd145adabb..3a5922e9090f 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -780,11 +780,17 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { val t2 = mergeIfSub(tp2, tp1) if (t2.exists) t2 else tp1 match { - case tp1: SingletonType => + case tp1: ConstantType => tp2 match { - case tp2: SingletonType => - // Make use of the fact that the intersection of two singleton - // types which are not subtypes of each other is empty. + case tp2: ConstantType => + // Make use of the fact that the intersection of two constant types + // types which are not subtypes of each other is known to be empty. + // Note: The same does not apply to singleton types in general. + // E.g. we could have a pattern match against `x.type & y.type` + // which might succeed if `x` and `y` happen to be the same ref + // at run time. It would not work to replace that with `Nothing`. + // However, maybe we can still apply the replacement to + // types which are not explicitly written. defn.NothingType case _ => andType(tp1, tp2) }