Skip to content

Conversation

hamzaremmal
Copy link
Member

@hamzaremmal hamzaremmal commented Jul 18, 2025

No description provided.

@hamzaremmal
Copy link
Member Author

@noti0na1 As agreed to, please push the changes to explicitly null check the stdlib here.

@noti0na1 noti0na1 force-pushed the explicit-nulls-stdlib branch from 2fe2627 to 2d178a7 Compare August 26, 2025 14:17
@@ -313,7 +313,7 @@ object ArraySeq extends StrictOptimizedClassTagSeqFactory[ArraySeq] { self =>
* `ArraySeq.unsafeWrapArray(a.asInstanceOf[Array[Int]])` does not work, it throws a
* `ClassCastException` at runtime.
*/
def unsafeWrapArray[T](x: Array[T]): ArraySeq[T] = ((x: @unchecked) match {
def unsafeWrapArray[T](x: Array[T] | Null): ArraySeq[T] | Null = ((x: @unchecked) match {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could pull something off with a universal match type and its mapNull helper.

scala> type MapNull[X, Y] <: Y | Null = X match { case Null => Null; case _ => Y }

scala> inline def mapNull[A, B](a: A | Null, inline f: A => B): MapNull[a.type, B] = (if a == null then null else f(a)).asInstanceOf[MapNull[a.type, B]]
def mapNull[A, B](a: A | Null, f: A => B): MapNull[a.type, B]

scala> def foo(x: String | Null): MapNull[x.type, Array[Char]] = mapNull(x, _.toCharArray())
def foo(x: String | Null): MapNull[x.type, Array[Char]]

scala> foo("hello"): Array[Char]
val res7: Array[Char] = Array(h, e, l, l, o)

scala> foo(null): Null
val res8: Null = null

scala> var s: String | Null = "hello"
var s: String | Null = hello

scala> foo(s): Array[Char]
-- [E007] Type Mismatch Error: -------------------------------------------------
1 |foo(s): Array[Char]
  |^^^^^^
  |Found:    MapNull[(?1 : String | Null), Array[Char]]
  |Required: Array[Char]
  |
  |where:    ?1 is an unknown value of type String | Null
  |
  |
  |Note: a match type could not be fully reduced:
  |
  |  trying to reduce  MapNull[(?1 : String | Null), Array[Char]]
  |  failed since selector (?1 : String | Null)
  |  does not match  case Null => Null
  |  and cannot be shown to be disjoint from it either.
  |  Therefore, reduction cannot advance to the remaining case
  |
  |    case _ => Array[Char]
  |
  | longer explanation available when compiling with `-explain`
1 error found

scala> foo(s): Array[Char] | Null
val res9: Array[Char] | Null = Array(h, e, l, l, o)

Note that the signature of foo is specially crafted so that its TASTy Signature and its binary descriptor are the same as if it were defined def foo(s: String | Null): Array[Char] | Null.

It's not bullet-proof. Sometimes, use site inference gets in the way, as in:

scala> foo(s): Array[Char] | Null
val res9: Array[Char] | Null = Array(h, e, l, l, o)

scala> Array(foo(s))
-- [E172] Type Error: ----------------------------------------------------------
1 |Array(foo(s))
  |             ^
  |      No ClassTag available for MapNull[(?1 : String | Null), Array[Char]]
  |
  |      where:    ?1 is an unknown value of type String | Null
  |
  |
  |      Note: a match type could not be fully reduced:
  |
  |        trying to reduce  MapNull[(?1 : String | Null), Array[Char]]
  |        failed since selector (?1 : String | Null)
  |        does not match  case Null => Null
  |        and cannot be shown to be disjoint from it either.
  |        Therefore, reduction cannot advance to the remaining case
  |
  |          case _ => Array[Char]
1 error found

scala> Array(foo("hello"))
val res10: Array[Array[Char]] = Array(Array(h, e, l, l, o))

However, that's only for the nullable argument case, and in some use site scenarios. It might be worth exploring.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an interesting idea. I will try this and see how it looks.

@noti0na1 noti0na1 marked this pull request as ready for review September 1, 2025 12:20
@noti0na1 noti0na1 requested a review from a team as a code owner September 1, 2025 12:20
@noti0na1 noti0na1 force-pushed the explicit-nulls-stdlib branch from cc174ce to 99df51b Compare September 1, 2025 14:49
@noti0na1
Copy link
Member

noti0na1 commented Sep 1, 2025

@hamzaremmal Do you know how the stdlib is compiled at the first step? When I do scalac, it seems compiling my new code using the old library.

@noti0na1 noti0na1 requested a review from Copilot September 2, 2025 12:04
Copilot

This comment was marked as outdated.

case null => null
case wrapper: JIteratorWrapper[A @uc] => wrapper.underlying
case _ => new IteratorWrapper(i)
case _ => new IteratorWrapper(i.nn)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.nn like this can be removed after #23864

noti0na1 added a commit that referenced this pull request Sep 2, 2025
When we compute `afterPatternContext`, the wrong `ctx` is passed to the
function.

```scala
def f(s: AnyRef | Null) = s match
  case null => 0
  case s: String => s.length
  case _ =>
    val a: AnyRef = s
    a.toString.length
```

Will be useful for #23566
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants