-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Explicitly null check the stdlib #23566
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@noti0na1 As agreed to, please push the changes to explicitly null check the stdlib here. |
2fe2627
to
2d178a7
Compare
@@ -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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
cc174ce
to
99df51b
Compare
@hamzaremmal Do you know how the stdlib is compiled at the first step? When I do |
case null => null | ||
case wrapper: JIteratorWrapper[A @uc] => wrapper.underlying | ||
case _ => new IteratorWrapper(i) | ||
case _ => new IteratorWrapper(i.nn) |
There was a problem hiding this comment.
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
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
No description provided.