From 19605eea4b926ab46f05c242b695ec4dc06b0158 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 1 Aug 2024 13:36:40 +0200 Subject: [PATCH 1/2] A tweak to type improvement When we replace Nothing by a fresh type variable, we should not accidentally instantiate that type variable to Any in case it is still undetermined. We achieve this by giving the type variable a slightly disguised version of Nothing which makes the compiler believe it has a lower bound. Fixes #21275 [Cherry-picked 053c8f9cd2c382481d147d90e37c6c8c327e2882] --- compiler/src/dotty/tools/dotc/typer/Inferencing.scala | 10 +++++++++- tests/pos/i21725.scala | 10 ++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i21725.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 92be3130c99d..09284d0a2874 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 roginal `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/i21725.scala b/tests/pos/i21725.scala new file mode 100644 index 000000000000..6d586aa891b6 --- /dev/null +++ b/tests/pos/i21725.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 From 6f9046c1639a8aad554a6b77464c8958ff935638 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 1 Aug 2024 18:15:13 +0200 Subject: [PATCH 2/2] Rename test to correct issue number [Cherry-picked 020587177672c4d0bdef9308bae59ad78130641a] --- compiler/src/dotty/tools/dotc/typer/Inferencing.scala | 2 +- tests/pos/{i21725.scala => i21275.scala} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/pos/{i21725.scala => i21275.scala} (100%) diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 09284d0a2874..c41fb2e60ae5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -186,7 +186,7 @@ object Inferencing { // 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 roginal `Nothing` + // 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 diff --git a/tests/pos/i21725.scala b/tests/pos/i21275.scala similarity index 100% rename from tests/pos/i21725.scala rename to tests/pos/i21275.scala