COMMUNITY

@:nullSafety inconsistent behavior on abstracts

null-safety
(Adrian V) #1

I wanted to bring @:nullSafety into one of my projects, which has a lot of @:optional fields. In order to make it easier I thought to use abstracts for the nullable fields which handle the null case.

for example for Null<Float>

@:nullSafety
abstract OFloat(Null<Float>) from Null<Float> to Null<Float> {

  public inline function val(): Float return this != null ? this : 0.0;

  @:op(A + B) static inline function addOp1(lhs: OFloat, rhs: Float): Float {
		 return lhs != null ? lhs.val() + rhs : rhs;
  }
 
}

@:nullSafety
class Test {
  static function main() {
    var x: OFloat = null;
    trace(x.val()); // works
    $type(x.val()); // Float
    var xx = x.val(); // error Null safety: Cannot assign nullable value here.
    x = x + 1.0; // error Null safety: Cannot perform binary operation on nullable value.
  }
}

strange here that trace(v.val()) works. I guess it is some trace magic.

When I make val() static it works:

using Test.OFloat;

@:nullSafety
abstract OFloat(Null<Float>) from Null<Float> to Null<Float> {

  public static inline function val(v: OFloat): Float return v != null ? v : 0.0;

  @:op(A + B) static inline function addOp1(lhs: OFloat, rhs: Float): Float {
		 return lhs != null ? lhs.val() + rhs : rhs;
  }
 
}

@:nullSafety
class Test {
  static function main() {
    var x: OFloat = null;
    trace(x.val()); // works
    $type(x.val()); // Float
    var xx = x.val(); // works
    x = x + 1.0; // works
    trace(x);
  }
}

now I understand that currently the only way to operate on a Null<T> is with static extensions, but since abstracts are different and the value of this can be checked against null before any operation on this shouldn’t this be allowed when the abstract is checked by the compiler with @:nullSafety ?
Anything that can happen, will happen inside the abstract (forwarded functions are of course only that safe they were before):


abstract OFloat(Null<Float>) from Null<Float> to Null<Float> {

  @:nullSafety
  public inline function valBad(): Float return this; // error Null safety: Cannot return nullable value of Null as Float
  @:nullSafety
  public inline function valGood(): Float return this != null ? this : 0.0; // ok 
}

So from outside the abstract it should be safe to use x.valGood()

what do you think ?

Adrian.

0 Likes

(Aleksandr Kuzmenko) #2

My guess is that it’s because of temp vars generated by the compiler. Could you please submit an issue?
Meanwhile, try this workaround:

return this != null ? nullSafety(Off) this : 0.0;
2 Likes

(Adrian V) #3

now it works :slight_smile: thanks

0 Likes