Repository navigation
feat(ts): drop the closures std's consumers and entries own where std's do - #438
Conversation
Quest-Claim: a234456d49680711
…'s do reduce, max_by, min_by, their _by_key forms, is_sorted_by_key, and a map entry's or_insert_with, or_insert_with_key, and and_modify translate a closure that owns a value that drops, dropping it where std's does, proven by the task fixture's new `released` step against native wasm32. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
…t to the sort port Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
quest land: this pull request conflicts with a pull request queued ahead of it, in these files:
Once one of them lands, this one conflicts with the base and is ejected when its turn comes. Resolve the overlap before then, such as by coordinating with that author, or by merging the base once it lands. |
|
quest land removed this pull request from the queue: PR #438 merge push failed: remote: Permission to moq-dev/rust2.git denied to moq-bot[bot]. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
quest/m3/ts-drop-order.mdlists the closures that own a value that drops which rust2-ts still refuses: given to a std consumer that takes another value that drops by value, other thanfor_eachandfold, or called throughFnOnceby a mapping that may never call it, asor_insert_withdoes. A probe of std's consumers found these refused:Iterator::reduce,max_by,min_by,max_by_key,min_by_key, andis_sorted_by_keyof an iterator that drops.or_insert_with,or_insert_with_key, andand_modify, for std'sHashMapandBTreeMapand for hashbrown's maps andHashTable.Approach
Each mapping now drops the closure where std's does.
library::releasesreplacesiter::releasesand covers both groups.called_closurestreats a released closure the way it treats a discarded one, so a closure thatFnorFnMutcall, given to aFnOnceparameter, becomesrt.onceof it and drops after the call.reduce,max_by,min_by: these takeowns. std'sreduceisself.next()?and thenfold. On a non-empty iterator, the closure drops as the fold ends, before the iterator. On an empty one, it drops before the iterator, because parameters drop in reverse order.max_by_key,min_by_key: std maps the iterator by the key's closure, sobesttakeskeyed. On an empty iterator, the closure drops after the iterator, asMap's fields drop. Otherwise it drops as the fold ends.is_sorted_by_key: std'sself.map(f).is_sorted()dropsfwith theMap, after the iterator.rt.iterMapalready does that once it is told to drop. A closure that drops now setsdropseven when its iterator drops nothing. A closure lent through a reference, boxed or not, isrt.lent; one it owns drops with the map, as std's does.rt.entryOrInsertWith,entryAndModify,tableOrInsertWith,tableAndModify, and theirMutforms take what drops the closure. They call it when they don't call the closure: on an occupied entry foror_insert_with, and on a vacant one forand_modify.Tests:
released(ts/fixtures/task/src/drops/released.rs, 7 scenarios), runs differentially against native wasm32. It covers each consumer over:takethat holds leftovers;Fncalls;HashTablesteps cover hashbrown'sor_insert_withandand_modifyby a closure owning a drop counter.Mutation checks:
max_by's comparator after an empty iteratorreduce's empty-iterator orderbest's closure after the fold, not inside itor_insert_withdropand_modifydropis_sorted_by_keyignoring a closure that dropsrt.oncenot dropping after the calltableOrInsertWithnot droppingtableAndModifyMutnot droppingEach now gives 0.
The reject fixture's
called_closureis deleted. It translates now, and thereleasedstep covers it.Impact
rt.iterReduce,rt.iterMaxBy,rt.iterMinBy,rt.iterMaxByKey, andrt.iterMinByKeytakeownsbefore thert.Nonethey take last. Recorded ints/MIGRATE.md.dropFonrt.entryOrInsertWith,entryOrInsertWithMut,entryAndModify,entryAndModifyMut,tableOrInsertWith,tableAndModify, andtableAndModifyMut.undefinedcheck per call, not per item.Alternatives
discardsby its type-argument index. The index differs betweenHashMap'sEntry(K, V, F) andBTreeMap's (K, V, A, F), so a by-namereleaseswith the closure taken as the last type argument is simpler.rt.dropAfter, as a spent closure is. That drops the closure after the iterator, where std drops it before, so it gives the wrong order on every non-empty fold.Follow-ups
quest/m3/ts-drop-order.mdnow holds only the zip, flatten, and B-tree collect refusals. Its plan namedpartition, butpartitionis refused as an unsupported call whatever the closure holds.quest/m3/ts-closure-calls.md:rfold,try_fold,try_for_each,partition,repeat_with,scan,sort_by_cached_key,LazyCell::new,Cell::update, andRefCell::replace_withhave no mapping at all.quest/m3/ts-string-closures.md:String::retainandstrsearches by a closure that drops. These use a separate refusal, outsidecalled_closures.Choices made without asking
Entry, so leaving them out would have meant a type check just to keep them refused.released, after the runtime'srelease.String::retainand str patterns to a quest. They are a different refusal path.Review
Local
quest review: Codex, gpt-6.1-sol, medium effort. Round 1 on head9d4e6d8ca53ebca4f2dbae95d2227044172b643c: no findings.Head
80168791adds only quest text:ts-closure-callsdropsrfoldand links #436'sts-iter-back-foldsfor folds from the back, andts-drop-ordernotes the B-treecollectfollows the sort port. Review skipped for that commit;just cipasses on it.Head
bde31876mergesorigin/main:ts/MIGRATE.mdkeeps both sides' entries, and the reject and stock snapshots are regenerated (the census drops bothHasher::write's refusal from main andEntry::or_insert_with's from this PR, 229 refusals). Review skipped for that merge;just cipasses on it.Checks:
just cipasses locally on9d4e6d8c. One self-caught bug along the way:is_sorted_by_keybriefly dropped a closure lent asBox<&F>. The lang fixture's differential test found it (35 differences), and it is fixed by reading the lend throughcalled_through.(written by Claude Opus 5.5)
🤖 Generated with Claude Code