Jump expressions
return, break, and continue can be used as expressions in addition to their statement forms. A jump expression
never produces a value: evaluating it escapes the enclosing statement. Its type is an internal bottom type that's
assignable to every type and absorbed when computing a conditional's result type. You can't write the bottom type in
Rell code; it only appears through inference.
This makes a bare jump legal wherever an expression is expected. The most useful cases are the following.
As an arm of a conditional expression, including the trailing expression of a value block
arm, which then needs no ;:
val a = if (b) { return 'early' } else null;
val v = if (n < 0) return -n else n * 2;
As the right operand of ?:, giving guard clauses. The elvis result type is the left operand's value type, and
afterwards the left operand is smart-cast to non-null:
function f(x: integer?): integer {
val y = x ?: return -1; // y: integer
return x + y; // x is known non-null here
}
print(f(null)); // -1
print(f(10)); // 20
This includes the fetch-or-return guard over an at-expression with @? cardinality. The jump evaluates after the query
produced null, outside the at-expression body, so the result is the non-nullable row type:
val u = user @? { .name == n } ?: return 'no such user';
// u: user
And break and continue in loop bodies, binding to the enclosing loop:
var s = 0;
for (x in [3, 0, 5, -1, 7]) {
s += if (x < 0) break else if (x == 0) continue else x;
}
print(s); // 8
Rules and diagnostics
The return operand is greedy: everything to the right of return binds to it, so x ?: return 1 + 2 returns 3. In
a when expression, a jump arm is an ordinary expression arm and is ;-terminated.
A jump expression follows the same placement rules as the corresponding statement. return stays prohibited inside
lambda bodies, jumps can't appear where no enclosing body statement or loop exists,
and a bottom-typed expression can never be part of a database at-expression's SQL.
A conditional whose arms all jump is itself bottom-typed, and the statements after it are dead code (stmt_deadcode).
Consuming a jump expression's value where a value is actually needed, such as a call argument, a val or var
initializer, or a collection literal element, compiles with an expr:unreachable warning, because the value never
materializes. Operands of binary operators aren't absorbed: 1 + (return 2) is still a type error.
Jump expressions are available from Rell 0.16.1.