From 2144462b39a6d92d7c3653e9bb242d116a60caba Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 10 Jan 2017 18:50:48 +0700 Subject: [PATCH 1/2] Fix #1891: Don't add redundant constraint Before adding a constraint, make sure there is no way the two types are already in a subtype relation. Adding redundant constraints is problematic because we might introduce cycles. See i1891.scala for a test. --- .../dotty/tools/dotc/core/ConstraintHandling.scala | 1 + .../src/dotty/tools/dotc/core/TypeComparer.scala | 12 ++++++++++-- tests/pos/i1891.scala | 11 +++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i1891.scala diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 0e155b9e1f2a..42df53fed980 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -57,6 +57,7 @@ trait ConstraintHandling { b match { case b: AndOrType => occursIn(b.tp1) || occursIn(b.tp2) case b: TypeVar => occursIn(b.origin) + case b: TermRef => occursIn(b.underlying) case _ => false } } diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 8930983f3724..baebd421471e 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -375,11 +375,19 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { thirdTryNamed(tp1, tp2) case tp2: PolyParam => def comparePolyParam = - (ctx.mode is Mode.TypevarsMissContext) || - isSubTypeWhenFrozen(tp1, bounds(tp2).lo) || { + (ctx.mode is Mode.TypevarsMissContext) || { + val alwaysTrue = + // The following condition is carefully formulated to catch all cases + // where the subtype relation is true without needing to add a constraint + // It's tricky because we might need to either appriximate tp2 by its + // lower bound or else widen tp1 and check that the result is a subtype of tp2. + if (frozenConstraint || alwaysFluid) isSubType(tp1, bounds(tp2).lo) + else isSubTypeWhenFrozen(tp1, tp2) + alwaysTrue || { if (canConstrain(tp2)) addConstraint(tp2, tp1.widenExpr, fromBelow = true) else fourthTry(tp1, tp2) } + } comparePolyParam case tp2: RefinedType => def compareRefinedSlow: Boolean = { diff --git a/tests/pos/i1891.scala b/tests/pos/i1891.scala new file mode 100644 index 000000000000..b178c256b3c4 --- /dev/null +++ b/tests/pos/i1891.scala @@ -0,0 +1,11 @@ +object Test { + class CC2[A, B](a: A, b: B) + + type T2[A, B] = CC2[A, B] + + class ArrowAssoc[A](val self: A) { + @inline def f[B](y: B): CC2[A, B] = new CC2(self, y) + } + + def foo = (new ArrowAssoc(1)).f(2) +} From 918a3fba3ceeebf0bc3e0087f9ead63f4ae30381 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 11 Jan 2017 08:49:52 +0700 Subject: [PATCH 2/2] Add more explanation. --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index baebd421471e..6063cbf38de3 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -381,6 +381,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { // where the subtype relation is true without needing to add a constraint // It's tricky because we might need to either appriximate tp2 by its // lower bound or else widen tp1 and check that the result is a subtype of tp2. + // So if the constraint is not yet frozen, we do the same comparison again + // with a frozen constraint, which means that we get a chance to do the + // widening in `fourthTry` before adding to the constraint. if (frozenConstraint || alwaysFluid) isSubType(tp1, bounds(tp2).lo) else isSubTypeWhenFrozen(tp1, tp2) alwaysTrue || {