From b567639707c89c11183e577ca15ee1ccc8b23b0a Mon Sep 17 00:00:00 2001 From: ch1ffa Date: Tue, 23 Sep 2025 19:08:38 +0300 Subject: [PATCH 1/2] add methods to project MemoRef --- .../src/graphql_network_protocol.rs | 2 +- .../src/read_schema.rs | 10 +- crates/pico/src/{view.rs => field_view.rs} | 20 +-- crates/pico/src/lib.rs | 4 +- crates/pico/src/memo_ref.rs | 147 +++++++++++++++++- crates/pico/tests/intern.rs | 8 +- crates/pico/tests/try_ok.rs | 125 +++++++++++++++ crates/pico/tests/try_ok_split.rs | 34 ++++ crates/pico/tests/try_some.rs | 78 ++++++++++ crates/pico/tests/tuple_split.rs | 32 ++++ crates/pico_macros/src/db_macro.rs | 12 +- crates/pico_macros/src/memo_macro.rs | 18 +-- 12 files changed, 440 insertions(+), 50 deletions(-) rename crates/pico/src/{view.rs => field_view.rs} (77%) create mode 100644 crates/pico/tests/try_ok.rs create mode 100644 crates/pico/tests/try_ok_split.rs create mode 100644 crates/pico/tests/try_some.rs create mode 100644 crates/pico/tests/tuple_split.rs diff --git a/crates/graphql_network_protocol/src/graphql_network_protocol.rs b/crates/graphql_network_protocol/src/graphql_network_protocol.rs index 4cbe95e29..8ea3f9fde 100644 --- a/crates/graphql_network_protocol/src/graphql_network_protocol.rs +++ b/crates/graphql_network_protocol/src/graphql_network_protocol.rs @@ -74,7 +74,7 @@ impl NetworkProtocol for GraphQLNetworkProtocol { let mut graphql_root_types = None; let (type_system_document, type_system_extension_documents) = - parse_graphql_schema(db).to_owned()?; + parse_graphql_schema(db).try_ok()?.split(); let (mut result, mut directives, mut refetch_fields) = process_graphql_type_system_document( diff --git a/crates/graphql_network_protocol/src/read_schema.rs b/crates/graphql_network_protocol/src/read_schema.rs index 90f5d88b2..0c4324c5e 100644 --- a/crates/graphql_network_protocol/src/read_schema.rs +++ b/crates/graphql_network_protocol/src/read_schema.rs @@ -13,7 +13,7 @@ pub fn parse_graphql_schema( db: &IsographDatabase, ) -> Result< ( - MemoRef, + GraphQLTypeSystemDocument, BTreeMap>, ), WithLocation, @@ -35,18 +35,18 @@ pub fn parse_graphql_schema( .iter() { let extensions_document = - parse_schema_extensions_file(db, *schema_extension_source_id).to_owned()?; + parse_schema_extensions_file(db, *schema_extension_source_id).try_ok()?; schema_extensions.insert(*relative_path, extensions_document); } - Ok((db.intern(schema), schema_extensions)) + Ok((schema, schema_extensions)) } #[memo] pub fn parse_schema_extensions_file( db: &IsographDatabase, schema_extension_source_id: SourceId, -) -> Result, WithLocation> { +) -> Result> { let SchemaSource { content, text_source, @@ -55,5 +55,5 @@ pub fn parse_schema_extensions_file let schema_extensions = parse_schema_extensions(content, *text_source) .map_err(|with_span| with_span.to_with_location(*text_source))?; - Ok(db.intern(schema_extensions)) + Ok(schema_extensions) } diff --git a/crates/pico/src/view.rs b/crates/pico/src/field_view.rs similarity index 77% rename from crates/pico/src/view.rs rename to crates/pico/src/field_view.rs index beecb0aaf..a7c2ceedc 100644 --- a/crates/pico/src/view.rs +++ b/crates/pico/src/field_view.rs @@ -6,16 +6,16 @@ pub trait Counter: Singleton + Default + Copy + Eq + 'static { fn increment(self) -> Self; } -pub type Projector = for<'a> fn(&'a Db) -> &'a T; +pub type FieldProjector = for<'a> fn(&'a Db) -> &'a T; -pub struct View<'a, Db: Database, T, C: Counter> { +pub struct FieldView<'a, Db: Database, T, C: Counter> { db: &'a Db, - projector: Projector, + projector: FieldProjector, phantom: PhantomData, } -impl<'a, Db: Database, T, C: Counter> View<'a, Db, T, C> { - pub fn new(db: &'a Db, projector: Projector) -> Self { +impl<'a, Db: Database, T, C: Counter> FieldView<'a, Db, T, C> { + pub fn new(db: &'a Db, projector: FieldProjector) -> Self { Self { db, projector, @@ -44,16 +44,16 @@ impl<'a, Db: Database, T, C: Counter> View<'a, Db, T, C> { } } -pub type ProjectorMut = for<'a> fn(&'a mut Db) -> &'a mut T; +pub type FieldProjectorMut = for<'a> fn(&'a mut Db) -> &'a mut T; -pub struct MutView<'a, Db: Database, T, C: Counter> { +pub struct FieldViewMut<'a, Db: Database, T, C: Counter> { db: &'a mut Db, - projector: ProjectorMut, + projector: FieldProjectorMut, phantom: PhantomData, } -impl<'a, Db: Database, T, C: Counter> MutView<'a, Db, T, C> { - pub fn new(db: &'a mut Db, projector: ProjectorMut) -> Self { +impl<'a, Db: Database, T, C: Counter> FieldViewMut<'a, Db, T, C> { + pub fn new(db: &'a mut Db, projector: FieldProjectorMut) -> Self { Self { db, projector, diff --git a/crates/pico/src/lib.rs b/crates/pico/src/lib.rs index 1da3e66c8..8bc0cbd6f 100644 --- a/crates/pico/src/lib.rs +++ b/crates/pico/src/lib.rs @@ -4,6 +4,7 @@ mod derived_node; mod dyn_eq; mod epoch; mod execute_memoized_function; +mod field_view; mod garbage_collection; mod index; mod intern; @@ -11,16 +12,15 @@ pub mod macro_fns; mod memo_ref; mod retained_query; mod source; -mod view; mod with_serialize; pub use database::*; pub use derived_node::*; pub use dyn_eq::*; pub use execute_memoized_function::*; +pub use field_view::*; pub use intern::*; pub use memo_ref::*; pub use retained_query::*; pub use source::*; -pub use view::*; pub use with_serialize::*; diff --git a/crates/pico/src/memo_ref.rs b/crates/pico/src/memo_ref.rs index c6cd1887a..06ab5c250 100644 --- a/crates/pico/src/memo_ref.rs +++ b/crates/pico/src/memo_ref.rs @@ -1,13 +1,66 @@ -use std::{marker::PhantomData, ops::Deref}; +use std::{ + any::Any, + hash::{Hash, Hasher}, + marker::PhantomData, + ops::Deref, +}; +use crate::{DatabaseDyn, DerivedNodeId, ParamId, dependency::NodeKind}; use intern::InternId; -use crate::{DatabaseDyn, DerivedNodeId, ParamId, dependency::NodeKind}; +type MemoRefProjectorStep = for<'a> fn(&'a dyn Any) -> &'a dyn Any; + +const MAX_PROJECTOR_STEPS: usize = 4; + +#[inline(always)] +fn step_identity(value: &dyn Any) -> &dyn Any { + value +} + +#[inline(always)] +fn step_result_ok(value: &dyn Any) -> &dyn Any { + match value + .downcast_ref::>() + .expect("MemoRef>: underlying value has unexpected type") + { + Ok(t) => t as &dyn Any, + Err(_) => unreachable!("Ok projection used only after Ok check"), + } +} + +#[inline(always)] +fn step_option_some(value: &dyn Any) -> &dyn Any { + match value + .downcast_ref::>() + .expect("MemoRef>: underlying value has unexpected type") + { + Some(t) => t as &dyn Any, + None => unreachable!("Some projection used only after Some check"), + } +} + +#[inline(always)] +fn step_tuple_0(value: &dyn Any) -> &dyn Any { + let (t0, _) = value + .downcast_ref::<(T0, T1)>() + .expect("MemoRef<(..)>: underlying value has unexpected type"); + t0 as &dyn Any +} + +#[inline(always)] +fn step_tuple_1(value: &dyn Any) -> &dyn Any { + let (_, t1) = value + .downcast_ref::<(T0, T1)>() + .expect("MemoRef<(..)>: underlying value has unexpected type"); + t1 as &dyn Any +} #[derive(Debug)] pub struct MemoRef { pub(crate) db: *const dyn DatabaseDyn, pub(crate) derived_node_id: DerivedNodeId, + projectors: [MemoRefProjectorStep; MAX_PROJECTOR_STEPS], + projectors_len: u8, phantom: PhantomData, } @@ -27,12 +80,23 @@ impl PartialEq for MemoRef { impl Eq for MemoRef {} +#[allow(clippy::unnecessary_cast)] +impl Hash for MemoRef { + fn hash(&self, state: &mut H) { + let data_ptr = self.db as *const dyn DatabaseDyn as *const (); + data_ptr.hash(state); + self.derived_node_id.hash(state); + } +} + #[allow(clippy::unnecessary_cast)] impl MemoRef { pub fn new(db: &dyn DatabaseDyn, derived_node_id: DerivedNodeId) -> Self { Self { db: db as *const _ as *const dyn DatabaseDyn, derived_node_id, + projectors: [step_identity; MAX_PROJECTOR_STEPS], + projectors_len: 0, phantom: PhantomData, } } @@ -63,6 +127,83 @@ impl Deref for MemoRef { NodeKind::Derived(self.derived_node_id), revision.time_updated, ); - value.downcast_ref::().unwrap() + let mut any_ref: &dyn Any = value; + let len = self.projectors_len as usize; + for step in &self.projectors[..len] { + any_ref = (step)(any_ref); + } + any_ref + .downcast_ref::() + .expect("MemoRef: projector chain produced unexpected type") + } +} + +impl MemoRef> { + pub fn try_ok(self) -> Result, E> { + match self.deref() { + Ok(_) => { + let mut next = MemoRef:: { + db: self.db, + derived_node_id: self.derived_node_id, + projectors: self.projectors, + projectors_len: self.projectors_len, + phantom: PhantomData, + }; + let idx = next.projectors_len as usize; + next.projectors[idx] = step_result_ok::; + next.projectors_len += 1; + Ok(next) + } + Err(err) => Err(err.clone()), + } + } +} + +impl MemoRef> { + pub fn try_some(self) -> Option> { + match self.deref() { + Some(_) => { + let mut next = MemoRef:: { + db: self.db, + derived_node_id: self.derived_node_id, + projectors: self.projectors, + projectors_len: self.projectors_len, + phantom: PhantomData, + }; + let idx = next.projectors_len as usize; + next.projectors[idx] = step_option_some::; + next.projectors_len += 1; + Some(next) + } + None => None, + } + } +} + +impl MemoRef<(T0, T1)> { + pub fn split(self) -> (MemoRef, MemoRef) { + let mut left = MemoRef:: { + db: self.db, + derived_node_id: self.derived_node_id, + projectors: self.projectors, + projectors_len: self.projectors_len, + phantom: PhantomData, + }; + let idx = left.projectors_len as usize; + left.projectors[idx] = step_tuple_0::; + left.projectors_len += 1; + + let mut right = MemoRef:: { + db: self.db, + derived_node_id: self.derived_node_id, + projectors: self.projectors, + projectors_len: self.projectors_len, + phantom: PhantomData, + }; + let idx = right.projectors_len as usize; + right.projectors[idx] = step_tuple_1::; + right.projectors_len += 1; + + (left, right) } } diff --git a/crates/pico/tests/intern.rs b/crates/pico/tests/intern.rs index 7ba66f5b1..6c00030b4 100644 --- a/crates/pico/tests/intern.rs +++ b/crates/pico/tests/intern.rs @@ -39,16 +39,12 @@ enum ProcessInputError { } #[memo] -fn first_letter( - db: &TestDatabase, - input_id: SourceId, -) -> Result, FirstLetterError> { +fn first_letter(db: &TestDatabase, input_id: SourceId) -> Result { db.get(input_id) .value .chars() .next() .ok_or(FirstLetterError::EmptyString) - .map(|v| db.intern(v)) } #[memo] @@ -56,6 +52,6 @@ fn process_input( db: &TestDatabase, input_id: SourceId, ) -> Result, ProcessInputError> { - let result = first_letter(db, input_id).to_owned()?; + let result = first_letter(db, input_id).try_ok()?; Ok(result) } diff --git a/crates/pico/tests/try_ok.rs b/crates/pico/tests/try_ok.rs new file mode 100644 index 000000000..42883e5b0 --- /dev/null +++ b/crates/pico/tests/try_ok.rs @@ -0,0 +1,125 @@ +use pico::{Database, MemoRef, SourceId, Storage}; +use pico_macros::{Db, Source, memo}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use thiserror::Error; + +static TRACK_CLONE_COUNT: AtomicUsize = AtomicUsize::new(0); + +#[derive(Db, Default)] +struct TestDatabase { + storage: Storage, +} + +#[test] +fn try_ok_projects_ok_value() { + let mut db = TestDatabase::default(); + + let id = db.set(Input { + key: "key", + value: "asdf".to_string(), + }); + + assert_eq!(*process_input(&db, id).unwrap(), 'a'); +} + +#[test] +fn projected_memo_correctly_restored() { + let mut db = TestDatabase::default(); + + let id = db.set(Input { + key: "key", + value: "asdf".to_string(), + }); + + let result = process_input(&db, id).unwrap(); + assert_eq!(*consume_result(&db, result), 'a'); +} + +#[derive(Debug, Clone, PartialEq, Eq, Source)] +struct Input { + #[key] + pub key: &'static str, + pub value: String, +} + +#[derive(Debug, Clone, Error, PartialEq, Eq)] +enum FirstLetterError { + #[error("empty string")] + EmptyString, +} + +#[derive(Debug, Clone, Error, PartialEq, Eq)] +enum ProcessInputError { + #[error("cannot process input")] + ReadError(#[from] FirstLetterError), +} + +#[memo] +fn first_letter(db: &TestDatabase, input_id: SourceId) -> Result { + db.get(input_id) + .value + .chars() + .next() + .ok_or(FirstLetterError::EmptyString) +} + +fn process_input( + db: &TestDatabase, + input_id: SourceId, +) -> Result, ProcessInputError> { + let result = first_letter(db, input_id).try_ok()?; + Ok(result) +} + +#[memo] +fn consume_result(db: &TestDatabase, first_letter: MemoRef) -> char { + let result = *first_letter; + result +} + +#[test] +fn try_ok_never_clones_ok_value() { + let db = TestDatabase::default(); + + let result = ok_value(&db).try_ok().expect("expected ok result"); + let _ = &*result; + + match err_value(&db).try_ok() { + Ok(_) => panic!("expected error"), + Err(_err) => {} + } + + assert_eq!(TRACK_CLONE_COUNT.load(Ordering::SeqCst), 1); +} + +#[memo] +fn ok_value(_db: &TestDatabase) -> Result { + Ok(PanicOnClone) +} + +#[memo] +fn err_value(_db: &TestDatabase) -> Result { + Err(TrackCloneError::Count) +} + +#[derive(Debug, PartialEq, Eq)] +struct PanicOnClone; + +impl Clone for PanicOnClone { + fn clone(&self) -> Self { + panic!("PanicOnClone should not be cloned"); + } +} + +#[derive(Debug, Error, PartialEq, Eq)] +enum TrackCloneError { + #[error("error cloned")] + Count, +} + +impl Clone for TrackCloneError { + fn clone(&self) -> Self { + TRACK_CLONE_COUNT.fetch_add(1, Ordering::SeqCst); + TrackCloneError::Count + } +} diff --git a/crates/pico/tests/try_ok_split.rs b/crates/pico/tests/try_ok_split.rs new file mode 100644 index 000000000..b8053b7b7 --- /dev/null +++ b/crates/pico/tests/try_ok_split.rs @@ -0,0 +1,34 @@ +use pico::Storage; +use pico_macros::{Db, memo}; + +#[derive(Db, Default)] +struct TestDatabase { + storage: Storage, +} + +#[test] +fn try_ok_then_split_projects_each_tuple_value() { + let db = TestDatabase::default(); + + let (left, right) = ok_pair_value(&db) + .try_ok() + .expect("expected Ok result before split") + .split(); + + let _ = &*left; + let _ = &*right; +} + +#[memo] +fn ok_pair_value(_db: &TestDatabase) -> Result<(PanicOnClone, PanicOnClone), ()> { + Ok((PanicOnClone, PanicOnClone)) +} + +#[derive(Debug, PartialEq, Eq)] +struct PanicOnClone; + +impl Clone for PanicOnClone { + fn clone(&self) -> Self { + panic!("PanicOnClone should not be cloned"); + } +} diff --git a/crates/pico/tests/try_some.rs b/crates/pico/tests/try_some.rs new file mode 100644 index 000000000..9e4ee86af --- /dev/null +++ b/crates/pico/tests/try_some.rs @@ -0,0 +1,78 @@ +use pico::{Database, SourceId, Storage}; +use pico_macros::{Db, Source, memo}; + +#[derive(Db, Default)] +struct TestDatabase { + storage: Storage, +} + +#[test] +fn try_some_projects_some_value() { + let mut db = TestDatabase::default(); + + let id = db.set(Input { + key: "key", + value: "asdf".to_string(), + }); + + let Some(result) = maybe_first_letter(&db, id).try_some() else { + panic!("expected Some") + }; + assert_eq!(*result, 'a'); +} + +#[test] +fn try_some_handles_none() { + let mut db = TestDatabase::default(); + + let id = db.set(Input { + key: "key", + value: "".to_string(), + }); + + assert!(maybe_first_letter(&db, id).try_some().is_none()); +} + +#[derive(Debug, Clone, PartialEq, Eq, Source)] +struct Input { + #[key] + pub key: &'static str, + pub value: String, +} + +#[memo] +fn maybe_first_letter(db: &TestDatabase, input_id: SourceId) -> Option { + db.get(input_id).value.chars().next() +} + +#[test] +fn try_some_never_clones_value() { + let db = TestDatabase::default(); + + let some_ref = some_value(&db); + let Some(inner) = some_ref.try_some() else { + panic!("expected Some") + }; + let _ = &*inner; + + assert!(none_value(&db).try_some().is_none()); +} + +#[memo] +fn some_value(_db: &TestDatabase) -> Option { + Some(PanicOnClone) +} + +#[memo] +fn none_value(_db: &TestDatabase) -> Option { + None +} + +#[derive(Debug, PartialEq, Eq)] +struct PanicOnClone; + +impl Clone for PanicOnClone { + fn clone(&self) -> Self { + panic!("PanicOnClone should not be cloned"); + } +} diff --git a/crates/pico/tests/tuple_split.rs b/crates/pico/tests/tuple_split.rs new file mode 100644 index 000000000..7cf5022d4 --- /dev/null +++ b/crates/pico/tests/tuple_split.rs @@ -0,0 +1,32 @@ +use pico::Storage; +use pico_macros::{Db, memo}; + +#[derive(Db, Default)] +struct TestDatabase { + storage: Storage, +} + +#[test] +fn split_projects_each_tuple_value() { + let db = TestDatabase::default(); + + let pair = pair_value(&db); + let (left, right) = pair.split(); + + let _ = &*left; + let _ = &*right; +} + +#[memo] +fn pair_value(_db: &TestDatabase) -> (PanicOnClone, PanicOnClone) { + (PanicOnClone, PanicOnClone) +} + +#[derive(Debug, PartialEq, Eq)] +struct PanicOnClone; + +impl Clone for PanicOnClone { + fn clone(&self) -> Self { + panic!("PanicOnClone should not be cloned"); + } +} diff --git a/crates/pico_macros/src/db_macro.rs b/crates/pico_macros/src/db_macro.rs index fab39cd35..e39cecb4d 100644 --- a/crates/pico_macros/src/db_macro.rs +++ b/crates/pico_macros/src/db_macro.rs @@ -80,17 +80,17 @@ pub(crate) fn db_macro(item: TokenStream) -> TokenStream { }); struct_impl.push(quote! { - const #proj_ident: ::pico::Projector<#struct_ident #type_generics, #field_ty> = |db: &#struct_ident #type_generics| &db.#field_ident; - const #proj_ident_mut: ::pico::ProjectorMut<#struct_ident #type_generics, #field_ty> = |db: &mut #struct_ident #type_generics| &mut db.#field_ident; + const #proj_ident: ::pico::FieldProjector<#struct_ident #type_generics, #field_ty> = |db: &#struct_ident #type_generics| &db.#field_ident; + const #proj_ident_mut: ::pico::FieldProjectorMut<#struct_ident #type_generics, #field_ty> = |db: &mut #struct_ident #type_generics| &mut db.#field_ident; #[inline] - pub fn #get_ident(&self) -> ::pico::View<'_, #struct_ident #type_generics, #field_ty, #counter_ident> { - ::pico::View::new(self, Self::#proj_ident) + pub fn #get_ident(&self) -> ::pico::FieldView<'_, #struct_ident #type_generics, #field_ty, #counter_ident> { + ::pico::FieldView::new(self, Self::#proj_ident) } #[inline] - pub fn #get_ident_mut(&mut self) -> ::pico::MutView<'_, #struct_ident #type_generics, #field_ty, #counter_ident> { - ::pico::MutView::new(self, Self::#proj_ident_mut) + pub fn #get_ident_mut(&mut self) -> ::pico::FieldViewMut<'_, #struct_ident #type_generics, #field_ty, #counter_ident> { + ::pico::FieldViewMut::new(self, Self::#proj_ident_mut) } }); } diff --git a/crates/pico_macros/src/memo_macro.rs b/crates/pico_macros/src/memo_macro.rs index 1445343c9..8ee1e527e 100644 --- a/crates/pico_macros/src/memo_macro.rs +++ b/crates/pico_macros/src/memo_macro.rs @@ -34,7 +34,7 @@ pub(crate) fn memo_macro(_args: TokenStream, item: TokenStream) -> TokenStream { }); let param_ids_blocks = args.clone().map(|(arg, ty)| match ArgType::parse(ty) { - ArgType::Source | ArgType::MemoRef => { + ArgType::Source => { let param_arg = match **ty { syn::Type::Reference(_) => quote!((*(#arg))), _ => quote!(#arg), @@ -84,18 +84,6 @@ pub(crate) fn memo_macro(_args: TokenStream, item: TokenStream) -> TokenStream { }; } } - ArgType::MemoRef => { - let binding_expr = match **ty { - syn::Type::Reference(_) => quote!(&::pico::MemoRef::new(#db_arg, param_id.into())), - _ => quote!(::pico::MemoRef::new(#db_arg, param_id.into())), - }; - quote! { - let #arg: #ty = { - let param_id = derived_node_id.params[#i]; - #binding_expr - }; - } - } ArgType::Other => { let (target_type, binding_expr) = match **ty { syn::Type::Reference(ref reference) => (&reference.elem, quote!(inner)), @@ -155,7 +143,6 @@ fn hash(input: &Signature) -> u64 { enum ArgType { Source, - MemoRef, Other, } @@ -164,9 +151,6 @@ impl ArgType { if type_is(ty, "SourceId") { return ArgType::Source; } - if type_is(ty, "MemoRef") { - return ArgType::MemoRef; - } ArgType::Other } } From dc7f6c72db23fd29aa375db39fffed16dcbbd726 Mon Sep 17 00:00:00 2001 From: ch1ffa Date: Fri, 3 Oct 2025 18:32:11 +0300 Subject: [PATCH 2/2] add len check --- crates/pico/src/memo_ref.rs | 27 ++++++++++++++------------- crates/pico/tests/try_some.rs | 10 ++++------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/crates/pico/src/memo_ref.rs b/crates/pico/src/memo_ref.rs index 06ab5c250..ceca6f3a4 100644 --- a/crates/pico/src/memo_ref.rs +++ b/crates/pico/src/memo_ref.rs @@ -127,7 +127,12 @@ impl Deref for MemoRef { NodeKind::Derived(self.derived_node_id), revision.time_updated, ); - let mut any_ref: &dyn Any = value; + if self.projectors_len == 0 { + return value + .downcast_ref::() + .expect("MemoRef: projector chain produced unexpected type"); + } + let mut any_ref = value; let len = self.projectors_len as usize; for step in &self.projectors[..len] { any_ref = (step)(any_ref); @@ -146,12 +151,11 @@ impl MemoRef> { db: self.db, derived_node_id: self.derived_node_id, projectors: self.projectors, - projectors_len: self.projectors_len, + projectors_len: self.projectors_len + 1, phantom: PhantomData, }; - let idx = next.projectors_len as usize; + let idx = self.projectors_len as usize; next.projectors[idx] = step_result_ok::; - next.projectors_len += 1; Ok(next) } Err(err) => Err(err.clone()), @@ -167,12 +171,11 @@ impl MemoRef> { db: self.db, derived_node_id: self.derived_node_id, projectors: self.projectors, - projectors_len: self.projectors_len, + projectors_len: self.projectors_len + 1, phantom: PhantomData, }; - let idx = next.projectors_len as usize; + let idx = self.projectors_len as usize; next.projectors[idx] = step_option_some::; - next.projectors_len += 1; Some(next) } None => None, @@ -186,23 +189,21 @@ impl MemoRef<(T0, T1)> { db: self.db, derived_node_id: self.derived_node_id, projectors: self.projectors, - projectors_len: self.projectors_len, + projectors_len: self.projectors_len + 1, phantom: PhantomData, }; - let idx = left.projectors_len as usize; + let idx = self.projectors_len as usize; left.projectors[idx] = step_tuple_0::; - left.projectors_len += 1; let mut right = MemoRef:: { db: self.db, derived_node_id: self.derived_node_id, projectors: self.projectors, - projectors_len: self.projectors_len, + projectors_len: self.projectors_len + 1, phantom: PhantomData, }; - let idx = right.projectors_len as usize; + let idx = self.projectors_len as usize; right.projectors[idx] = step_tuple_1::; - right.projectors_len += 1; (left, right) } diff --git a/crates/pico/tests/try_some.rs b/crates/pico/tests/try_some.rs index 9e4ee86af..392d468da 100644 --- a/crates/pico/tests/try_some.rs +++ b/crates/pico/tests/try_some.rs @@ -15,9 +15,9 @@ fn try_some_projects_some_value() { value: "asdf".to_string(), }); - let Some(result) = maybe_first_letter(&db, id).try_some() else { - panic!("expected Some") - }; + let result = maybe_first_letter(&db, id) + .try_some() + .expect("expected Some"); assert_eq!(*result, 'a'); } @@ -50,9 +50,7 @@ fn try_some_never_clones_value() { let db = TestDatabase::default(); let some_ref = some_value(&db); - let Some(inner) = some_ref.try_some() else { - panic!("expected Some") - }; + let inner = some_ref.try_some().expect("expected Some"); let _ = &*inner; assert!(none_value(&db).try_some().is_none());