1use crate::runtime::region_heap::HeapIndex;
42use crate::types::RegionId;
43use std::fmt;
44use std::hash::{Hash, Hasher};
45use std::marker::PhantomData;
46
47pub struct RRef<T> {
63 region_id: RegionId,
65 index: HeapIndex,
67 _marker: PhantomData<T>,
77}
78
79impl<T> Clone for RRef<T> {
81 #[inline]
82 fn clone(&self) -> Self {
83 *self
84 }
85}
86
87impl<T> Copy for RRef<T> {}
88
89impl<T> fmt::Debug for RRef<T> {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 f.debug_struct("RRef")
92 .field("region_id", &self.region_id)
93 .field("index", &self.index)
94 .finish()
95 }
96}
97
98impl<T> PartialEq for RRef<T> {
99 #[inline]
100 fn eq(&self, other: &Self) -> bool {
101 self.region_id == other.region_id && self.index == other.index
102 }
103}
104
105impl<T> Eq for RRef<T> {}
106
107impl<T> Hash for RRef<T> {
108 #[inline]
109 fn hash<H: Hasher>(&self, state: &mut H) {
110 self.region_id.hash(state);
111 self.index.hash(state);
112 }
113}
114
115impl<T> RRef<T> {
127 #[inline]
129 #[must_use]
130 pub const fn region_id(&self) -> RegionId {
131 self.region_id
132 }
133
134 #[inline]
136 #[must_use]
137 pub const fn heap_index(&self) -> HeapIndex {
138 self.index
139 }
140}
141
142impl<T: Send + Sync + 'static> RRef<T> {
144 #[inline]
164 #[must_use]
165 pub const fn new(region_id: RegionId, index: HeapIndex) -> Self {
166 Self {
167 region_id,
168 index,
169 _marker: PhantomData,
170 }
171 }
172}
173
174#[derive(Debug, Clone, PartialEq, Eq)]
176pub enum RRefError {
177 RegionNotFound(RegionId),
179 AllocationInvalid,
181 RegionMismatch {
183 expected: RegionId,
185 actual: RegionId,
187 },
188 RegionClosed,
190 WrongRegion,
192}
193
194impl fmt::Display for RRefError {
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 match self {
197 Self::RegionNotFound(id) => write!(f, "region not found: {id:?}"),
198 Self::AllocationInvalid => write!(f, "heap allocation is invalid"),
199 Self::RegionMismatch { expected, actual } => {
200 write!(f, "region mismatch: expected {expected:?}, got {actual:?}")
201 }
202 Self::RegionClosed => write!(f, "region is closed"),
203 Self::WrongRegion => write!(f, "access witness references wrong region"),
204 }
205 }
206}
207
208impl std::error::Error for RRefError {}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq)]
223pub struct RRefAccessWitness {
224 region_id: RegionId,
225}
226
227impl RRefAccessWitness {
228 #[must_use]
233 pub(crate) const fn new(region_id: RegionId) -> Self {
234 Self { region_id }
235 }
236
237 #[must_use]
239 pub const fn region(&self) -> RegionId {
240 self.region_id
241 }
242}
243
244impl<T> RRef<T> {
245 pub fn validate_witness(&self, witness: &RRefAccessWitness) -> Result<(), RRefError> {
250 if witness.region() != self.region_id {
251 return Err(RRefError::WrongRegion);
252 }
253 Ok(())
254 }
255}
256
257pub trait RRefAccess {
262 fn rref_get<T: Clone + 'static>(&self, rref: &RRef<T>) -> Result<T, RRefError>;
266
267 fn rref_with<T: 'static, R, F: FnOnce(&T) -> R>(
271 &self,
272 rref: &RRef<T>,
273 f: F,
274 ) -> Result<R, RRefError>;
275
276 fn rref_get_with<T: Clone + 'static>(
281 &self,
282 rref: &RRef<T>,
283 witness: RRefAccessWitness,
284 ) -> Result<T, RRefError>;
285
286 fn rref_with_witness<T: 'static, R, F: FnOnce(&T) -> R>(
290 &self,
291 rref: &RRef<T>,
292 witness: RRefAccessWitness,
293 f: F,
294 ) -> Result<R, RRefError>;
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300 use crate::record::RegionRecord;
301 use crate::types::Budget;
302 use crate::util::ArenaIndex;
303
304 fn test_region_id() -> RegionId {
305 RegionId::from_arena(ArenaIndex::new(0, 0))
306 }
307
308 #[test]
309 fn rref_is_copy_and_clone() {
310 let region_id = test_region_id();
311 let record = RegionRecord::new(region_id, None, Budget::INFINITE);
312 let index = record.heap_alloc(42u32).expect("heap alloc");
313 let rref = RRef::<u32>::new(region_id, index);
314
315 let rref2 = rref;
317 assert_eq!(rref.region_id(), rref2.region_id());
318
319 assert_clone::<RRef<u32>>();
321 }
322
323 #[test]
324 fn rref_equality() {
325 let region_id = test_region_id();
326 let record = RegionRecord::new(region_id, None, Budget::INFINITE);
327
328 let index1 = record.heap_alloc(1u32).expect("heap alloc");
329 let index2 = record.heap_alloc(2u32).expect("heap alloc");
330
331 let rref1a = RRef::<u32>::new(region_id, index1);
332 let rref1_clone = RRef::<u32>::new(region_id, index1);
333 let rref2 = RRef::<u32>::new(region_id, index2);
334
335 assert_eq!(rref1a, rref1_clone);
336 assert_ne!(rref1a, rref2);
337 }
338
339 #[test]
340 fn rref_accessors() {
341 let region_id = test_region_id();
342 let record = RegionRecord::new(region_id, None, Budget::INFINITE);
343 let index = record.heap_alloc("hello".to_string()).expect("heap alloc");
344 let rref = RRef::<String>::new(region_id, index);
345
346 assert_eq!(rref.region_id(), region_id);
347 assert_eq!(rref.heap_index(), index);
348 }
349
350 #[test]
351 fn rref_debug_format() {
352 let region_id = test_region_id();
353 let record = RegionRecord::new(region_id, None, Budget::INFINITE);
354 let index = record.heap_alloc(42u32).expect("heap alloc");
355 let rref = RRef::<u32>::new(region_id, index);
356
357 let debug_str = format!("{rref:?}");
358 assert!(debug_str.contains("RRef"));
359 assert!(debug_str.contains("region_id"));
360 assert!(debug_str.contains("index"));
361 }
362
363 #[test]
364 fn rref_access_through_region_record() {
365 let region_id = test_region_id();
366 let record = RegionRecord::new(region_id, None, Budget::INFINITE);
367 let index = record.heap_alloc("hello".to_string()).expect("heap alloc");
368 let rref = RRef::<String>::new(region_id, index);
369
370 let value = record.rref_get(&rref).expect("rref_get");
371 assert_eq!(value, "hello");
372
373 let len = record.rref_with(&rref, String::len).expect("rref_with");
374 assert_eq!(len, 5);
375 }
376
377 #[test]
378 fn rref_region_mismatch_is_error() {
379 let region_a = test_region_id();
380 let region_b = RegionId::from_arena(ArenaIndex::new(1, 0));
381 let record_a = RegionRecord::new(region_a, None, Budget::INFINITE);
382 let record_b = RegionRecord::new(region_b, None, Budget::INFINITE);
383
384 let index = record_a.heap_alloc(7u32).expect("heap alloc");
385 let rref = RRef::<u32>::new(region_a, index);
386
387 let err = record_b.rref_get(&rref).expect_err("region mismatch");
388 assert_eq!(
389 err,
390 RRefError::RegionMismatch {
391 expected: region_a,
392 actual: region_b,
393 }
394 );
395 }
396
397 fn assert_clone<T: Clone>() {}
399 fn assert_send<T: Send>() {}
400 fn assert_sync<T: Sync>() {}
401
402 #[test]
403 fn rref_send_sync_bounds() {
404 assert_send::<RRef<u32>>();
406 assert_send::<RRef<String>>();
407 assert_send::<RRef<Vec<i32>>>();
408
409 assert_sync::<RRef<u32>>();
411 assert_sync::<RRef<String>>();
412 assert_sync::<RRef<Vec<i32>>>();
413 }
414
415 #[test]
420 fn validate_witness_matching_region_succeeds() {
421 let rid = test_region_id();
422 let record = RegionRecord::new(rid, None, Budget::INFINITE);
423 let index = record.heap_alloc(42u32).expect("heap alloc");
424 let rref = RRef::<u32>::new(rid, index);
425
426 let witness = RRefAccessWitness::new(rid);
427 assert!(rref.validate_witness(&witness).is_ok());
428 }
429
430 #[test]
431 fn validate_witness_wrong_region_fails() {
432 let rid_a = test_region_id();
433 let rid_b = RegionId::from_arena(ArenaIndex::new(77, 0));
434 let record = RegionRecord::new(rid_a, None, Budget::INFINITE);
435 let index = record.heap_alloc(42u32).expect("heap alloc");
436 let rref = RRef::<u32>::new(rid_a, index);
437
438 let wrong_witness = RRefAccessWitness::new(rid_b);
439 let err = rref.validate_witness(&wrong_witness);
440 assert_eq!(err.unwrap_err(), RRefError::WrongRegion);
441 }
442
443 #[test]
444 fn access_witness_is_copy_and_eq() {
445 let rid = test_region_id();
446 let w1 = RRefAccessWitness::new(rid);
447 let w2 = w1; assert_eq!(w1, w2);
449 assert_eq!(w1.region(), rid);
450 }
451
452 #[test]
453 fn rref_error_display_coverage() {
454 let rid = test_region_id();
455 let cases: Vec<(RRefError, &str)> = vec![
456 (RRefError::RegionNotFound(rid), "region not found"),
457 (RRefError::AllocationInvalid, "heap allocation is invalid"),
458 (
459 RRefError::RegionMismatch {
460 expected: rid,
461 actual: rid,
462 },
463 "region mismatch",
464 ),
465 (RRefError::RegionClosed, "region is closed"),
466 (
467 RRefError::WrongRegion,
468 "access witness references wrong region",
469 ),
470 ];
471
472 for (err, expected_substring) in cases {
473 let msg = format!("{err}");
474 assert!(
475 msg.contains(expected_substring),
476 "expected '{expected_substring}' in '{msg}'"
477 );
478 }
479 }
480}