Skip to content

warn about unnecessary uses of .nn #23327

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

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

olhotak
Copy link
Contributor

@olhotak olhotak commented Jun 6, 2025

Issue warnings when .nn is called on a receiver that is already not null or when the expected type of the result of .nn admits null.

cc @noti0na1

@olhotak olhotak requested a review from noti0na1 June 6, 2025 23:33
@@ -1088,7 +1088,19 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
errorTree(tree, em"cannot convert to type selection") // will never be printed due to fallback
}

if (tree.qualifier.isType) {
def warnUnnecessaryNN(tree: Tree): Unit = {
if ctx.explicitNulls then {
Copy link
Member

Choose a reason for hiding this comment

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

Do you agree we should check unnecessary nn even without explicit nulls?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We could, but I didn't want to rock the boat too much before we fine-tune which corner cases should/shouldn't warn.

@@ -50,6 +50,18 @@ object NullOpsDecorator:
val stripped = self.stripNull()
stripped ne self
}

def admitsNull(using Context): Boolean = {
Copy link
Member

Choose a reason for hiding this comment

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

Since we are basically checking if a type has Null as a subtype, I'm not sure whether we should widen a singleton type.

For example,

val s: String | Null = ???
val ss: s.type | Null = ???
val snotnull: s.type = ss.nn

I'd like to allow ss.nn to say if it returns a value, the value is equal to s and not null.

@@ -373,7 +373,7 @@ object Types extends TypeUtils {
final def isNotNull(using Context): Boolean = this match {
case tp: ConstantType => tp.value.value != null
case tp: FlexibleType => false
case tp: ClassInfo => !tp.cls.isNullableClass && tp.cls != defn.NothingClass
case tp: ClassInfo => !tp.cls.isNullableClass && !tp.isNothingType
case tp: AppliedType => tp.superType.isNotNull
case tp: TypeBounds => tp.lo.isNotNull
Copy link
Member

Choose a reason for hiding this comment

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

After thinking about admitsNull, I'm now wondering: should we check the hi of a type bound? For example, T >: String <: String | Null, I don't think T is "NotNull".

Copy link
Contributor Author

@olhotak olhotak Jun 7, 2025

Choose a reason for hiding this comment

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

The test we want is NullType <:< tp, but <:< has side effects.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, sorry, never mind, I thought this was about admitsNull.

For isNotNull, you may be right, but it didn't change (the case for TypeBounds). It was like that before this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, but you said admitsNull.

For admitsNull, the test is NullType <:< tp, so lo is right when tp is a TypeBounds.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I mean the case tp: TypeBounds in isNotNull. I feel it is not correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right. It should be hi, not lo. I've pushed a fix.

@olhotak
Copy link
Contributor Author

olhotak commented Jun 7, 2025

I can't figure out the bootstrapped test failures. It seems to be a Heisenbug.

Locally, if I do scala3-compiler-bootstrapped/testCompilation after rm -rf out; sbt clean, sometimes it fully passes and sometimes every test fails.

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.

2 participants