1use crate::gc_protect::ProtectScope;
4use crate::{R_xlen_t, SEXP, SEXPTYPE, SexpExt};
5
6#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for NativeBorrow { }
#[automatically_derived]
impl ::core::clone::Clone for NativeBorrow {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<BorrowShape>;
let _: ::core::clone::AssertParamIsClone<usize>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for NativeBorrow { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for NativeBorrow {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "NativeBorrow",
"shape", &self.shape, "list_depth", &&self.list_depth)
}
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for NativeBorrow { }
#[automatically_derived]
impl ::core::cmp::PartialEq for NativeBorrow {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.shape == other.shape && self.list_depth == other.list_depth
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for NativeBorrow {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<BorrowShape>;
let _: ::core::cmp::AssertParamIsEq<usize>;
}
}Eq)]
12pub struct NativeBorrow {
13 shape: BorrowShape,
14 list_depth: usize,
15}
16
17#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for BorrowShape { }
#[automatically_derived]
impl ::core::clone::Clone for BorrowShape {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<SEXPTYPE>;
let _: ::core::clone::AssertParamIsClone<bool>;
let _:
::core::clone::AssertParamIsClone<&'static [Option<NativeBorrow>]>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for BorrowShape { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for BorrowShape {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::Vector {
sexptype: __self_0, mutable: __self_1, scalar: __self_2 } =>
::core::fmt::Formatter::debug_struct_field3_finish(f,
"Vector", "sexptype", __self_0, "mutable", __self_1,
"scalar", &__self_2),
Self::Tuple(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Tuple",
&__self_0),
}
}
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for BorrowShape { }
#[automatically_derived]
impl ::core::cmp::PartialEq for BorrowShape {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other) &&
match (self, other) {
(Self::Vector {
sexptype: __self_0, mutable: __self_1, scalar: __self_2 },
Self::Vector {
sexptype: __arg1_0, mutable: __arg1_1, scalar: __arg1_2 })
=>
__self_1 == __arg1_1 && __self_2 == __arg1_2 &&
__self_0 == __arg1_0,
(Self::Tuple(__self_0), Self::Tuple(__arg1_0)) =>
__self_0 == __arg1_0,
_ => unsafe { ::core::intrinsics::unreachable() }
}
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for BorrowShape {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<SEXPTYPE>;
let _: ::core::cmp::AssertParamIsEq<bool>;
let _: ::core::cmp::AssertParamIsEq<&'static [Option<NativeBorrow>]>;
}
}Eq)]
18enum BorrowShape {
19 Vector {
20 sexptype: SEXPTYPE,
21 mutable: bool,
22 scalar: bool,
23 },
24 Tuple(&'static [Option<NativeBorrow>]),
25}
26
27impl NativeBorrow {
28 pub const fn scalar(sexptype: SEXPTYPE, mutable: bool) -> Self {
30 Self {
31 shape: BorrowShape::Vector {
32 sexptype,
33 mutable,
34 scalar: true,
35 },
36 list_depth: 0,
37 }
38 }
39
40 pub const fn slice(sexptype: SEXPTYPE, mutable: bool) -> Self {
42 Self {
43 shape: BorrowShape::Vector {
44 sexptype,
45 mutable,
46 scalar: false,
47 },
48 list_depth: 0,
49 }
50 }
51
52 pub const fn tuple(fields: &'static [Option<Self>]) -> Option<Self> {
55 let mut index = 0;
56 while index < fields.len() {
57 if fields[index].is_some() {
58 return Some(Self {
59 shape: BorrowShape::Tuple(fields),
60 list_depth: 0,
61 });
62 }
63 index += 1;
64 }
65 None
66 }
67
68 pub const fn in_list(borrow: Option<Self>) -> Option<Self> {
70 match borrow {
71 Some(mut borrow) => {
72 borrow.list_depth += 1;
73 Some(borrow)
74 }
75 None => None,
76 }
77 }
78
79 fn conflicts_with(self, other: Self) -> bool {
80 match (self.shape, other.shape) {
81 (BorrowShape::Tuple(fields), _) => {
82 fields.iter().flatten().any(|a| a.conflicts_with(other))
83 }
84 (_, BorrowShape::Tuple(fields)) => {
85 fields.iter().flatten().any(|b| self.conflicts_with(*b))
86 }
87 (
88 BorrowShape::Vector {
89 sexptype: a,
90 mutable: am,
91 ..
92 },
93 BorrowShape::Vector {
94 sexptype: b,
95 mutable: bm,
96 ..
97 },
98 ) => (am || bm) && a == b,
99 }
100 }
101
102 fn needs_internal_check(self) -> bool {
103 if self.list_depth > 0 {
104 return self.conflicts_with(self);
105 }
106 match self.shape {
107 BorrowShape::Tuple(fields) => needs_check(fields),
108 BorrowShape::Vector { .. } => false,
109 }
110 }
111
112 fn accepts_length(self, length: R_xlen_t) -> bool {
113 match self.shape {
114 BorrowShape::Vector { scalar: true, .. } => length == 1,
115 BorrowShape::Vector { scalar: false, .. } => length > 0,
116 BorrowShape::Tuple(_) => false,
117 }
118 }
119}
120
121#[doc(hidden)]
126pub fn needs_check(borrows: &[Option<NativeBorrow>]) -> bool {
127 for (i, borrow) in borrows.iter().enumerate() {
128 let Some(a) = borrow else { continue };
129 if a.needs_internal_check() {
130 return true;
131 }
132 if borrows[i + 1..]
133 .iter()
134 .flatten()
135 .any(|b| a.conflicts_with(*b))
136 {
137 return true;
138 }
139 }
140 false
141}
142
143#[doc(hidden)]
151pub unsafe fn assert_no_aliases<const N: usize>(
152 args: [(SEXP, &str); N],
153 borrows: [Option<NativeBorrow>; N],
154) {
155 let mut errors = Vec::new();
156 for i in 0..N {
157 let Some(a) = borrows[i] else { continue };
158 for j in i..N {
159 let Some(b) = borrows[j] else { continue };
160 let same_arg = i == j;
161 if !a.conflicts_with(b) || (same_arg && !a.needs_internal_check()) {
162 continue;
163 }
164 let mut path_a = Vec::new();
165 let mut path_b = Vec::new();
166 let aliases = unsafe {
167 visit_leaves(
168 args[i].0,
169 a,
170 &mut path_a,
171 &mut |leaf_a, indices_a, borrow_a| {
172 visit_leaves(
173 args[j].0,
174 b,
175 &mut path_b,
176 &mut |leaf_b, indices_b, borrow_b| {
177 (!same_arg || indices_a < indices_b)
178 && leaf_a == leaf_b
179 && borrow_a.conflicts_with(borrow_b)
180 },
181 )
182 },
183 )
184 };
185 if aliases {
186 let name_a = args[i].1;
187 let name_b = args[j].1;
188 errors.push(if same_arg {
189 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("parameter `{0}` contains duplicate elements that borrow the same non-empty R vector mutably",
name_a))
})format!("parameter `{name_a}` contains duplicate elements that borrow the same non-empty R vector mutably")
190 } else {
191 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("parameters `{0}` and `{1}` borrow the same non-empty R object, and at least one borrows it mutably; pass distinct vectors",
name_a, name_b))
})format!("parameters `{name_a}` and `{name_b}` borrow the same non-empty R object, and at least one borrows it mutably; pass distinct vectors")
192 });
193 }
194 }
195 }
196 if !errors.is_empty() {
{
::core::panicking::panic_fmt(format_args!("aliasing native arguments: {0}",
errors.join("; ")));
}
};assert!(
197 errors.is_empty(),
198 "aliasing native arguments: {}",
199 errors.join("; ")
200 );
201}
202
203unsafe fn visit_leaves(
206 sexp: SEXP,
207 borrow: NativeBorrow,
208 path: &mut Vec<R_xlen_t>,
209 visit: &mut impl FnMut(SEXP, &[R_xlen_t], NativeBorrow) -> bool,
210) -> bool {
211 if borrow.list_depth == 0
212 && let BorrowShape::Vector { sexptype, .. } = borrow.shape
213 {
214 return sexp.type_of() == sexptype
215 && borrow.accepts_length(sexp.xlength())
216 && visit(sexp, path, borrow);
217 }
218 if sexp.type_of() != SEXPTYPE::VECSXP {
219 return false;
220 }
221 let length = sexp.xlength();
222 if borrow.list_depth == 0
223 && let BorrowShape::Tuple(fields) = borrow.shape
224 && usize::try_from(length).ok() != Some(fields.len())
225 {
226 return false;
228 }
229 for index in 0..length {
230 let child_borrow = if borrow.list_depth > 0 {
231 NativeBorrow {
232 list_depth: borrow.list_depth - 1,
233 ..borrow
234 }
235 } else {
236 let BorrowShape::Tuple(fields) = borrow.shape else {
237 ::core::panicking::panic("internal error: entered unreachable code")unreachable!()
238 };
239 let Some(field) = fields[usize::try_from(index).unwrap()] else {
240 continue;
241 };
242 field
243 };
244 let scope = unsafe { ProtectScope::new() };
245 let child = unsafe { scope.protect(sexp.vector_elt(index)).get() };
246 path.push(index);
247 let found = unsafe { visit_leaves(child, child_borrow, path, visit) };
248 path.pop();
249 if found {
250 return true;
251 }
252 }
253 false
254}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259 use crate::from_r::TryFromSexp;
260
261 #[test]
262 fn native_borrow_metadata_follows_actual_converters() {
263 macro_rules! check {
264 ($t:ty, $sexptype:ident) => {{
265 let shared = Some(NativeBorrow::scalar(SEXPTYPE::$sexptype, false));
266 let mutable = Some(NativeBorrow::scalar(SEXPTYPE::$sexptype, true));
267 assert_eq!(<&$t>::NATIVE_BORROW, shared);
268 assert_eq!(<&mut $t>::NATIVE_BORROW, mutable);
269 assert_eq!(<Option<&$t>>::NATIVE_BORROW, shared);
270 assert_eq!(<Option<&mut $t>>::NATIVE_BORROW, mutable);
271 assert_eq!(
272 <Vec<&mut $t>>::NATIVE_BORROW,
273 NativeBorrow::in_list(mutable)
274 );
275 assert_eq!(
276 <Box<[Option<&mut $t>]>>::NATIVE_BORROW,
277 NativeBorrow::in_list(mutable)
278 );
279 assert_eq!(
280 <Option<Vec<Vec<&mut $t>>>>::NATIVE_BORROW,
281 NativeBorrow::in_list(NativeBorrow::in_list(mutable))
282 );
283 let slice = Some(NativeBorrow::slice(SEXPTYPE::$sexptype, true));
284 assert_eq!(<&mut [$t]>::NATIVE_BORROW, slice);
285 assert_eq!(
286 <Box<[Option<&mut [$t]>]>>::NATIVE_BORROW,
287 NativeBorrow::in_list(slice)
288 );
289 assert_eq!(<Vec<$t>>::NATIVE_BORROW, None);
290 assert_eq!(<Box<[$t]>>::NATIVE_BORROW, None);
291 }};
292 }
293 check!(i32, INTSXP);
294 check!(f64, REALSXP);
295 check!(u8, RAWSXP);
296 check!(crate::RLogical, LGLSXP);
297 check!(crate::Rcomplex, CPLXSXP);
298 type HiddenBorrow = Option<Vec<Vec<&'static mut i32>>>;
299 assert!(HiddenBorrow::NATIVE_BORROW.is_some());
300 }
301
302 #[test]
303 fn forwarding_wrappers_preserve_native_borrow_metadata() {
304 type Leaf = &'static mut i32;
305 let scalar = Leaf::NATIVE_BORROW;
306 let list = NativeBorrow::in_list(scalar);
307 assert_eq!(<Result<Leaf, ()>>::NATIVE_BORROW, scalar);
308 assert_eq!(<crate::Missing<Leaf>>::NATIVE_BORROW, scalar);
309 assert_eq!(
310 <std::collections::HashMap<String, Leaf>>::NATIVE_BORROW,
311 list
312 );
313 assert_eq!(
314 <Option<std::collections::BTreeMap<String, Leaf>>>::NATIVE_BORROW,
315 list
316 );
317 assert_eq!(
318 <Vec<std::collections::HashMap<String, Leaf>>>::NATIVE_BORROW,
319 NativeBorrow::in_list(list)
320 );
321 assert_eq!(
322 <std::borrow::Cow<'static, [i32]> as TryFromSexp>::NATIVE_BORROW,
323 None
324 );
325 assert_eq!(
326 <std::borrow::Cow<'_, [i32]> as crate::TryFromRoot<'_>>::NATIVE_BORROW,
327 <&[i32]>::NATIVE_BORROW
328 );
329 assert_eq!(
330 <crate::RCow<'static, i32>>::NATIVE_BORROW,
331 <&[i32]>::NATIVE_BORROW
332 );
333 #[cfg(feature = "indexmap")]
334 assert_eq!(<indexmap::IndexMap<String, Leaf>>::NATIVE_BORROW, list);
335 }
336
337 #[test]
338 fn custom_reference_conversions_default_to_no_native_borrow() {
339 struct RLogical;
340 impl TryFromSexp for &RLogical {
341 type Error = ();
342 fn try_from_sexp(_: SEXP) -> Result<Self, Self::Error> {
343 unreachable!()
344 }
345 }
346 assert_eq!(<&RLogical>::NATIVE_BORROW, None);
347 assert_eq!(<String>::NATIVE_BORROW, None);
348 }
349
350 #[test]
351 fn metadata_avoids_unneeded_walks_and_preserves_length_rules() {
352 let shared = NativeBorrow::scalar(SEXPTYPE::INTSXP, false);
353 let mutable = NativeBorrow::scalar(SEXPTYPE::INTSXP, true);
354 assert!(!needs_check(&[]));
355 assert!(!needs_check(&[None, Some(shared), Some(shared)]));
356 assert!(!needs_check(&[Some(mutable)]));
357 assert!(!needs_check(&[
358 Some(mutable),
359 Some(NativeBorrow::slice(SEXPTYPE::REALSXP, true))
360 ]));
361 assert!(needs_check(&[Some(mutable), Some(shared)]));
362 assert!(needs_check(&[NativeBorrow::in_list(Some(mutable))]));
363 assert!(mutable.accepts_length(1));
364 assert!(!mutable.accepts_length(0));
365 assert!(!mutable.accepts_length(2));
366 assert!(NativeBorrow::slice(SEXPTYPE::INTSXP, true).accepts_length(2));
367 assert!(!NativeBorrow::slice(SEXPTYPE::INTSXP, true).accepts_length(0));
368 }
369
370 #[test]
371 fn tuple_metadata_keeps_field_shapes_and_owned_positions() {
372 type Pair = (&'static mut i32, &'static i32);
373 assert_eq!(
374 Pair::NATIVE_BORROW,
375 NativeBorrow::tuple(&[<&mut i32>::NATIVE_BORROW, <&i32>::NATIVE_BORROW])
376 );
377 assert!(needs_check(&[Pair::NATIVE_BORROW]));
378 assert_eq!(<(i32, String)>::NATIVE_BORROW, None);
379 assert!(!needs_check(&[<(i32, &mut i32)>::NATIVE_BORROW]));
380 assert!(!needs_check(&[<(&i32, &i32)>::NATIVE_BORROW]));
381 assert!(!needs_check(&[<(&mut i32, &f64)>::NATIVE_BORROW]));
382 assert!(needs_check(&[<((i32, &mut i32), (&i32,))>::NATIVE_BORROW]));
383 assert!(needs_check(&[
384 <(&mut [i32], String)>::NATIVE_BORROW,
385 <&[i32]>::NATIVE_BORROW,
386 ]));
387 assert!(needs_check(&[<(Vec<&mut i32>,)>::NATIVE_BORROW,]));
388 assert!(needs_check(&[NativeBorrow::in_list(Pair::NATIVE_BORROW)]));
389 }
390}