From a65f73b093a0ead921b8ec1901bcf9c6c9673a6a Mon Sep 17 00:00:00 2001 From: Perfloop Agent Date: Tue, 3 Nov 2026 01:03:37 +0000 Subject: [PATCH 1/2] Test map iterator erase destruction at the call boundary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Perfloop Agent Signed-off-by: Tomás Senart --- tests/unit/map_erase_destructor.cpp | 78 ++ .../unit/out/refcount/map_erase_destructor.rs | 834 ++++++++++++++++++ tests/unit/out/unsafe/map_erase_destructor.rs | 475 ++++++++++ 3 files changed, 1387 insertions(+) create mode 100644 tests/unit/map_erase_destructor.cpp create mode 100644 tests/unit/out/refcount/map_erase_destructor.rs create mode 100644 tests/unit/out/unsafe/map_erase_destructor.rs diff --git a/tests/unit/map_erase_destructor.cpp b/tests/unit/map_erase_destructor.cpp new file mode 100644 index 000000000..ca7f1fe06 --- /dev/null +++ b/tests/unit/map_erase_destructor.cpp @@ -0,0 +1,78 @@ +// XFAIL: unsafe +#include +#include + +int destroyed[32]; + +struct Tracked { + int id; + Tracked() : id(0) {} + Tracked(int id) : id(id) {} + ~Tracked() { ++destroyed[id]; } +}; + +bool operator<(const Tracked &a, const Tracked &b) { return a.id < b.id; } + +template +void check_erase(bool key_destructor, bool value_destructor) { + K one(1), two(2), three(3); + std::map m; + m[one] = V(11); + m[two] = V(12); + m[three] = V(13); + for (int &n : destroyed) + n = 0; + + { + auto first = m.find(one); + auto middle = m.find(two); + auto last = m.find(three); + auto retained = middle; + typename std::map::const_iterator retained_const = middle; + auto *first_value = &first->second; + auto *last_value = &last->second; + auto end = m.end(); + + auto next = m.erase(middle); + assert(destroyed[2] == key_destructor); + assert(destroyed[12] == value_destructor); + assert(destroyed[1] == 0 && destroyed[3] == 0); + assert(destroyed[11] == 0 && destroyed[13] == 0); + assert(m.size() == 2 && m.find(two) == end); + assert(next == last && first == m.begin()); + assert(&first->second == first_value && &last->second == last_value); + auto walking = first; + ++walking; + assert(walking == last); + ++walking; + assert(walking == end); + + typename std::map::const_iterator const_first = first; + next = m.erase(const_first); + assert(destroyed[1] == key_destructor); + assert(destroyed[11] == value_destructor); + assert(m.size() == 1 && next == last && m.begin() == last); + assert(&last->second == last_value); + + next = m.erase(last); + assert(destroyed[3] == key_destructor); + assert(destroyed[13] == value_destructor); + assert(next == end && m.size() == 0 && m.begin() == end); + // The copies of erased iterators are retained, but never used. + } + for (int i = 0; i < 32; ++i) { + int expected = 0; + if (i >= 1 && i <= 3) + expected = key_destructor; + if (i >= 11 && i <= 13) + expected = value_destructor; + assert(destroyed[i] == expected); + } +} + +int main() { + check_erase(true, true); + check_erase(true, false); + check_erase(false, true); + check_erase(false, false); +} diff --git a/tests/unit/out/refcount/map_erase_destructor.rs b/tests/unit/out/refcount/map_erase_destructor.rs new file mode 100644 index 000000000..2f09c8bed --- /dev/null +++ b/tests/unit/out/refcount/map_erase_destructor.rs @@ -0,0 +1,834 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +thread_local!( + pub static destroyed_0: Value> = + Rc::new(RefCell::new((0..32).map(|_| 0_i32).collect::>())); +); +#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg, CppFrom, Destructor)] +#[cpp_from(i32, new_1, value)] +#[byte_size(4)] +pub struct Tracked { + #[offset(0)] + pub id: i32, +} +impl Tracked { + pub fn new_2() -> Self { + Self { id: 0 } + } + pub fn new_1(mut id: i32) -> Self { + Self { id: id } + } +} +impl CppLess for Tracked { + fn less(__lhs: &Ptr, __rhs: &Ptr) -> bool { + operator_lt_1(__lhs.clone(), __rhs.clone()) + } +} +impl CppEq for Tracked { + fn equal(__lhs: &Ptr, __rhs: &Ptr) -> bool { + !(operator_lt_1(__lhs.clone(), __rhs.clone())) + && !(operator_lt_1(__rhs.clone(), __lhs.clone())) + } +} +impl Default for Tracked { + fn default() -> Self { + { Tracked::new_2() } + } +} +pub fn operator_lt_1(a: Ptr, b: Ptr) -> bool { + return ({ a.with(|__s| __s.id) } < { b.with(|__s| __s.id) }); +} +pub fn check_erase_2(mut key_destructor: bool, mut value_destructor: bool) { + let one: Value = Rc::new(RefCell::new(Tracked::new_1({ 1 }))); + let _dtor_one = ScopedDestructor::new(&one, |__p| __p.destructor()); + let two: Value = Rc::new(RefCell::new(Tracked::new_1({ 2 }))); + let _dtor_two = ScopedDestructor::new(&two, |__p| __p.destructor()); + let three: Value = Rc::new(RefCell::new(Tracked::new_1({ 3 }))); + let _dtor_three = ScopedDestructor::new(&three, |__p| __p.destructor()); + let m: Value, Value)>> = + Rc::new(RefCell::new(Map::new())); + Map::subscript(m.as_pointer(), one.as_pointer()).write(Tracked::new_1({ 11 })); + Map::subscript(m.as_pointer(), two.as_pointer()).write(Tracked::new_1({ 12 })); + Map::subscript(m.as_pointer(), three.as_pointer()).write(Tracked::new_1({ 13 })); + 'loop_: for n in IterRange::new( + (destroyed_0.with(|v| v.as_pointer()) as Ptr), + (destroyed_0.with(|v| v.as_pointer()) as Ptr).offset((32_i64) as isize), + ) { + let n: Ptr = (n).clone(); + n.write(0); + } + { + let first: Value, Value)>> = + Rc::new(RefCell::new(m.as_pointer().with( + |__m: &Map, Value)>| __m.find(&one.as_pointer()), + ))); + let middle: Value, Value)>> = + Rc::new(RefCell::new(m.as_pointer().with( + |__m: &Map, Value)>| __m.find(&two.as_pointer()), + ))); + let last: Value, Value)>> = + Rc::new(RefCell::new(m.as_pointer().with( + |__m: &Map, Value)>| { + __m.find(&three.as_pointer()) + }, + ))); + let retained: Value, Value)>> = + Rc::new(RefCell::new((*middle.borrow()).clone())); + let retained_const: Value, Value)>> = + Rc::new(RefCell::new(, + Value, + )>>::from( + ({ (*middle.borrow()).clone() }) + ))); + let mut first_value: Ptr = ((*(*first.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()); + let mut last_value: Ptr = ((*(*last.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()); + let end: Value, Value)>> = + Rc::new(RefCell::new(m.as_pointer().with( + |__m: &Map, Value)>| __m.end(), + ))); + let next: Value, Value)>> = + Rc::new(RefCell::new(Map::erase( + m.as_pointer(), + &({ (*middle.borrow()).clone() }), + ))); + assert!( + (({ + let __idx0 = (2) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (key_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (12) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (value_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (1) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + && (({ + let __idx0 = (3) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + ); + assert!( + (({ + let __idx0 = (11) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + && (({ + let __idx0 = (13) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + ); + assert!( + ((*m.borrow()).len() == 2_usize) + && (m + .as_pointer() + .with(|__m: &Map, Value)>| __m + .find(&two.as_pointer())) + == (*end.borrow())) + ); + assert!( + ((*next.borrow()) == (*last.borrow())) + && ((*first.borrow()) + == m.as_pointer() + .with(|__m: &Map, Value)>| __m.begin())) + ); + assert!( + ({ + ({ + ((*(*first.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()) + } == { (first_value).clone() }) + && ({ + ((*(*last.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()) + } == { (last_value).clone() }) + }) + ); + let walking: Value, Value)>> = + Rc::new(RefCell::new((*first.borrow()).clone())); + (*walking.borrow_mut()).iter_inc(); + assert!(({ (*walking.borrow()) == (*last.borrow()) })); + (*walking.borrow_mut()).iter_inc(); + assert!(({ (*walking.borrow()) == (*end.borrow()) })); + let const_first: Value, Value)>> = Rc::new( + RefCell::new( + , Value)>>::from( + ({ (*first.borrow()).clone() }), + ), + ), + ); + (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*const_first.borrow()).clone() })); + assert!( + (({ + let __idx0 = (1) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (key_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (11) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (value_destructor as i32)) + ); + assert!( + (((*m.borrow()).len() == 1_usize) && ((*next.borrow()) == (*last.borrow()))) + && (m + .as_pointer() + .with(|__m: &Map, Value)>| __m.begin()) + == (*last.borrow())) + ); + assert!( + ({ + ({ + ((*(*last.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()) + } == { (last_value).clone() }) + }) + ); + (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*last.borrow()).clone() })); + assert!( + (({ + let __idx0 = (3) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (key_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (13) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (value_destructor as i32)) + ); + assert!( + (((*next.borrow()) == (*end.borrow())) && ((*m.borrow()).len() == 0_usize)) + && (m + .as_pointer() + .with(|__m: &Map, Value)>| __m.begin()) + == (*end.borrow())) + ); + } + let mut i: i32 = 0; + 'loop_: while (i < 32) { + let mut expected: i32 = 0; + if (i >= 1) && (i <= 3) { + expected = (key_destructor as i32); + } + if (i >= 11) && (i <= 13) { + expected = (value_destructor as i32); + } + assert!( + (({ + let __idx0 = (i) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == expected) + ); + i.prefix_inc(); + } +} +pub fn check_erase_3(mut key_destructor: bool, mut value_destructor: bool) { + let one: Value = Rc::new(RefCell::new(Tracked::new_1({ 1 }))); + let _dtor_one = ScopedDestructor::new(&one, |__p| __p.destructor()); + let two: Value = Rc::new(RefCell::new(Tracked::new_1({ 2 }))); + let _dtor_two = ScopedDestructor::new(&two, |__p| __p.destructor()); + let three: Value = Rc::new(RefCell::new(Tracked::new_1({ 3 }))); + let _dtor_three = ScopedDestructor::new(&three, |__p| __p.destructor()); + let m: Value, Value)>> = Rc::new(RefCell::new(Map::new())); + Map::subscript(m.as_pointer(), one.as_pointer()).write(11); + Map::subscript(m.as_pointer(), two.as_pointer()).write(12); + Map::subscript(m.as_pointer(), three.as_pointer()).write(13); + 'loop_: for n in IterRange::new( + (destroyed_0.with(|v| v.as_pointer()) as Ptr), + (destroyed_0.with(|v| v.as_pointer()) as Ptr).offset((32_i64) as isize), + ) { + let n: Ptr = (n).clone(); + n.write(0); + } + { + let first: Value, Value)>> = + Rc::new(RefCell::new(m.as_pointer().with( + |__m: &Map, Value)>| __m.find(&one.as_pointer()), + ))); + let middle: Value, Value)>> = + Rc::new(RefCell::new(m.as_pointer().with( + |__m: &Map, Value)>| __m.find(&two.as_pointer()), + ))); + let last: Value, Value)>> = + Rc::new(RefCell::new(m.as_pointer().with( + |__m: &Map, Value)>| __m.find(&three.as_pointer()), + ))); + let retained: Value, Value)>> = + Rc::new(RefCell::new((*middle.borrow()).clone())); + let retained_const: Value, Value)>> = Rc::new( + RefCell::new(, Value)>>::from( + ({ (*middle.borrow()).clone() }), + )), + ); + let mut first_value: Ptr = ((*(*first.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()); + let mut last_value: Ptr = ((*(*last.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()); + let end: Value, Value)>> = + Rc::new(RefCell::new(m.as_pointer().with( + |__m: &Map, Value)>| __m.end(), + ))); + let next: Value, Value)>> = + Rc::new(RefCell::new(Map::erase( + m.as_pointer(), + &({ (*middle.borrow()).clone() }), + ))); + assert!( + (({ + let __idx0 = (2) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (key_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (12) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (value_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (1) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + && (({ + let __idx0 = (3) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + ); + assert!( + (({ + let __idx0 = (11) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + && (({ + let __idx0 = (13) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + ); + assert!( + ((*m.borrow()).len() == 2_usize) + && (m + .as_pointer() + .with(|__m: &Map, Value)>| __m + .find(&two.as_pointer())) + == (*end.borrow())) + ); + assert!( + ((*next.borrow()) == (*last.borrow())) + && ((*first.borrow()) + == m.as_pointer() + .with(|__m: &Map, Value)>| __m.begin())) + ); + assert!( + ({ + ({ + ((*(*first.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()) + } == { (first_value).clone() }) + && ({ + ((*(*last.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()) + } == { (last_value).clone() }) + }) + ); + let walking: Value, Value)>> = + Rc::new(RefCell::new((*first.borrow()).clone())); + (*walking.borrow_mut()).iter_inc(); + assert!(({ (*walking.borrow()) == (*last.borrow()) })); + (*walking.borrow_mut()).iter_inc(); + assert!(({ (*walking.borrow()) == (*end.borrow()) })); + let const_first: Value, Value)>> = Rc::new( + RefCell::new(, Value)>>::from( + ({ (*first.borrow()).clone() }), + )), + ); + (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*const_first.borrow()).clone() })); + assert!( + (({ + let __idx0 = (1) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (key_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (11) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (value_destructor as i32)) + ); + assert!( + (((*m.borrow()).len() == 1_usize) && ((*next.borrow()) == (*last.borrow()))) + && (m + .as_pointer() + .with(|__m: &Map, Value)>| __m.begin()) + == (*last.borrow())) + ); + assert!( + ({ + ({ + ((*(*last.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()) + } == { (last_value).clone() }) + }) + ); + (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*last.borrow()).clone() })); + assert!( + (({ + let __idx0 = (3) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (key_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (13) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (value_destructor as i32)) + ); + assert!( + (((*next.borrow()) == (*end.borrow())) && ((*m.borrow()).len() == 0_usize)) + && (m + .as_pointer() + .with(|__m: &Map, Value)>| __m.begin()) + == (*end.borrow())) + ); + } + let mut i: i32 = 0; + 'loop_: while (i < 32) { + let mut expected: i32 = 0; + if (i >= 1) && (i <= 3) { + expected = (key_destructor as i32); + } + if (i >= 11) && (i <= 13) { + expected = (value_destructor as i32); + } + assert!( + (({ + let __idx0 = (i) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == expected) + ); + i.prefix_inc(); + } +} +pub fn check_erase_4(mut key_destructor: bool, mut value_destructor: bool) { + let one: Value = Rc::new(RefCell::new(1)); + let two: Value = Rc::new(RefCell::new(2)); + let three: Value = Rc::new(RefCell::new(3)); + let m: Value, Value)>> = Rc::new(RefCell::new(Map::new())); + Map::subscript(m.as_pointer(), one.as_pointer()).write(Tracked::new_1({ 11 })); + Map::subscript(m.as_pointer(), two.as_pointer()).write(Tracked::new_1({ 12 })); + Map::subscript(m.as_pointer(), three.as_pointer()).write(Tracked::new_1({ 13 })); + 'loop_: for n in IterRange::new( + (destroyed_0.with(|v| v.as_pointer()) as Ptr), + (destroyed_0.with(|v| v.as_pointer()) as Ptr).offset((32_i64) as isize), + ) { + let n: Ptr = (n).clone(); + n.write(0); + } + { + let first: Value, Value)>> = + Rc::new(RefCell::new(m.as_pointer().with( + |__m: &Map, Value)>| __m.find(&one.as_pointer()), + ))); + let middle: Value, Value)>> = + Rc::new(RefCell::new(m.as_pointer().with( + |__m: &Map, Value)>| __m.find(&two.as_pointer()), + ))); + let last: Value, Value)>> = + Rc::new(RefCell::new(m.as_pointer().with( + |__m: &Map, Value)>| __m.find(&three.as_pointer()), + ))); + let retained: Value, Value)>> = + Rc::new(RefCell::new((*middle.borrow()).clone())); + let retained_const: Value, Value)>> = Rc::new( + RefCell::new(, Value)>>::from( + ({ (*middle.borrow()).clone() }), + )), + ); + let mut first_value: Ptr = ((*(*first.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()); + let mut last_value: Ptr = ((*(*last.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()); + let end: Value, Value)>> = + Rc::new(RefCell::new( + m.as_pointer() + .with(|__m: &Map, Value)>| __m.end()), + )); + let next: Value, Value)>> = + Rc::new(RefCell::new(Map::erase( + m.as_pointer(), + &({ (*middle.borrow()).clone() }), + ))); + assert!( + (({ + let __idx0 = (2) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (key_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (12) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (value_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (1) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + && (({ + let __idx0 = (3) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + ); + assert!( + (({ + let __idx0 = (11) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + && (({ + let __idx0 = (13) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + ); + assert!( + ((*m.borrow()).len() == 2_usize) + && (m.as_pointer().with( + |__m: &Map, Value)>| __m.find(&two.as_pointer()) + ) == (*end.borrow())) + ); + assert!( + ((*next.borrow()) == (*last.borrow())) + && ((*first.borrow()) + == m.as_pointer() + .with(|__m: &Map, Value)>| __m.begin())) + ); + assert!( + ({ + ({ + ((*(*first.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()) + } == { (first_value).clone() }) + && ({ + ((*(*last.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()) + } == { (last_value).clone() }) + }) + ); + let walking: Value, Value)>> = + Rc::new(RefCell::new((*first.borrow()).clone())); + (*walking.borrow_mut()).iter_inc(); + assert!(({ (*walking.borrow()) == (*last.borrow()) })); + (*walking.borrow_mut()).iter_inc(); + assert!(({ (*walking.borrow()) == (*end.borrow()) })); + let const_first: Value, Value)>> = Rc::new( + RefCell::new(, Value)>>::from( + ({ (*first.borrow()).clone() }), + )), + ); + (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*const_first.borrow()).clone() })); + assert!( + (({ + let __idx0 = (1) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (key_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (11) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (value_destructor as i32)) + ); + assert!( + (((*m.borrow()).len() == 1_usize) && ((*next.borrow()) == (*last.borrow()))) + && (m + .as_pointer() + .with(|__m: &Map, Value)>| __m.begin()) + == (*last.borrow())) + ); + assert!( + ({ + ({ + ((*(*last.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()) + } == { (last_value).clone() }) + }) + ); + (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*last.borrow()).clone() })); + assert!( + (({ + let __idx0 = (3) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (key_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (13) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (value_destructor as i32)) + ); + assert!( + (((*next.borrow()) == (*end.borrow())) && ((*m.borrow()).len() == 0_usize)) + && (m + .as_pointer() + .with(|__m: &Map, Value)>| __m.begin()) + == (*end.borrow())) + ); + } + let mut i: i32 = 0; + 'loop_: while (i < 32) { + let mut expected: i32 = 0; + if (i >= 1) && (i <= 3) { + expected = (key_destructor as i32); + } + if (i >= 11) && (i <= 13) { + expected = (value_destructor as i32); + } + assert!( + (({ + let __idx0 = (i) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == expected) + ); + i.prefix_inc(); + } +} +pub fn check_erase_5(mut key_destructor: bool, mut value_destructor: bool) { + let one: Value = Rc::new(RefCell::new(1)); + let two: Value = Rc::new(RefCell::new(2)); + let three: Value = Rc::new(RefCell::new(3)); + let m: Value, Value)>> = Rc::new(RefCell::new(Map::new())); + Map::subscript(m.as_pointer(), one.as_pointer()).write(11); + Map::subscript(m.as_pointer(), two.as_pointer()).write(12); + Map::subscript(m.as_pointer(), three.as_pointer()).write(13); + 'loop_: for n in IterRange::new( + (destroyed_0.with(|v| v.as_pointer()) as Ptr), + (destroyed_0.with(|v| v.as_pointer()) as Ptr).offset((32_i64) as isize), + ) { + let n: Ptr = (n).clone(); + n.write(0); + } + { + let first: Value, Value)>> = Rc::new(RefCell::new( + m.as_pointer() + .with(|__m: &Map, Value)>| __m.find(&one.as_pointer())), + )); + let middle: Value, Value)>> = Rc::new(RefCell::new( + m.as_pointer() + .with(|__m: &Map, Value)>| __m.find(&two.as_pointer())), + )); + let last: Value, Value)>> = Rc::new(RefCell::new( + m.as_pointer() + .with(|__m: &Map, Value)>| __m.find(&three.as_pointer())), + )); + let retained: Value, Value)>> = + Rc::new(RefCell::new((*middle.borrow()).clone())); + let retained_const: Value, Value)>> = Rc::new( + RefCell::new(, Value)>>::from( + ({ (*middle.borrow()).clone() }), + )), + ); + let mut first_value: Ptr = ((*(*first.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()); + let mut last_value: Ptr = ((*(*last.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()); + let end: Value, Value)>> = Rc::new(RefCell::new( + m.as_pointer() + .with(|__m: &Map, Value)>| __m.end()), + )); + let next: Value, Value)>> = Rc::new(RefCell::new( + Map::erase(m.as_pointer(), &({ (*middle.borrow()).clone() })), + )); + assert!( + (({ + let __idx0 = (2) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (key_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (12) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (value_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (1) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + && (({ + let __idx0 = (3) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + ); + assert!( + (({ + let __idx0 = (11) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + && (({ + let __idx0 = (13) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == 0) + ); + assert!( + ((*m.borrow()).len() == 2_usize) + && (m + .as_pointer() + .with(|__m: &Map, Value)>| __m.find(&two.as_pointer())) + == (*end.borrow())) + ); + assert!( + ((*next.borrow()) == (*last.borrow())) + && ((*first.borrow()) + == m.as_pointer() + .with(|__m: &Map, Value)>| __m.begin())) + ); + assert!( + ({ + ({ + ((*(*first.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()) + } == { (first_value).clone() }) + && ({ + ((*(*last.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()) + } == { (last_value).clone() }) + }) + ); + let walking: Value, Value)>> = + Rc::new(RefCell::new((*first.borrow()).clone())); + (*walking.borrow_mut()).iter_inc(); + assert!(({ (*walking.borrow()) == (*last.borrow()) })); + (*walking.borrow_mut()).iter_inc(); + assert!(({ (*walking.borrow()) == (*end.borrow()) })); + let const_first: Value, Value)>> = Rc::new( + RefCell::new(, Value)>>::from( + ({ (*first.borrow()).clone() }), + )), + ); + (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*const_first.borrow()).clone() })); + assert!( + (({ + let __idx0 = (1) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (key_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (11) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (value_destructor as i32)) + ); + assert!( + (((*m.borrow()).len() == 1_usize) && ((*next.borrow()) == (*last.borrow()))) + && (m + .as_pointer() + .with(|__m: &Map, Value)>| __m.begin()) + == (*last.borrow())) + ); + assert!( + ({ + ({ + ((*(*last.borrow()).iter_deref().upgrade().deref()) + .1 + .as_pointer()) + } == { (last_value).clone() }) + }) + ); + (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*last.borrow()).clone() })); + assert!( + (({ + let __idx0 = (3) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (key_destructor as i32)) + ); + assert!( + (({ + let __idx0 = (13) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == (value_destructor as i32)) + ); + assert!( + (((*next.borrow()) == (*end.borrow())) && ((*m.borrow()).len() == 0_usize)) + && (m + .as_pointer() + .with(|__m: &Map, Value)>| __m.begin()) + == (*end.borrow())) + ); + } + let mut i: i32 = 0; + 'loop_: while (i < 32) { + let mut expected: i32 = 0; + if (i >= 1) && (i <= 3) { + expected = (key_destructor as i32); + } + if (i >= 11) && (i <= 13) { + expected = (value_destructor as i32); + } + assert!( + (({ + let __idx0 = (i) as usize; + destroyed_0.with(|rc| rc.borrow()[__idx0]) + }) == expected) + ); + i.prefix_inc(); + } +} +pub fn main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + ({ check_erase_2(true, true) }); + ({ check_erase_3(true, false) }); + ({ check_erase_4(false, true) }); + ({ check_erase_5(false, false) }); + 0 +} +pub trait TrackedImpl { + fn destructor(&self); +} +impl TrackedImpl for Ptr { + fn destructor(&self) { + (*destroyed_0.with(Value::clone).borrow_mut())[((*self).with(|__s| __s.id)) as usize] + .prefix_inc(); + } +} +pub fn __cpp2rust_init_globals() { + let _ = destroyed_0.with(|_| ()); +} diff --git a/tests/unit/out/unsafe/map_erase_destructor.rs b/tests/unit/out/unsafe/map_erase_destructor.rs new file mode 100644 index 000000000..50fd18105 --- /dev/null +++ b/tests/unit/out/unsafe/map_erase_destructor.rs @@ -0,0 +1,475 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub static mut destroyed_0: std::cell::LazyCell<[i32; 32]> = + std::cell::LazyCell::new(|| unsafe { [0_i32; 32] }); +#[repr(C)] +#[derive(Clone, VaArg, FnPtrArg, CppFromUnsafe, DestructorUnsafe)] +#[cpp_from(i32, new_1, value)] +pub struct Tracked { + pub id: i32, +} +impl Tracked { + pub unsafe fn new_2() -> Self { + let mut this = Self { id: 0 }; + this + } + pub unsafe fn new_1(mut id: i32) -> Self { + let mut this = Self { id: id }; + this + } + pub unsafe fn destructor(&mut self) { + (*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(self.id) as usize] + .prefix_inc(); + } +} +impl std::cmp::Ord for Tracked { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + unsafe { + if operator_lt_1(self as *const Tracked, other as *const Tracked) { + std::cmp::Ordering::Less + } else if operator_lt_1(other as *const Tracked, self as *const Tracked) { + std::cmp::Ordering::Greater + } else { + std::cmp::Ordering::Equal + } + } + } +} +impl std::cmp::PartialOrd for Tracked { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl std::cmp::PartialEq for Tracked { + fn eq(&self, other: &Self) -> bool { + unsafe { + !(operator_lt_1(self as *const Tracked, other as *const Tracked)) + && !(operator_lt_1(other as *const Tracked, self as *const Tracked)) + } + } +} +impl std::cmp::Eq for Tracked {} +impl Default for Tracked { + fn default() -> Self { + unsafe { Tracked::new_2() } + } +} +pub unsafe fn operator_lt_1(a: *const Tracked, b: *const Tracked) -> bool { + return (((*a).id) < ((*b).id)); +} +pub unsafe fn check_erase_2(mut key_destructor: bool, mut value_destructor: bool) { + let mut one: Tracked = Tracked::new_1({ 1 }); + let _dtor_one = ScopedDestructorUnsafe::new(&raw mut one, Tracked::destructor); + let mut two: Tracked = Tracked::new_1({ 2 }); + let _dtor_two = ScopedDestructorUnsafe::new(&raw mut two, Tracked::destructor); + let mut three: Tracked = Tracked::new_1({ 3 }); + let _dtor_three = ScopedDestructorUnsafe::new(&raw mut three, Tracked::destructor); + let mut m: UnsafeMap = UnsafeMap::new(); + (*m.subscript(&one)) = Tracked::new_1({ 11 }); + (*m.subscript(&two)) = Tracked::new_1({ 12 }); + (*m.subscript(&three)) = Tracked::new_1({ 13 }); + 'loop_: for n in IterRange::new( + (*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0)).as_mut_ptr(), + (*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0)) + .as_mut_ptr() + .offset((32_i64) as isize), + ) { + let n: *mut i32 = &mut (*n); + (*n) = 0; + } + { + let mut first: UnsafeBidirectionalIterator<(Tracked, Tracked)> = m.find(&one); + let mut middle: UnsafeBidirectionalIterator<(Tracked, Tracked)> = m.find(&two); + let mut last: UnsafeBidirectionalIterator<(Tracked, Tracked)> = m.find(&three); + let mut retained: UnsafeBidirectionalIterator<(Tracked, Tracked)> = (middle).clone(); + let mut retained_const: UnsafeBidirectionalIterator<(Tracked, Tracked)> = + >::from((middle).clone()); + let mut first_value: *mut Tracked = (&mut (*first.iter_deref()).1 as *mut Tracked); + let mut last_value: *mut Tracked = (&mut (*last.iter_deref()).1 as *mut Tracked); + let mut end: UnsafeBidirectionalIterator<(Tracked, Tracked)> = m.end(); + let mut next: UnsafeBidirectionalIterator<(Tracked, Tracked)> = m.erase(&(middle).clone()); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(2) as usize]) + == (key_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(12) as usize]) + == (value_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(1) as usize]) == (0)) + && (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(3) as usize]) + == (0)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(11) as usize]) == (0)) + && (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(13) as usize]) + == (0)) + ); + assert!(((m.len()) == (2_usize)) && (m.find(&two) == end)); + assert!((next == last) && (first == m.begin())); + assert!( + ((&mut (*first.iter_deref()).1 as *mut Tracked) == (first_value)) + && ((&mut (*last.iter_deref()).1 as *mut Tracked) == (last_value)) + ); + let mut walking: UnsafeBidirectionalIterator<(Tracked, Tracked)> = (first).clone(); + walking.iter_inc(); + assert!(walking == last); + walking.iter_inc(); + assert!(walking == end); + let mut const_first: UnsafeBidirectionalIterator<(Tracked, Tracked)> = + >::from((first).clone()); + next = m.erase(&(const_first).clone()); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(1) as usize]) + == (key_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(11) as usize]) + == (value_destructor as i32)) + ); + assert!((((m.len()) == (1_usize)) && (next == last)) && (m.begin() == last)); + assert!(((&mut (*last.iter_deref()).1 as *mut Tracked) == (last_value))); + next = m.erase(&(last).clone()); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(3) as usize]) + == (key_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(13) as usize]) + == (value_destructor as i32)) + ); + assert!(((next == end) && ((m.len()) == (0_usize))) && (m.begin() == end)); + } + let mut i: i32 = 0; + 'loop_: while ((i) < (32)) { + let mut expected: i32 = 0; + if ((i) >= (1)) && ((i) <= (3)) { + expected = (key_destructor as i32); + } + if ((i) >= (11)) && ((i) <= (13)) { + expected = (value_destructor as i32); + } + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(i) as usize]) + == (expected)) + ); + i.prefix_inc(); + } +} +pub unsafe fn check_erase_3(mut key_destructor: bool, mut value_destructor: bool) { + let mut one: Tracked = Tracked::new_1({ 1 }); + let _dtor_one = ScopedDestructorUnsafe::new(&raw mut one, Tracked::destructor); + let mut two: Tracked = Tracked::new_1({ 2 }); + let _dtor_two = ScopedDestructorUnsafe::new(&raw mut two, Tracked::destructor); + let mut three: Tracked = Tracked::new_1({ 3 }); + let _dtor_three = ScopedDestructorUnsafe::new(&raw mut three, Tracked::destructor); + let mut m: UnsafeMap = UnsafeMap::new(); + (*m.subscript(&one)) = 11; + (*m.subscript(&two)) = 12; + (*m.subscript(&three)) = 13; + 'loop_: for n in IterRange::new( + (*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0)).as_mut_ptr(), + (*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0)) + .as_mut_ptr() + .offset((32_i64) as isize), + ) { + let n: *mut i32 = &mut (*n); + (*n) = 0; + } + { + let mut first: UnsafeBidirectionalIterator<(Tracked, i32)> = m.find(&one); + let mut middle: UnsafeBidirectionalIterator<(Tracked, i32)> = m.find(&two); + let mut last: UnsafeBidirectionalIterator<(Tracked, i32)> = m.find(&three); + let mut retained: UnsafeBidirectionalIterator<(Tracked, i32)> = (middle).clone(); + let mut retained_const: UnsafeBidirectionalIterator<(Tracked, i32)> = + >::from((middle).clone()); + let mut first_value: *mut i32 = (&mut (*first.iter_deref()).1 as *mut i32); + let mut last_value: *mut i32 = (&mut (*last.iter_deref()).1 as *mut i32); + let mut end: UnsafeBidirectionalIterator<(Tracked, i32)> = m.end(); + let mut next: UnsafeBidirectionalIterator<(Tracked, i32)> = m.erase(&(middle).clone()); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(2) as usize]) + == (key_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(12) as usize]) + == (value_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(1) as usize]) == (0)) + && (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(3) as usize]) + == (0)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(11) as usize]) == (0)) + && (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(13) as usize]) + == (0)) + ); + assert!(((m.len()) == (2_usize)) && (m.find(&two) == end)); + assert!((next == last) && (first == m.begin())); + assert!( + ((&mut (*first.iter_deref()).1 as *mut i32) == (first_value)) + && ((&mut (*last.iter_deref()).1 as *mut i32) == (last_value)) + ); + let mut walking: UnsafeBidirectionalIterator<(Tracked, i32)> = (first).clone(); + walking.iter_inc(); + assert!(walking == last); + walking.iter_inc(); + assert!(walking == end); + let mut const_first: UnsafeBidirectionalIterator<(Tracked, i32)> = + >::from((first).clone()); + next = m.erase(&(const_first).clone()); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(1) as usize]) + == (key_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(11) as usize]) + == (value_destructor as i32)) + ); + assert!((((m.len()) == (1_usize)) && (next == last)) && (m.begin() == last)); + assert!(((&mut (*last.iter_deref()).1 as *mut i32) == (last_value))); + next = m.erase(&(last).clone()); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(3) as usize]) + == (key_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(13) as usize]) + == (value_destructor as i32)) + ); + assert!(((next == end) && ((m.len()) == (0_usize))) && (m.begin() == end)); + } + let mut i: i32 = 0; + 'loop_: while ((i) < (32)) { + let mut expected: i32 = 0; + if ((i) >= (1)) && ((i) <= (3)) { + expected = (key_destructor as i32); + } + if ((i) >= (11)) && ((i) <= (13)) { + expected = (value_destructor as i32); + } + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(i) as usize]) + == (expected)) + ); + i.prefix_inc(); + } +} +pub unsafe fn check_erase_4(mut key_destructor: bool, mut value_destructor: bool) { + let mut one: i32 = 1; + let mut two: i32 = 2; + let mut three: i32 = 3; + let mut m: UnsafeMap = UnsafeMap::new(); + (*m.subscript(&one)) = Tracked::new_1({ 11 }); + (*m.subscript(&two)) = Tracked::new_1({ 12 }); + (*m.subscript(&three)) = Tracked::new_1({ 13 }); + 'loop_: for n in IterRange::new( + (*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0)).as_mut_ptr(), + (*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0)) + .as_mut_ptr() + .offset((32_i64) as isize), + ) { + let n: *mut i32 = &mut (*n); + (*n) = 0; + } + { + let mut first: UnsafeBidirectionalIterator<(i32, Tracked)> = m.find(&one); + let mut middle: UnsafeBidirectionalIterator<(i32, Tracked)> = m.find(&two); + let mut last: UnsafeBidirectionalIterator<(i32, Tracked)> = m.find(&three); + let mut retained: UnsafeBidirectionalIterator<(i32, Tracked)> = (middle).clone(); + let mut retained_const: UnsafeBidirectionalIterator<(i32, Tracked)> = + >::from((middle).clone()); + let mut first_value: *mut Tracked = (&mut (*first.iter_deref()).1 as *mut Tracked); + let mut last_value: *mut Tracked = (&mut (*last.iter_deref()).1 as *mut Tracked); + let mut end: UnsafeBidirectionalIterator<(i32, Tracked)> = m.end(); + let mut next: UnsafeBidirectionalIterator<(i32, Tracked)> = m.erase(&(middle).clone()); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(2) as usize]) + == (key_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(12) as usize]) + == (value_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(1) as usize]) == (0)) + && (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(3) as usize]) + == (0)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(11) as usize]) == (0)) + && (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(13) as usize]) + == (0)) + ); + assert!(((m.len()) == (2_usize)) && (m.find(&two) == end)); + assert!((next == last) && (first == m.begin())); + assert!( + ((&mut (*first.iter_deref()).1 as *mut Tracked) == (first_value)) + && ((&mut (*last.iter_deref()).1 as *mut Tracked) == (last_value)) + ); + let mut walking: UnsafeBidirectionalIterator<(i32, Tracked)> = (first).clone(); + walking.iter_inc(); + assert!(walking == last); + walking.iter_inc(); + assert!(walking == end); + let mut const_first: UnsafeBidirectionalIterator<(i32, Tracked)> = + >::from((first).clone()); + next = m.erase(&(const_first).clone()); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(1) as usize]) + == (key_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(11) as usize]) + == (value_destructor as i32)) + ); + assert!((((m.len()) == (1_usize)) && (next == last)) && (m.begin() == last)); + assert!(((&mut (*last.iter_deref()).1 as *mut Tracked) == (last_value))); + next = m.erase(&(last).clone()); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(3) as usize]) + == (key_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(13) as usize]) + == (value_destructor as i32)) + ); + assert!(((next == end) && ((m.len()) == (0_usize))) && (m.begin() == end)); + } + let mut i: i32 = 0; + 'loop_: while ((i) < (32)) { + let mut expected: i32 = 0; + if ((i) >= (1)) && ((i) <= (3)) { + expected = (key_destructor as i32); + } + if ((i) >= (11)) && ((i) <= (13)) { + expected = (value_destructor as i32); + } + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(i) as usize]) + == (expected)) + ); + i.prefix_inc(); + } +} +pub unsafe fn check_erase_5(mut key_destructor: bool, mut value_destructor: bool) { + let mut one: i32 = 1; + let mut two: i32 = 2; + let mut three: i32 = 3; + let mut m: UnsafeMap = UnsafeMap::new(); + (*m.subscript(&one)) = 11; + (*m.subscript(&two)) = 12; + (*m.subscript(&three)) = 13; + 'loop_: for n in IterRange::new( + (*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0)).as_mut_ptr(), + (*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0)) + .as_mut_ptr() + .offset((32_i64) as isize), + ) { + let n: *mut i32 = &mut (*n); + (*n) = 0; + } + { + let mut first: UnsafeBidirectionalIterator<(i32, i32)> = m.find(&one); + let mut middle: UnsafeBidirectionalIterator<(i32, i32)> = m.find(&two); + let mut last: UnsafeBidirectionalIterator<(i32, i32)> = m.find(&three); + let mut retained: UnsafeBidirectionalIterator<(i32, i32)> = (middle).clone(); + let mut retained_const: UnsafeBidirectionalIterator<(i32, i32)> = + >::from((middle).clone()); + let mut first_value: *mut i32 = (&mut (*first.iter_deref()).1 as *mut i32); + let mut last_value: *mut i32 = (&mut (*last.iter_deref()).1 as *mut i32); + let mut end: UnsafeBidirectionalIterator<(i32, i32)> = m.end(); + let mut next: UnsafeBidirectionalIterator<(i32, i32)> = m.erase(&(middle).clone()); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(2) as usize]) + == (key_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(12) as usize]) + == (value_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(1) as usize]) == (0)) + && (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(3) as usize]) + == (0)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(11) as usize]) == (0)) + && (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(13) as usize]) + == (0)) + ); + assert!(((m.len()) == (2_usize)) && (m.find(&two) == end)); + assert!((next == last) && (first == m.begin())); + assert!( + ((&mut (*first.iter_deref()).1 as *mut i32) == (first_value)) + && ((&mut (*last.iter_deref()).1 as *mut i32) == (last_value)) + ); + let mut walking: UnsafeBidirectionalIterator<(i32, i32)> = (first).clone(); + walking.iter_inc(); + assert!(walking == last); + walking.iter_inc(); + assert!(walking == end); + let mut const_first: UnsafeBidirectionalIterator<(i32, i32)> = + >::from((first).clone()); + next = m.erase(&(const_first).clone()); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(1) as usize]) + == (key_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(11) as usize]) + == (value_destructor as i32)) + ); + assert!((((m.len()) == (1_usize)) && (next == last)) && (m.begin() == last)); + assert!(((&mut (*last.iter_deref()).1 as *mut i32) == (last_value))); + next = m.erase(&(last).clone()); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(3) as usize]) + == (key_destructor as i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(13) as usize]) + == (value_destructor as i32)) + ); + assert!(((next == end) && ((m.len()) == (0_usize))) && (m.begin() == end)); + } + let mut i: i32 = 0; + 'loop_: while ((i) < (32)) { + let mut expected: i32 = 0; + if ((i) >= (1)) && ((i) <= (3)) { + expected = (key_destructor as i32); + } + if ((i) >= (11)) && ((i) <= (13)) { + expected = (value_destructor as i32); + } + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut destroyed_0))[(i) as usize]) + == (expected)) + ); + i.prefix_inc(); + } +} +pub fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + (unsafe { check_erase_2(true, true) }); + (unsafe { check_erase_3(true, false) }); + (unsafe { check_erase_4(false, true) }); + (unsafe { check_erase_5(false, false) }); + 0 +} +pub unsafe fn __cpp2rust_init_globals() { + std::cell::LazyCell::force(&*&raw const destroyed_0); +} From c8a7b42442bab680362d4e1b88ee89228ac6eb4e Mon Sep 17 00:00:00 2001 From: Perfloop Agent Date: Tue, 3 Nov 2026 01:12:54 +0000 Subject: [PATCH 2/2] Run map element destructors during refcount iterator erase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Perfloop Agent Signed-off-by: Tomás Senart --- docs/src/rules/writing-rules.md | 16 ++- rules/map/shim.rs | 23 ++-- rules/map/tgt_refcount.rs | 12 +- tests/unit/out/refcount/map.rs | 33 +++++- .../unit/out/refcount/map_erase_destructor.rs | 108 ++++++++++++++++-- tests/unit/out/refcount/map_key_copy_ctor.rs | 5 + 6 files changed, 167 insertions(+), 30 deletions(-) diff --git a/docs/src/rules/writing-rules.md b/docs/src/rules/writing-rules.md index fb0f7e25b..9485fd5e7 100644 --- a/docs/src/rules/writing-rules.md +++ b/docs/src/rules/writing-rules.md @@ -102,9 +102,19 @@ std::string f9(std::size_t n, char ch) { return std::string(n, ch); } Overloads that differ in value category are distinct rules too: `rules/vector` has separate rules for `push_back(const T1 &)` and `push_back(T1 &&)`. -No destructor rules exist so far: the STL and libc APIs covered by the current -rules have not needed any, since their types map to Rust types whose `Drop` -implementations already do the right thing. +Destruction of a translated record uses the explicit `Destructor` trait, not +Rust's `Drop`. Container rules must therefore distinguish releasing Rust storage +from calling a contained record's C++ destructor. For example, the refcount map +iterator-erase rules (`f3` and `f27` in +[`rules/map/tgt_refcount.rs`](https://github.com/Cpp2Rust/cpp2rust/blob/master/rules/map/tgt_refcount.rs)) +pass a finalizer to `Map::erase`. The runtime unlinks the entry and removes its +key from the index, then calls the finalizer with pointers to the stored key and +mapped value, without holding a borrow of the map. The rules reuse the +`__swap::Destroy` dispatch to call each type's destructor if it has one, and do +nothing for trivial types. This dispatch is in the expanded rule, where the +concrete types are known; a `Destructor` bound on the generic runtime method +would exclude trivial types. Iterator copies may retain the Rust storage, but do +not delay or repeat these explicit destructor calls. ## Operators diff --git a/rules/map/shim.rs b/rules/map/shim.rs index bc3505e3f..4d9480659 100644 --- a/rules/map/shim.rs +++ b/rules/map/shim.rs @@ -255,18 +255,23 @@ impl Map> { pub fn erase( map: Ptr, it: &BidirectionalIterator>, + destroy: impl FnOnce(Ptr, Ptr), ) -> BidirectionalIterator> { let BidirectionalIterator::Map(node) = it; - let key = node - .borrow() - .value - .as_ref() - .expect("ub: erase of end iterator") - .borrow() - .0 - .as_pointer(); + let (key, value) = { + let node = node.borrow(); + let entry = node + .value + .as_ref() + .expect("ub: erase of end iterator") + .borrow(); + (entry.0.as_pointer(), entry.1.as_pointer()) + }; let next = Self::unlink(node); - map.with_mut(|map| map.nodes.remove(&MapKey(key))); + map.with_mut(|map| map.nodes.remove(&MapKey(key.clone()))); + // Run C++ destructors after removing the key from the index and ending + // the map borrow. Iterator copies may still keep the node alive. + destroy(key, value); BidirectionalIterator::Map(next) } diff --git a/rules/map/tgt_refcount.rs b/rules/map/tgt_refcount.rs index 8cc942748..c9ea4ec44 100644 --- a/rules/map/tgt_refcount.rs +++ b/rules/map/tgt_refcount.rs @@ -24,7 +24,11 @@ fn f3( a0: Ptr, Value)>>, a1: BidirectionalIterator<(Value, Value)>, ) -> BidirectionalIterator<(Value, Value)> { - Map::erase(a0, &a1) + Map::erase(a0, &a1, |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }) } fn f5() -> Map, Value)> { @@ -87,7 +91,11 @@ fn f27( a0: Ptr, Value)>>, a1: BidirectionalIterator<(Value, Value)>, ) -> BidirectionalIterator<(Value, Value)> { - Map::erase(a0, &a1) + Map::erase(a0, &a1, |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }) } fn f28(a0: Ptr, Value)>>, a1: Ptr) -> usize { diff --git a/tests/unit/out/refcount/map.rs b/tests/unit/out/refcount/map.rs index fd2480e79..38e943a94 100644 --- a/tests/unit/out/refcount/map.rs +++ b/tests/unit/out/refcount/map.rs @@ -315,7 +315,15 @@ fn main_0() -> i32 { .as_pointer() .with(|__m: &Map, Value)>| __m.end()) ); - Map::erase((r).clone(), &({ (*it4.borrow()).clone() })); + Map::erase( + (r).clone(), + &({ (*it4.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, + ); assert!(({ ((*r.upgrade().deref()).len() == 3_usize) })); assert!( ({ @@ -370,9 +378,16 @@ fn main_0() -> i32 { }), ), )); - let next: Value, Value)>> = Rc::new(RefCell::new( - Map::erase(m.as_pointer(), &({ (*cit.borrow()).clone() })), - )); + let next: Value, Value)>> = + Rc::new(RefCell::new(Map::erase( + m.as_pointer(), + &({ (*cit.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, + ))); assert!( (*next.borrow()) == ({ @@ -383,7 +398,15 @@ fn main_0() -> i32 { }) }) ); - (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*next.borrow()).clone() })); + (*next.borrow_mut()) = Map::erase( + m.as_pointer(), + &({ (*next.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, + ); assert!( (*next.borrow()) == m.as_pointer() diff --git a/tests/unit/out/refcount/map_erase_destructor.rs b/tests/unit/out/refcount/map_erase_destructor.rs index 2f09c8bed..4f753a614 100644 --- a/tests/unit/out/refcount/map_erase_destructor.rs +++ b/tests/unit/out/refcount/map_erase_destructor.rs @@ -101,6 +101,11 @@ pub fn check_erase_2(mut key_destructor: bool, mut value_destructor: bool) { Rc::new(RefCell::new(Map::erase( m.as_pointer(), &({ (*middle.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, ))); assert!( (({ @@ -175,7 +180,15 @@ pub fn check_erase_2(mut key_destructor: bool, mut value_destructor: bool) { ), ), ); - (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*const_first.borrow()).clone() })); + (*next.borrow_mut()) = Map::erase( + m.as_pointer(), + &({ (*const_first.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, + ); assert!( (({ let __idx0 = (1) as usize; @@ -204,7 +217,15 @@ pub fn check_erase_2(mut key_destructor: bool, mut value_destructor: bool) { } == { (last_value).clone() }) }) ); - (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*last.borrow()).clone() })); + (*next.borrow_mut()) = Map::erase( + m.as_pointer(), + &({ (*last.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, + ); assert!( (({ let __idx0 = (3) as usize; @@ -295,6 +316,11 @@ pub fn check_erase_3(mut key_destructor: bool, mut value_destructor: bool) { Rc::new(RefCell::new(Map::erase( m.as_pointer(), &({ (*middle.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, ))); assert!( (({ @@ -367,7 +393,15 @@ pub fn check_erase_3(mut key_destructor: bool, mut value_destructor: bool) { ({ (*first.borrow()).clone() }), )), ); - (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*const_first.borrow()).clone() })); + (*next.borrow_mut()) = Map::erase( + m.as_pointer(), + &({ (*const_first.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, + ); assert!( (({ let __idx0 = (1) as usize; @@ -396,7 +430,15 @@ pub fn check_erase_3(mut key_destructor: bool, mut value_destructor: bool) { } == { (last_value).clone() }) }) ); - (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*last.borrow()).clone() })); + (*next.borrow_mut()) = Map::erase( + m.as_pointer(), + &({ (*last.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, + ); assert!( (({ let __idx0 = (3) as usize; @@ -485,6 +527,11 @@ pub fn check_erase_4(mut key_destructor: bool, mut value_destructor: bool) { Rc::new(RefCell::new(Map::erase( m.as_pointer(), &({ (*middle.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, ))); assert!( (({ @@ -555,7 +602,15 @@ pub fn check_erase_4(mut key_destructor: bool, mut value_destructor: bool) { ({ (*first.borrow()).clone() }), )), ); - (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*const_first.borrow()).clone() })); + (*next.borrow_mut()) = Map::erase( + m.as_pointer(), + &({ (*const_first.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, + ); assert!( (({ let __idx0 = (1) as usize; @@ -584,7 +639,15 @@ pub fn check_erase_4(mut key_destructor: bool, mut value_destructor: bool) { } == { (last_value).clone() }) }) ); - (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*last.borrow()).clone() })); + (*next.borrow_mut()) = Map::erase( + m.as_pointer(), + &({ (*last.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, + ); assert!( (({ let __idx0 = (3) as usize; @@ -668,9 +731,16 @@ pub fn check_erase_5(mut key_destructor: bool, mut value_destructor: bool) { m.as_pointer() .with(|__m: &Map, Value)>| __m.end()), )); - let next: Value, Value)>> = Rc::new(RefCell::new( - Map::erase(m.as_pointer(), &({ (*middle.borrow()).clone() })), - )); + let next: Value, Value)>> = + Rc::new(RefCell::new(Map::erase( + m.as_pointer(), + &({ (*middle.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, + ))); assert!( (({ let __idx0 = (2) as usize; @@ -741,7 +811,15 @@ pub fn check_erase_5(mut key_destructor: bool, mut value_destructor: bool) { ({ (*first.borrow()).clone() }), )), ); - (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*const_first.borrow()).clone() })); + (*next.borrow_mut()) = Map::erase( + m.as_pointer(), + &({ (*const_first.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, + ); assert!( (({ let __idx0 = (1) as usize; @@ -770,7 +848,15 @@ pub fn check_erase_5(mut key_destructor: bool, mut value_destructor: bool) { } == { (last_value).clone() }) }) ); - (*next.borrow_mut()) = Map::erase(m.as_pointer(), &({ (*last.borrow()).clone() })); + (*next.borrow_mut()) = Map::erase( + m.as_pointer(), + &({ (*last.borrow()).clone() }), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, + ); assert!( (({ let __idx0 = (3) as usize; diff --git a/tests/unit/out/refcount/map_key_copy_ctor.rs b/tests/unit/out/refcount/map_key_copy_ctor.rs index 17de5bfa0..ebaaf062c 100644 --- a/tests/unit/out/refcount/map_key_copy_ctor.rs +++ b/tests/unit/out/refcount/map_key_copy_ctor.rs @@ -200,6 +200,11 @@ fn main_0() -> i32 { m.as_pointer(), &m.as_pointer() .with(|__m: &Map, Value)>| __m.find(&a.as_pointer())), + |__key, __value| { + use libcc2rs::__swap::{Destroy, DestroyNone, DestroyWithDtor}; + (&&Destroy(&__value)).destroy(); + (&&Destroy(&__key)).destroy(); + }, ); { let rhs_0 = ((ok as i32)