From 4c73265035166301308d86bf9fe2bdf8eb471966 Mon Sep 17 00:00:00 2001 From: noti0na1 Date: Wed, 22 Oct 2025 15:59:17 +0200 Subject: [PATCH 1/2] Revert nullable Option.apply --- .../dotc/transform/localopt/FormatChecker.scala | 2 +- library/src/scala/Option.scala | 2 +- library/src/scala/concurrent/impl/Promise.scala | 2 +- library/src/scala/quoted/FromExpr.scala | 2 +- .../src/dotty/tools/scaladoc/SourceLinks.scala | 4 ++-- .../tools/scaladoc/site/StaticSiteLoader.scala | 4 ++-- .../src/dotty/tools/scaladoc/tasty/SymOps.scala | 2 +- tests/pos/i24206.scala | 16 ++++++++++++++++ 8 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 tests/pos/i24206.scala diff --git a/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala b/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala index 83ec7fb8399e..1185ecd6ee3c 100644 --- a/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala @@ -126,7 +126,7 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List def at(g: SpecGroup): Int = descriptor.start(g.ordinal) def end(g: SpecGroup): Int = descriptor.end(g.ordinal) def offset(g: SpecGroup, i: Int = 0): Int = at(g) + i - def group(g: SpecGroup): Option[String] = Option(descriptor.group(g.ordinal)) + def group(g: SpecGroup): Option[String] = Option(descriptor.group(g.ordinal).asInstanceOf[String]) def stringOf(g: SpecGroup): String = group(g).getOrElse("") def intOf(g: SpecGroup): Option[Int] = group(g).map(_.toInt) diff --git a/library/src/scala/Option.scala b/library/src/scala/Option.scala index b2eadf2c1be3..894eade2445a 100644 --- a/library/src/scala/Option.scala +++ b/library/src/scala/Option.scala @@ -28,7 +28,7 @@ object Option { * @param x the value * @return Some(value) if value != null, None if value == null */ - def apply[A](x: A | Null): Option[A] = if (x == null) None else Some(x) + def apply[A](x: A): Option[A] = if (x == null) None else Some(x) /** An Option factory which returns `None` in a manner consistent with * the collections hierarchy. diff --git a/library/src/scala/concurrent/impl/Promise.scala b/library/src/scala/concurrent/impl/Promise.scala index 2ca2228d159f..8ecbc0f75c30 100644 --- a/library/src/scala/concurrent/impl/Promise.scala +++ b/library/src/scala/concurrent/impl/Promise.scala @@ -274,7 +274,7 @@ private[concurrent] object Promise { override final def isCompleted: Boolean = value0 ne null - override final def value: Option[Try[T]] = Option(value0) + override final def value: Option[Try[T]] = Option(value0.asInstanceOf[Try[T]]) @tailrec // returns null if not completed private final def value0: Try[T] | Null = { diff --git a/library/src/scala/quoted/FromExpr.scala b/library/src/scala/quoted/FromExpr.scala index fb230c6d22b9..2d9e3ebc5ab6 100644 --- a/library/src/scala/quoted/FromExpr.scala +++ b/library/src/scala/quoted/FromExpr.scala @@ -104,7 +104,7 @@ object FromExpr { */ given OptionFromExpr[T](using Type[T], FromExpr[T]): FromExpr[Option[T]] with { def unapply(x: Expr[Option[T]])(using Quotes) = x match { - case '{ Option[T](${Expr(y)}: T) } => Some(Option(y)) + case '{ Option[T](${Expr(y)}) } => Some(Option(y)) case '{ None } => Some(None) case '{ ${Expr(opt)} : Some[T] } => Some(opt) case _ => None diff --git a/scaladoc/src/dotty/tools/scaladoc/SourceLinks.scala b/scaladoc/src/dotty/tools/scaladoc/SourceLinks.scala index 00344aa5ad75..9a79c5a84fe1 100644 --- a/scaladoc/src/dotty/tools/scaladoc/SourceLinks.scala +++ b/scaladoc/src/dotty/tools/scaladoc/SourceLinks.scala @@ -74,8 +74,8 @@ class SourceLinkParser(revision: Option[String]) extends ArgParser[SourceLink]: else Right(TemplateSourceLink(supported.foldLeft(string)((template, pattern) => template.replace(pattern, SupportedScalaDocPatternReplacements(pattern))))) case KnownProvider(name: String, organization: String, repo: String, rawRevision, rawSubPath) => - val subPath = Option(rawSubPath).fold("")("/" + _.drop(1)) - val pathRev = Option(rawRevision).map(_.drop(1)).orElse(revision) + val subPath = Option.fromNullable(rawSubPath).fold("")("/" + _.drop(1)) + val pathRev = Option.fromNullable(rawRevision).map(_.drop(1)).orElse(revision) def withRevision(template: String => SourceLink) = pathRev.fold(Left(s"No revision provided"))(r => Right(template(r))) diff --git a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala index af0d1208c5cb..e0834ee78ecf 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala @@ -116,8 +116,8 @@ class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSite def loadBlog(): Option[LoadedTemplate] = { val blogConfig = BlogParser.readYml(root) - val rootPath = Option(blogConfig.input).map(input => ctx.resolveNewBlogPath(input)).getOrElse(ctx.blogPath) - val defaultDirectory = Option(blogConfig.output).getOrElse("blog") + val rootPath = Option.fromNullable(blogConfig.input).map(input => ctx.resolveNewBlogPath(input)).getOrElse(ctx.blogPath) + val defaultDirectory = Option.fromNullable(blogConfig.output).getOrElse("blog") type Date = (String, String, String) if (!Files.exists(rootPath) || blogConfig.hidden) None diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala index 0b38af38a3b3..a84137caa5a7 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala @@ -277,7 +277,7 @@ class SymOpsWithLinkCache: then externalLinkCache(csym.associatedFile) else { def calculatePath(file: AbstractFile): String = file.underlyingSource.filter(_ != file).fold("")(f => calculatePath(f) + "/") + file.path - val calculatedLink = Option(csym.associatedFile).map(f => calculatePath(f)).flatMap { path => + val calculatedLink = Option.fromNullable(csym.associatedFile).map(f => calculatePath(f)).flatMap { path => dctx.externalDocumentationLinks.find(_.originRegexes.exists(r => r.matches(path))) } externalLinkCache += (csym.associatedFile -> calculatedLink) diff --git a/tests/pos/i24206.scala b/tests/pos/i24206.scala new file mode 100644 index 000000000000..8f2fd7976597 --- /dev/null +++ b/tests/pos/i24206.scala @@ -0,0 +1,16 @@ +class DispatchQuery: + type CompareFunction = (AnyRef, AnyRef) => Boolean + private var _sortedWith: Option[CompareFunction] = Option.empty + + def sortedWith(f: PartialFunction[(AnyRef, AnyRef), Boolean]): DispatchQuery = { + // fails + _sortedWith = Option { case (a, b) => f.applyOrElse((a, b), _ => false) } + // workaround + _sortedWith = Option[CompareFunction] { case (a, b) => f.applyOrElse((a, b), _ => false) } + this + } + +trait Result +def getAll(nameFilter: Option[String => Boolean]): List[Result] = ??? +def get(collectionName: String): List[Result] = + getAll(Option(_.startsWith(collectionName))) \ No newline at end of file From 45d022d05c6cbb9ea2f973e2ed1e45d1b660ec2f Mon Sep 17 00:00:00 2001 From: noti0na1 Date: Wed, 22 Oct 2025 16:32:52 +0200 Subject: [PATCH 2/2] Moving casting outside the Option --- .../src/dotty/tools/dotc/transform/localopt/FormatChecker.scala | 2 +- library/src/scala/concurrent/impl/Promise.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala b/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala index 1185ecd6ee3c..142a9db3a3ce 100644 --- a/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala @@ -126,7 +126,7 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List def at(g: SpecGroup): Int = descriptor.start(g.ordinal) def end(g: SpecGroup): Int = descriptor.end(g.ordinal) def offset(g: SpecGroup, i: Int = 0): Int = at(g) + i - def group(g: SpecGroup): Option[String] = Option(descriptor.group(g.ordinal).asInstanceOf[String]) + def group(g: SpecGroup): Option[String] = Option(descriptor.group(g.ordinal)).asInstanceOf[Option[String]] def stringOf(g: SpecGroup): String = group(g).getOrElse("") def intOf(g: SpecGroup): Option[Int] = group(g).map(_.toInt) diff --git a/library/src/scala/concurrent/impl/Promise.scala b/library/src/scala/concurrent/impl/Promise.scala index 8ecbc0f75c30..d9bf2879e155 100644 --- a/library/src/scala/concurrent/impl/Promise.scala +++ b/library/src/scala/concurrent/impl/Promise.scala @@ -274,7 +274,7 @@ private[concurrent] object Promise { override final def isCompleted: Boolean = value0 ne null - override final def value: Option[Try[T]] = Option(value0.asInstanceOf[Try[T]]) + override final def value: Option[Try[T]] = Option(value0).asInstanceOf[Option[Try[T]]] @tailrec // returns null if not completed private final def value0: Try[T] | Null = {