Skip to main content

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.

note

Jump expressions are available from Rell 0.16.1.