-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Pain points for explicit null checking #14622
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
Comments
|
There's a semantic difference between == and eq so I don't see how that suggestion could make sense. |
Can we simply add this to extension (inline x: AnyRef | Null)
inline def eq(inline y: AnyRef | Null): Boolean =
x.asInstanceOf[AnyRef] eq y.asInstanceOf[AnyRef]
inline def ne(inline y: AnyRef | Null): Boolean =
!(x eq y) |
I think this should work. |
On point 1, if we never assign |
Here's another possible solution for 1. The idea is to use an explicit type type MonotoneNull <: Null
val initialNull = null.asInstanceOf[MonotoneNull]
class C {
private var field: String | MonotoneNull = initialNull
if field == null then field = "foo" // OK
field = null // error
} It's possible to implement |
That looks nice. You could also call that type |
IIUC, this is about the following pattern: private var myAssignmentSpans: Map[Int, List[Span]] | Null = null
def assignmentSpans(using Context): Map[Int, List[Span]] =
if myAssignmentSpans == null then myAssignmentSpans = Nullables.assignmentSpans
myAssignmentSpans.nn ? Because if that's the case, there's a non-language solution to this, that will even reduce boilerplate: // define once:
inline def initIfNull[A](value: A | Null)(inline set: A => Unit)(inline init: => A): A =
if value == null then
val initValue = init
set(initValue)
initValue
else
value
// then use at will:
def assignmentSpans(using Context): Map[Int, List[Span]] =
initIfNull(myAssignmentSpans)(myAssignmentSpans = _)(Nullables.assignmentSpans) |
I just had a unit test fail in CI where bootstrapped implies explicit nulls, because of methods on String considered nullable. It would be handy to patch return types of methods on String as I see |
In my own first experiments with getting some Scala 3 code of my own to compile under Currently I have to insert Without improvements in this area, the feature is really very painful to enable on much ordinary Scala code, IMO. |
Another pain point I hit over and over again that I don't think has been mentioned before is: I routinely get irrelevant errors after the first nullability-related failure. For example, when I write (simplified from actual code): Iterator(" foo bar ", " baz qux").map(_.trim.split(' ')).toArray I get two errors:
The first error is desired, but the second one is unhelpful. After issuing a nullability error, could the compiler pretend I added |
scala/scala#9447 would be a good fit for that. To include the non-null annotations in a project one would only need to add a library dependency to the build. |
Compiler version
3.1.3 RC1 with -Yexplicit-nulls
Minimized example
When reviewing #14032, I observed some pain points for explicit null checking that we might be able to fix. In decreasing order of importance:
Flow typing does not extend to variables of enclosing methods or fields of enclosing classes. It's clearly more difficult to know when a nullability info needs to be invalidated in these cases. But there is one situation, which happens to be the most common one, where I think we should be able to check nullability: It's when we know that a variable is initially null and every assignment to the variable is a non-null value. In that case, there's nothing to invalidate: once we know a variable is non-null at some point, it will stay non-null forever. If we could exploit that knowledge, the vast majority of caches in dotty could be null-checked without needing
.nn
.The tricky bit is sequencing. To know that all assignments are non-null we need to type check the program. But that means we might have to type check dereferences to a nullable variable first. We could try to find a better phase ordering. Maybe delay flow-typing until after typer. During type checking, always proceed under the
unsafeNulls
assumption. During subsequent flow typing, remove inserted.nn
calls if they are redundant according to flow typing rules. If any inserted.nn
calls remain, issue errors unlessunsafeNulls
is imported.Calls to Java methods. Super annoying to have to write
System.err.nn.println(...)
orstr.substring(a, b).nn
. We should consider erring more towards unsoundness here.eq
, andne
should also work for nullable types. We create a lot of complexity in comparisons since that's currently not the case.The text was updated successfully, but these errors were encountered: