diff --git a/Cargo.toml b/Cargo.toml index 8b80d249..85baf443 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,12 +35,8 @@ memoization = [] # Allows extending chumsky by writing your own parser implementations. extension = [] -# Make builtin parsers such as `Boxed` use atomic instead of non-atomic internals. -# TODO: Remove or rework this -sync = ["spin"] - # Enable Pratt parsing combinator -pratt = ["unstable"] +pratt = [] # Allow the use of unstable features (aka features where the API is not settled) unstable = [] @@ -66,7 +62,7 @@ docsrs = [] # An alias of all features that work with the stable compiler. # Do not use this feature, its removal is not considered a breaking change and its behaviour may change. # If you're working on chumsky and you're adding a feature that does not require nightly support, please add it to this list. -_test_stable = ["std", "stacker", "memoization", "extension", "sync"] +_test_stable = ["std", "stacker", "memoization", "extension", "pratt"] [package.metadata.docs.rs] all-features = true @@ -76,7 +72,7 @@ rustdoc-args = ["--cfg", "docsrs"] hashbrown = "0.15" stacker = { version = "0.1", optional = true } regex-automata = { version = "0.3", default-features = false, optional = true, features = ["alloc", "meta", "perf", "unicode", "nfa", "dfa", "hybrid"] } -spin = { version = "0.9", features = ["once"], default-features = false, optional = true } +spin = { version = "0.9", features = ["once"], default-features = false } lexical = { version = "6.1.1", default-features = false, features = ["parse-integers", "parse-floats", "format"], optional = true } either = { version = "1.8.1", optional = true } serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } diff --git a/examples/mini_ml.rs b/examples/mini_ml.rs index 3fa7280e..01d13916 100644 --- a/examples/mini_ml.rs +++ b/examples/mini_ml.rs @@ -116,7 +116,7 @@ where I: BorrowInput<'tokens, Token = Token<'src>, Span = SimpleSpan>, // Because this function is generic over the input type, we need the caller to tell us how to create a new input, // `I`, from a nested token tree. This function serves that purpose. - M: Fn(SimpleSpan, &'tokens [Spanned>]) -> I + Clone + 'src, + M: Fn(SimpleSpan, &'tokens [Spanned>]) -> I + Clone + Send + Sync + 'src, { recursive(|expr| { let ident = select_ref! { Token::Ident(x) => *x }; diff --git a/examples/nested_spans.rs b/examples/nested_spans.rs index 1cff24fc..bed546e6 100644 --- a/examples/nested_spans.rs +++ b/examples/nested_spans.rs @@ -13,7 +13,7 @@ enum Token { fn parser<'src, I, M>(make_input: M) -> impl Parser<'src, I, i64> where I: BorrowInput<'src, Token = Token, Span = SimpleSpan>, - M: Fn(SimpleSpan, &'src [(Token, SimpleSpan)]) -> I + Clone + 'src, + M: Fn(SimpleSpan, &'src [(Token, SimpleSpan)]) -> I + Send + Sync + Clone + 'src, { recursive(|expr| { let num = select_ref! { Token::Num(x) => *x }; diff --git a/src/lib.rs b/src/lib.rs index 47faa5d3..55120495 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,8 +89,8 @@ pub mod prelude { use crate::input::InputOwn; use alloc::{ boxed::Box, - rc::{self, Rc}, string::String, + sync::{Arc, Weak}, vec, vec::Vec, }; @@ -157,9 +157,10 @@ impl Unpin for EmptyPhantom {} impl core::panic::UnwindSafe for EmptyPhantom {} impl core::panic::RefUnwindSafe for EmptyPhantom {} -pub(crate) type DynParser<'src, 'b, I, O, E> = dyn Parser<'src, I, O, E> + 'b; +pub(crate) type DynParser<'src, 'b, I, O, E> = dyn Parser<'src, I, O, E> + Send + Sync + 'b; #[cfg(feature = "pratt")] -pub(crate) type DynOperator<'src, 'b, I, O, E> = dyn pratt::Operator<'src, I, O, E> + 'b; +pub(crate) type DynOperator<'src, 'b, I, O, E> = + dyn pratt::Operator<'src, I, O, E> + Send + Sync + 'b; /// Labels corresponding to a variety of patterns. #[derive(Clone, Debug, PartialEq)] @@ -2201,10 +2202,10 @@ pub trait Parser<'src, I: Input<'src>, O, E: ParserExtra<'src, I> = extra::Defau /// fn boxed<'b>(self) -> Boxed<'src, 'b, I, O, E> where - Self: Sized + 'b, + Self: Sized + Send + Sync + 'b, { Boxed { - inner: Rc::new(self), + inner: Arc::new(self), } } @@ -2795,12 +2796,11 @@ where /// See [`Parser::boxed`]. /// -/// Due to current implementation details, the inner value is not, in fact, a [`Box`], but is an [`Rc`] to facilitate -/// efficient cloning. This is likely to change in the future. Unlike [`Box`], [`Rc`] has no size guarantees: although +/// Due to current implementation details, the inner value is not, in fact, a [`Box`], but is an [`Arc`] to facilitate +/// efficient cloning. This is likely to change in the future. Unlike [`Box`], [`Arc`] has no size guarantees: although /// it is *currently* the same size as a raw pointer. -// TODO: Don't use an Rc (why?) pub struct Boxed<'src, 'b, I: Input<'src>, O, E: ParserExtra<'src, I> = extra::Default> { - inner: Rc>, + inner: Arc>, } impl<'src, I: Input<'src>, O, E: ParserExtra<'src, I>> Clone for Boxed<'src, '_, I, O, E> { diff --git a/src/pratt.rs b/src/pratt.rs index dbbd115b..89f6dfe0 100644 --- a/src/pratt.rs +++ b/src/pratt.rs @@ -205,9 +205,9 @@ where /// Box this operator, allowing it to be used via dynamic dispatch. fn boxed<'a>(self) -> Boxed<'src, 'a, I, O, E> where - Self: Sized + 'a, + Self: Sized + Send + Sync + 'a, { - Boxed(Rc::new(self)) + Boxed(Arc::new(self)) } #[doc(hidden)] @@ -317,7 +317,7 @@ where } /// A boxed pratt parser operator. See [`Operator`]. -pub struct Boxed<'src, 'a, I, O, E = extra::Default>(Rc>); +pub struct Boxed<'src, 'a, I, O, E = extra::Default>(Arc>); impl Clone for Boxed<'_, '_, I, O, E> { fn clone(&self) -> Self { diff --git a/src/recovery.rs b/src/recovery.rs index 6860fbfc..40500011 100644 --- a/src/recovery.rs +++ b/src/recovery.rs @@ -239,10 +239,10 @@ pub fn nested_delimiters<'src, 'parse, I, O, E, F, const N: usize>( ) -> impl Parser<'src, I, O, E> + Clone + 'parse where I: ValueInput<'src>, - I::Token: PartialEq + Clone, + I::Token: PartialEq + Clone + Send + Sync, E: extra::ParserExtra<'src, I> + 'parse, 'src: 'parse, - F: Fn(I::Span) -> O + Clone + 'parse, + F: Fn(I::Span) -> O + Clone + Send + Sync + 'parse, { // TODO: Does this actually work? TESTS! #[allow(clippy::tuple_array_conversions)] diff --git a/src/recursive.rs b/src/recursive.rs index b1969ebc..298394e3 100644 --- a/src/recursive.rs +++ b/src/recursive.rs @@ -10,35 +10,10 @@ use super::*; -struct OnceCell(core::cell::Cell>); -impl OnceCell { - pub fn new() -> Self { - Self(core::cell::Cell::new(None)) - } - pub fn set(&self, x: T) -> Result<(), ()> { - // SAFETY: Function is not reentrant so we have exclusive access to the inner data - unsafe { - let vacant = (*self.0.as_ptr()).is_none(); - if vacant { - self.0.as_ptr().write(Some(x)); - Ok(()) - } else { - Err(()) - } - } - } - #[inline] - pub fn get(&self) -> Option<&T> { - // SAFETY: We ensure that we never insert twice (so the inner `T` always lives as long as us, if it exists) and - // neither function is possibly reentrant so there's no way we can invalidate mut xor shared aliasing - unsafe { (*self.0.as_ptr()).as_ref() } - } -} - // TODO: Ensure that this doesn't produce leaks enum RecursiveInner { - Owned(Rc), - Unowned(rc::Weak), + Owned(Arc), + Unowned(Weak), } /// Type for recursive parsers that are defined through a call to `recursive`, and as such @@ -48,7 +23,7 @@ pub type Direct<'src, 'b, I, O, Extra> = DynParser<'src, 'b, I, O, Extra>; /// Type for recursive parsers that are defined through a call to [`Recursive::declare`], and as /// such require an additional layer of allocation. pub struct Indirect<'src, 'b, I: Input<'src>, O, Extra: ParserExtra<'src, I>> { - inner: OnceCell>>, + inner: spin::Once>>, } /// A parser that can be defined in terms of itself by separating its [declaration](Recursive::declare) from its @@ -100,29 +75,21 @@ impl<'src, 'b, I: Input<'src>, O, E: ParserExtra<'src, I>> Recursive Self { Recursive { - inner: RecursiveInner::Owned(Rc::new(Indirect { - inner: OnceCell::new(), + inner: RecursiveInner::Owned(Arc::new(Indirect { + inner: spin::Once::new(), })), } } /// Defines the parser after declaring it, allowing it to be used for parsing. - // INFO: Clone bound not actually needed, but good to be safe for future compat - #[track_caller] - pub fn define + Clone + 'b>(&mut self, parser: P) { - let location = *Location::caller(); - self.parser() - .inner - .set(Box::new(parser)) - .unwrap_or_else(|_| { - panic!("recursive parsers can only be defined once, trying to redefine it at {location}") - }); + pub fn define + Send + Sync + 'b>(&mut self, parser: P) { + self.parser().inner.call_once(|| Box::new(parser)); } } impl Recursive

