diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index a8004c8230bb..86f90b2c505e 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -738,7 +738,7 @@ class Definitions { @tu lazy val TypeBox_CAP: TypeSymbol = TypeBoxClass.requiredType(tpnme.CAP) @tu lazy val MatchCaseClass: ClassSymbol = requiredClass("scala.internal.MatchCase") - @tu lazy val NotClass: ClassSymbol = requiredClass("scala.implicits.Not") + @tu lazy val NotClass: ClassSymbol = requiredClass("scala.util.Not") @tu lazy val Not_value: Symbol = NotClass.companionModule.requiredMethod(nme.value) @tu lazy val ValueOfClass: ClassSymbol = requiredClass("scala.ValueOf") diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 85bbafdc3426..267ed18ffbc4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1234,7 +1234,7 @@ trait Implicits: |According to the new implicit resolution rules this is no longer possible; |the search will fail with a global ambiguity error instead. | - |Consider using the scala.implicits.Not class to implement similar functionality.""", + |Consider using the scala.util.Not class to implement similar functionality.""", ctx.source.atSpan(span)) /** A relation that influences the order in which implicits are tried. diff --git a/compiler/test-resources/repl/importFromObj b/compiler/test-resources/repl/importFromObj index 4192e6d8d4c5..144859891519 100644 --- a/compiler/test-resources/repl/importFromObj +++ b/compiler/test-resources/repl/importFromObj @@ -11,11 +11,11 @@ scala> buf += xs | Required: Int scala> buf ++= xs val res0: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3) -scala> import util.foo -1 | import util.foo - | ^^^ - | value foo is not a member of util -scala> import util.foo.bar -1 | import util.foo.bar - | ^^^^^^^^ - | value foo is not a member of util +scala> import util.foobar +1 | import util.foobar + | ^^^^^^ + | value foobar is not a member of util +scala> import util.foobar.bar +1 | import util.foobar.bar + | ^^^^^^^^^^^ + | value foobar is not a member of util diff --git a/docs/docs/reference/changed-features/implicit-resolution.md b/docs/docs/reference/changed-features/implicit-resolution.md index 6732e7e706d5..174baa089f83 100644 --- a/docs/docs/reference/changed-features/implicit-resolution.md +++ b/docs/docs/reference/changed-features/implicit-resolution.md @@ -107,7 +107,7 @@ which means that the alternative `c` would be chosen as solution! Scala 2's somewhat puzzling behavior with respect to ambiguity has been exploited to implement the analogue of a "negated" search in implicit resolution, where a query `Q1` fails if some other query `Q2` succeeds and `Q1` succeeds if `Q2` fails. With the new cleaned up behavior -these techniques no longer work. But there is now a new special type `scala.implicits.Not` +these techniques no longer work. But there is now a new special type `scala.util.Not` which implements negation directly. For any query type `Q`: `Not[Q]` succeeds if and only if the implicit search for `Q` fails. diff --git a/library/src-bootstrapped/scala/implicits/package.scala b/library/src-bootstrapped/scala/implicits/package.scala new file mode 100644 index 000000000000..089096d6ff59 --- /dev/null +++ b/library/src-bootstrapped/scala/implicits/package.scala @@ -0,0 +1,6 @@ +package scala + +package object implicits { + @deprecated("scala.implicits.Not has been renamed scala.util.Not", "0.27.0") + type Not[A] = scala.util.Not[A] +} diff --git a/library/src-bootstrapped/scala/util/Not.scala b/library/src-bootstrapped/scala/util/Not.scala new file mode 100644 index 000000000000..398b620747b8 --- /dev/null +++ b/library/src-bootstrapped/scala/util/Not.scala @@ -0,0 +1,45 @@ +package scala.util + +/** A special class used to implement negation in implicit search. + * + * Consider the problem of using implicit `i1` for a query type `D` if an implicit + * for some other class `C` is available, and using an implicit `i2` if no implicit + * value of type `C` is available. If we do not want to prioritize `i1` and `i2` by + * putting them in different traits we can instead define the following: + * + * given i1: D(using ev: C) = ... + * given i2: D(using ev: Not[C]) = ... + * + * `Not` is treated specially in implicit search, similar to the way logical negation + * is treated in Prolog: The implicit search for `Not[C]` succeeds if and only if the implicit + * search for `C` fails. + * + * In Scala 2 this form of negation can be simulated by setting up a conditional + * ambiguous implicit and an unconditional fallback, the way it is done with the + * `default`, `amb1` and `amb2` methods below. Due to the way these two methods are + * defined, `Not` is also usable from Scala 2. + * + * In Dotty, ambiguity is a global error, and therefore cannot be used to implement negation. + * Instead, `Not` is treated natively in implicit search. + */ +final class Not[+T] private () + +trait LowPriorityNot { + + /** A fallback method used to emulate negation in Scala 2 */ + given default[T] as Not[T] = Not.value +} +object Not extends LowPriorityNot { + + /** A value of type `Not` to signal a successful search for `Not[C]` (i.e. a failing + * search for `C`). A reference to this value will be explicitly constructed by Dotty's + * implicit search algorithm + */ + def value: Not[Nothing] = new Not[Nothing]() + + /** One of two ambiguous methods used to emulate negation in Scala 2 */ + given amb1[T](using ev: T) as Not[T] = ??? + + /** One of two ambiguous methods used to emulate negation in Scala 2 */ + given amb2[T](using ev: T) as Not[T] = ??? +} diff --git a/library/src/scala/implicits/Not.scala b/library/src-non-bootstrapped/scala/implicits/Not.scala similarity index 100% rename from library/src/scala/implicits/Not.scala rename to library/src-non-bootstrapped/scala/implicits/Not.scala