Skip to content

Allow Null in isInstanceOf checks #14897

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

Closed
wants to merge 1 commit into from
Closed

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Apr 9, 2022

With -Yexplicit-nulls Null is a class like any other. No reason to treat it specially
in type tests.

Aside: Going through the issues I have noted much higher-breakage rates than usual
in everything that concerns type test optimization and diagnostics. I don't think we
can afford much more breakage in this area. In the future, we should try harder to
err on the side of caution, which means no second guessing of what the programmer
wrote or what a previous phase generated. Default to a runtime test, unless you
know with 100% certainty that no possible scenario would be affected by that test's outcome.

With -Yexplicit-nulls Null is a class like any other. No reason to treat it specially
in type tests. Besides, as scala#14896 shows these type tests might not be user-written.

Aside: Going through the issues I have noted much higher-breakage rates than usual
in everything that concerns type test optimization and diagnostics. I don't think we
can afford much more breakage in this area. In the future, we should try harder to
err on the side of caution, which means no second guessing of what the programmer
wrote or what a previous phase generated. Default to a runtime test, unless you
know with 100% certainty that no possible scenario would be affected by that test's outcome.

Fixes scala#14896
@sjrd
Copy link
Member

sjrd commented Apr 9, 2022

I really don't think this is the correct fix. It's been argued and concluded many times over the years that x.isInstanceOf[Null] must always be false, because the definition of x.isInstanceOf[T] is defined as testing whether x is a non-null instance of T.

Even with Null being a more normal type, it is still the case that null.isInstanceOf[Any] will return false. Since Null<: Any, it is not consistent for null.isInstanceOf[Null] to return true.

@odersky
Copy link
Contributor Author

odersky commented Apr 9, 2022

I would actually go the other way. null.isInstanceOf[Any] should be true (but null.isInstanceOf[Object] is of course false). Null is (or will be) a class like any other, so we can do away with special casing it.

We have argued endlessly about Null, only to still see bugs like #14896 come up. It would be great if we could put all this on firmer ground.

@olhotak
Copy link
Contributor

olhotak commented Apr 9, 2022

I've always thought Java's rule that null instanceof C == false for all C was silly, so I like this in theory.

Although it seems a radical departure from the semantics of Java, the JVM, and Scala 2, if I understand correctly the outcome of null.isInstanceOf[T] would only actually change in three cases: when T is Null, Matchable, or Any. null.isInstanceOf[AnyRef] would remain false. It seems unlikely that existing code would be doing .isInstanceOf[Any] or .isInstanceOf[Null], and Matchable is quite new.

One remaining question is whether or not this should hold only with -Yexplicit-nulls. Do we really want null.isInstanceOf[Any] == false without -Yexplicit-nulls but true with -Yexplicit-nulls?

@odersky
Copy link
Contributor Author

odersky commented Apr 9, 2022

One remaining question is whether or not this should hold only with -Yexplicit-nulls. Do we really want null.isInstanceOf[Any] == false without -Yexplicit-nulls but true with -Yexplicit-nulls?

Ideally, -Yexplicit-nulls should not change runtime semantics. So that would mean null.isInstanceOf[Any] == true even without -Yexplicit-nulls.

@sjrd
Copy link
Member

sjrd commented Apr 9, 2022

But without -Yexplicit-nulls, the situation is even worse:

  • null.isInstanceOf[Null] would be true
  • null.isInstanceOf[SomeClass] would be false
  • null.isInstanceOf[Any] would be true

all that while Null <: SomeClass <: Any!

@odersky
Copy link
Contributor Author

odersky commented Apr 9, 2022

But without -Yexplicit-nulls, the situation is even worse:

Yes, I think the only way to explain this is to say that, for the purposes of type tests, Null has the position in the class Hierarchy that is implied by -Yexplicit-nulls. I.e. it is a direct subclass of Matchable.

@griggt
Copy link
Contributor

griggt commented Apr 9, 2022

Fixes #14896

Setting aside for a moment the debate over Null, this does not solve the issue in general, and in particular the other use case on the ticket, involving js.Tuple2 of Scala.js. I was actually more concerned with that use case than the Null one.

@odersky
Copy link
Contributor Author

odersky commented Apr 9, 2022

@griggt Ah yes, I see now. I agree we should treat Null separately.

@odersky
Copy link
Contributor Author

odersky commented Apr 9, 2022

So this has nothing to do with #14896 after all, but is related to nullability. With explicit nulls, the logical thing to do would be this PR + a change that makes type tests against Any or Matchable always true. But until explicit nulls is the default, we'd want to avoid any situations that would change semantics. Hence, it is better
to disallow Null as a target in isInstanceOf tests. Ideally, we should disallow Matchable and Any as well since their behavior would also change for null.

In any case it's better to close this PR now and revisit it once explicit nulls is the default.

@odersky odersky closed this Apr 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants