From d3da1416a96f3f87f68fd277bba4acbf5a13b3ac Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 4 Nov 2026 14:54:17 +0000 Subject: [PATCH 01/12] Use [T; N] for std::array translation --- rules/array/tgt_refcount.rs | 6 +++--- rules/array/tgt_unsafe.rs | 36 ++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/rules/array/tgt_refcount.rs b/rules/array/tgt_refcount.rs index ca9978f414..e80dc9311c 100644 --- a/rules/array/tgt_refcount.rs +++ b/rules/array/tgt_refcount.rs @@ -21,11 +21,11 @@ fn f3(a0: Ptr) -> Ptr { a0 } -fn f5(a0: Ptr>, a1: &mut Vec) { - a0.write(std::mem::take(&mut *a1)) +fn f5(a0: Ptr<[T1; T2]>, a1: &mut [T1; T2]) { + a0.write(std::array::from_fn(|__i| std::mem::take(&mut a1[__i]))) } -fn f7(a0: Ptr>, a1: Vec) { +fn f7(a0: Ptr<[T1; T2]>, a1: [T1; T2]) { a0.write(a1) } diff --git a/rules/array/tgt_unsafe.rs b/rules/array/tgt_unsafe.rs index 6ab69c1e82..9e4bc2781e 100644 --- a/rules/array/tgt_unsafe.rs +++ b/rules/array/tgt_unsafe.rs @@ -3,8 +3,8 @@ use libcc2rs::*; -fn t1() -> Vec { - std::array::from_fn::<_, T2, _>(|_| ::default()).to_vec() +fn t1() -> [T1; T2] { + std::array::from_fn::<_, T2, _>(|_| ::default()) } fn t2() -> usize { @@ -27,50 +27,50 @@ fn t6() -> T2 { unimplemented!() } -unsafe fn f1(a0: Vec) -> *const T1 { +unsafe fn f1(a0: [T1; T2]) -> *const T1 { a0.as_ptr() } -unsafe fn f2(a0: Vec) -> usize { - a0.len() +unsafe fn f2(a0: [T1; T2]) -> usize { + T2 } -unsafe fn f3(a0: &mut Vec) -> *mut T1 { +unsafe fn f3(a0: &mut [T1; T2]) -> *mut T1 { a0.as_mut_ptr() } -unsafe fn f4(a0: &mut Vec) -> Vec { - std::mem::take(&mut *a0) +unsafe fn f4(a0: &mut [T1; T2]) -> [T1; T2] { + std::array::from_fn(|__i| std::mem::take(&mut a0[__i])) } -unsafe fn f5(a0: &mut Vec, a1: &mut Vec) { - *a0 = std::mem::take(&mut *a1) +unsafe fn f5(a0: &mut [T1; T2], a1: &mut [T1; T2]) { + *a0 = std::array::from_fn(|__i| std::mem::take(&mut a1[__i])) } -unsafe fn f6(a0: Vec) -> Vec { +unsafe fn f6(a0: [T1; T2]) -> [T1; T2] { a0.clone() } -unsafe fn f7(a0: &mut Vec, a1: Vec) { +unsafe fn f7(a0: &mut [T1; T2], a1: [T1; T2]) { *a0 = a1.clone() } -unsafe fn f8(a0: [T1; T2]) -> Vec { - Vec::from(a0) +unsafe fn f8(a0: [T1; T2]) -> [T1; T2] { + a0 } -unsafe fn f9(a0: &mut Vec) -> *mut T2 { +unsafe fn f9(a0: &mut [T2; T3]) -> *mut T2 { a0.as_mut_ptr().offset(T1 as isize) } -unsafe fn f10(a0: &Vec) -> *const T2 { +unsafe fn f10(a0: &[T2; T3]) -> *const T2 { a0.as_ptr().offset(T1 as isize) } -unsafe fn f11(a0: &mut Vec) -> *mut T2 { +unsafe fn f11(a0: &mut [T2; T3]) -> *mut T2 { a0.as_mut_ptr().offset(T1 as isize) } -unsafe fn f12(a0: &Vec) -> *const T2 { +unsafe fn f12(a0: &[T2; T3]) -> *const T2 { a0.as_ptr().offset(T1 as isize) } From 8349cbd374b7f9fbb28f19878261e3c23f10fe06 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 4 Nov 2026 15:14:33 +0000 Subject: [PATCH 02/12] Update tests --- tests/unit/out/refcount/destructor_params.rs | 2 +- .../unit/out/refcount/destructor_temporaries.rs | 7 +++++-- tests/unit/out/refcount/empty_array_init.rs | 4 +--- tests/unit/out/refcount/init_list.rs | 2 +- .../unit/out/refcount/inline_container_fields.rs | 16 +++++++++------- .../out/refcount/libc_struct_without_default.rs | 12 +++++++----- .../out/refcount/structured_binding_array.rs | 4 ++-- .../unit/out/refcount/vector_subscript_direct.rs | 2 +- tests/unit/out/unsafe/empty_array_init.rs | 2 +- tests/unit/out/unsafe/init_list.rs | 2 +- tests/unit/out/unsafe/inline_container_fields.rs | 8 ++++---- .../out/unsafe/libc_struct_without_default.rs | 8 ++++---- .../unit/out/unsafe/structured_binding_array.rs | 4 ++-- tests/unit/out/unsafe/vector_subscript_direct.rs | 2 +- 14 files changed, 40 insertions(+), 35 deletions(-) diff --git a/tests/unit/out/refcount/destructor_params.rs b/tests/unit/out/refcount/destructor_params.rs index 9e41c147e9..38335c532a 100644 --- a/tests/unit/out/refcount/destructor_params.rs +++ b/tests/unit/out/refcount/destructor_params.rs @@ -49,7 +49,7 @@ fn main_0() -> i32 { { let s: Value = Rc::new(RefCell::new(S { v: 1 })); let _dtor_s = ScopedDestructor::new(&s, |__p| __p.destructor()); - assert!((({ by_value_2((*s.borrow()).clone(),) }) == 1)); + assert!(({ (({ by_value_2((*s.borrow()).clone(),) }) == 1) })); assert!( (destroyed_0.with(|rc| *rc.borrow()) == 1) && (last_1.with(|rc| *rc.borrow()) == 1) ); diff --git a/tests/unit/out/refcount/destructor_temporaries.rs b/tests/unit/out/refcount/destructor_temporaries.rs index 8de7ada023..12855a5587 100644 --- a/tests/unit/out/refcount/destructor_temporaries.rs +++ b/tests/unit/out/refcount/destructor_temporaries.rs @@ -119,8 +119,11 @@ fn main_0() -> i32 { let _dtor_s = ScopedDestructor::new(&s, |__p| __p.destructor()); (*s.borrow_mut()) = ({ make_2(13) }); assert!( - ((destroyed_0.with(|rc| *rc.borrow()) == 1) && (last_1.with(|rc| *rc.borrow()) == 13)) - && ({ (*s.borrow()).v } == 13) + ({ + ((destroyed_0.with(|rc| *rc.borrow()) == 1) + && (last_1.with(|rc| *rc.borrow()) == 13)) + && ({ (*s.borrow()).v } == 13) + }) ); } assert!((destroyed_0.with(|rc| *rc.borrow()) == 2) && (last_1.with(|rc| *rc.borrow()) == 13)); diff --git a/tests/unit/out/refcount/empty_array_init.rs b/tests/unit/out/refcount/empty_array_init.rs index d6a069ab30..9988439edc 100644 --- a/tests/unit/out/refcount/empty_array_init.rs +++ b/tests/unit/out/refcount/empty_array_init.rs @@ -11,9 +11,7 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let vec_: Value> = Rc::new(RefCell::new( - std::array::from_fn::<_, 3, _>(|_| 0_i32).to_vec(), - )); + let vec_: Value<[i32; 3]> = Rc::new(RefCell::new(std::array::from_fn::<_, 3, _>(|_| 0_i32))); return 0; } pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/refcount/init_list.rs b/tests/unit/out/refcount/init_list.rs index e262a65720..d3dfe2b74c 100644 --- a/tests/unit/out/refcount/init_list.rs +++ b/tests/unit/out/refcount/init_list.rs @@ -45,7 +45,7 @@ fn main_0() -> i32 { let mut i2: i32 = 0_i32; let mut carr1: [i32; 2] = [1, 2]; let mut carr2: [i32; 3] = [1, 0_i32, 0_i32]; - let arr: Value> = Rc::new(RefCell::new(Vec::from([1, 2, 3]))); + let arr: Value<[i32; 3]> = Rc::new(RefCell::new([1, 2, 3])); let mut vec_: Vec = Vec::from([1, 2, 3]); ({ f_0(Vec::from([1, 2, 3, 4])) }); let p: Value = Rc::new(RefCell::new(Pair { a: 1, b: 2 })); diff --git a/tests/unit/out/refcount/inline_container_fields.rs b/tests/unit/out/refcount/inline_container_fields.rs index c35ed4dad5..1d5853ba0f 100644 --- a/tests/unit/out/refcount/inline_container_fields.rs +++ b/tests/unit/out/refcount/inline_container_fields.rs @@ -50,7 +50,7 @@ pub struct State { pub grid: Box<[Box<[i32]>]>, #[offset(28)] #[byte_size(12)] - pub arr: Vec, + pub arr: [i32; 3], #[offset(40)] #[byte_size(24)] pub elems: Vec, @@ -67,7 +67,7 @@ impl State { (*_a0.upgrade().deref()).grid[(__i) as usize][(__i2) as usize] })) })), - arr: field!(_a0, arr).with_mut(std::mem::take), + arr: std::array::from_fn(|__i| field!(_a0, arr).with_mut(std::mem::take)[__i]), elems: field!(_a0, elems).with_mut(std::mem::take), rows: field!(_a0, rows).with_mut(std::mem::take), } @@ -81,7 +81,7 @@ impl Default for State { grid: (0..2) .map(|_| (0..3).map(|_| 0_i32).collect::>()) .collect::]>>(), - arr: std::array::from_fn::<_, 3, _>(|_| 0_i32).to_vec(), + arr: std::array::from_fn::<_, 3, _>(|_| 0_i32), elems: Default::default(), rows: Default::default(), } @@ -107,7 +107,7 @@ pub fn fill_1(mut s: Ptr) { } i.prefix_inc(); } - vec_elem!(s, arr, 1_usize).write(7); + elem!(field_ref!(s, arr).decay(), 1_usize).write(7); { let __a0 = 3_usize as usize; field!(s, elems).with_mut(|__v: &mut Vec| __v.resize_with(__a0, || ::default())) @@ -150,7 +150,7 @@ fn main_0() -> i32 { }) == 12) }) ); - assert!(((vec_elem!(s.as_pointer(), arr, 1_usize).read()) == 7)); + assert!(((elem!(field_ref!(s.as_pointer(), arr).decay(), 1_usize).read()) == 7)); let mut e: Ptr = (field_ref!(s.as_pointer(), elems).decay().offset(2_usize)); let mut h: Ptr = (array_field_ptr!(e, hist) as Ptr); let mut c: Ptr = (field_ref!(e, coeffs).decay().offset(1_usize)); @@ -224,7 +224,7 @@ fn main_0() -> i32 { ); let mut p: Ptr = (t.as_pointer()); let __rhs = ({ (*p.upgrade().deref()).grid[(1) as usize][(1) as usize] } + { - (vec_elem!(p, arr, 1_usize).read()) + (elem!(field_ref!(p, arr).decay(), 1_usize).read()) }); index!(field!(vec_elem!(p, elems, 0_usize), hist), 3).write(__rhs); assert!(((index!(field!(vec_elem!(t.as_pointer(), elems, 0_usize), hist), 3).read()) == 11)); @@ -329,7 +329,9 @@ impl StateImpl for Ptr { }; field_ref!((*self), arr) .as_pointer() - .write(field!(_a0, arr).with_mut(std::mem::take)); + .write(std::array::from_fn(|__i| { + field!(_a0, arr).with_mut(std::mem::take)[__i] + })); field_ref!((*self), elems) .as_pointer() .write(field!(_a0, elems).with_mut(std::mem::take)); diff --git a/tests/unit/out/refcount/libc_struct_without_default.rs b/tests/unit/out/refcount/libc_struct_without_default.rs index 9b994c8958..b293770396 100644 --- a/tests/unit/out/refcount/libc_struct_without_default.rs +++ b/tests/unit/out/refcount/libc_struct_without_default.rs @@ -11,7 +11,7 @@ use std::rc::{Rc, Weak}; pub struct UserDefined { #[offset(0)] #[byte_size(4)] - pub a: Vec, + pub a: [i32; 1], #[offset(8)] #[byte_size(24)] pub v: Vec, @@ -19,7 +19,7 @@ pub struct UserDefined { impl UserDefined { pub fn move_from(_a0: Ptr) -> Self { Self { - a: field!(_a0, a).with_mut(std::mem::take), + a: std::array::from_fn(|__i| field!(_a0, a).with_mut(std::mem::take)[__i]), v: field!(_a0, v).with_mut(std::mem::take), } } @@ -28,7 +28,7 @@ impl Default for UserDefined { fn default() -> Self { { UserDefined { - a: std::array::from_fn::<_, 1, _>(|_| 0_i32).to_vec(), + a: std::array::from_fn::<_, 1, _>(|_| 0_i32), v: Default::default(), } } @@ -76,7 +76,7 @@ fn main_0() -> i32 { (*st.borrow_mut()).st_size = (1024 as ::libc::off_t); assert!(({ ({ (*st.borrow()).st_size } == (1024 as ::libc::off_t)) })); let ud: Value = Rc::new(RefCell::new(::default())); - assert!(((vec_elem!(ud.as_pointer(), a, 0_usize).read()) == 0)); + assert!(((elem!(field_ref!(ud.as_pointer(), a).decay(), 0_usize).read()) == 0)); assert!(({ ((*ud.borrow()).v.len() == 0_usize) })); let mut filt: FieldIsLibcType = ::default(); assert!(((filt.addr.sa_family as i32) == 0)); @@ -89,7 +89,9 @@ impl UserDefinedImpl for Ptr { fn move_assign(&self, _a0: Ptr) -> Ptr { field_ref!((*self), a) .as_pointer() - .write(field!(_a0, a).with_mut(std::mem::take)); + .write(std::array::from_fn(|__i| { + field!(_a0, a).with_mut(std::mem::take)[__i] + })); field_ref!((*self), v) .as_pointer() .write(field!(_a0, v).with_mut(std::mem::take)); diff --git a/tests/unit/out/refcount/structured_binding_array.rs b/tests/unit/out/refcount/structured_binding_array.rs index f40e79d878..b941c8de8c 100644 --- a/tests/unit/out/refcount/structured_binding_array.rs +++ b/tests/unit/out/refcount/structured_binding_array.rs @@ -29,10 +29,10 @@ fn main_0() -> i32 { }; assert!(({ ((*arr.borrow())[(0) as usize] == 7) })); assert!(({ ((*arr.borrow())[(2) as usize] == 5) })); - let std_arr: Value> = Rc::new(RefCell::new(Vec::from([4, 5, 6]))); + let std_arr: Value<[i32; 3]> = Rc::new(RefCell::new([4, 5, 6])); let mut last: i32 = (std_arr.decay().offset(2 as isize).read()); std_arr.decay().offset(0 as isize).write(8); - let __decomp_2: Ptr> = std_arr.as_pointer(); + let __decomp_2: Ptr<[i32; 3]> = std_arr.as_pointer(); let p: Ptr = __decomp_2.decay().offset(0 as isize); let q: Ptr = __decomp_2.decay().offset(1 as isize); let r: Ptr = __decomp_2.decay().offset(2 as isize); diff --git a/tests/unit/out/refcount/vector_subscript_direct.rs b/tests/unit/out/refcount/vector_subscript_direct.rs index 9f37a653bc..fe37f6dedd 100644 --- a/tests/unit/out/refcount/vector_subscript_direct.rs +++ b/tests/unit/out/refcount/vector_subscript_direct.rs @@ -136,7 +136,7 @@ fn main_0() -> i32 { ((index!(elem!(grid.decay(), 0_usize), 1_usize).read()) == 8) && ((index!(elem!(grid.decay(), 1_usize), 0_usize).read()) == 8) ); - let a: Value> = Rc::new(RefCell::new(Vec::from([4, 5, 6]))); + let a: Value<[i32; 3]> = Rc::new(RefCell::new([4, 5, 6])); let __rhs = ({ (*a.borrow())[0_usize] } + { (*a.borrow())[2_usize] }); (*a.borrow_mut())[1_usize] = __rhs; assert!(({ ({ (*a.borrow())[1_usize] } == 10) })); diff --git a/tests/unit/out/unsafe/empty_array_init.rs b/tests/unit/out/unsafe/empty_array_init.rs index 77ba5f75c8..d8b3a2ea2b 100644 --- a/tests/unit/out/unsafe/empty_array_init.rs +++ b/tests/unit/out/unsafe/empty_array_init.rs @@ -13,7 +13,7 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut vec_: Vec = std::array::from_fn::<_, 3, _>(|_| 0_i32).to_vec(); + let mut vec_: [i32; 3] = std::array::from_fn::<_, 3, _>(|_| 0_i32); return 0; } pub unsafe fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/init_list.rs b/tests/unit/out/unsafe/init_list.rs index 42f59c23e2..5c7a064f57 100644 --- a/tests/unit/out/unsafe/init_list.rs +++ b/tests/unit/out/unsafe/init_list.rs @@ -47,7 +47,7 @@ unsafe fn main_0() -> i32 { let mut i2: i32 = 0_i32; let mut carr1: [i32; 2] = [1, 2]; let mut carr2: [i32; 3] = [1, 0_i32, 0_i32]; - let mut arr: Vec = Vec::from([1, 2, 3]); + let mut arr: [i32; 3] = [1, 2, 3]; let mut vec_: Vec = Vec::from([1, 2, 3]); (unsafe { f_0(Vec::from([1, 2, 3, 4])) }); let mut p: Pair = Pair { a: 1, b: 2 }; diff --git a/tests/unit/out/unsafe/inline_container_fields.rs b/tests/unit/out/unsafe/inline_container_fields.rs index 9637f98bd6..c674bf5e96 100644 --- a/tests/unit/out/unsafe/inline_container_fields.rs +++ b/tests/unit/out/unsafe/inline_container_fields.rs @@ -44,7 +44,7 @@ impl Elem { pub struct State { pub count: i32, pub grid: [[i32; 3]; 2], - pub arr: Vec, + pub arr: [i32; 3], pub elems: Vec, pub rows: Vec>, } @@ -55,7 +55,7 @@ impl State { grid: std::array::from_fn::<_, 2, _>(|__i: usize| { std::array::from_fn::<_, 3, _>(|__i2: usize| (*_a0).grid[(__i)][(__i2)]) }), - arr: std::mem::take(&mut (*_a0).arr), + arr: std::array::from_fn(|__i| std::mem::take(&mut (&(*_a0)).arr)[__i]), elems: std::mem::take(&mut (*_a0).elems), rows: std::mem::take(&mut (*_a0).rows), }; @@ -74,7 +74,7 @@ impl State { __tmp_2 } }; - self.arr = std::mem::take(&mut (*_a0).arr); + self.arr = std::array::from_fn(|__i| std::mem::take(&mut (&(*_a0)).arr)[__i]); self.elems = std::mem::take(&mut (*_a0).elems); self.rows = std::mem::take(&mut (*_a0).rows); return &mut (*(self as *mut State)); @@ -86,7 +86,7 @@ impl Default for State { State { count: 0_i32, grid: [[0_i32; 3]; 2], - arr: std::array::from_fn::<_, 3, _>(|_| 0_i32).to_vec(), + arr: std::array::from_fn::<_, 3, _>(|_| 0_i32), elems: Default::default(), rows: Default::default(), } diff --git a/tests/unit/out/unsafe/libc_struct_without_default.rs b/tests/unit/out/unsafe/libc_struct_without_default.rs index 11cc04c114..7e724f46e6 100644 --- a/tests/unit/out/unsafe/libc_struct_without_default.rs +++ b/tests/unit/out/unsafe/libc_struct_without_default.rs @@ -9,19 +9,19 @@ use std::rc::Rc; #[repr(C)] #[derive(Clone, VaArg, FnPtrArg, MoveCtorUnsafe, MoveAssignUnsafe)] pub struct UserDefined { - pub a: Vec, + pub a: [i32; 1], pub v: Vec, } impl UserDefined { pub unsafe fn move_from(_a0: *mut UserDefined) -> Self { let mut this = Self { - a: std::mem::take(&mut (*_a0).a), + a: std::array::from_fn(|__i| std::mem::take(&mut (&(*_a0)).a)[__i]), v: std::mem::take(&mut (*_a0).v), }; this } pub unsafe fn move_assign(&mut self, _a0: *mut UserDefined) -> *mut UserDefined { - self.a = std::mem::take(&mut (*_a0).a); + self.a = std::array::from_fn(|__i| std::mem::take(&mut (&(*_a0)).a)[__i]); self.v = std::mem::take(&mut (*_a0).v); return &mut (*(self as *mut UserDefined)); } @@ -30,7 +30,7 @@ impl Default for UserDefined { fn default() -> Self { unsafe { UserDefined { - a: std::array::from_fn::<_, 1, _>(|_| 0_i32).to_vec(), + a: std::array::from_fn::<_, 1, _>(|_| 0_i32), v: Default::default(), } } diff --git a/tests/unit/out/unsafe/structured_binding_array.rs b/tests/unit/out/unsafe/structured_binding_array.rs index 19c0a3f333..33770aa019 100644 --- a/tests/unit/out/unsafe/structured_binding_array.rs +++ b/tests/unit/out/unsafe/structured_binding_array.rs @@ -25,10 +25,10 @@ unsafe fn main_0() -> i32 { (*__decomp_1)[(2) as usize] += (*__decomp_1)[(1) as usize]; assert!(((arr[(0) as usize]) == (7))); assert!(((arr[(2) as usize]) == (5))); - let mut std_arr: Vec = Vec::from([4, 5, 6]); + let mut std_arr: [i32; 3] = [4, 5, 6]; let mut last: i32 = (*std_arr.as_mut_ptr().offset(2 as isize)); (*std_arr.as_mut_ptr().offset(0 as isize)) = 8; - let __decomp_2: *mut Vec = &mut std_arr; + let __decomp_2: *mut [i32; 3] = &mut std_arr; let p: *mut i32 = (*__decomp_2).as_mut_ptr().offset(0 as isize); let q: *mut i32 = (*__decomp_2).as_mut_ptr().offset(1 as isize); let r: *mut i32 = (*__decomp_2).as_mut_ptr().offset(2 as isize); diff --git a/tests/unit/out/unsafe/vector_subscript_direct.rs b/tests/unit/out/unsafe/vector_subscript_direct.rs index 4cee7f1fdf..49704ecdb2 100644 --- a/tests/unit/out/unsafe/vector_subscript_direct.rs +++ b/tests/unit/out/unsafe/vector_subscript_direct.rs @@ -127,7 +127,7 @@ unsafe fn main_0() -> i32 { grid[(0_usize)][(1_usize)] = grid[(1_usize)][(2_usize)]; grid[(1_usize)][(0_usize)] += grid[(0_usize)][(1_usize)]; assert!(((grid[(0_usize)][(1_usize)]) == (8)) && ((grid[(1_usize)][(0_usize)]) == (8))); - let mut a: Vec = Vec::from([4, 5, 6]); + let mut a: [i32; 3] = [4, 5, 6]; a[(1_usize)] = ((a[(0_usize)]) + (a[(2_usize)])); assert!(((a[(1_usize)]) == (10))); return 0; From a2f1617c8f354b42095c78677cfdebeb820ac9ad Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 4 Nov 2026 15:17:08 +0000 Subject: [PATCH 03/12] Add elems attribute in type --- cpp2rust/converter/mapper.cpp | 12 ++++ cpp2rust/converter/mapper.h | 3 + .../converter/models/converter_refcount.cpp | 58 +++++++------------ .../converter/models/converter_refcount.h | 7 ++- cpp2rust/converter/translation_rule.cpp | 10 ++++ cpp2rust/converter/translation_rule.h | 9 +++ rule-preprocessor/src/ir.rs | 16 +++++ rule-preprocessor/src/syntactic.rs | 31 +++++++++- 8 files changed, 106 insertions(+), 40 deletions(-) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 3a6166866c..0bb14d7e7d 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -58,6 +58,18 @@ std::string Map(Converter &converter, clang::QualType qual_type) { return {}; } +std::optional Elems(Converter &converter, + clang::QualType qual_type) { + auto [rule, subs] = + RuleRegistry::Search(converter.GetASTContext(), qual_type); + if (!rule || !rule->type_info.elems) { + return std::nullopt; + } + auto &elems = *rule->type_info.elems; + return TranslationRule::Elems{ + elems.kind, Matcher::InstantiateTgt(converter, subs, elems.type)}; +} + std::string MapInitializer(Converter &converter, clang::QualType qual_type) { auto [rule, subs] = RuleRegistry::Search(converter.GetASTContext(), qual_type); diff --git a/cpp2rust/converter/mapper.h b/cpp2rust/converter/mapper.h index fff44f6336..74a68a3710 100644 --- a/cpp2rust/converter/mapper.h +++ b/cpp2rust/converter/mapper.h @@ -7,6 +7,7 @@ #include #include +#include #include #include "converter/translation_rule.h" @@ -22,6 +23,8 @@ bool IsRefExpr(clang::ASTContext &ctx, const clang::Expr *expr); std::string Map(clang::ASTContext &ctx, clang::QualType qual_type); std::string Map(Converter &converter, clang::QualType qual_type); +std::optional Elems(Converter &converter, + clang::QualType qual_type); std::string MapInitializer(Converter &converter, clang::QualType qual_type); std::string MapFunctionName(clang::ASTContext &ctx, const clang::FunctionDecl *decl); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 800e452e42..8666731aa2 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -52,12 +52,13 @@ use std::rc::{Rc, Weak}; )"); } -static bool IsBoxedType(std::string_view type) { - return type.starts_with("Vec<") || type.starts_with("Box<"); +bool ConverterRefCount::IsBoxedType(clang::QualType type) { + return Mapper::Elems(*this, type.getUnqualifiedType()).has_value(); } -static bool IsBoxedType(clang::ASTContext &ctx, clang::QualType type) { - return IsBoxedType(Mapper::Map(ctx, type.getUnqualifiedType())); +bool ConverterRefCount::IsVecType(clang::QualType type) { + auto elems = Mapper::Elems(*this, type.getUnqualifiedType()); + return elems && elems->kind == TranslationRule::ElemsKind::kVec; } // A Rust type without whitespace, and with the paths into libcc2rs, which the @@ -73,11 +74,10 @@ static std::string NormalizeType(std::string type) { // Virtual methods take &mut self, as the fields of the struct are stored in // it. -static bool NeedsMutAccess(clang::ASTContext &ctx, - const clang::CXXMethodDecl *method, - clang::QualType base_type) { +bool ConverterRefCount::NeedsMutAccess(const clang::CXXMethodDecl *method, + clang::QualType base_type) { return !method->isConst() && - (IsBoxedType(ctx, base_type) || method->isVirtual()); + (IsBoxedType(base_type) || method->isVirtual()); } // Whether `expr` is an array field whose elements are accessed through @@ -135,19 +135,6 @@ void ConverterRefCount::PendingDeref::set_unchecked(std::string str, type = ComputedExprType::Pending; } -std::string ConverterRefCount::GetInnerType(clang::QualType type) { - PushConversionKind push(*this, ConversionKind::Unboxed); - auto str = ToString(type); - auto pos = str.find('<'); - auto end = str.rfind('>'); - if (str[pos + 1] == '[' && str[end - 1] == ']') { - // Unwrap inner array type - pos++; - end--; - } - return std::move(str).substr(pos + 1, end - pos - 1); -} - std::string ConverterRefCount::GetSafeTypeAsString(clang::QualType qual_type) const { std::string type_as_string; @@ -383,7 +370,7 @@ std::string ConverterRefCount::ConvertObject(clang::Expr *expr, if (is_ptr) { type = type->getPointeeType(); } - if (IsBoxedType(ctx_, type) || (is_ptr && type->isArrayType())) { + if (IsBoxedType(type) || (is_ptr && type->isArrayType())) { MarkFresh(); return std::move(str) + ".decay()"; } @@ -393,7 +380,7 @@ std::string ConverterRefCount::ConvertObject(clang::Expr *expr, std::string ConverterRefCount::AsPointerMethod(clang::QualType type) { if (isObject() && object_shape_ == ObjectShape::Element && - type->isRecordType() && IsBoxedType(ctx_, type)) { + type->isRecordType() && IsBoxedType(type)) { object_shape_ = ObjectShape::Whole; return ".decay()"; } @@ -419,7 +406,7 @@ bool ConverterRefCount::ParamIsElementPtr(clang::Expr *expr, clang::Expr *arg, type = type->getPointeeType(); } type = type.getUnqualifiedType(); - if (!IsBoxedType(ctx_, type)) { + if (!IsBoxedType(type)) { return false; } // The C++ parameter is a reference or pointer to the container, which the @@ -531,9 +518,9 @@ ConverterRefCount::MaterializeTemp(const std::string &binding_name, std::string ConverterRefCount::ConvertPtrType(clang::QualType type) { std::string str; - // decays into Ptr; remove the outer type Vec<> - if (IsBoxedType(ctx_, type)) { - str = GetInnerType(type); + if (auto elems = Mapper::Elems(*this, type.getUnqualifiedType())) { + PushConversionKind push(*this, ConversionKind::Unboxed); + str = std::move(elems->type); } else { PushConversionKind push(*this, ConversionKind::Ptr); str = ToString(type); @@ -768,7 +755,7 @@ void ConverterRefCount::EmitByteSizeAttr(const clang::RecordDecl *decl) { std::string ConverterRefCount::GetSelfMaybeWithMut(const clang::CXXMethodDecl *decl) { - return NeedsMutAccess(ctx_, decl, decl->getThisType()->getPointeeType()) + return NeedsMutAccess(decl, decl->getThisType()->getPointeeType()) ? "&mut self" : "&self"; } @@ -1776,7 +1763,7 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) { if (base_type->isPointerType()) { base_type = base_type->getPointeeType(); } - bool needs_mut = NeedsMutAccess(ctx_, method, base_type); + bool needs_mut = NeedsMutAccess(method, base_type); PushExprKind push(*this, needs_mut ? ExprKind::LValue : ExprKind::RValue); Converter::ConvertMemberExpr(expr); MarkFresh(); @@ -1845,7 +1832,7 @@ void ConverterRefCount::ConvertInlineField(clang::MemberExpr *expr) { if (type->isArrayType()) { StrCat(std::format("array_field_ptr!({}, {})", ConvertRecordPtr(expr), name)); - } else if (IsBoxedType(ctx_, type)) { + } else if (IsBoxedType(type)) { StrCat(std::format("field_ref!({}, {}){}", ConvertRecordPtr(expr), name, AsPointerMethod(type))); } else { @@ -2483,8 +2470,8 @@ bool ConverterRefCount::ConvertCXXOperatorCallExpr( case clang::OverloadedOperatorKind::OO_Subscript: { bool is_inner_boxed = - IsBoxedType(ctx_, expr->getType().getNonReferenceType()) && - IsBoxedType(ctx_, expr->getArg(0)->getType().getNonReferenceType()); + IsBoxedType(expr->getType().getNonReferenceType()) && + IsBoxedType(expr->getArg(0)->getType().getNonReferenceType()); // An element of an unboxed container is accessed directly, as is one of // a container stored in a Value that can be borrowed. @@ -2583,9 +2570,7 @@ std::string ConverterRefCount::ConvertElemPlace(clang::Expr *container, : nullptr; if (field && !field->getType()->isReferenceType() && !field->getParent()->isUnion() && !Mapper::Contains(ctx_, member) && - !IsUnboxedPlace(member) && - Mapper::Map(ctx_, field->getType().getUnqualifiedType()) - .starts_with("Vec<")) { + !IsUnboxedPlace(member) && IsVecType(field->getType())) { if (IsFieldOfVectorElem(member)) { return std::format("index!({}, {})", ConvertPlace(member), ConvertSubscriptIndex(idx)); @@ -2600,8 +2585,7 @@ std::string ConverterRefCount::ConvertElemPlace(clang::Expr *container, container->IgnoreParenNoopCasts(ctx_)); if ((IsReferenceType(container) || (deref && deref->getOpcode() == clang::UO_Deref)) && - Mapper::Map(ctx_, container->getType().getUnqualifiedType()) - .starts_with("Vec<")) { + IsVecType(container->getType())) { ConvertLValue(container); if (!pending_deref_.empty()) { auto place = pending_deref_.take(); diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index 3099df1bde..864815a741 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -422,7 +422,12 @@ class ConverterRefCount final : public Converter { const char *GetConversionTraitName() const override; - std::string GetInnerType(clang::QualType type); + bool IsBoxedType(clang::QualType type); + bool IsVecType(clang::QualType type); + + bool NeedsMutAccess(const clang::CXXMethodDecl *method, + clang::QualType base_type); + // What an Object-kind conversion of a boxed container/array should yield: // a pointer to the whole object (Ptr>), or one to its first diff --git a/cpp2rust/converter/translation_rule.cpp b/cpp2rust/converter/translation_rule.cpp index f8b15d84ec..814f3d88d7 100644 --- a/cpp2rust/converter/translation_rule.cpp +++ b/cpp2rust/converter/translation_rule.cpp @@ -35,6 +35,14 @@ TypeInfo ParseTypeInfoJSON(const llvm::json::Object &obj) { info.derives.emplace_back(s->str()); } } + if (auto *elems = obj.getObject("elems")) { + auto kind = elems->getString("kind"); + auto type = elems->getString("type"); + assert(kind && type); + assert(*kind == "vec" || *kind == "array"); + info.elems = Elems{*kind == "vec" ? ElemsKind::kVec : ElemsKind::kArray, + type->str()}; + } return info; } @@ -346,6 +354,8 @@ void TypeInfo::dump() const { log() << " [rc_ptr]"; if (is_unsafe_pointer) log() << " [unsafe_ptr]"; + if (elems) + log() << " [elems " << elems->type << ']'; for (const auto &d : derives) log() << " +" << d; } diff --git a/cpp2rust/converter/translation_rule.h b/cpp2rust/converter/translation_rule.h index 17998c2621..5fd3343c1a 100644 --- a/cpp2rust/converter/translation_rule.h +++ b/cpp2rust/converter/translation_rule.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -93,12 +94,20 @@ struct Bound { bool AnyFragment(const std::vector &body, llvm::function_ref pred); +enum class ElemsKind { kVec, kArray }; + +struct Elems { + ElemsKind kind; + std::string type; +}; + struct TypeInfo { std::vector derives; std::string type; std::string cpp_type; bool is_refcount_pointer = false; bool is_unsafe_pointer = false; + std::optional elems; bool is_pointer() const { return is_refcount_pointer || is_unsafe_pointer; } diff --git a/rule-preprocessor/src/ir.rs b/rule-preprocessor/src/ir.rs index e58ee24041..67ae983970 100644 --- a/rule-preprocessor/src/ir.rs +++ b/rule-preprocessor/src/ir.rs @@ -72,6 +72,22 @@ pub struct TypeInfo { pub derives: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] pub cpp_type: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub elems: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Elems { + pub kind: ElemsKind, + #[serde(rename = "type")] + pub ty: String, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ElemsKind { + Vec, + Array, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/rule-preprocessor/src/syntactic.rs b/rule-preprocessor/src/syntactic.rs index b564eb807d..3b249316c2 100644 --- a/rule-preprocessor/src/syntactic.rs +++ b/rule-preprocessor/src/syntactic.rs @@ -12,8 +12,8 @@ use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use crate::ir::{ - Access, BodyFragment, Bound, FileIr, FnIr, GenericCallInner, MethodCallInner, Model, - PlaceholderInner, RefExprInner, RuleIr, RulesIR, TypeInfo, TypeIr, + Access, BodyFragment, Bound, Elems, ElemsKind, FileIr, FnIr, GenericCallInner, + MethodCallInner, Model, PlaceholderInner, RefExprInner, RuleIr, RulesIR, TypeInfo, TypeIr, }; /// Classify a type AST node as refcount pointer (`Ptr`), unsafe pointer @@ -43,6 +43,30 @@ fn pointer_flags(ty: &ast::Type) -> (bool, bool) { flags } +fn elems(ty: &ast::Type) -> Option { + match ty { + ast::Type::ArrayType(array) => Some(Elems { + kind: ElemsKind::Array, + ty: array.ty()?.syntax().text().to_string(), + }), + ast::Type::PathType(path) => { + let segment = path.path()?.segment()?; + if segment.name_ref()?.text() != "Vec" { + return None; + } + let ast::GenericArg::TypeArg(arg) = segment.generic_arg_list()?.generic_args().next()? + else { + return None; + }; + Some(Elems { + kind: ElemsKind::Vec, + ty: arg.ty()?.syntax().text().to_string(), + }) + } + _ => None, + } +} + fn cpp_type_of(ty: &ast::Type) -> Option { let ast::Type::MacroType(macro_type) = ty else { return None; @@ -472,6 +496,7 @@ impl<'a> FnIrBuilder<'a> { is_unsafe_pointer, derives: Vec::new(), cpp_type: None, + elems: None, }) } } @@ -696,6 +721,7 @@ impl<'a> FnIrBuilder<'a> { is_unsafe_pointer: p.is_unsafe_pointer, derives: Vec::new(), cpp_type: p.cpp_type.clone(), + elems: None, }, ) }) @@ -807,6 +833,7 @@ impl<'a> TypeIrBuilder<'a> { is_unsafe_pointer, derives: Vec::new(), cpp_type: None, + elems: elems(&ty), }, } } From beb2aba4e61a14916c25a773ba5bf1e1b8a77ac9 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 4 Nov 2026 15:33:14 +0000 Subject: [PATCH 04/12] Fix missing traits on const generics --- rule-preprocessor/src/semantic.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/rule-preprocessor/src/semantic.rs b/rule-preprocessor/src/semantic.rs index a0c637e1bd..00f8518d7d 100644 --- a/rule-preprocessor/src/semantic.rs +++ b/rule-preprocessor/src/semantic.rs @@ -212,7 +212,7 @@ impl MethodResolver { Some(RuleIr::Fn(fn_ir)) => f.resolve_unknowns(tcx, fn_ir), Some(RuleIr::Type(type_ir)) => { let ret_ty = tcx.fn_sig(f.def_id).skip_binder().output().skip_binder(); - type_ir.type_info.derives = type_derives(tcx, ret_ty); + type_ir.type_info.derives = type_derives(tcx, tcx.param_env(f.def_id), ret_ty); } None => {} } @@ -292,7 +292,11 @@ fn decl_source_file( ) } -fn is_copy<'tcx>(tcx: rustc_middle::ty::TyCtxt<'tcx>, ty: rustc_middle::ty::Ty<'tcx>) -> bool { +fn is_copy<'tcx>( + tcx: rustc_middle::ty::TyCtxt<'tcx>, + param_env: rustc_middle::ty::ParamEnv<'tcx>, + ty: rustc_middle::ty::Ty<'tcx>, +) -> bool { use rustc_infer::infer::TyCtxtInferExt; use rustc_trait_selection::infer::InferCtxtExt; @@ -303,12 +307,13 @@ fn is_copy<'tcx>(tcx: rustc_middle::ty::TyCtxt<'tcx>, ty: rustc_middle::ty::Ty<' .infer_ctxt() .build(rustc_middle::ty::TypingMode::non_body_analysis()); infcx - .type_implements_trait(copy_trait, [ty], rustc_middle::ty::ParamEnv::empty()) + .type_implements_trait(copy_trait, [ty], param_env) .must_apply_modulo_regions() } fn type_derives<'tcx>( tcx: rustc_middle::ty::TyCtxt<'tcx>, + param_env: rustc_middle::ty::ParamEnv<'tcx>, ty: rustc_middle::ty::Ty<'tcx>, ) -> Vec { use rustc_infer::infer::TyCtxtInferExt; @@ -338,7 +343,7 @@ fn type_derives<'tcx>( .filter(|&trait_def_id| { let args = vec![ty; tcx.generics_of(trait_def_id).count()]; infcx - .type_implements_trait(trait_def_id, args, rustc_middle::ty::ParamEnv::empty()) + .type_implements_trait(trait_def_id, args, param_env) .must_apply_modulo_regions() }) .map(|trait_def_id| tcx.item_name(trait_def_id).to_string()) @@ -545,7 +550,8 @@ impl<'a, 'tcx> AstVisitor<'a, 'tcx> { .expr_adjustments(expr) .iter() .any(|adj| matches!(adj.kind, rustc_middle::ty::adjustment::Adjust::Borrow(_))); - !borrowed && !is_copy(self.tcx, results.expr_ty(expr)) + let param_env = self.tcx.param_env(expr.hir_id.owner.to_def_id()); + !borrowed && !is_copy(self.tcx, param_env, results.expr_ty(expr)) } fn expr_as_decl_ref(&self, expr: &rustc_hir::Expr<'_>) -> Option { From adb71b320530e8c5401caa21793e1e6f50760242 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 4 Nov 2026 15:39:51 +0000 Subject: [PATCH 05/12] Update tests --- tests/unit/inline_container_fields.cpp | 49 ++++++ .../out/refcount/inline_container_fields.rs | 152 +++++++++++++++++- .../refcount/libc_struct_without_default.rs | 11 +- .../out/unsafe/inline_container_fields.rs | 99 +++++++++++- .../out/unsafe/libc_struct_without_default.rs | 4 +- 5 files changed, 297 insertions(+), 18 deletions(-) diff --git a/tests/unit/inline_container_fields.cpp b/tests/unit/inline_container_fields.cpp index 964433bc2b..cb3388b55f 100644 --- a/tests/unit/inline_container_fields.cpp +++ b/tests/unit/inline_container_fields.cpp @@ -41,6 +41,32 @@ static void fill(State *s) { s->rows.push_back({1, 2, 3}); } +static int copies = 0; +static int moves = 0; +static int copy_assigns = 0; +static int move_assigns = 0; + +struct Counted { + int v = 0; + Counted() = default; + Counted(const Counted &o) : v(o.v) { ++copies; } + Counted(Counted &&o) : v(o.v) { + o.v = -1; + ++moves; + } + Counted &operator=(const Counted &o) { + v = o.v; + ++copy_assigns; + return *this; + } + Counted &operator=(Counted &&o) { + v = o.v; + o.v = -1; + ++move_assigns; + return *this; + } +}; + int main() { State s{}; fill(&s); @@ -92,9 +118,32 @@ int main() { State moved(std::move(t)); assert(moved.grid[0][0] == 100 && moved.grid[1][2] == 5); assert(moved.elems.size() == 4 && t.elems.empty() && t.rows.empty()); + assert(moved.arr[1] == 7 && t.arr[1] == 7); State assigned{}; assigned = std::move(moved); assert(assigned.grid[0][0] == 100 && assigned.grid[1][1] == 4); assert(assigned.elems[2].coeffs[1] == 7 && moved.elems.empty()); + assert(assigned.arr[1] == 7 && moved.arr[1] == 7); + + std::array ca{}; + ca[1].v = 5; + std::array cb(std::move(ca)); + assert(moves == 3 && copies == 0 && cb[1].v == 5 && ca[1].v == -1); + std::array cc(cb); + assert(copies == 3 && cc[1].v == 5 && cb[1].v == 5); + cc[2].v = 8; + cb = cc; + assert(copy_assigns == 3 && move_assigns == 0 && cb[2].v == 8); + ca = std::move(cb); + assert(move_assigns == 3 && ca[2].v == 8 && cb[2].v == -1); + assert(moves == 3 && copies == 3 && copy_assigns == 3); + + std::array, 2> va{}; + va[0].push_back(1); + std::array, 2> vb(std::move(va)); + assert(vb[0].size() == 1 && va[0].empty()); + std::array, 2> vc{}; + vc = std::move(vb); + assert(vc[0].size() == 1 && vb[0].empty()); return 0; } diff --git a/tests/unit/out/refcount/inline_container_fields.rs b/tests/unit/out/refcount/inline_container_fields.rs index 1d5853ba0f..760013027a 100644 --- a/tests/unit/out/refcount/inline_container_fields.rs +++ b/tests/unit/out/refcount/inline_container_fields.rs @@ -67,7 +67,7 @@ impl State { (*_a0.upgrade().deref()).grid[(__i) as usize][(__i2) as usize] })) })), - arr: std::array::from_fn(|__i| field!(_a0, arr).with_mut(std::mem::take)[__i]), + arr: cpp_move!(field_ref!(_a0, arr).as_pointer()), elems: field!(_a0, elems).with_mut(std::mem::take), rows: field!(_a0, rows).with_mut(std::mem::take), } @@ -130,6 +130,52 @@ pub fn fill_1(mut s: Ptr) { field!(s, rows).with_mut(|__v: &mut Vec>| __v.push(__a1)) }; } +thread_local!( + pub static copies_2: Value = Rc::new(RefCell::new(0)); +); +thread_local!( + pub static moves_3: Value = Rc::new(RefCell::new(0)); +); +thread_local!( + pub static copy_assigns_4: Value = Rc::new(RefCell::new(0)); +); +thread_local!( + pub static move_assigns_5: Value = Rc::new(RefCell::new(0)); +); +#[derive(Record, ByteRepr, VaArg, FnPtrArg, MoveCtor, CopyAssign, MoveAssign)] +#[byte_size(4)] +pub struct Counted { + #[offset(0)] + pub v: i32, +} +impl Counted { + pub fn copy_from(o: Ptr) -> Self { + let __this: Counted = Self { + v: o.with(|__s| __s.v), + }; + (*copies_2.with(Value::clone).borrow_mut()).prefix_inc(); + __this + } + pub fn move_from(o: Ptr) -> Self { + let __this: Counted = Self { + v: o.with(|__s| __s.v), + }; + field!(o, v).write(-1_i32); + (*moves_3.with(Value::clone).borrow_mut()).prefix_inc(); + __this + } +} +impl Clone for Counted { + fn clone(&self) -> Self { + let __src: Value = Rc::new(RefCell::new(Counted { v: self.v.clone() })); + Counted::copy_from(__src.as_pointer()) + } +} +impl Default for Counted { + fn default() -> Self { + { Counted { v: 0 } } + } +} pub fn main() { __cpp2rust_init_globals(); std::process::exit(main_0()); @@ -270,6 +316,10 @@ fn main_0() -> i32 { && ((*t.borrow()).rows.is_empty()) }) ); + assert!( + ((elem!(field_ref!(moved.as_pointer(), arr).decay(), 1_usize).read()) == 7) + && ((elem!(field_ref!(t.as_pointer(), arr).decay(), 1_usize).read()) == 7) + ); let assigned: Value = Rc::new(RefCell::new(::default())); ({ StateImpl::move_assign(&assigned.as_pointer(), moved.as_pointer()) }); assert!( @@ -289,8 +339,92 @@ fn main_0() -> i32 { && ((*moved.borrow()).elems.is_empty()) }) ); + assert!( + ((elem!(field_ref!(assigned.as_pointer(), arr).decay(), 1_usize).read()) == 7) + && ((elem!(field_ref!(moved.as_pointer(), arr).decay(), 1_usize).read()) == 7) + ); + let ca: Value<[Counted; 3]> = Rc::new(RefCell::new(std::array::from_fn::<_, 3, _>(|_| { + ::default() + }))); + (*ca.borrow_mut())[1_usize].v = 5; + let cb: Value<[Counted; 3]> = Rc::new(RefCell::new(cpp_move!(ca.as_pointer()))); + assert!( + ({ + (((moves_3.with(|rc| *rc.borrow()) == 3) && (copies_2.with(|rc| *rc.borrow()) == 0)) + && ({ (*cb.borrow())[1_usize].v } == 5)) + && ({ (*ca.borrow())[1_usize].v } == -1_i32) + }) + ); + let cc: Value<[Counted; 3]> = Rc::new(RefCell::new((*cb.borrow()).clone())); + assert!( + ({ + ((copies_2.with(|rc| *rc.borrow()) == 3) && ({ (*cc.borrow())[1_usize].v } == 5)) + && ({ (*cb.borrow())[1_usize].v } == 5) + }) + ); + (*cc.borrow_mut())[2_usize].v = 8; + cpp_copy_assign!(cb.as_pointer(), cc.as_pointer()); + assert!( + ({ + ((copy_assigns_4.with(|rc| *rc.borrow()) == 3) + && (move_assigns_5.with(|rc| *rc.borrow()) == 0)) + && ({ (*cb.borrow())[2_usize].v } == 8) + }) + ); + cpp_move_assign!(ca.as_pointer(), cb.as_pointer()); + assert!( + ({ + ((move_assigns_5.with(|rc| *rc.borrow()) == 3) && ({ (*ca.borrow())[2_usize].v } == 8)) + && ({ (*cb.borrow())[2_usize].v } == -1_i32) + }) + ); + assert!( + ((moves_3.with(|rc| *rc.borrow()) == 3) && (copies_2.with(|rc| *rc.borrow()) == 3)) + && (copy_assigns_4.with(|rc| *rc.borrow()) == 3) + ); + let va: Value<[Vec; 2]> = Rc::new(RefCell::new(std::array::from_fn::<_, 2, _>(|_| { + Default::default() + }))); + { + let __a1 = 1; + elem!(va.decay(), 0_usize).with_mut(|__v: &mut Vec| __v.push(__a1)) + }; + let vb: Value<[Vec; 2]> = Rc::new(RefCell::new(cpp_move!(va.as_pointer()))); + assert!( + ({ + ((*vb.decay().offset(0_usize).upgrade().deref()).len() == 1_usize) + && ((*va.decay().offset(0_usize).upgrade().deref()).is_empty()) + }) + ); + let vc: Value<[Vec; 2]> = Rc::new(RefCell::new(std::array::from_fn::<_, 2, _>(|_| { + Default::default() + }))); + cpp_move_assign!(vc.as_pointer(), vb.as_pointer()); + assert!( + ({ + ((*vc.decay().offset(0_usize).upgrade().deref()).len() == 1_usize) + && ((*vb.decay().offset(0_usize).upgrade().deref()).is_empty()) + }) + ); return 0; } +pub trait CountedImpl { + fn copy_assign(&self, o: Ptr) -> Ptr; + fn move_assign(&self, o: Ptr) -> Ptr; +} +impl CountedImpl for Ptr { + fn copy_assign(&self, o: Ptr) -> Ptr { + field!((*self), v).write({ o.with(|__s| __s.v) }); + (*copy_assigns_4.with(Value::clone).borrow_mut()).prefix_inc(); + return (*self).clone(); + } + fn move_assign(&self, o: Ptr) -> Ptr { + field!((*self), v).write({ o.with(|__s| __s.v) }); + field!(o, v).write(-1_i32); + (*move_assigns_5.with(Value::clone).borrow_mut()).prefix_inc(); + return (*self).clone(); + } +} pub trait ElemImpl { fn move_assign(&self, _a0: Ptr) -> Ptr; } @@ -327,11 +461,10 @@ impl StateImpl for Ptr { (__tmp_1).clone() } }; - field_ref!((*self), arr) - .as_pointer() - .write(std::array::from_fn(|__i| { - field!(_a0, arr).with_mut(std::mem::take)[__i] - })); + cpp_move_assign!( + field_ref!((*self), arr).as_pointer(), + field_ref!(_a0, arr).as_pointer() + ); field_ref!((*self), elems) .as_pointer() .write(field!(_a0, elems).with_mut(std::mem::take)); @@ -341,4 +474,9 @@ impl StateImpl for Ptr { return (*self).clone(); } } -pub fn __cpp2rust_init_globals() {} +pub fn __cpp2rust_init_globals() { + let _ = copies_2.with(|_| ()); + let _ = moves_3.with(|_| ()); + let _ = copy_assigns_4.with(|_| ()); + let _ = move_assigns_5.with(|_| ()); +} diff --git a/tests/unit/out/refcount/libc_struct_without_default.rs b/tests/unit/out/refcount/libc_struct_without_default.rs index b293770396..990b05fc36 100644 --- a/tests/unit/out/refcount/libc_struct_without_default.rs +++ b/tests/unit/out/refcount/libc_struct_without_default.rs @@ -19,7 +19,7 @@ pub struct UserDefined { impl UserDefined { pub fn move_from(_a0: Ptr) -> Self { Self { - a: std::array::from_fn(|__i| field!(_a0, a).with_mut(std::mem::take)[__i]), + a: cpp_move!(field_ref!(_a0, a).as_pointer()), v: field!(_a0, v).with_mut(std::mem::take), } } @@ -87,11 +87,10 @@ pub trait UserDefinedImpl { } impl UserDefinedImpl for Ptr { fn move_assign(&self, _a0: Ptr) -> Ptr { - field_ref!((*self), a) - .as_pointer() - .write(std::array::from_fn(|__i| { - field!(_a0, a).with_mut(std::mem::take)[__i] - })); + cpp_move_assign!( + field_ref!((*self), a).as_pointer(), + field_ref!(_a0, a).as_pointer() + ); field_ref!((*self), v) .as_pointer() .write(field!(_a0, v).with_mut(std::mem::take)); diff --git a/tests/unit/out/unsafe/inline_container_fields.rs b/tests/unit/out/unsafe/inline_container_fields.rs index c674bf5e96..90e42d98d6 100644 --- a/tests/unit/out/unsafe/inline_container_fields.rs +++ b/tests/unit/out/unsafe/inline_container_fields.rs @@ -55,7 +55,7 @@ impl State { grid: std::array::from_fn::<_, 2, _>(|__i: usize| { std::array::from_fn::<_, 3, _>(|__i2: usize| (*_a0).grid[(__i)][(__i2)]) }), - arr: std::array::from_fn(|__i| std::mem::take(&mut (&(*_a0)).arr)[__i]), + arr: cpp_move_unsafe!(&mut (*_a0).arr), elems: std::mem::take(&mut (*_a0).elems), rows: std::mem::take(&mut (*_a0).rows), }; @@ -74,7 +74,7 @@ impl State { __tmp_2 } }; - self.arr = std::array::from_fn(|__i| std::mem::take(&mut (&(*_a0)).arr)[__i]); + cpp_move_assign_unsafe!(&mut self.arr, &mut (*_a0).arr); self.elems = std::mem::take(&mut (*_a0).elems); self.rows = std::mem::take(&mut (*_a0).rows); return &mut (*(self as *mut State)); @@ -134,6 +134,49 @@ pub unsafe fn fill_1(mut s: *mut State) { (*s).rows.push(__a1) }; } +pub static mut copies_2: std::cell::LazyCell = std::cell::LazyCell::new(|| unsafe { 0 }); +pub static mut moves_3: std::cell::LazyCell = std::cell::LazyCell::new(|| unsafe { 0 }); +pub static mut copy_assigns_4: std::cell::LazyCell = std::cell::LazyCell::new(|| unsafe { 0 }); +pub static mut move_assigns_5: std::cell::LazyCell = std::cell::LazyCell::new(|| unsafe { 0 }); +#[repr(C)] +#[derive(VaArg, FnPtrArg, MoveCtorUnsafe, CopyAssignUnsafe, MoveAssignUnsafe)] +pub struct Counted { + pub v: i32, +} +impl Counted { + pub unsafe fn copy_from(o: *const Counted) -> Self { + let mut this = Self { v: (*o).v }; + (*std::cell::LazyCell::force_mut(&mut *&raw mut copies_2)).prefix_inc(); + this + } + pub unsafe fn move_from(o: *mut Counted) -> Self { + let mut this = Self { v: (*o).v }; + (*o).v = -1_i32; + (*std::cell::LazyCell::force_mut(&mut *&raw mut moves_3)).prefix_inc(); + this + } + pub unsafe fn copy_assign(&mut self, o: *const Counted) -> *mut Counted { + self.v = (*o).v; + (*std::cell::LazyCell::force_mut(&mut *&raw mut copy_assigns_4)).prefix_inc(); + return &mut (*(self as *mut Counted)); + } + pub unsafe fn move_assign(&mut self, o: *mut Counted) -> *mut Counted { + self.v = (*o).v; + (*o).v = -1_i32; + (*std::cell::LazyCell::force_mut(&mut *&raw mut move_assigns_5)).prefix_inc(); + return &mut (*(self as *mut Counted)); + } +} +impl Clone for Counted { + fn clone(&self) -> Self { + unsafe { Counted::copy_from(self as *const Counted) } + } +} +impl Default for Counted { + fn default() -> Self { + unsafe { Counted { v: 0 } } + } +} pub fn main() { unsafe { __cpp2rust_init_globals(); @@ -213,6 +256,7 @@ unsafe fn main_0() -> i32 { && ((moved.grid[(1) as usize][(2) as usize]) == (5)) ); assert!((((moved.elems.len()) == (4_usize)) && (t.elems.is_empty())) && (t.rows.is_empty())); + assert!(((moved.arr[(1_usize)]) == (7)) && ((t.arr[(1_usize)]) == (7))); let mut assigned: State = ::default(); (unsafe { State::move_assign(&mut assigned, &mut moved) }); assert!( @@ -222,6 +266,55 @@ unsafe fn main_0() -> i32 { assert!( ((assigned.elems[(2_usize)].coeffs[(1_usize)] as i32) == (7)) && (moved.elems.is_empty()) ); + assert!(((assigned.arr[(1_usize)]) == (7)) && ((moved.arr[(1_usize)]) == (7))); + let mut ca: [Counted; 3] = std::array::from_fn::<_, 3, _>(|_| ::default()); + ca[(1_usize)].v = 5; + let mut cb: [Counted; 3] = cpp_move_unsafe!(&mut ca); + assert!( + ((((*std::cell::LazyCell::force_mut(&mut *&raw mut moves_3)) == (3)) + && ((*std::cell::LazyCell::force_mut(&mut *&raw mut copies_2)) == (0))) + && ((cb[(1_usize)].v) == (5))) + && ((ca[(1_usize)].v) == (-1_i32)) + ); + let mut cc: [Counted; 3] = cb.clone(); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut copies_2)) == (3)) + && ((cc[(1_usize)].v) == (5))) + && ((cb[(1_usize)].v) == (5)) + ); + cc[(2_usize)].v = 8; + cpp_copy_assign_unsafe!(&mut cb, &cc); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut copy_assigns_4)) == (3)) + && ((*std::cell::LazyCell::force_mut(&mut *&raw mut move_assigns_5)) == (0))) + && ((cb[(2_usize)].v) == (8)) + ); + cpp_move_assign_unsafe!(&mut ca, &mut cb); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut move_assigns_5)) == (3)) + && ((ca[(2_usize)].v) == (8))) + && ((cb[(2_usize)].v) == (-1_i32)) + ); + assert!( + (((*std::cell::LazyCell::force_mut(&mut *&raw mut moves_3)) == (3)) + && ((*std::cell::LazyCell::force_mut(&mut *&raw mut copies_2)) == (3))) + && ((*std::cell::LazyCell::force_mut(&mut *&raw mut copy_assigns_4)) == (3)) + ); + let mut va: [Vec; 2] = std::array::from_fn::<_, 2, _>(|_| Default::default()); + { + let __a1 = 1; + va[(0_usize)].push(__a1) + }; + let mut vb: [Vec; 2] = cpp_move_unsafe!(&mut va); + assert!(((vb[(0_usize)].len()) == (1_usize)) && (va[(0_usize)].is_empty())); + let mut vc: [Vec; 2] = std::array::from_fn::<_, 2, _>(|_| Default::default()); + cpp_move_assign_unsafe!(&mut vc, &mut vb); + assert!(((vc[(0_usize)].len()) == (1_usize)) && (vb[(0_usize)].is_empty())); return 0; } -pub unsafe fn __cpp2rust_init_globals() {} +pub unsafe fn __cpp2rust_init_globals() { + std::cell::LazyCell::force(&*&raw const copies_2); + std::cell::LazyCell::force(&*&raw const moves_3); + std::cell::LazyCell::force(&*&raw const copy_assigns_4); + std::cell::LazyCell::force(&*&raw const move_assigns_5); +} diff --git a/tests/unit/out/unsafe/libc_struct_without_default.rs b/tests/unit/out/unsafe/libc_struct_without_default.rs index 7e724f46e6..47cbd56c5e 100644 --- a/tests/unit/out/unsafe/libc_struct_without_default.rs +++ b/tests/unit/out/unsafe/libc_struct_without_default.rs @@ -15,13 +15,13 @@ pub struct UserDefined { impl UserDefined { pub unsafe fn move_from(_a0: *mut UserDefined) -> Self { let mut this = Self { - a: std::array::from_fn(|__i| std::mem::take(&mut (&(*_a0)).a)[__i]), + a: cpp_move_unsafe!(&mut (*_a0).a), v: std::mem::take(&mut (*_a0).v), }; this } pub unsafe fn move_assign(&mut self, _a0: *mut UserDefined) -> *mut UserDefined { - self.a = std::array::from_fn(|__i| std::mem::take(&mut (&(*_a0)).a)[__i]); + cpp_move_assign_unsafe!(&mut self.a, &mut (*_a0).a); self.v = std::mem::take(&mut (*_a0).v); return &mut (*(self as *mut UserDefined)); } From ee03d47b92ddaf1fb44ceb9f74f6eb5fcbfad10c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 4 Nov 2026 15:51:15 +0000 Subject: [PATCH 06/12] Update tests --- tests/unit/out/refcount/empty_array_init.rs | 6 +++- tests/unit/out/refcount/init_list.rs | 2 +- .../out/refcount/inline_container_fields.rs | 33 +++++++++++-------- .../refcount/libc_struct_without_default.rs | 4 +-- .../out/refcount/structured_binding_array.rs | 4 +-- .../out/refcount/vector_subscript_direct.rs | 2 +- 6 files changed, 30 insertions(+), 21 deletions(-) diff --git a/tests/unit/out/refcount/empty_array_init.rs b/tests/unit/out/refcount/empty_array_init.rs index 9988439edc..d8c6c70ac7 100644 --- a/tests/unit/out/refcount/empty_array_init.rs +++ b/tests/unit/out/refcount/empty_array_init.rs @@ -11,7 +11,11 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let vec_: Value<[i32; 3]> = Rc::new(RefCell::new(std::array::from_fn::<_, 3, _>(|_| 0_i32))); + let vec_: Value> = Rc::new(RefCell::new(Box::<[i32]>::from(std::array::from_fn::< + _, + 3, + _, + >(|_| 0_i32)))); return 0; } pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/refcount/init_list.rs b/tests/unit/out/refcount/init_list.rs index d3dfe2b74c..46ebadee25 100644 --- a/tests/unit/out/refcount/init_list.rs +++ b/tests/unit/out/refcount/init_list.rs @@ -45,7 +45,7 @@ fn main_0() -> i32 { let mut i2: i32 = 0_i32; let mut carr1: [i32; 2] = [1, 2]; let mut carr2: [i32; 3] = [1, 0_i32, 0_i32]; - let arr: Value<[i32; 3]> = Rc::new(RefCell::new([1, 2, 3])); + let arr: Value> = Rc::new(RefCell::new(Box::<[i32]>::from([1, 2, 3]))); let mut vec_: Vec = Vec::from([1, 2, 3]); ({ f_0(Vec::from([1, 2, 3, 4])) }); let p: Value = Rc::new(RefCell::new(Pair { a: 1, b: 2 })); diff --git a/tests/unit/out/refcount/inline_container_fields.rs b/tests/unit/out/refcount/inline_container_fields.rs index 760013027a..c1a1a6383f 100644 --- a/tests/unit/out/refcount/inline_container_fields.rs +++ b/tests/unit/out/refcount/inline_container_fields.rs @@ -50,7 +50,7 @@ pub struct State { pub grid: Box<[Box<[i32]>]>, #[offset(28)] #[byte_size(12)] - pub arr: [i32; 3], + pub arr: Box<[i32]>, #[offset(40)] #[byte_size(24)] pub elems: Vec, @@ -81,7 +81,7 @@ impl Default for State { grid: (0..2) .map(|_| (0..3).map(|_| 0_i32).collect::>()) .collect::]>>(), - arr: std::array::from_fn::<_, 3, _>(|_| 0_i32), + arr: Box::<[i32]>::from(std::array::from_fn::<_, 3, _>(|_| 0_i32)), elems: Default::default(), rows: Default::default(), } @@ -343,11 +343,16 @@ fn main_0() -> i32 { ((elem!(field_ref!(assigned.as_pointer(), arr).decay(), 1_usize).read()) == 7) && ((elem!(field_ref!(moved.as_pointer(), arr).decay(), 1_usize).read()) == 7) ); - let ca: Value<[Counted; 3]> = Rc::new(RefCell::new(std::array::from_fn::<_, 3, _>(|_| { - ::default() - }))); + let ca: Value> = + Rc::new(RefCell::new(Box::<[Counted]>::from(std::array::from_fn::< + _, + 3, + _, + >(|_| { + ::default() + })))); (*ca.borrow_mut())[1_usize].v = 5; - let cb: Value<[Counted; 3]> = Rc::new(RefCell::new(cpp_move!(ca.as_pointer()))); + let cb: Value> = Rc::new(RefCell::new(cpp_move!(ca.as_pointer()))); assert!( ({ (((moves_3.with(|rc| *rc.borrow()) == 3) && (copies_2.with(|rc| *rc.borrow()) == 0)) @@ -355,7 +360,7 @@ fn main_0() -> i32 { && ({ (*ca.borrow())[1_usize].v } == -1_i32) }) ); - let cc: Value<[Counted; 3]> = Rc::new(RefCell::new((*cb.borrow()).clone())); + let cc: Value> = Rc::new(RefCell::new((*cb.borrow()).clone())); assert!( ({ ((copies_2.with(|rc| *rc.borrow()) == 3) && ({ (*cc.borrow())[1_usize].v } == 5)) @@ -382,23 +387,23 @@ fn main_0() -> i32 { ((moves_3.with(|rc| *rc.borrow()) == 3) && (copies_2.with(|rc| *rc.borrow()) == 3)) && (copy_assigns_4.with(|rc| *rc.borrow()) == 3) ); - let va: Value<[Vec; 2]> = Rc::new(RefCell::new(std::array::from_fn::<_, 2, _>(|_| { - Default::default() - }))); + let va: Value]>> = Rc::new(RefCell::new(Box::<[Vec]>::from( + std::array::from_fn::<_, 2, _>(|_| Default::default()), + ))); { let __a1 = 1; elem!(va.decay(), 0_usize).with_mut(|__v: &mut Vec| __v.push(__a1)) }; - let vb: Value<[Vec; 2]> = Rc::new(RefCell::new(cpp_move!(va.as_pointer()))); + let vb: Value]>> = Rc::new(RefCell::new(cpp_move!(va.as_pointer()))); assert!( ({ ((*vb.decay().offset(0_usize).upgrade().deref()).len() == 1_usize) && ((*va.decay().offset(0_usize).upgrade().deref()).is_empty()) }) ); - let vc: Value<[Vec; 2]> = Rc::new(RefCell::new(std::array::from_fn::<_, 2, _>(|_| { - Default::default() - }))); + let vc: Value]>> = Rc::new(RefCell::new(Box::<[Vec]>::from( + std::array::from_fn::<_, 2, _>(|_| Default::default()), + ))); cpp_move_assign!(vc.as_pointer(), vb.as_pointer()); assert!( ({ diff --git a/tests/unit/out/refcount/libc_struct_without_default.rs b/tests/unit/out/refcount/libc_struct_without_default.rs index 990b05fc36..72095fea13 100644 --- a/tests/unit/out/refcount/libc_struct_without_default.rs +++ b/tests/unit/out/refcount/libc_struct_without_default.rs @@ -11,7 +11,7 @@ use std::rc::{Rc, Weak}; pub struct UserDefined { #[offset(0)] #[byte_size(4)] - pub a: [i32; 1], + pub a: Box<[i32]>, #[offset(8)] #[byte_size(24)] pub v: Vec, @@ -28,7 +28,7 @@ impl Default for UserDefined { fn default() -> Self { { UserDefined { - a: std::array::from_fn::<_, 1, _>(|_| 0_i32), + a: Box::<[i32]>::from(std::array::from_fn::<_, 1, _>(|_| 0_i32)), v: Default::default(), } } diff --git a/tests/unit/out/refcount/structured_binding_array.rs b/tests/unit/out/refcount/structured_binding_array.rs index b941c8de8c..0e724c182e 100644 --- a/tests/unit/out/refcount/structured_binding_array.rs +++ b/tests/unit/out/refcount/structured_binding_array.rs @@ -29,10 +29,10 @@ fn main_0() -> i32 { }; assert!(({ ((*arr.borrow())[(0) as usize] == 7) })); assert!(({ ((*arr.borrow())[(2) as usize] == 5) })); - let std_arr: Value<[i32; 3]> = Rc::new(RefCell::new([4, 5, 6])); + let std_arr: Value> = Rc::new(RefCell::new(Box::<[i32]>::from([4, 5, 6]))); let mut last: i32 = (std_arr.decay().offset(2 as isize).read()); std_arr.decay().offset(0 as isize).write(8); - let __decomp_2: Ptr<[i32; 3]> = std_arr.as_pointer(); + let __decomp_2: Ptr> = std_arr.as_pointer(); let p: Ptr = __decomp_2.decay().offset(0 as isize); let q: Ptr = __decomp_2.decay().offset(1 as isize); let r: Ptr = __decomp_2.decay().offset(2 as isize); diff --git a/tests/unit/out/refcount/vector_subscript_direct.rs b/tests/unit/out/refcount/vector_subscript_direct.rs index fe37f6dedd..ecce8b7d10 100644 --- a/tests/unit/out/refcount/vector_subscript_direct.rs +++ b/tests/unit/out/refcount/vector_subscript_direct.rs @@ -136,7 +136,7 @@ fn main_0() -> i32 { ((index!(elem!(grid.decay(), 0_usize), 1_usize).read()) == 8) && ((index!(elem!(grid.decay(), 1_usize), 0_usize).read()) == 8) ); - let a: Value<[i32; 3]> = Rc::new(RefCell::new([4, 5, 6])); + let a: Value> = Rc::new(RefCell::new(Box::<[i32]>::from([4, 5, 6]))); let __rhs = ({ (*a.borrow())[0_usize] } + { (*a.borrow())[2_usize] }); (*a.borrow_mut())[1_usize] = __rhs; assert!(({ ({ (*a.borrow())[1_usize] } == 10) })); From 54631d27ece5c5b20b8a8d6bfcaf973469df3ef9 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 4 Nov 2026 16:34:25 +0000 Subject: [PATCH 07/12] Update rules --- rules/array/tgt_refcount.rs | 24 ++++++++++++++++++++---- rules/array/tgt_unsafe.rs | 16 ++++++++++------ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/rules/array/tgt_refcount.rs b/rules/array/tgt_refcount.rs index e80dc9311c..7b9b72cdd0 100644 --- a/rules/array/tgt_refcount.rs +++ b/rules/array/tgt_refcount.rs @@ -5,6 +5,10 @@ use libcc2rs::*; use std::cell::RefCell; use std::rc::Rc; +fn t1() -> Box<[T1]> { + Box::<[T1]>::from(std::array::from_fn::<_, T2, _>(|_| ::default())) +} + fn t4() -> Ptr { Ptr::null() } @@ -21,12 +25,24 @@ fn f3(a0: Ptr) -> Ptr { a0 } -fn f5(a0: Ptr<[T1; T2]>, a1: &mut [T1; T2]) { - a0.write(std::array::from_fn(|__i| std::mem::take(&mut a1[__i]))) +fn f4(a0: Ptr) -> Box<[T1]> { + (0..T2).map(|__i| T1::move_ctor(a0.offset(__i))).collect() +} + +fn f5(a0: Ptr, a1: Ptr) { + for __i in 0..T2 { + T1::move_assign(&a0.offset(__i), a1.offset(__i)); + } +} + +fn f7(a0: Ptr, a1: Ptr) { + for __i in 0..T2 { + T1::copy_assign(&a0.offset(__i), a1.offset(__i)); + } } -fn f7(a0: Ptr<[T1; T2]>, a1: [T1; T2]) { - a0.write(a1) +fn f8(a0: [T1; T2]) -> Box<[T1]> { + Box::<[T1]>::from(a0) } fn f9(a0: Ptr) -> Ptr { diff --git a/rules/array/tgt_unsafe.rs b/rules/array/tgt_unsafe.rs index 9e4bc2781e..7d666065bc 100644 --- a/rules/array/tgt_unsafe.rs +++ b/rules/array/tgt_unsafe.rs @@ -39,20 +39,24 @@ unsafe fn f3(a0: &mut [T1; T2]) -> *mut T1 { a0.as_mut_ptr() } -unsafe fn f4(a0: &mut [T1; T2]) -> [T1; T2] { - std::array::from_fn(|__i| std::mem::take(&mut a0[__i])) +unsafe fn f4(a0: &mut [T1; T2]) -> [T1; T2] { + std::array::from_fn(|__i| T1::move_ctor(&mut a0[__i])) } -unsafe fn f5(a0: &mut [T1; T2], a1: &mut [T1; T2]) { - *a0 = std::array::from_fn(|__i| std::mem::take(&mut a1[__i])) +unsafe fn f5(a0: &mut [T1; T2], a1: &mut [T1; T2]) { + for __i in 0..T2 { + T1::move_assign(&mut a0[__i], &mut a1[__i]); + } } unsafe fn f6(a0: [T1; T2]) -> [T1; T2] { a0.clone() } -unsafe fn f7(a0: &mut [T1; T2], a1: [T1; T2]) { - *a0 = a1.clone() +unsafe fn f7(a0: &mut [T1; T2], a1: &[T1; T2]) { + for __i in 0..T2 { + T1::copy_assign(&mut a0[__i], &a1[__i]); + } } unsafe fn f8(a0: [T1; T2]) -> [T1; T2] { From 9748cf8a7cfc9719b78065e00ab9f1722e4a581c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 4 Nov 2026 16:34:43 +0000 Subject: [PATCH 08/12] Add Box in the list of boxed types --- rule-preprocessor/src/syntactic.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/rule-preprocessor/src/syntactic.rs b/rule-preprocessor/src/syntactic.rs index 3b249316c2..8b8bf9ce05 100644 --- a/rule-preprocessor/src/syntactic.rs +++ b/rule-preprocessor/src/syntactic.rs @@ -51,17 +51,21 @@ fn elems(ty: &ast::Type) -> Option { }), ast::Type::PathType(path) => { let segment = path.path()?.segment()?; - if segment.name_ref()?.text() != "Vec" { - return None; - } let ast::GenericArg::TypeArg(arg) = segment.generic_arg_list()?.generic_args().next()? else { return None; }; - Some(Elems { - kind: ElemsKind::Vec, - ty: arg.ty()?.syntax().text().to_string(), - }) + match (segment.name_ref()?.text().as_str(), arg.ty()?) { + ("Vec", elem) => Some(Elems { + kind: ElemsKind::Vec, + ty: elem.syntax().text().to_string(), + }), + ("Box", ast::Type::SliceType(slice)) => Some(Elems { + kind: ElemsKind::Array, + ty: slice.ty()?.syntax().text().to_string(), + }), + _ => None, + } } _ => None, } From 67f38c0262ed811c2acc74eabb3ea239ec4b2956 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 4 Nov 2026 16:34:58 +0000 Subject: [PATCH 09/12] ADd decay for Box<[T]> --- libcc2rs/src/field.rs | 26 ++++++++++++++++++++++++++ libcc2rs/src/rc.rs | 7 +++++++ 2 files changed, 33 insertions(+) diff --git a/libcc2rs/src/field.rs b/libcc2rs/src/field.rs index fafdab0d0c..57193f0b9f 100644 --- a/libcc2rs/src/field.rs +++ b/libcc2rs/src/field.rs @@ -462,6 +462,32 @@ where } } +impl AsPointer> for FieldRef, G, M> +where + S: Record, + T: ByteRepr, + G: Fn(&S) -> &Box<[T]> + Copy + 'static, + M: Fn(&mut S) -> &mut Box<[T]> + Copy + 'static, +{ + #[inline] + fn as_pointer(&self) -> Ptr> { + self.base.field_ptr(self.get, self.get_mut, self.offset) + } +} + +impl Decay for FieldRef, G, M> +where + S: Record, + T: ByteRepr, + G: Fn(&S) -> &Box<[T]> + Copy + 'static, + M: Fn(&mut S) -> &mut Box<[T]> + Copy + 'static, +{ + #[inline] + fn decay(&self) -> Ptr { + self.base.array_field_ptr(self.get, self.get_mut, self.offset) + } +} + // The sequence that `get` gives in the struct that `base` points to, which is // reached through `base`: a field, as a sequence of one element, or the // elements of an array or vector field. It is the parent of the pointers into diff --git a/libcc2rs/src/rc.rs b/libcc2rs/src/rc.rs index d52c73a3f1..34674233a4 100644 --- a/libcc2rs/src/rc.rs +++ b/libcc2rs/src/rc.rs @@ -1381,6 +1381,13 @@ impl Decay for Rc>> { } } +impl Decay for Rc>> { + #[inline] + fn decay(&self) -> Ptr { + AsPointer::::as_pointer(self) + } +} + pub struct ScopedDestructor { owner: Rc>, destroy: fn(Ptr), From b9cbab0dd39f42f69892a309124db09753e79e5d Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 4 Nov 2026 16:43:11 +0000 Subject: [PATCH 10/12] Update tests --- .../out/refcount/inline_container_fields.rs | 40 ++++++++++++++----- .../refcount/libc_struct_without_default.rs | 14 ++++--- .../out/unsafe/inline_container_fields.rs | 23 +++++++---- .../out/unsafe/libc_struct_without_default.rs | 6 ++- 4 files changed, 59 insertions(+), 24 deletions(-) diff --git a/tests/unit/out/refcount/inline_container_fields.rs b/tests/unit/out/refcount/inline_container_fields.rs index c1a1a6383f..bdb9c6ed7b 100644 --- a/tests/unit/out/refcount/inline_container_fields.rs +++ b/tests/unit/out/refcount/inline_container_fields.rs @@ -67,7 +67,9 @@ impl State { (*_a0.upgrade().deref()).grid[(__i) as usize][(__i2) as usize] })) })), - arr: cpp_move!(field_ref!(_a0, arr).as_pointer()), + arr: (0..3) + .map(|__i| field_ref!(_a0, arr).decay().offset(__i).read()) + .collect(), elems: field!(_a0, elems).with_mut(std::mem::take), rows: field!(_a0, rows).with_mut(std::mem::take), } @@ -352,7 +354,11 @@ fn main_0() -> i32 { ::default() })))); (*ca.borrow_mut())[1_usize].v = 5; - let cb: Value> = Rc::new(RefCell::new(cpp_move!(ca.as_pointer()))); + let cb: Value> = Rc::new(RefCell::new( + (0..3) + .map(|__i| Counted::move_from(ca.decay().offset(__i))) + .collect(), + )); assert!( ({ (((moves_3.with(|rc| *rc.borrow()) == 3) && (copies_2.with(|rc| *rc.borrow()) == 0)) @@ -368,7 +374,9 @@ fn main_0() -> i32 { }) ); (*cc.borrow_mut())[2_usize].v = 8; - cpp_copy_assign!(cb.as_pointer(), cc.as_pointer()); + for __i in 0..3 { + &cb.decay().offset(__i).copy_assign(cc.decay().offset(__i)); + } assert!( ({ ((copy_assigns_4.with(|rc| *rc.borrow()) == 3) @@ -376,7 +384,9 @@ fn main_0() -> i32 { && ({ (*cb.borrow())[2_usize].v } == 8) }) ); - cpp_move_assign!(ca.as_pointer(), cb.as_pointer()); + for __i in 0..3 { + &ca.decay().offset(__i).move_assign(cb.decay().offset(__i)); + } assert!( ({ ((move_assigns_5.with(|rc| *rc.borrow()) == 3) && ({ (*ca.borrow())[2_usize].v } == 8)) @@ -394,7 +404,11 @@ fn main_0() -> i32 { let __a1 = 1; elem!(va.decay(), 0_usize).with_mut(|__v: &mut Vec| __v.push(__a1)) }; - let vb: Value]>> = Rc::new(RefCell::new(cpp_move!(va.as_pointer()))); + let vb: Value]>> = Rc::new(RefCell::new( + (0..2) + .map(|__i| as MoveCtor>::move_ctor(va.decay().offset(__i))) + .collect(), + )); assert!( ({ ((*vb.decay().offset(0_usize).upgrade().deref()).len() == 1_usize) @@ -404,7 +418,11 @@ fn main_0() -> i32 { let vc: Value]>> = Rc::new(RefCell::new(Box::<[Vec]>::from( std::array::from_fn::<_, 2, _>(|_| Default::default()), ))); - cpp_move_assign!(vc.as_pointer(), vb.as_pointer()); + for __i in 0..2 { + &vc.decay() + .offset(__i) + .write( as MoveCtor>::move_ctor(vb.decay().offset(__i))); + } assert!( ({ ((*vc.decay().offset(0_usize).upgrade().deref()).len() == 1_usize) @@ -466,10 +484,12 @@ impl StateImpl for Ptr { (__tmp_1).clone() } }; - cpp_move_assign!( - field_ref!((*self), arr).as_pointer(), - field_ref!(_a0, arr).as_pointer() - ); + for __i in 0..3 { + &field_ref!((*self), arr) + .decay() + .offset(__i) + .write(field_ref!(_a0, arr).decay().offset(__i).read()); + } field_ref!((*self), elems) .as_pointer() .write(field!(_a0, elems).with_mut(std::mem::take)); diff --git a/tests/unit/out/refcount/libc_struct_without_default.rs b/tests/unit/out/refcount/libc_struct_without_default.rs index 72095fea13..b4b69c6f66 100644 --- a/tests/unit/out/refcount/libc_struct_without_default.rs +++ b/tests/unit/out/refcount/libc_struct_without_default.rs @@ -19,7 +19,9 @@ pub struct UserDefined { impl UserDefined { pub fn move_from(_a0: Ptr) -> Self { Self { - a: cpp_move!(field_ref!(_a0, a).as_pointer()), + a: (0..1) + .map(|__i| field_ref!(_a0, a).decay().offset(__i).read()) + .collect(), v: field!(_a0, v).with_mut(std::mem::take), } } @@ -87,10 +89,12 @@ pub trait UserDefinedImpl { } impl UserDefinedImpl for Ptr { fn move_assign(&self, _a0: Ptr) -> Ptr { - cpp_move_assign!( - field_ref!((*self), a).as_pointer(), - field_ref!(_a0, a).as_pointer() - ); + for __i in 0..1 { + &field_ref!((*self), a) + .decay() + .offset(__i) + .write(field_ref!(_a0, a).decay().offset(__i).read()); + } field_ref!((*self), v) .as_pointer() .write(field!(_a0, v).with_mut(std::mem::take)); diff --git a/tests/unit/out/unsafe/inline_container_fields.rs b/tests/unit/out/unsafe/inline_container_fields.rs index 90e42d98d6..d90a7bc543 100644 --- a/tests/unit/out/unsafe/inline_container_fields.rs +++ b/tests/unit/out/unsafe/inline_container_fields.rs @@ -55,7 +55,7 @@ impl State { grid: std::array::from_fn::<_, 2, _>(|__i: usize| { std::array::from_fn::<_, 3, _>(|__i2: usize| (*_a0).grid[(__i)][(__i2)]) }), - arr: cpp_move_unsafe!(&mut (*_a0).arr), + arr: std::array::from_fn(|__i| (*&mut (&mut (*_a0)).arr[__i])), elems: std::mem::take(&mut (*_a0).elems), rows: std::mem::take(&mut (*_a0).rows), }; @@ -74,7 +74,9 @@ impl State { __tmp_2 } }; - cpp_move_assign_unsafe!(&mut self.arr, &mut (*_a0).arr); + for __i in 0..3 { + *&mut self.arr[__i] = (*&mut (&mut (*_a0)).arr[__i]); + } self.elems = std::mem::take(&mut (*_a0).elems); self.rows = std::mem::take(&mut (*_a0).rows); return &mut (*(self as *mut State)); @@ -269,7 +271,7 @@ unsafe fn main_0() -> i32 { assert!(((assigned.arr[(1_usize)]) == (7)) && ((moved.arr[(1_usize)]) == (7))); let mut ca: [Counted; 3] = std::array::from_fn::<_, 3, _>(|_| ::default()); ca[(1_usize)].v = 5; - let mut cb: [Counted; 3] = cpp_move_unsafe!(&mut ca); + let mut cb: [Counted; 3] = std::array::from_fn(|__i| Counted::move_from(&mut ca[__i])); assert!( ((((*std::cell::LazyCell::force_mut(&mut *&raw mut moves_3)) == (3)) && ((*std::cell::LazyCell::force_mut(&mut *&raw mut copies_2)) == (0))) @@ -283,13 +285,17 @@ unsafe fn main_0() -> i32 { && ((cb[(1_usize)].v) == (5)) ); cc[(2_usize)].v = 8; - cpp_copy_assign_unsafe!(&mut cb, &cc); + for __i in 0..3 { + (*&mut cb[__i]).copy_assign(&cc[__i]); + } assert!( (((*std::cell::LazyCell::force_mut(&mut *&raw mut copy_assigns_4)) == (3)) && ((*std::cell::LazyCell::force_mut(&mut *&raw mut move_assigns_5)) == (0))) && ((cb[(2_usize)].v) == (8)) ); - cpp_move_assign_unsafe!(&mut ca, &mut cb); + for __i in 0..3 { + (*&mut ca[__i]).move_assign(&mut cb[__i]); + } assert!( (((*std::cell::LazyCell::force_mut(&mut *&raw mut move_assigns_5)) == (3)) && ((ca[(2_usize)].v) == (8))) @@ -305,10 +311,13 @@ unsafe fn main_0() -> i32 { let __a1 = 1; va[(0_usize)].push(__a1) }; - let mut vb: [Vec; 2] = cpp_move_unsafe!(&mut va); + let mut vb: [Vec; 2] = + std::array::from_fn(|__i| as MoveCtorUnsafe>::move_ctor(&mut va[__i])); assert!(((vb[(0_usize)].len()) == (1_usize)) && (va[(0_usize)].is_empty())); let mut vc: [Vec; 2] = std::array::from_fn::<_, 2, _>(|_| Default::default()); - cpp_move_assign_unsafe!(&mut vc, &mut vb); + for __i in 0..2 { + *&mut vc[__i] = as MoveCtorUnsafe>::move_ctor(&mut vb[__i]); + } assert!(((vc[(0_usize)].len()) == (1_usize)) && (vb[(0_usize)].is_empty())); return 0; } diff --git a/tests/unit/out/unsafe/libc_struct_without_default.rs b/tests/unit/out/unsafe/libc_struct_without_default.rs index 47cbd56c5e..3e207dee63 100644 --- a/tests/unit/out/unsafe/libc_struct_without_default.rs +++ b/tests/unit/out/unsafe/libc_struct_without_default.rs @@ -15,13 +15,15 @@ pub struct UserDefined { impl UserDefined { pub unsafe fn move_from(_a0: *mut UserDefined) -> Self { let mut this = Self { - a: cpp_move_unsafe!(&mut (*_a0).a), + a: std::array::from_fn(|__i| (*&mut (&mut (*_a0)).a[__i])), v: std::mem::take(&mut (*_a0).v), }; this } pub unsafe fn move_assign(&mut self, _a0: *mut UserDefined) -> *mut UserDefined { - cpp_move_assign_unsafe!(&mut self.a, &mut (*_a0).a); + for __i in 0..1 { + *&mut self.a[__i] = (*&mut (&mut (*_a0)).a[__i]); + } self.v = std::mem::take(&mut (*_a0).v); return &mut (*(self as *mut UserDefined)); } From 5fcebf57ae8b97a63257ea1640be04a5e5075f70 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 4 Nov 2026 20:08:24 +0000 Subject: [PATCH 11/12] Rewrite move_ctor, move_assign and copy_assign in the converter --- cpp2rust/converter/converter.cpp | 57 +++++++++++++++++++ cpp2rust/converter/converter.h | 12 ++++ .../converter/models/converter_refcount.cpp | 19 +++++++ .../converter/models/converter_refcount.h | 12 ++++ 4 files changed, 100 insertions(+) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 993bcdca64..2f672f549a 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -5301,6 +5301,42 @@ std::string Converter::ConvertGenericCall( assert(call_args.empty() && "default takes no arguments"); return GetUnboxedDefaultAsString(type); } + if (call.method == "move_ctor") { + assert(call_args.size() == 1 && "move_ctor takes one argument"); + auto *record = type->getAsCXXRecordDecl(); + if (record && HasMoveFromConstructor(record)) { + return std::format("{}::move_from({})", GetUnboxedTypeAsString(type), + call_args[0]); + } + if (record && !IsUserDefinedDecl(record) && + !record->hasTrivialMoveConstructor()) { + return std::format("<{} as {}>::move_ctor({})", + GetUnboxedTypeAsString(type), GetMoveCtorTraitName(), + call_args[0]); + } + return ConvertPtrRead(type, call_args[0]); + } + if (call.method == "move_assign" || call.method == "copy_assign") { + assert(call_args.size() == 2 && "assignments take two arguments"); + bool is_move = call.method == "move_assign"; + auto *record = type->getAsCXXRecordDecl(); + if (record && is_move && HasMoveAssignOperator(record)) { + return ConvertPointeeMethodCall("move_assign", call_args[0], + call_args[1]); + } + if (record && HasCopyAssignOperator(record)) { + return ConvertPointeeMethodCall("copy_assign", call_args[0], + call_args[1]); + } + if (is_move && record && !IsUserDefinedDecl(record) && + !record->hasTrivialMoveAssignment()) { + return ConvertPtrWrite(call_args[0], + std::format("<{} as {}>::move_ctor({})", + GetUnboxedTypeAsString(type), + GetMoveCtorTraitName(), call_args[1])); + } + return ConvertPtrWrite(call_args[0], ConvertPtrRead(type, call_args[1])); + } if (call.method == "cpp_from" || call.method == "cpp_from_move") { assert(call_args.size() == 1 && "cpp_from takes one argument"); return ConvertConversion( @@ -5337,6 +5373,27 @@ const char *Converter::GetConversionTraitName() const { return "CppFromUnsafe"; } +std::string Converter::ConvertPtrRead(clang::QualType type, + const std::string &src) { + if (type->isScalarType()) { + return std::format("(*{})", src); + } + return std::format("(*{}).clone()", src); +} + +std::string Converter::ConvertPtrWrite(const std::string &dst, + const std::string &value) { + return std::format("*{} = {}", dst, value); +} + +std::string Converter::ConvertPointeeMethodCall(const char *method, + const std::string &dst, + const std::string &src) { + return std::format("(*{}).{}({})", dst, method, src); +} + +const char *Converter::GetMoveCtorTraitName() const { return "MoveCtorUnsafe"; } + std::string Converter::DerefPtrExpr(std::string_view ptr_expr, clang::QualType pointee_type) { return std::format("({}{})", GetPointerDerefPrefix(pointee_type), ptr_expr); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index e0560d8c4a..ebc92cd42b 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -319,6 +319,18 @@ class Converter : public clang::RecursiveASTVisitor { virtual const char *GetConversionTraitName() const; + virtual std::string ConvertPtrRead(clang::QualType type, + const std::string &src); + + virtual std::string ConvertPtrWrite(const std::string &dst, + const std::string &value); + + virtual std::string ConvertPointeeMethodCall(const char *method, + const std::string &dst, + const std::string &src); + + virtual const char *GetMoveCtorTraitName() const; + virtual std::string DerefPtrExpr(std::string_view ptr_expr, clang::QualType pointee_type); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 8666731aa2..44559dccb2 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -2258,6 +2258,25 @@ const char *ConverterRefCount::GetConversionTraitName() const { return "CppFrom"; } +std::string ConverterRefCount::ConvertPtrRead(clang::QualType, + const std::string &src) { + return std::format("{}.read()", src); +} + +std::string ConverterRefCount::ConvertPtrWrite(const std::string &dst, + const std::string &value) { + return std::format("{}.write({})", dst, value); +} + +std::string ConverterRefCount::ConvertPointeeMethodCall( + const char *method, const std::string &dst, const std::string &src) { + return std::format("{}.{}({})", dst, method, src); +} + +const char *ConverterRefCount::GetMoveCtorTraitName() const { + return "MoveCtor"; +} + bool ConverterRefCount::TypeDerivesDefault(clang::QualType qual_type) { // Arrays are translated to Value>, whose Default is empty return !qual_type->isArrayType() && Converter::TypeDerivesDefault(qual_type); diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index 864815a741..cb3c2cacf3 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -422,6 +422,18 @@ class ConverterRefCount final : public Converter { const char *GetConversionTraitName() const override; + std::string ConvertPtrRead(clang::QualType type, + const std::string &src) override; + + std::string ConvertPtrWrite(const std::string &dst, + const std::string &value) override; + + std::string ConvertPointeeMethodCall(const char *method, + const std::string &dst, + const std::string &src) override; + + const char *GetMoveCtorTraitName() const override; + bool IsBoxedType(clang::QualType type); bool IsVecType(clang::QualType type); From 37ceab5e6921500f9c79016c4d9a98832a6106a0 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 4 Nov 2026 20:09:53 +0000 Subject: [PATCH 12/12] format --- cpp2rust/converter/models/converter_refcount.cpp | 3 +-- cpp2rust/converter/models/converter_refcount.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 44559dccb2..94d199073a 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -76,8 +76,7 @@ static std::string NormalizeType(std::string type) { // it. bool ConverterRefCount::NeedsMutAccess(const clang::CXXMethodDecl *method, clang::QualType base_type) { - return !method->isConst() && - (IsBoxedType(base_type) || method->isVirtual()); + return !method->isConst() && (IsBoxedType(base_type) || method->isVirtual()); } // Whether `expr` is an array field whose elements are accessed through diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index cb3c2cacf3..ed535b487e 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -440,7 +440,6 @@ class ConverterRefCount final : public Converter { bool NeedsMutAccess(const clang::CXXMethodDecl *method, clang::QualType base_type); - // What an Object-kind conversion of a boxed container/array should yield: // a pointer to the whole object (Ptr>), or one to its first // element (Ptr).