{ #[inline] - fn parser(&self) -> Rc

{ + fn parser(&self) -> Arc

{ match &self.inner { RecursiveInner::Owned(x) => x.clone(), RecursiveInner::Unowned(x) => x @@ -243,11 +210,11 @@ pub fn recursive<'src, 'b, I, O, E, A, F>(f: F) -> Recursive, E: ParserExtra<'src, I>, - A: Parser<'src, I, O, E> + Clone + 'b, + A: Parser<'src, I, O, E> + Send + Sync + 'b, F: FnOnce(Recursive>) -> A, { - let rc = Rc::new_cyclic(|rc| { - let rc: rc::Weak> = rc.clone() as _; + let rc = Arc::new_cyclic(|rc| { + let rc: Weak> = rc.clone() as _; let parser = Recursive { inner: RecursiveInner::Unowned(rc.clone()), }; diff --git a/src/text.rs b/src/text.rs index 4e997c25..ae25cf5e 100644 --- a/src/text.rs +++ b/src/text.rs @@ -251,7 +251,8 @@ where /// // ...including none at all! /// assert_eq!(whitespace.parse("").into_result(), Ok(())); /// ``` -pub fn whitespace<'src, I, E>() -> Repeated + Copy, (), I, E> +pub fn whitespace<'src, I, E>( +) -> Repeated + Copy + Send + Sync, (), I, E> where I: StrInput<'src>, I::Token: Char + 'src, @@ -287,7 +288,8 @@ where /// // ... but not newlines /// assert!(inline_whitespace.at_least(1).parse("\n\r").has_errors()); /// ``` -pub fn inline_whitespace<'src, I, E>() -> Repeated + Copy, (), I, E> +pub fn inline_whitespace<'src, I, E>( +) -> Repeated + Copy + Send + Sync, (), I, E> where I: StrInput<'src>, I::Token: Char + 'src, @@ -335,7 +337,7 @@ where /// assert_eq!(newline.parse("\u{2029}").into_result(), Ok(())); /// ``` #[must_use] -pub fn newline<'src, I, E>() -> impl Parser<'src, I, (), E> + Copy +pub fn newline<'src, I, E>() -> impl Parser<'src, I, (), E> + Copy + Send + Sync where I: StrInput<'src>, I::Token: Char + 'src, @@ -398,7 +400,7 @@ where #[must_use] pub fn digits<'src, I, E>( radix: u32, -) -> Repeated>::Token, E> + Copy, I::Token, I, E> +) -> Repeated>::Token, E> + Copy + Send + Sync, I::Token, I, E> where I: StrInput<'src>, I::Token: Char + 'src, @@ -446,10 +448,12 @@ where /// ``` /// #[must_use] -pub fn int<'src, I, E>(radix: u32) -> impl Parser<'src, I, >::Slice, E> + Copy +pub fn int<'src, I, E>( + radix: u32, +) -> impl Parser<'src, I, >::Slice, E> + Copy + Send + Sync where I: StrInput<'src>, - I::Token: Char + 'src, + I::Token: Char + Send + Sync + 'src, E: ParserExtra<'src, I>, E::Error: LabelError<'src, I, TextExpected<'src, I>> + LabelError<'src, I, MaybeRef<'src, I::Token>>, @@ -486,7 +490,8 @@ pub mod ascii { /// An identifier is defined as an ASCII alphabetic character or an underscore followed by any number of alphanumeric /// characters or underscores. The regex pattern for it is `[a-zA-Z_][a-zA-Z0-9_]*`. #[must_use] - pub fn ident<'src, I, E>() -> impl Parser<'src, I, >::Slice, E> + Copy + pub fn ident<'src, I, E>( + ) -> impl Parser<'src, I, >::Slice, E> + Copy + Send + Sync where I: StrInput<'src>, I::Token: Char + 'src, @@ -539,12 +544,12 @@ pub mod ascii { #[track_caller] pub fn keyword<'src, I, S, E>( keyword: S, - ) -> impl Parser<'src, I, >::Slice, E> + Clone + 'src + ) -> impl Parser<'src, I, >::Slice, E> + Clone + Send + Sync + 'src where I: StrInput<'src>, I::Slice: PartialEq, I::Token: Char + fmt::Debug + 'src, - S: PartialEq + Clone + 'src, + S: PartialEq + Clone + Send + Sync + 'src, E: ParserExtra<'src, I> + 'src, E::Error: LabelError<'src, I, TextExpected<'src, I>> + LabelError<'src, I, S>, { @@ -981,7 +986,8 @@ pub mod unicode { /// /// An identifier is defined as per "Default Identifiers" in [Unicode Standard Annex #31](https://www.unicode.org/reports/tr31/). #[must_use] - pub fn ident<'src, I, E>() -> impl Parser<'src, I, >::Slice, E> + Copy + pub fn ident<'src, I, E>( + ) -> impl Parser<'src, I, >::Slice, E> + Copy + Send + Sync where I: StrInput<'src>, I::Token: Char + 'src, @@ -1028,12 +1034,12 @@ pub mod unicode { #[track_caller] pub fn keyword<'src, I, S, E>( keyword: S, - ) -> impl Parser<'src, I, >::Slice, E> + Clone + 'src + ) -> impl Parser<'src, I, >::Slice, E> + Clone + Send + Sync + 'src where I: StrInput<'src>, I::Slice: PartialEq, I::Token: Char + fmt::Debug + 'src, - S: PartialEq + Clone + 'src, + S: PartialEq + Clone + Send + Sync + 'src, E: ParserExtra<'src, I> + 'src, E::Error: LabelError<'src, I, TextExpected<'src, I>> + LabelError<'src, I, S>, { @@ -1095,7 +1101,7 @@ mod tests { fn make_ascii_kw_parser<'src, I>(s: I::Slice) -> impl Parser<'src, I, ()> where I: crate::StrInput<'src>, - I::Slice: PartialEq + Clone, + I::Slice: PartialEq + Send + Sync + Clone, I::Token: crate::Char + fmt::Debug + 'src, { text::ascii::keyword(s).ignored() @@ -1104,7 +1110,7 @@ mod tests { fn make_unicode_kw_parser<'src, I>(s: I::Slice) -> impl Parser<'src, I, ()> where I: crate::StrInput<'src>, - I::Slice: PartialEq + Clone, + I::Slice: PartialEq + Clone + Send + Sync, I::Token: crate::Char + fmt::Debug + 'src, { text::unicode::keyword(s).ignored()