-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Description
TL;DR
Introduce a new expression, todo
, which is coercible to any type, traps at runtime in any build
mode, and is used to communicate incomplete code which is being actively authored right now:
pub fn eval(op: Op, lhs: u64, rhs: u64) i64 {
return switch (op) {
.add => lhs +% rhs,
.sub => lhs -% rhs,
.mul => lhs *% rhs,
.div => if (rhs == 0)
todo // Figure out how to signal division by zero.
else
lhs / rhs
};
}
Proposed todo
is a bit like undefined
, and a bit like unrechable
, but clearly communicates
that the code itself is incomplete.
Motivation & Use Cases
Direct Use
The simplest case is for direct usage by the programmer when they write code! It's hard to
write everything right from start to finish, often it is useful to spike only the central use-case
and happy path, and just "stub out" all the more tricky cases. Today, you can use unreachable
or
@panic("todo")
for this use-case, and they work OK. However, unreachable
can be confused with
intentional unreachable
, and @panic("todo")
is a pain to type. While optimizing typing is not
the goal in general, when you are spiking out solution it becomes relatively more important.
Another subtle point is that unreachable
and @panic()
are typed as noreturn
, but occasionally
you want to type something like
const x: Thing = todo;
x.frobnicate();
and get compilation erros for the code with follows.
todo
is simple to type, simple to delete, and unambigious.
"IDEs"
The primary motivation comes from "IDE" land. IDEs offer various automated refactors, and very
often you want to explictly mark parts of the code to be filled-in by the user. For example,
returning to the original example, a typical IDE feature would be for the user to type
return op.switch
, mash tab
key, and get that exanded to
return switch (op) {
.add => todo,
.sub => todo,
.mul => todo,
.div => todo,
};
Again, an IDE can generate @panic("todo"),
, but then it'll be harder to replace for the user (due
to ,
, just dW
won't work).
Depending on how deep you want to go into the IDE-land, you can imagine strctural-editing-like
experience, where if
exands to if (todo) todo else todo
, and then tab
key cycles through
todo
s. In general, explicit todo
makes building something like
hazel, but text easier.
Again, this is not a deal breaker, @panic("todo")
would work ok, but todo
would be nicer.
Resilitent Compilation
In some sense this is a continuation of the previous use-case, but interally, and IDE might want
to be resilient to compilation errors. To provide type-guided features in downstream code, an
IDE-concious compiler might want to treat erroneous code const x = xs.no_such_method()
like
const x = todo;
. Having an explicit todo
makes that sort of desugaring more natural! Note, however, that implementing resilient compilation is explicitly out of scope for this proposal.
Proposal
Add a todo
expression, which behaves like a cross between undefined
and @panic()
:
- Like
undefined
,todo
is typed using RLS, it is notnoreturn
. - But, like
@panic
,undefined
is guaranteed to generate a trap. - Unlike both, it is runtime-known.
todo
can desugar as
if (runtime_true()) @panic("todo") else undefined
where runtime_true
is just an anti-comptime barrier:
fn runtime_true() bool { return true; }
Here's a todo
test:
pub fn todo_test() []const u8 {
const x = todo;
const y: i32 = todo;
todo;
@as(std.time.Timer, todo).reset();
return todo;
}
This function should compile succesfully. Notably, it shouldn't give "unreachable code" errors.
At runtime, under any optimization mode, it should trap when evaluating const x = todo
.