diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 967b35f4f87b..5ba57b2f1ce3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -79,8 +79,10 @@ object Inferencing { * constrained upper bound != given upper bound and * constrained lower bound == given lower bound). * If (1) and (2) do not apply: - * 3. T is maximized if it appears only contravariantly in the given type. - * 4. T is minimized in all other cases. + * 3. T is minimized if forceDegree is minimizeAll. + * 4. Otherwise, T is maximized if it appears only contravariantly in the given type, + * or if forceDegree is `noBottom` and T's minimized value is a bottom type. + * 5. Otherwise, T is minimized. * * The instantiation is done in two phases: * 1st Phase: Try to instantiate minimizable type variables to @@ -89,12 +91,15 @@ object Inferencing { * to their upper bound. */ private class IsFullyDefinedAccumulator(force: ForceDegree.Value)(implicit ctx: Context) extends TypeAccumulator[Boolean] { + private def instantiate(tvar: TypeVar, fromBelow: Boolean): Type = { val inst = tvar.instantiate(fromBelow) typr.println(i"forced instantiation of ${tvar.origin} = $inst") inst } + private[this] var toMaximize: Boolean = false + def apply(x: Boolean, tp: Type): Boolean = tp.dealias match { case _: WildcardType | _: ProtoType => false @@ -102,19 +107,13 @@ object Inferencing { if !tvar.isInstantiated && ctx.typerState.constraint.contains(tvar) => force.appliesTo(tvar) && { val direction = instDirection(tvar.origin) - if (direction != 0) { - //if (direction > 0) println(s"inst $tvar dir = up") - instantiate(tvar, direction < 0) - } - else { - val minimize = - force.minimizeAll || - variance >= 0 && !( - !force.allowBottom && - defn.isBottomType(ctx.typeComparer.approximation(tvar.origin, fromBelow = true))) - if (minimize) instantiate(tvar, fromBelow = true) - else toMaximize = true - } + def avoidBottom = + !force.allowBottom && + defn.isBottomType(ctx.typeComparer.approximation(tvar.origin, fromBelow = true)) + def preferMin = force.minimizeAll || variance >= 0 && !avoidBottom + if (direction != 0) instantiate(tvar, direction < 0) + else if (preferMin) instantiate(tvar, fromBelow = true) + else toMaximize = true foldOver(x, tvar) } case tp => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 3142d1fa3418..94e0aa3aeeff 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2535,32 +2535,6 @@ class Typer extends Namer } } - /** If `tp` is a TypeVar which is fully constrained (i.e. its upper bound `hi` conforms - * to its lower bound `lo`), replace `tp` by `hi`. This is necessary to - * keep the right constraints for some implicit search problems. The paradigmatic case - * is `implicitNums.scala`. Without the healing done in `followAlias`, we cannot infer - * implicitly[_3], where _2 is the typelevel number 3. The problem here is that if a - * prototype is, say, Succ[Succ[Zero]], we can infer that it's argument type is Succ[Zero]. - * But if the prototype is N? >: Succ[Succ[Zero]] <: Succ[Succ[Zero]], the same - * decomposition does not work - we'd get a N?#M where M is the element type name of Succ - * instead. - */ - def followAlias(tp: Type)(implicit ctx: Context): Type = { - val constraint = ctx.typerState.constraint - def inst(tp: Type): Type = tp match { - case TypeBounds(lo, hi) - if (lo eq hi) || ctx.test(implicit ctx => hi <:< lo) => - inst(lo) - case tp: TypeParamRef => - constraint.typeVarOfParam(tp).orElse(tp) - case _ => tp - } - tp match { - case tp: TypeVar if constraint.contains(tp) => inst(constraint.entry(tp.origin)) - case _ => tp - } - } - /** Reveal ignored parts of prototype when synthesizing the receiver * of an extension method. This is necessary for pos/i5773a.scala */ @@ -2809,7 +2783,7 @@ class Typer extends Namer } def resultMatches(wtp: Type, pt: Type) = - constrainResult(tree.symbol, wtp, revealProtoOfExtMethod(followAlias(pt))) + constrainResult(tree.symbol, wtp, revealProtoOfExtMethod(pt)) def adaptNoArgs(wtp: Type): Tree = { val ptNorm = underlyingApplied(pt) diff --git a/tests/pos/i6127.scala b/tests/pos/i6127.scala new file mode 100644 index 000000000000..958bdf6764df --- /dev/null +++ b/tests/pos/i6127.scala @@ -0,0 +1,18 @@ +import reflect.ClassTag +class Co[+S] +object Co { + def empty[X: ClassTag]: Co[X] = ??? +} +class Contra[-S] +object Contra { + def empty[X: ClassTag]: Contra[X] = ??? +} +class Foo[+FT](x: FT) { + def fooArray: Foo[Array[String]] = new Foo(Array.empty) + val y1: Array[String] = Array.empty + def fooCo: Foo[Co[String]] = new Foo(Co.empty) + val y2: Co[String] = Co.empty + def fooContra: Foo[Contra[String]] = new Foo(Contra.empty) + val y3: Contra[String] = Contra.empty +} +