errdefer and try/defer fix

This commit is contained in:
2026-07-13 19:20:22 +02:00
parent 9e75549d02
commit 6de4d9f9f3
23 changed files with 113190 additions and 105482 deletions
+87 -1
View File
@@ -1,4 +1,4 @@
# Milestone 19: `defer` and bare block statements.
# 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
@@ -24,6 +24,66 @@ enclosing_defer_check func() i32 {
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
@@ -78,5 +138,31 @@ main func() i32 {
# 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
}