-
Notifications
You must be signed in to change notification settings - Fork 1.1k
A New UnsafeNulls Language Feature for Explicit Nulls #9884
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
Conversation
8a8d308
to
3923aa5
Compare
c5909d2
to
da75df4
Compare
b68ed5c
to
04982b1
Compare
test performance please |
performance test scheduled: 1 job(s) in queue, 1 running. |
performance test failed: Please check http://lamppc37.epfl.ch:8000/pull-9884-10-08-14.45.out for more information |
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.
We need a performance test for this. I tried but the scodec conflicts need to be resolved first.
Overall, I think the approach is workable, but we should work on streamlining with the goals of increasing clarity and efficiency.
My main concern is that there are currently too many booleans that are tested in too many places. We should try to standardize on a minimal set of switches only and define them all in the same place (probably as Mode bits). Then, add a doc comment explaining what each boolean means and where it is used, and why it is different from the others.
@odersky I have rebased the branch and scodec. Will the performance test run if I comment "test performance please" here? |
84e8d0b
to
b4376a1
Compare
3c67572
to
afa320c
Compare
008b21a
to
fd83a44
Compare
ac266a0
to
08fc382
Compare
db0d5f9
to
5a9b30b
Compare
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 did a first pass. I have not looked at everything yet. The general direction looks good IMO but right now I find the logic complicated to follow because there are too many mode switches represented as boolean parameters with defaults. So before going further, I have the following suggestion:
Try to formulate the logic without using any boolean default parameters. Work on streamlining things in the context instead. Default parameters are a too easy way to hide complicated logic. Once that is done, consider bringing back some selected default parameters, but only if that leads to performance improvements or more streamlined code (but only after all other alternatives to streamline code have been considered).
I also added a commit with some small fixes of typos and wordings. |
Hi @noti0na1. What's the current state of this? Do you still want to work on improvements here? I just had a look again, but I still find there are too many modes and default parameters floating around. Is there no way to bundle all of it in a mode bit in the context, say? Please re-assign to me, when you think this is ready for review again |
@odersky I have combined all the commits. |
test performance please |
performance test scheduled: 2 job(s) in queue, 1 running. |
performance test failed: Please check http://lamppc37.epfl.ch:8000/pull-9884-02-10-18.40.out for more information |
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 PR still has a lot of complications, which might not be necessary. To be able to merge this, it should be simplified ruthlessly. I mean not just a few cosmetic changes but every element of this PR should be put to the test whether it is really needed. I did that with #11375, and found that a lot of concepts seem not to be needed, after all. At least all the explicitNulls
tests pass if they are removed. Among the removed constructs are:
- The UnsafeNullsSubType mode
- The double caches in Implicits
- The withOrNull in translateParameterized
- The special case in adapt
- A few other smaller things
So, maybe it's best to take #11375 as a basis, and only add some specific changes to this simpler base that are needed for correctness.
The reason I am so strict here is that I worry about future maintainability of the compiler codebase. Maintainers need to be able to verify and modify every single element of the codebase. Unnecessary code, and worse, unnecessary abstractions and conditions are
very bad for this since they destroy confidence that the code we deal with is there for a reason.
What about we try the following procedure to push this over the finish line?
When in doubt, let's discuss each failure and proposed fix together. |
@odersky Sorry for the late response. I will merge and look at these changes soon. |
6cc2028
to
f6e6385
Compare
@odersky I have merged the changes from your PR. I think the introducing of These are several tests for I will update the document recently. |
@noti0na1 Should we merge this then? |
Looks very good now! I think this is a great basis to experiment with and to possibly refine things later. |
This PR removes the
UncheckedNull
type, and adds a new language featureunsafeNulls
to explicit nulls, which allows unsafe null operations in a certain scope. In addition, a relaxed overriding rule is implemented for overriding Java classes.Description
In the original explicit nulls design,
UncheckedNull
was introduced to allow member selections on nullable objects from Java code (with typeT | UncheckedNull
). We could defineUncheckedNull
as a type alias ofNull
or a true subtype ofNull
; however, we find it is difficult to implement in both ways. See #7828 for more discussion.We decided to create a new language feature, called
unsafeNulls
. Inside this "unsafe" scope, allT | Null
objects can be used asT
, andNull
keeps being a subtype ofAny
.Users can import
scala.language.unsafeNulls
to create such scope, or use-language:unsafeNulls
to enable this feature globally. The following unsafe null operations will apply to all nullable types:T
onT | Null
objectT
onT | Null
T1
toT2
ifT1.stripAllNulls <:< T2.stripAllNulls
orT1
isNull
andT2
has null value after erasureT
andT | Null
The intention of this
unsafeNulls
is to give users a better migration path for explicit nulls. TheUncheckedNull
is only limited to unsafe member selections. We think this approach can do more thanUncheckedNull
and in a better way.Projects for Scala2 or regular dotty can try this by adding
-Yexplicit-nulls -language:unsafeNulls
to the compile options. A small number of manual modifications are expected (for example, some code relies on the facts ofNull <:< AnyRef
). To migrate to full explicit nulls in the future,-language:unsafeNulls
can be dropped and addimport scala.language.unsafeNulls
only when needed.Examples
All the examples are under
-Yexplicit-nulls
flag.Without the
unsafeNulls
, all these unsafe operations will not be compiled.unsafeNulls
also works for extension methods and implicit search.See more examples about
unsafeNulls
intests/explicit-nulls/pos/unsafe-*.scala
.An example for relaxed overriding:
See more examples about overriding in
tests/explicit-nulls/pos/override-*
.We are also considering a Java-compatible flag, which enables unsafeNulls to all Java method calls. This will be in a seperate PR.