errdefer and try/defer fix
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user