#1887 Compiler nullability check not strict enough

ralphch Thu 3 May 2012

Void invite(Person host, Person guest) { ... }

Void main() {

Person? host = null;
Person? guest = null;
invite(host, guest); //no compiler error

}

I see this as a hole in compiler checking. If I am not allowed to assign a nullable variable to a non-nullable variable, why am I allowed to pass a nullable value for a non-nullable parameter. I don't know if this is by design or not, but here is why I think Fan should do a full nullability check:

  • Most variables will be Non-null, as they are more frequently used than nullable ones
  • Non-Nullables can be implicitely assigned to Nullables
  • I don't think downcasting will be a major problem if there is some good syntactic sugar to help
    • invite(host!, guest!) // cast to not null, throws an exception if host or guest is null
    • invite(host?, guest?) // skips the method call if host or guest is null (return null if method has return type)
    • invite(host ?: john, guest ?: peter) // provide defaults in case of null
    • notNull keyword

notNull(host, guest) {

log.info("calling do it");
invite(host, guest);

}

What this can do under the covers is check if both variables are not null and if so cast the nullable person variables to new variables in the notNull scope of type Person (not nullable) that shadow the nullable ones within the scope. If name shadowing is not acceptable, then it could just be a compiler check feature (at runtime this would translate to an if condition and variables will be cast before passed to the method.

One major benefit of having full nullability check comes when a variable that used to be not nullable, becomes optional because of some business requirement. You will need to look for any code that was written assuming that that variable will not be null. That can be tedious and you definitely gonna miss some. The compiler on the other hand can do it much more easily giving you the opportunity to handle each case in the client code.

brian Thu 3 May 2012

I see this as a hole in compiler checking. If I am not allowed to assign a nullable variable to a non-nullable variable, why am I allowed to pass a nullable value for a non-nullable parameter

All coercion in between types which might work are checked at runtime, not compile time. So essentially:

Str? foo
invite(foo)

// becomes
invite((Str)foo)

Where the coercion cast is implicitly added and checked at that location. Same for other types such as coercion b/w Obj to Str or whatnot. See TypeSystem

ralphch Fri 4 May 2012

What is the motivation for Implicit Casts?

Is there a flag that will make the compiler produce a warning on downcast?

brian Fri 4 May 2012

The motivation is simply based on experience that they rarely actually cause problems at runtime, but that explicit casts, checks for null, etc are a worse trade off. This is specially true if heavily using reflection or "->" operator where casting everything would be a pain in the ass yet yield marginal actual safety.

Login or Signup to reply.