diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 92be3130c99d..c41fb2e60ae5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -180,7 +180,15 @@ object Inferencing { t match case t: TypeRef => if t.symbol == defn.NothingClass then - newTypeVar(TypeBounds.empty, nestingLevel = tvar.nestingLevel) + val notExactlyNothing = LazyRef(_ => defn.NothingType) + val bounds = TypeBounds(notExactlyNothing, defn.AnyType) + // The new type variale has a slightly disguised lower bound Nothing. + // This foils the `isExactlyNothing` test in `hasLowerBound` and + // therefore makes the new type variable have a lower bound. That way, + // we favor in `apply` below instantiating from below to `Nothing` instead + // of from above to `Any`. That avoids a spurious flip of the original `Nothing` + // instance to `Any`. See i21275 for a test case. + newTypeVar(bounds, nestingLevel = tvar.nestingLevel) else if t.symbol.is(ModuleClass) then tryWidened(t.parents.filter(!_.isTransparent()) .foldLeft(defn.AnyType: Type)(TypeComparer.andType(_, _))) diff --git a/tests/pos/i21275.scala b/tests/pos/i21275.scala new file mode 100644 index 000000000000..6d586aa891b6 --- /dev/null +++ b/tests/pos/i21275.scala @@ -0,0 +1,10 @@ +class Box[+O]: + def ++[O2 >: O](other: Box[O2]): Box[O2] = ??? +object Box: + val empty: Box[Nothing] = ??? + +def test[T]: Box[T] = + List(Box.empty, Box.empty) + // .reduceOption[Box[T]](_ ++ _) // works + .reduceOption(_ ++ _) // fails + .getOrElse(Box.empty) \ No newline at end of file