Description
[Edit: Closed this as a duplicate of #2025.]
We have requests now and then for introducing an immediate and non-normal completion of an expression evaluation.
We already have the ability to abruptly terminate an ongoing expression evaluation using throw e
, but in some situations it can be convenient to complete the expression evaluation 'returning' rather than 'throwing', or 'continuing', or 'breaking', and it is not optimal from a performance or code readability point of view to throw
something and then break
, continue
, return
from a catch
or finally
block, and this kind of workaround will not work with rethrow
.
So we could introduce <returnExpression>
, <breakExpression>
, <continueExpression>
, <rethrowExpression>
, and allow them to occur as expressions of type Never
, with the same syntactic precedence as <throwExpression>
. The semantics would be to immediately terminate the evaluation of the enclosing expression, and then give rise to the same data transfer and control transfer as the corresponding <returnStatement>
etc. do today.
An issue where a similar request has been put forward is #2095, where it is requested that e ?? return
can be used as an expression, and #2088 is a similar proposal.
#2025 is very similar to this issue; it mentions both continue
and break
as well as return
, and proposes the type Never
for these expressions. It may or may not assume that the new expressions can only occur as the second operand of ??
or ||
. This proposal intends the new expressions to be usable in any location where they can occur syntactically (and they can be enclosed in parentheses to become a <primaryExpression>
, which means that they can occur basically anywhere).
This proposal may seem quite disruptive at the syntactic level, but an experimental change to Dart.g
seems to support the assumption that we can simply do the following:
returnExpression // Updated, was `returnStatement`.
: RETURN expression?
;
breakExpression // Updated, was `breakStatement`.
: BREAK identifier?
;
continueExpression // Updated, was `continueStatement`.
: CONTINUE identifier?
;
rethrowExpression // Updated, was `rethrowStatement`.
: RETHROW
;
// Delete `returnStatement`, `breakStatement`, `continueStatement`,
// `rethrowStatement` from `nonLabelledStatement`, they are now
// covered by `expressionStatement`.
throwExpression // Should be renamed, e.g., `abruptCompleteExpression`.
: THROW expression
| breakExpression
| continueExpression
| returnExpression
| rethrowExpression
;
@johnniwinther, @scheglov, @mkustermann, @sigmundch, how disruptive does this feature seem to be from your point of view, for the static analysis, code generation, or execution of programs?
My guess is that the static analysis can just give the new expressions the type Never
, and almost everything done for throw
expressions could then be reused with these new expressions.
But during code generation and execution there might be local state (say, a non-empty expression evaluation stack) that calls for further operations before jumping, and I have no idea how hard it would be to handle that, or how much of the existing functionality concerned with throw expressions could be reused with the new expressions.
@munificent, @jakemac53, @lhrn, @leafpetersen, @natebosch, @stereotype441, WDYT?