diff --git a/crates/roc_std/src/lib.rs b/crates/roc_std/src/lib.rs index d95c384ed7..608e6060c3 100644 --- a/crates/roc_std/src/lib.rs +++ b/crates/roc_std/src/lib.rs @@ -18,8 +18,8 @@ mod roc_str; mod storage; pub use roc_box::RocBox; -pub use roc_list::{RocList, SendSafeRocList}; -pub use roc_str::{InteriorNulError, RocStr, SendSafeRocStr}; +pub use roc_list::{RocList, SendSafeRocList, ReadOnlyRocList}; +pub use roc_str::{InteriorNulError, RocStr, SendSafeRocStr, ReadOnlyRocStr}; pub use storage::Storage; // A list of C functions that are being imported diff --git a/crates/roc_std/src/roc_list.rs b/crates/roc_std/src/roc_list.rs index 1d2892d08b..ec8da32866 100644 --- a/crates/roc_std/src/roc_list.rs +++ b/crates/roc_std/src/roc_list.rs @@ -172,7 +172,7 @@ where /// There is no way to tell how many references it has and if it is safe to free. /// As such, only values that should have a static lifetime for the entire application run /// should be considered for marking read-only. - pub unsafe fn set_readonly(&self) { + pub unsafe fn set_readonly(&mut self) { if let Some((_, storage)) = self.elements_and_storage() { storage.set(Storage::Readonly); } @@ -916,6 +916,64 @@ where } } +#[repr(transparent)] +pub struct ReadOnlyRocList(RocList) +where + T: RocRefcounted; + +unsafe impl Send for ReadOnlyRocList where T: Send + RocRefcounted {} +unsafe impl Sync for ReadOnlyRocList where T: Sync + RocRefcounted {} + +impl RocRefcounted for ReadOnlyRocList +where + T: RocRefcounted, +{ + fn inc(&mut self) { + } + + fn dec(&mut self) { + } + + fn is_refcounted() -> bool { + true + } +} + +impl Clone for ReadOnlyRocList +where + T: Clone + RocRefcounted, +{ + fn clone(&self) -> Self { + ReadOnlyRocList(self.0.clone()) + } +} + +impl From> for ReadOnlyRocList +where + T: Clone + RocRefcounted, +{ + fn from(mut l: RocList) -> Self { + if l.is_unique() { + unsafe {l.set_readonly()}; + } + if l.is_readonly() { + ReadOnlyRocList(l) + } else { + // This is not unique, do a deep copy. + ReadOnlyRocList::from(RocList::from_slice(&l)) + } + } +} + +impl From> for RocList +where + T: RocRefcounted, +{ + fn from(l: ReadOnlyRocList) -> Self { + l.0 + } +} + #[cfg(feature = "serde")] struct RocListVisitor where diff --git a/crates/roc_std/src/roc_str.rs b/crates/roc_std/src/roc_str.rs index a59a0a4a91..3c699a92a1 100644 --- a/crates/roc_std/src/roc_str.rs +++ b/crates/roc_std/src/roc_str.rs @@ -824,6 +824,50 @@ impl RocRefcounted for RocStr { } } +#[repr(transparent)] +pub struct ReadOnlyRocStr(RocStr); + +unsafe impl Send for ReadOnlyRocStr {} +unsafe impl Sync for ReadOnlyRocStr {} + +impl RocRefcounted for ReadOnlyRocStr { + fn inc(&mut self) { + } + + fn dec(&mut self) { + } + + fn is_refcounted() -> bool { + true + } +} + +impl Clone for ReadOnlyRocStr { + fn clone(&self) -> Self { + ReadOnlyRocStr(self.0.clone()) + } +} + +impl From for ReadOnlyRocStr { + fn from(mut s: RocStr) -> Self { + if s.is_unique() { + unsafe {s.set_readonly()}; + } + if s.is_readonly() { + ReadOnlyRocStr(s) + } else { + // This is not readonly or unique, do a deep copy. + ReadOnlyRocStr::from(RocStr::from(s.as_str())) + } + } +} + +impl From for RocStr { + fn from(s: ReadOnlyRocStr) -> Self { + s.0 + } +} + #[repr(C)] struct BigString { elements: NonNull,