169 lines
4.0 KiB
Plaintext
169 lines
4.0 KiB
Plaintext
# Milestone 19: `defer`, `errdefer`, and bare block statements.
|
|
#
|
|
# `defer <stmt>` runs the statement when the enclosing scope exits, in reverse
|
|
# (LIFO) order, on every exit path. A bare `{ ... }` introduces a scope. Each
|
|
# section returns a distinct code on failure; success falls through to 42.
|
|
|
|
# The return value is captured before defers run, so the mutation here does not
|
|
# change what is returned (Zig semantics).
|
|
spill_check func() i32 {
|
|
x i32 = 5
|
|
defer x = 999
|
|
return x
|
|
}
|
|
|
|
# A function-scope defer runs only at function exit; a `break` runs the loop-body
|
|
# defer but NOT the enclosing function-scope defer.
|
|
enclosing_defer_check func() i32 {
|
|
v i32 = 0
|
|
defer v = v + 100
|
|
for 0..3 |i| {
|
|
defer v = v + 1
|
|
if (i == 1) break
|
|
}
|
|
return v # 0->1 (i=0 fall-through), ->2 (i=1 break); the +100 runs after capture
|
|
}
|
|
|
|
CleanupError :: enum {
|
|
bad
|
|
}
|
|
|
|
ExtraError :: enum {
|
|
extra
|
|
}
|
|
|
|
CleanupErrors :: alias CleanupError | ExtraError
|
|
|
|
explicit_cleanup func(fail bool, trace *mut i32) i32 ! CleanupError {
|
|
defer trace^ = trace^ * 10 + 1
|
|
errdefer |err| {
|
|
if (err == .bad) {
|
|
trace^ = trace^ * 10 + 2
|
|
} else {
|
|
trace^ = 99
|
|
}
|
|
}
|
|
defer trace^ = trace^ * 10 + 3
|
|
if (fail) return .bad
|
|
return 7
|
|
}
|
|
|
|
fail_cleanup func() i32 ! CleanupError {
|
|
return .bad
|
|
}
|
|
|
|
try_cleanup func(trace *mut i32) i32 ! CleanupError {
|
|
defer trace^ = trace^ * 10 + 4
|
|
errdefer trace^ = trace^ * 10 + 5
|
|
return try fail_cleanup()
|
|
}
|
|
|
|
widen_cleanup func(trace *mut i32) i32 ! CleanupErrors {
|
|
errdefer |err| {
|
|
match err {
|
|
.bad: trace^ = trace^ * 10 + 6
|
|
.extra: trace^ = 99
|
|
}
|
|
}
|
|
return try fail_cleanup()
|
|
}
|
|
|
|
scoped_cleanup func(trace *mut i32) i32 ! CleanupError {
|
|
errdefer trace^ = trace^ * 10 + 7
|
|
{
|
|
errdefer trace^ = 99
|
|
}
|
|
return .bad
|
|
}
|
|
|
|
multi_exit_cleanup func(direct bool, trace *mut i32) i32 ! CleanupError {
|
|
errdefer |err| {
|
|
if (err == .bad) trace^ = trace^ + 8
|
|
}
|
|
if (direct) return .bad
|
|
return try fail_cleanup()
|
|
}
|
|
|
|
main func() i32 {
|
|
# 1. return value captured before defers run.
|
|
if (spill_check() != 5) return 101
|
|
|
|
# 2. LIFO ordering, run at end of each loop iteration.
|
|
r i32 = 0
|
|
for 0..1 |i| {
|
|
defer r = r * 2 + 1 # registered first -> runs last
|
|
defer r = r * 2 # registered second -> runs first
|
|
_ = i
|
|
}
|
|
if (r != 1) return 102 # 0 -> (r*2)=0 -> (r*2+1)=1 ; FIFO would give 2
|
|
|
|
# 3. scoped bare block + scoped defer (defer fires at the closing brace, and
|
|
# the block-local is not visible afterwards).
|
|
a i32 = 1
|
|
{
|
|
defer a = 4
|
|
c i32 = 3
|
|
_ = c
|
|
}
|
|
if (a != 4) return 103
|
|
|
|
# 4. `defer { ... }` block: all its statements run (in order) at scope close.
|
|
s i32 = 0
|
|
{
|
|
defer {
|
|
s = s + 1
|
|
s = s * 10
|
|
}
|
|
s = 5
|
|
}
|
|
if (s != 60) return 104 # 5 -> 6 -> 60
|
|
|
|
# 5. `break` flushes the loop-body defer.
|
|
bc i32 = 0
|
|
for 0..5 |i| {
|
|
defer bc = bc + 1
|
|
if (i == 2) break
|
|
}
|
|
if (bc != 3) return 105 # i=0,1 fall-through + i=2 break
|
|
|
|
# 6. `continue` flushes the loop-body defer.
|
|
cc i32 = 0
|
|
for 0..3 |i| {
|
|
defer cc = cc + 1
|
|
if (i == 1) continue
|
|
cc = cc + 10
|
|
}
|
|
if (cc != 23) return 106 # 10,+1 ; +1 (continue) ; +10,+1
|
|
|
|
# 7. break does not run an enclosing function-scope defer.
|
|
if (enclosing_defer_check() != 2) return 107
|
|
|
|
# 8. errdefer is skipped on success; ordinary defers stay interleaved.
|
|
trace i32 = 0
|
|
if ((explicit_cleanup(false, &trace) catch 0) != 7 or trace != 31) return 108
|
|
|
|
# 9. Explicit errors run errdefer and expose the captured error.
|
|
trace = 0
|
|
if ((explicit_cleanup(true, &trace) catch 9) != 9 or trace != 321) return 109
|
|
|
|
# 10. Propagated errors run both errdefer and ordinary defer.
|
|
trace = 0
|
|
if ((try_cleanup(&trace) catch 9) != 9 or trace != 54) return 110
|
|
|
|
# 11. Captures observe the widened enclosing error type.
|
|
trace = 0
|
|
if ((widen_cleanup(&trace) catch 9) != 9 or trace != 6) return 111
|
|
|
|
# 12. An errdefer expires when its block exits normally.
|
|
trace = 0
|
|
if ((scoped_cleanup(&trace) catch 9) != 9 or trace != 7) return 112
|
|
|
|
# 13. One captured errdefer can be replayed at several distinct error exits.
|
|
trace = 0
|
|
_ = multi_exit_cleanup(true, &trace) catch 0
|
|
_ = multi_exit_cleanup(false, &trace) catch 0
|
|
if (trace != 16) return 113
|
|
|
|
return 42
|
|
}
|