Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,7 @@ MEMCHECK_CASES := \
tests/cases/test_list_transform_ownership \
tests/cases/test_result_unwrap_or_fallback_leak \
tests/cases/test_borrowed_operand_extraction \
tests/cases/test_unwrap_or_borrowed_fallback \
tests/cases/test_task_result_payloads \
tests/cases/test_struct_closure_field tests/cases/test_generic_struct_field_dtor \
tests/cases/test_iterator_drain tests/cases/test_method_fresh_string_return \
Expand Down
21 changes: 13 additions & 8 deletions docs/ownership.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,14 +206,19 @@ gaps, all bounded leaks rather than dangling pointers:
- **a result or optional bound to a name can leak its payload.** `T!` and
`T?` lower to a three-slot value, and releasing one frees those slots
without dropping the payload they own. a local whose every use is a
probe (`.is_ok`, `.is_err`, `== none`) or a payload read (`.ok`, `.err`)
now releases its payload: a probe never touches it, and a read borrows,
taking a fresh count only where the value escapes. either way the local
is still the last owner. any other use leaks as before — passed to a
call, returned whole, consumed by `catch` or `unwrap_or`, bound by
`if let`, or captured by a closure, which takes the shell and not the
payload. writing `x := call()!` avoids the whole question — `!` hands
the count to you rather than leaving it in the tuple.
probe (`.is_ok`, `.is_err`, `== none`), a payload read (`.ok`, `.err`),
or — for a result — an extraction (`r.unwrap_or(d)`, `r catch d`, `r!`)
releases its payload: a probe never touches it, a read borrows, and an
extraction hands its consumer a freshly retained count, so in every
case the local is still the last owner of the count the shell arrived
with. any other use leaks as before — passed to a call, returned
whole, bound by `if let`, or captured by a closure, which takes the
shell and not the payload. one more shape stays on the leak side:
rebinding one name to results of different payload types (`r :=
await ta` then `r := await tb` where the two tasks return different
`T!`s) — the cleanup path is keyed by name and cannot pick one payload
shape, so it frees only the shell. bind results of different types to
different names.

none of these produce a dangling pointer; the discipline trades a
bounded leak for that guarantee.
Expand Down
Loading
Loading