Thanks to @sgrekhov for bringing up this topic. Consider the following program:
var f1 = () sync* {
return;
};
var f2 = () sync* {};
The specification in this section implies that the function literal that contains return; has a return type of Iterable<Never>. This makes sense because the returned iterable is empty, which means that it is safe to claim that every returned object has whichever type you want.
The reason why the return is Iterable<Never> rather than Iterable<Null> is that PR #4210 disabled the action taken when a return; is encountered: With that PR, the actual returned type is not updated to include Null when the function is a generator. This is working correctly because return; in a sync* body doesn't actually add a null to the returned iterable. Similarly for async* generators and Stream.
However, the rules imply that the second function literal — the one that does not contain a return; statement — has a return type of Iterable<Null> for sync* and Stream<Null> for async*.
The culprit is that the initial value of the 'actual returned value' is chosen to be Null in the case where the end of the function body is reachable. This is suboptimal in the case of generators for the same reason as the one in #4210. Hence, we should change the relevant sentence as follows. This is the wording that we currently have:
The actual returned type of a function literal with a block body is computed as follows. Let T be Never ...
We can change it as follows:
The actual returned type of a function literal with a block body is computed as follows. If the function literal is a generator then let T be Never. Otherwise, let T be Never ...
@dart-lang/language-team, WDYT? Does it need breaking change processing, or can we just go ahead and fix it?
Thanks to @sgrekhov for bringing up this topic. Consider the following program:
The specification in this section implies that the function literal that contains
return;has a return type ofIterable<Never>. This makes sense because the returned iterable is empty, which means that it is safe to claim that every returned object has whichever type you want.The reason why the return is
Iterable<Never>rather thanIterable<Null>is that PR #4210 disabled the action taken when areturn;is encountered: With that PR, the actual returned type is not updated to includeNullwhen the function is a generator. This is working correctly becausereturn;in async*body doesn't actually add a null to the returned iterable. Similarly forasync*generators andStream.However, the rules imply that the second function literal — the one that does not contain a
return;statement — has a return type ofIterable<Null>forsync*andStream<Null>forasync*.The culprit is that the initial value of the 'actual returned value' is chosen to be
Nullin the case where the end of the function body is reachable. This is suboptimal in the case of generators for the same reason as the one in #4210. Hence, we should change the relevant sentence as follows. This is the wording that we currently have:We can change it as follows:
@dart-lang/language-team, WDYT? Does it need breaking change processing, or can we just go ahead and fix it?