Skip to content

Commit 34f2030

Browse files
[library] Make Option.orNull public in binary (#26751)
Fixes #26626 by making legacy `Option.orNull` public in binary - this allows to consume it from macros, but continues to disallow direct usage in users code ## How was the solution tested? Local tests + dedicated unit test
1 parent 750cfa2 commit 34f2030

3 files changed

Lines changed: 39 additions & 3 deletions

File tree

library/src/scala/Option.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package scala
1414

1515
import scala.language.`2.13`
16+
import scala.annotation.publicInBinary
1617

1718
object Option {
1819

@@ -253,9 +254,10 @@ sealed abstract class Option[+A] extends IterableOnce[A] with Product with Seria
253254
* @return the option's value if nonempty, or `null` if empty
254255
*/
255256
@inline final def orNull[A1 >: A | Null]: A1 = this.getOrElse(null)
256-
257-
// for binary and TASTy backwards compatibility
258-
@deprecated @inline protected final def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse ev(null)
257+
258+
// for binary and TASTy backwards compatibility
259+
@publicInBinary
260+
@deprecated @inline private[Option] final def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse ev(null)
259261

260262
/** Returns a $some containing the result of applying $f to this $option's
261263
* value if this $option is nonempty.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Compiles with published Scala 3.8.4 (via Vulpix `_c` suffix / Coursier).
2+
// Repro for https://github.com/scala/scala3/issues/26626:
3+
// macros that expand to Option.orNull must remain usable on newer compilers.
4+
5+
package sangria
6+
7+
import scala.quoted.*
8+
import scala.compiletime.testing.typeChecks
9+
10+
object Macros:
11+
final val LegacyOrNullSnippet = "Option.empty[String].orNull[String | Null](using summon[Null <:< (String | Null)])"
12+
13+
// To confirms it's Scala 3.8
14+
assert(typeChecks(LegacyOrNullSnippet))
15+
16+
inline def useOrNull[T](inline o: Option[T]): T =
17+
${ useOrNullImpl('o) }
18+
19+
def useOrNullImpl[T: Type](o: Expr[Option[T]])(using Quotes): Expr[T] =
20+
'{ $o.orNull.asInstanceOf[T] }
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//> using options -Yexplicit-nulls
2+
import scala.compiletime.testing.typeChecks
3+
4+
object AstVisitor:
5+
def visit(o: Option[String]): String = sangria.Macros.useOrNull(o)
6+
7+
@main def Test =
8+
assert(AstVisitor.visit(Some("ok")) == "ok")
9+
assert(AstVisitor.visit(None) == null)
10+
11+
// protected/private[scala] def orNull[A1 >: A](implicit ev: Null <:< A1): A1
12+
// typechecks under 3.8, tested in Macro
13+
assert(!typeChecks(sangria.Macros.LegacyOrNullSnippet))
14+
val _: String | Null = Option.empty[String].orNull

0 commit comments

Comments
 (0)