-
Notifications
You must be signed in to change notification settings - Fork 170
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
null_check_on_nullable_type_parameter: runtime cost difference between t!
and t as T
#3844
Comments
What is the performance impact? /cc @mraleph |
The code is different with dart2js – but I can't parse if it's any worse or not. |
I'm also quite skeptical of this lint. Its effect in practice is to lead people to use the |
@kevmoo: Could you share the code difference so we have something concrete to analyze here? In general, I would expect Like our other null-checks, I don't think we currently recognize when |
Maybe the better fix in this case would be to use a local variable and type promotion to avoid the second operation (null check or cast) all together. class Example<T> {
Example(this._value);
final T? _value;
bool transformNullable(bool Function(T value) transformer) {
var localValue = _value;
return localValue == null || transformer(localValue);
} |
Look at this repo - and comment out this ignore: https://github.com/google/quiver-dart/blob/master/analysis_options.yaml#L7 |
I think the problem here is that for the tree bits and optional, we should use |
Generally speaking (not just about type parameters), I prefer That said, it seems to me that this lint is really about raising the fact that a type variable that appears non-nullable can still be a nullable value, as in: class A<T extends Object?> {
T doError(T? value) => value!;
}
...
int result = A<int>().doError(null); // intended use, expected runtime error
int? result2 = A<int?>().doError(null); // unintended runtime error Using I wonder whether the lint suggestion message should hint that, even though class A<T extends Object /*non-nullable!!*/> {
T noUnintendedErrorAnymore(T? value) => value!;
}
...
int result = A<int>().noUnintendedErrorAnymore(2); // you can't pass `int?` as a type parameter anymore
int result2 = A<int>().noUnintendedErrorAnymore(null); // runtime error as intended
|
I looked more closely at quiver – I think this lint is okay. See here google/quiver-dart#711 CC @yjbanov |
FWIW on VM we turn |
Like the VM, dart2js compiles T? _current;
T get current => _current as T; As has been pointed out above, this pattern works correctly when Give that this pattern is used heavily in the built-in iterators, I think we would notice some benchmark regressions if this optimization became disabled. In the original example class Example<T> {
Example(this._value);
final T? _value;
bool transformNullable(bool Function(T value) transformer) =>
_value == null || transformer(_value as T);
} the dart2js would also optimize away the null check if it was written as I like Nick's suggestion of using promotion to infer a non-nullable type since there no check of any kind to be optimized away. The only downside is that when we eventually get better type promotion for fields, the |
Drive-by-comment: Could |
@lrhn I don't think Each internal type object ( The VM has similar type-cast stubs. |
Perhaps the issue is in the semantics of Perhaps the semantics of |
Beyond that being a large change of semantics, what if there are three layers of nullability? What if you really want the value to be non-null. Do you use I don't see how the author of the original code example could know how many levels of nullability were being passed in through the type argument. |
If you really wanted the value to be non-null, wouldn't you have declared |
If I understand correctly your suggestion @yjbanov , you'd like an operator that, given a type class A {}
method<T>() {
A? a = ...;
T? t = ...;
A a0 = a^; // equivalent to `a!` and `a as A`, null effectively not allowed
T t0 = t^; // equivalent to `t as T`, null allowed if T is nullable
} I see the value added here: an operator would be less error prone and make more explicit that we are really trying to manage details on nullability and nothing else about the type itself (a cast hides this and makes it very implicit). Potentially backends could optimize So this brings up two questions for the language folks (@lrhn @leafpetersen):
|
So, If we have I'd recommend just using I know it's an easy mistake to make, forgetting that the actual value may also be Might as well just teach them to use (I have encouraged our compilers to recognize the pattern of checking |
Describe the issue
Chatting with @yjbanov about the performance impact of switching many cases of
t!
tot as T
Should we expect our compilers to "do the right thing" in the cases below?
vs
The text was updated successfully, but these errors were encountered: