From b4805bf9dacbd61271675c7bbbd2ccb826e84d91 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 14 Jan 2018 20:19:51 +0100 Subject: [PATCH] Fix #2982: Avoid instantiating to Nothing in some situations If variance == 0, we have to choice whether to instantiate a type variable to its lower or upper bound. We used to always pick the lower bound, but now we pick the upper bound if the lower bound is Nothing. --- compiler/src/dotty/tools/dotc/core/Types.scala | 4 ++++ compiler/src/dotty/tools/dotc/typer/Inferencing.scala | 2 +- compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala | 3 ++- tests/neg-tailcall/tailrec.scala | 2 +- tests/pos/i2982.scala | 10 ++++------ 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index cfc773e796f0..256cefb403c4 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3327,6 +3327,10 @@ object Types { def instantiate(fromBelow: Boolean)(implicit ctx: Context): Type = instantiateWith(ctx.typeComparer.instanceType(origin, fromBelow)) + /** For uninstantiated type variables: Is the lower bound different from Nothing? */ + def hasLowerBound(implicit ctx: Context) = + !ctx.typerState.constraint.entry(origin).loBound.isBottomType + /** Unwrap to instance (if instantiated) or origin (if not), until result * is no longer a TypeVar */ diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 77555154a256..29fed41a91bd 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -389,7 +389,7 @@ trait Inferencing { this: Typer => if (!(vs contains tvar) && qualifies(tvar)) { typr.println(s"instantiating non-occurring ${tvar.show} in ${tp.show} / $tp") ensureConstrained() - tvar.instantiate(fromBelow = true) + tvar.instantiate(fromBelow = tvar.hasLowerBound) } } if (constraint.uninstVars exists qualifies) interpolate() diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index f1a4ef09099a..36241ff23e12 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -113,7 +113,8 @@ trait TypeAssigner { case tp: SkolemType if partsToAvoid(mutable.Set.empty, tp.info).nonEmpty => range(tp.info.bottomType, apply(tp.info)) case tp: TypeVar if ctx.typerState.constraint.contains(tp) => - val lo = ctx.typeComparer.instanceType(tp.origin, fromBelow = variance >= 0) + val lo = ctx.typeComparer.instanceType( + tp.origin, fromBelow = variance > 0 || variance == 0 && tp.hasLowerBound) val lo1 = apply(lo) if (lo1 ne lo) lo1 else tp case _ => diff --git a/tests/neg-tailcall/tailrec.scala b/tests/neg-tailcall/tailrec.scala index 49d71ce13eb3..f2047cdaf1be 100644 --- a/tests/neg-tailcall/tailrec.scala +++ b/tests/neg-tailcall/tailrec.scala @@ -56,7 +56,7 @@ class Failures { } // unsafe - @tailrec final def fail3[T](x: Int): Int = fail3(x - 1) + @tailrec final def fail3[T](x: Int): Int = fail3(x - 1) // error // error: recursive application has different type arguments // unsafe class Tom[T](x: Int) { diff --git a/tests/pos/i2982.scala b/tests/pos/i2982.scala index e4a9bbfad430..04279add36a4 100644 --- a/tests/pos/i2982.scala +++ b/tests/pos/i2982.scala @@ -1,8 +1,6 @@ + object A { - def fun[E >: B](a: A[E]): E => Unit = ??? - val x = fun(new A[C]) + def fun[E](a: A[E]): Unit = () + fun(new A[Int]) } -class B extends C -class C - -class A[-X >: B] +class A[-X] \ No newline at end of file