REFERENSIdeferred-cleanup.md
Status dokumen bukan klaim bahwa fiturnya sudah tersedia.

RFC dan proposal mencatat rancangan. Untuk kemampuan yang benar-benar dapat dipakai, lihat matriks kapabilitas runtime dan catatan rilis.

Scoped cleanup and PostgreSQL transactions

YAZA supports an explicit defer cleanup_call(arguments) statement. It registers one direct function call in the current lexical C scope. Arguments are type-checked and evaluated once, from left to right, when the statement runs; the resulting values are captured by value. Registered cleanups run in last-in, first-out order when control leaves that scope through normal fallthrough, return, break, continue, or ? error propagation. A branch or loop body gets a separate scope, so a cleanup in a skipped branch does not run and a loop cleanup runs on each iteration that exits its body.

fn read_data(handle: Number) -> Result[Text, Text]:
    let connection: Number = open_connection(handle)?
    defer close_connection(connection)

    let transaction: PgTransactionScope = pg_scope_begin(connection, 5000)?
    defer pg_scope_cleanup(transaction)

    let row: PgQueryResult = pg_scope_query(transaction, "SELECT $1::text", parameters)?
    let text: Option[Text] = pg_query_text(row, 0, 0)?
    let committed: Boolean = pg_scope_commit(transaction)?
    return ok(option_unwrap(text))

The transaction example demonstrates ordering: the transaction rollback guard runs before the connection closes. Commit is explicit. If an operation returns an error, ? exits the function and cleanup rolls back. Explicit rollback and successful commit mark the scope complete, so the deferred guard becomes a no-op. A failed or timed-out rollback closes the connection and attempts to emit a fixed cleanup-failure event. If the commit result cannot be confirmed, the scope closes the connection and reports the outcome as unknown; it never retries a write automatically.

For a pooled connection, register pool release before transaction cleanup so LIFO order cleans up the transaction first:

defer pg_pool_release(connection, true)
defer pg_scope_cleanup(transaction)

pg_pool_release checks that the socket handle is still active before making it idle. If transaction cleanup closed a failed connection, the pool discards it even though the deferred release was registered with healthy: true. tests/pg_pool.c covers this closed-handle case. Pool leases are still explicit API conventions; the type system does not enforce that callers register cleanup or prevent concurrent use.

defer accepts a direct call only. It rejects malformed calls, type mismatches, unknown functions, extra expressions, and a deferred Result value so cleanup failure cannot be silently discarded as a Result. The PostgreSQL guard attempts to emit the fixed postgres_transaction_cleanup_failed event if an implicit rollback fails or times out; its fields contain only the operation and the fact that the connection was closed. Cleanup functions should report best-effort failure through their explicitly managed state or logs; the C backend ignores a non-Result return value. Cleanup is not registered automatically for every variable. It is not ownership, borrowing, a destructor system, a cancellation hook, or a process-exit/panic guarantee. A cleanup function that terminates the process cannot be made reliable by the compiler.

The current macOS native backend lowers each registration to a typed capture object with a scope-exit cleanup hook supported by Clang and GCC. Generated programs still contain native C; this is not an end-to-end memory-safety claim. The YAZA PostgreSQL guard is a borrowed-connection convention: callers must not use that connection concurrently, call raw transaction commands behind the guard, or release it to a pool before the guard finishes. Those restrictions are not yet enforced by the ownership or type system.

tests/defer-conformance.sh checks capture timing, LIFO order, nested scopes, conditional branches, return, ?, break, and continue, as well as rejected syntax/type cases. tests/pg-transaction-scope-smoke.sh runs a strict local PostgreSQL-protocol peer normally and with ASan/UBSan. The peer checks the exact transaction/query wire order, rollback after a failed query, explicit commit/rollback, duplicate cleanup, nested-BEGIN rejection, rollback failure and timeout, BEGIN failure, and unknown commit outcome without retry; the harness requires exactly two fixed cleanup-failure events for the rollback failure and timeout cases. It also runs scoped transactions through pooled leases: successful commit returns one live connection to idle, while rollback failure and timeout on function return run transaction cleanup before pool release and leave zero pooled connections. tests/api-defer-smoke.sh verifies that secret aliases are zeroized once after both successful and error HTTP responses and never appear in the response or logs, normally and under sanitizers. Live PostgreSQL verification of the new scope guard and migration integration remains pending; the existing manual transaction/savepoint helpers retain their prior live PostgreSQL evidence.