diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index c2e29edd31..737941dba0 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -619,7 +619,7 @@ impl_from!( /// A constant, which might appears as a const item, an anonymous const block in expressions /// or patterns, or as a constant in types with const generics. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] pub enum GeneralConstId { ConstId(ConstId), StaticId(StaticId), diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 8d66d47334..d1a1e135ff 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -10,7 +10,6 @@ use hir_def::{ type_ref::LiteralConstRef, }; use hir_expand::Lookup; -use salsa::Cycle; use stdx::never; use triomphe::Arc; @@ -220,9 +219,8 @@ pub fn try_const_isize(db: &dyn HirDatabase, c: &Const) -> Option { } } -pub(crate) fn const_eval_recover( +pub(crate) fn const_eval_cycle_result( _: &dyn HirDatabase, - _: &Cycle, _: GeneralConstId, _: Substitution, _: Option>, @@ -230,17 +228,15 @@ pub(crate) fn const_eval_recover( Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) } -pub(crate) fn const_eval_static_recover( +pub(crate) fn const_eval_static_cycle_result( _: &dyn HirDatabase, - _: &Cycle, _: StaticId, ) -> Result { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) } -pub(crate) fn const_eval_discriminant_recover( +pub(crate) fn const_eval_discriminant_cycle_result( _: &dyn HirDatabase, - _: &Cycle, _: EnumVariantId, ) -> Result { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 75a680b588..c24ef16b49 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -36,14 +36,14 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { // region:mir #[salsa::invoke(crate::mir::mir_body_query)] - #[salsa::cycle(crate::mir::mir_body_recover)] + #[salsa::cycle(cycle_result = crate::mir::mir_body_cycle_result)] fn mir_body(&self, def: DefWithBodyId) -> Result, MirLowerError>; #[salsa::invoke(crate::mir::mir_body_for_closure_query)] fn mir_body_for_closure(&self, def: InternedClosureId) -> Result, MirLowerError>; #[salsa::invoke(crate::mir::monomorphized_mir_body_query)] - #[salsa::cycle(crate::mir::monomorphized_mir_body_recover)] + #[salsa::cycle(cycle_result = crate::mir::monomorphized_mir_body_cycle_result)] fn monomorphized_mir_body( &self, def: DefWithBodyId, @@ -64,7 +64,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { fn borrowck(&self, def: DefWithBodyId) -> Result, MirLowerError>; #[salsa::invoke(crate::consteval::const_eval_query)] - #[salsa::cycle(crate::consteval::const_eval_recover)] + #[salsa::cycle(cycle_result = crate::consteval::const_eval_cycle_result)] fn const_eval( &self, def: GeneralConstId, @@ -73,11 +73,11 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { ) -> Result; #[salsa::invoke(crate::consteval::const_eval_static_query)] - #[salsa::cycle(crate::consteval::const_eval_static_recover)] + #[salsa::cycle(cycle_result = crate::consteval::const_eval_static_cycle_result)] fn const_eval_static(&self, def: StaticId) -> Result; #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)] - #[salsa::cycle(crate::consteval::const_eval_discriminant_recover)] + #[salsa::cycle(cycle_result = crate::consteval::const_eval_discriminant_cycle_result)] fn const_eval_discriminant(&self, def: EnumVariantId) -> Result; #[salsa::invoke(crate::method_resolution::lookup_impl_method_query)] @@ -91,7 +91,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { // endregion:mir #[salsa::invoke(crate::layout::layout_of_adt_query)] - #[salsa::cycle(crate::layout::layout_of_adt_recover)] + #[salsa::cycle(cycle_result = crate::layout::layout_of_adt_cycle_result)] fn layout_of_adt( &self, def: AdtId, @@ -100,7 +100,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { ) -> Result, LayoutError>; #[salsa::invoke(crate::layout::layout_of_ty_query)] - #[salsa::cycle(crate::layout::layout_of_ty_recover)] + #[salsa::cycle(cycle_result = crate::layout::layout_of_ty_cycle_result)] fn layout_of_ty(&self, ty: Ty, env: Arc) -> Result, LayoutError>; #[salsa::invoke(crate::layout::target_data_layout_query)] @@ -113,8 +113,8 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::transparent] fn ty(&self, def: TyDefId) -> Binders; - #[salsa::cycle(crate::lower::type_for_type_alias_with_diagnostics_query_recover)] #[salsa::invoke(crate::lower::type_for_type_alias_with_diagnostics_query)] + #[salsa::cycle(cycle_result = crate::lower::type_for_type_alias_with_diagnostics_cycle_result)] fn type_for_type_alias_with_diagnostics(&self, def: TypeAliasId) -> (Binders, Diagnostics); /// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is @@ -123,7 +123,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { fn value_ty(&self, def: ValueTyDefId) -> Option>; #[salsa::invoke(crate::lower::impl_self_ty_with_diagnostics_query)] - #[salsa::cycle(crate::lower::impl_self_ty_with_diagnostics_recover)] + #[salsa::cycle(cycle_result = crate::lower::impl_self_ty_with_diagnostics_cycle_result)] fn impl_self_ty_with_diagnostics(&self, def: ImplId) -> (Binders, Diagnostics); #[salsa::invoke(crate::lower::impl_self_ty_query)] @@ -165,7 +165,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { fn type_alias_impl_traits(&self, def: TypeAliasId) -> Option>>; #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] - #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] + #[salsa::cycle(cycle_result = crate::lower::generic_predicates_for_param_cycle_result)] fn generic_predicates_for_param( &self, def: GenericDefId, @@ -194,7 +194,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { fn trait_environment(&self, def: GenericDefId) -> Arc; #[salsa::invoke(crate::lower::generic_defaults_with_diagnostics_query)] - #[salsa::cycle(crate::lower::generic_defaults_with_diagnostics_recover)] + #[salsa::cycle(cycle_result = crate::lower::generic_defaults_with_diagnostics_cycle_result)] fn generic_defaults_with_diagnostics( &self, def: GenericDefId, @@ -282,7 +282,10 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { fn adt_variance(&self, adt_id: AdtId) -> chalk_db::Variances; #[salsa::invoke(crate::variance::variances_of)] - #[salsa::cycle(crate::variance::variances_of_cycle)] + #[salsa::cycle( + cycle_fn = crate::variance::variances_of_cycle_fn, + cycle_initial = crate::variance::variances_of_cycle_initial, + )] fn variances_of(&self, def: GenericDefId) -> Option>; #[salsa::invoke(chalk_db::associated_ty_value_query)] @@ -317,7 +320,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { ) -> chalk_ir::ProgramClauses; #[salsa::invoke(crate::drop::has_drop_glue)] - #[salsa::cycle(crate::drop::has_drop_glue_recover)] + #[salsa::cycle(cycle_result = crate::drop::has_drop_glue_cycle_result)] fn has_drop_glue(&self, ty: Ty, env: Arc) -> DropGlue; } diff --git a/crates/hir-ty/src/drop.rs b/crates/hir-ty/src/drop.rs index 9ea0b58559..9823c854d5 100644 --- a/crates/hir-ty/src/drop.rs +++ b/crates/hir-ty/src/drop.rs @@ -193,9 +193,8 @@ fn is_copy(db: &dyn HirDatabase, ty: Ty, env: Arc) -> bool { db.trait_solve(env.krate, env.block, goal).is_some() } -pub(crate) fn has_drop_glue_recover( +pub(crate) fn has_drop_glue_cycle_result( _db: &dyn HirDatabase, - _cycle: &salsa::Cycle, _ty: Ty, _env: Arc, ) -> DropGlue { diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index a7a4e8d404..c253fe2567 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -13,7 +13,6 @@ use hir_def::{ use la_arena::{Idx, RawIdx}; use rustc_abi::AddressSpace; use rustc_index::IndexVec; -use salsa::Cycle; use triomphe::Arc; @@ -25,7 +24,7 @@ use crate::{ utils::ClosureSubst, }; -pub(crate) use self::adt::layout_of_adt_recover; +pub(crate) use self::adt::layout_of_adt_cycle_result; pub use self::{adt::layout_of_adt_query, target::target_data_layout_query}; mod adt; @@ -365,9 +364,8 @@ pub fn layout_of_ty_query( Ok(Arc::new(result)) } -pub(crate) fn layout_of_ty_recover( +pub(crate) fn layout_of_ty_cycle_result( _: &dyn HirDatabase, - _: &Cycle, _: Ty, _: Arc, ) -> Result, LayoutError> { diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 73dba300ef..3a020bf050 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -9,7 +9,6 @@ use hir_def::{ }; use intern::sym; use rustc_index::IndexVec; -use salsa::Cycle; use smallvec::SmallVec; use triomphe::Arc; @@ -131,9 +130,8 @@ fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound, (get(sym::rustc_layout_scalar_valid_range_start), get(sym::rustc_layout_scalar_valid_range_end)) } -pub(crate) fn layout_of_adt_recover( +pub(crate) fn layout_of_adt_cycle_result( _: &dyn HirDatabase, - _: &Cycle, _: AdtId, _: Substitution, _: Arc, diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index ea42c59296..2fb6cdc02a 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -45,7 +45,6 @@ use hir_expand::name::Name; use la_arena::{Arena, ArenaMap}; use rustc_hash::FxHashSet; use rustc_pattern_analysis::Captures; -use salsa::Cycle; use stdx::{impl_from, never}; use triomphe::{Arc, ThinArc}; @@ -961,9 +960,8 @@ pub(crate) fn generic_predicates_for_param_query( GenericPredicates(predicates.is_empty().not().then(|| predicates.into())) } -pub(crate) fn generic_predicates_for_param_recover( +pub(crate) fn generic_predicates_for_param_cycle_result( _db: &dyn HirDatabase, - _cycle: &salsa::Cycle, _def: GenericDefId, _param_id: TypeOrConstParamId, _assoc_name: Option, @@ -1264,9 +1262,8 @@ pub(crate) fn generic_defaults_with_diagnostics_query( } } -pub(crate) fn generic_defaults_with_diagnostics_recover( +pub(crate) fn generic_defaults_with_diagnostics_cycle_result( _db: &dyn HirDatabase, - _cycle: &Cycle, _def: GenericDefId, ) -> (GenericDefaults, Diagnostics) { (GenericDefaults(None), None) @@ -1402,16 +1399,12 @@ fn type_for_enum_variant_constructor( } } -#[salsa::tracked(recovery_fn = type_for_adt_recovery)] +#[salsa::tracked(cycle_result = type_for_adt_cycle_result)] fn type_for_adt_tracked(db: &dyn HirDatabase, adt: AdtId) -> Binders { type_for_adt(db, adt) } -pub(crate) fn type_for_adt_recovery( - db: &dyn HirDatabase, - _cycle: &salsa::Cycle, - adt: AdtId, -) -> Binders { +fn type_for_adt_cycle_result(db: &dyn HirDatabase, adt: AdtId) -> Binders { let generics = generics(db, adt.into()); make_binders(db, &generics, TyKind::Error.intern(Interner)) } @@ -1449,9 +1442,8 @@ pub(crate) fn type_for_type_alias_with_diagnostics_query( (make_binders(db, &generics, inner), diags) } -pub(crate) fn type_for_type_alias_with_diagnostics_query_recover( +pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result( db: &dyn HirDatabase, - _cycle: &salsa::Cycle, adt: TypeAliasId, ) -> (Binders, Diagnostics) { let generics = generics(db, adt.into()); @@ -1555,12 +1547,11 @@ pub(crate) fn const_param_ty_with_diagnostics_query( (ty, create_diagnostics(ctx.diagnostics)) } -pub(crate) fn impl_self_ty_with_diagnostics_recover( +pub(crate) fn impl_self_ty_with_diagnostics_cycle_result( db: &dyn HirDatabase, - _cycle: &salsa::Cycle, impl_id: ImplId, ) -> (Binders, Diagnostics) { - let generics = generics(db, (impl_id).into()); + let generics = generics(db, impl_id.into()); (make_binders(db, &generics, TyKind::Error.intern(Interner)), None) } diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index 247f0ec429..6dc20203e0 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -41,8 +41,8 @@ use rustc_hash::FxHashMap; use smallvec::{SmallVec, smallvec}; use stdx::{impl_from, never}; -pub(crate) use lower::mir_body_recover; -pub(crate) use monomorphization::monomorphized_mir_body_recover; +pub(crate) use lower::mir_body_cycle_result; +pub(crate) use monomorphization::monomorphized_mir_body_cycle_result; use super::consteval::{intern_const_scalar, try_const_usize}; diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index c2501dc35e..557027756f 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -2,7 +2,7 @@ use std::{fmt::Write, iter, mem}; -use base_db::{Crate, salsa::Cycle}; +use base_db::Crate; use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use hir_def::{ AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId, @@ -2145,9 +2145,8 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result Result> { Err(MirLowerError::Loop) diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs index 91081d70cc..d4f10c032c 100644 --- a/crates/hir-ty/src/mir/monomorphization.rs +++ b/crates/hir-ty/src/mir/monomorphization.rs @@ -313,9 +313,8 @@ pub fn monomorphized_mir_body_query( Ok(Arc::new(body)) } -pub(crate) fn monomorphized_mir_body_recover( - _: &dyn HirDatabase, - _: &salsa::Cycle, +pub(crate) fn monomorphized_mir_body_cycle_result( + _db: &dyn HirDatabase, _: DefWithBodyId, _: Substitution, _: Arc, diff --git a/crates/hir-ty/src/variance.rs b/crates/hir-ty/src/variance.rs index d711f2e57b..4053982788 100644 --- a/crates/hir-ty/src/variance.rs +++ b/crates/hir-ty/src/variance.rs @@ -19,10 +19,10 @@ use crate::{ AliasTy, Const, ConstScalar, DynTyExt, GenericArg, GenericArgData, Interner, Lifetime, LifetimeData, Ty, TyKind, }; -use base_db::salsa::Cycle; use chalk_ir::Mutability; use hir_def::signatures::StructFlags; use hir_def::{AdtId, GenericDefId, GenericParamId, VariantId}; +use salsa::CycleRecoveryAction; use std::fmt; use std::ops::Not; use stdx::never; @@ -55,9 +55,17 @@ pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option>, + _count: u32, + _def: GenericDefId, +) -> CycleRecoveryAction>> { + CycleRecoveryAction::Fallback(result.clone()) +} + +pub(crate) fn variances_of_cycle_initial( db: &dyn HirDatabase, - _cycle: &Cycle, def: GenericDefId, ) -> Option> { let generics = generics(db, def); diff --git a/crates/query-group-macro/src/lib.rs b/crates/query-group-macro/src/lib.rs index 2e2a24908e..3ade12733a 100644 --- a/crates/query-group-macro/src/lib.rs +++ b/crates/query-group-macro/src/lib.rs @@ -10,10 +10,13 @@ use queries::{ Queries, SetterKind, TrackedQuery, Transparent, }; use quote::{ToTokens, format_ident, quote}; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::visit_mut::VisitMut; use syn::{ - Attribute, FnArg, ItemTrait, Path, TraitItem, TraitItemFn, parse_quote, parse_quote_spanned, + Attribute, FnArg, ItemTrait, Path, Token, TraitItem, TraitItemFn, parse_quote, + parse_quote_spanned, }; mod queries; @@ -106,6 +109,66 @@ enum QueryKind { Interned, } +#[derive(Default, Debug, Clone)] +struct Cycle { + cycle_fn: Option<(syn::Ident, Path)>, + cycle_initial: Option<(syn::Ident, Path)>, + cycle_result: Option<(syn::Ident, Path)>, +} + +impl Parse for Cycle { + fn parse(input: ParseStream<'_>) -> syn::Result { + let options = Punctuated::::parse_terminated(input)?; + let mut cycle_fn = None; + let mut cycle_initial = None; + let mut cycle_result = None; + for option in options { + let name = option.name.to_string(); + match &*name { + "cycle_fn" => { + if cycle_fn.is_some() { + return Err(syn::Error::new_spanned(&option.name, "duplicate option")); + } + cycle_fn = Some((option.name, option.value)); + } + "cycle_initial" => { + if cycle_initial.is_some() { + return Err(syn::Error::new_spanned(&option.name, "duplicate option")); + } + cycle_initial = Some((option.name, option.value)); + } + "cycle_result" => { + if cycle_result.is_some() { + return Err(syn::Error::new_spanned(&option.name, "duplicate option")); + } + cycle_result = Some((option.name, option.value)); + } + _ => { + return Err(syn::Error::new_spanned( + &option.name, + "unknown cycle option. Accepted values: `cycle_result`, `cycle_fn`, `cycle_initial`", + )); + } + } + } + return Ok(Self { cycle_fn, cycle_initial, cycle_result }); + + struct Option { + name: syn::Ident, + value: Path, + } + + impl Parse for Option { + fn parse(input: ParseStream) -> syn::Result { + let name = input.parse()?; + input.parse::()?; + let value = input.parse()?; + Ok(Self { name, value }) + } + } + } +} + pub(crate) fn query_group_impl( _args: proc_macro::TokenStream, input: proc_macro::TokenStream, @@ -155,8 +218,8 @@ pub(crate) fn query_group_impl( for SalsaAttr { name, tts, span } in salsa_attrs { match name.as_str() { "cycle" => { - let path = syn::parse::>(tts)?; - cycle = Some(path.0.clone()) + let c = syn::parse::>(tts)?; + cycle = Some(c.0); } "input" => { if !pat_and_tys.is_empty() { @@ -415,7 +478,7 @@ impl syn::parse::Parse for Parenthesized where T: syn::parse::Parse, { - fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + fn parse(input: ParseStream<'_>) -> syn::Result { let content; syn::parenthesized!(content in input); content.parse::().map(Parenthesized) diff --git a/crates/query-group-macro/src/queries.rs b/crates/query-group-macro/src/queries.rs index 20458acd55..edbb645bf4 100644 --- a/crates/query-group-macro/src/queries.rs +++ b/crates/query-group-macro/src/queries.rs @@ -3,13 +3,15 @@ use quote::{ToTokens, format_ident, quote, quote_spanned}; use syn::{FnArg, Ident, PatType, Path, Receiver, ReturnType, Type, parse_quote, spanned::Spanned}; +use crate::Cycle; + pub(crate) struct TrackedQuery { pub(crate) trait_name: Ident, pub(crate) signature: syn::Signature, pub(crate) pat_and_tys: Vec, pub(crate) invoke: Option, pub(crate) default: Option, - pub(crate) cycle: Option, + pub(crate) cycle: Option, pub(crate) lru: Option, pub(crate) generated_struct: Option, } @@ -34,12 +36,20 @@ impl ToTokens for TrackedQuery { let fn_ident = &sig.ident; let shim: Ident = format_ident!("{}_shim", fn_ident); - let annotation = match (self.cycle.clone(), self.lru) { - (Some(cycle), Some(lru)) => quote!(#[salsa::tracked(lru = #lru, recovery_fn = #cycle)]), - (Some(cycle), None) => quote!(#[salsa::tracked(recovery_fn = #cycle)]), - (None, Some(lru)) => quote!(#[salsa::tracked(lru = #lru)]), - (None, None) => quote!(#[salsa::tracked]), - }; + let options = self + .cycle + .as_ref() + .map(|Cycle { cycle_fn, cycle_initial, cycle_result }| { + let cycle_fn = cycle_fn.as_ref().map(|(ident, path)| quote!(#ident=#path)); + let cycle_initial = + cycle_initial.as_ref().map(|(ident, path)| quote!(#ident=#path)); + let cycle_result = cycle_result.as_ref().map(|(ident, path)| quote!(#ident=#path)); + let options = cycle_fn.into_iter().chain(cycle_initial).chain(cycle_result); + quote!(#(#options),*) + }) + .into_iter() + .chain(self.lru.map(|lru| quote!(lru = #lru))); + let annotation = quote!(#[salsa::tracked( #(#options),* )]); let pat_and_tys = &self.pat_and_tys; let params = self diff --git a/crates/query-group-macro/tests/cycle.rs b/crates/query-group-macro/tests/cycle.rs deleted file mode 100644 index 8d195cbd8d..0000000000 --- a/crates/query-group-macro/tests/cycle.rs +++ /dev/null @@ -1,265 +0,0 @@ -use std::panic::UnwindSafe; - -use expect_test::expect; -use query_group_macro::query_group; -use salsa::Setter; - -/// The queries A, B, and C in `Database` can be configured -/// to invoke one another in arbitrary ways using this -/// enum. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum CycleQuery { - None, - A, - B, - C, - AthenC, -} - -#[salsa::input] -struct ABC { - a: CycleQuery, - b: CycleQuery, - c: CycleQuery, -} - -impl CycleQuery { - fn invoke(self, db: &dyn CycleDatabase, abc: ABC) -> Result<(), Error> { - match self { - CycleQuery::A => db.cycle_a(abc), - CycleQuery::B => db.cycle_b(abc), - CycleQuery::C => db.cycle_c(abc), - CycleQuery::AthenC => { - let _ = db.cycle_a(abc); - db.cycle_c(abc) - } - CycleQuery::None => Ok(()), - } - } -} - -#[salsa::input] -struct MyInput {} - -#[salsa::tracked] -fn memoized_a(db: &dyn CycleDatabase, input: MyInput) { - memoized_b(db, input) -} - -#[salsa::tracked] -fn memoized_b(db: &dyn CycleDatabase, input: MyInput) { - memoized_a(db, input) -} - -#[salsa::tracked] -fn volatile_a(db: &dyn CycleDatabase, input: MyInput) { - db.report_untracked_read(); - volatile_b(db, input) -} - -#[salsa::tracked] -fn volatile_b(db: &dyn CycleDatabase, input: MyInput) { - db.report_untracked_read(); - volatile_a(db, input) -} - -#[track_caller] -fn extract_cycle(f: impl FnOnce() + UnwindSafe) -> salsa::Cycle { - let v = std::panic::catch_unwind(f); - if let Err(d) = &v { - if let Some(cycle) = d.downcast_ref::() { - return cycle.clone(); - } - } - panic!("unexpected value: {:?}", v) -} - -#[derive(PartialEq, Eq, Hash, Clone, Debug)] -struct Error { - cycle: Vec, -} - -#[query_group] -trait CycleDatabase: salsa::Database { - #[salsa::cycle(recover_a)] - fn cycle_a(&self, abc: ABC) -> Result<(), Error>; - - #[salsa::cycle(recover_b)] - fn cycle_b(&self, abc: ABC) -> Result<(), Error>; - - fn cycle_c(&self, abc: ABC) -> Result<(), Error>; -} - -fn cycle_a(db: &dyn CycleDatabase, abc: ABC) -> Result<(), Error> { - abc.a(db).invoke(db, abc) -} - -fn recover_a(_db: &dyn CycleDatabase, cycle: &salsa::Cycle, _abc: ABC) -> Result<(), Error> { - Err(Error { cycle: cycle.participant_keys().map(|k| format!("{k:?}")).collect() }) -} - -fn cycle_b(db: &dyn CycleDatabase, abc: ABC) -> Result<(), Error> { - abc.b(db).invoke(db, abc) -} - -fn recover_b(_db: &dyn CycleDatabase, cycle: &salsa::Cycle, _abc: ABC) -> Result<(), Error> { - Err(Error { cycle: cycle.participant_keys().map(|k| format!("{k:?}")).collect() }) -} - -fn cycle_c(db: &dyn CycleDatabase, abc: ABC) -> Result<(), Error> { - abc.c(db).invoke(db, abc) -} - -#[test] -fn cycle_memoized() { - let db = salsa::DatabaseImpl::new(); - - let input = MyInput::new(&db); - let cycle = extract_cycle(|| memoized_a(&db, input)); - let expected = expect![[r#" - [ - DatabaseKeyIndex( - IngredientIndex( - 1, - ), - Id(0), - ), - DatabaseKeyIndex( - IngredientIndex( - 2, - ), - Id(0), - ), - ] - "#]]; - expected.assert_debug_eq(&cycle.all_participants(&db)); -} - -#[test] -fn inner_cycle() { - // A --> B <-- C - // ^ | - // +-----+ - let db = salsa::DatabaseImpl::new(); - - let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::B); - let err = db.cycle_c(abc); - assert!(err.is_err()); - let expected = expect![[r#" - [ - "cycle_a_shim(Id(0))", - "cycle_b_shim(Id(0))", - ] - "#]]; - expected.assert_debug_eq(&err.unwrap_err().cycle); -} - -#[test] -fn cycle_revalidate() { - // A --> B - // ^ | - // +-----+ - let mut db = salsa::DatabaseImpl::new(); - let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::None); - assert!(db.cycle_a(abc).is_err()); - abc.set_b(&mut db).to(CycleQuery::A); // same value as default - assert!(db.cycle_a(abc).is_err()); -} - -#[test] -fn cycle_recovery_unchanged_twice() { - // A --> B - // ^ | - // +-----+ - let mut db = salsa::DatabaseImpl::new(); - let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::None); - assert!(db.cycle_a(abc).is_err()); - - abc.set_c(&mut db).to(CycleQuery::A); // force new revision - assert!(db.cycle_a(abc).is_err()); -} - -#[test] -fn cycle_appears() { - let mut db = salsa::DatabaseImpl::new(); - // A --> B - let abc = ABC::new(&db, CycleQuery::B, CycleQuery::None, CycleQuery::None); - assert!(db.cycle_a(abc).is_ok()); - - // A --> B - // ^ | - // +-----+ - abc.set_b(&mut db).to(CycleQuery::A); - assert!(db.cycle_a(abc).is_err()); -} - -#[test] -fn cycle_disappears() { - let mut db = salsa::DatabaseImpl::new(); - - // A --> B - // ^ | - // +-----+ - let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::None); - assert!(db.cycle_a(abc).is_err()); - - // A --> B - abc.set_b(&mut db).to(CycleQuery::None); - assert!(db.cycle_a(abc).is_ok()); -} - -#[test] -fn cycle_multiple() { - // No matter whether we start from A or B, we get the same set of participants: - let db = salsa::DatabaseImpl::new(); - - // Configuration: - // - // A --> B <-- C - // ^ | ^ - // +-----+ | - // | | - // +-----+ - // - // Here, conceptually, B encounters a cycle with A and then - // recovers. - let abc = ABC::new(&db, CycleQuery::B, CycleQuery::AthenC, CycleQuery::A); - - let c = db.cycle_c(abc); - let b = db.cycle_b(abc); - let a = db.cycle_a(abc); - let expected = expect![[r#" - ( - [ - "cycle_a_shim(Id(0))", - "cycle_b_shim(Id(0))", - ], - [ - "cycle_a_shim(Id(0))", - "cycle_b_shim(Id(0))", - ], - [ - "cycle_a_shim(Id(0))", - "cycle_b_shim(Id(0))", - ], - ) - "#]]; - expected.assert_debug_eq(&(c.unwrap_err().cycle, b.unwrap_err().cycle, a.unwrap_err().cycle)); -} - -#[test] -fn cycle_mixed_1() { - let db = salsa::DatabaseImpl::new(); - // A --> B <-- C - // | ^ - // +-----+ - let abc = ABC::new(&db, CycleQuery::B, CycleQuery::C, CycleQuery::B); - - let expected = expect![[r#" - [ - "cycle_b_shim(Id(0))", - "cycle_c_shim(Id(0))", - ] - "#]]; - expected.assert_debug_eq(&db.cycle_c(abc).unwrap_err().cycle); -} diff --git a/crates/rust-analyzer/src/handlers/dispatch.rs b/crates/rust-analyzer/src/handlers/dispatch.rs index de0a5aba89..3b76edf528 100644 --- a/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/crates/rust-analyzer/src/handlers/dispatch.rs @@ -4,7 +4,7 @@ use std::{ panic, thread, }; -use ide_db::base_db::salsa::{self, Cancelled, Cycle}; +use ide_db::base_db::salsa::{self, Cancelled}; use lsp_server::{ExtractError, Response, ResponseError}; use serde::{Serialize, de::DeserializeOwned}; use stdx::thread::ThreadIntent; @@ -309,14 +309,12 @@ impl RequestDispatcher<'_> { #[derive(Debug)] enum HandlerCancelledError { - PropagatedPanic, Inner(salsa::Cancelled), } impl std::error::Error for HandlerCancelledError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { - HandlerCancelledError::PropagatedPanic => None, HandlerCancelledError::Inner(cancelled) => Some(cancelled), } } @@ -349,9 +347,6 @@ where if let Some(panic_message) = panic_message { message.push_str(": "); message.push_str(panic_message) - } else if let Some(cycle) = panic.downcast_ref::() { - tracing::error!("Cycle propagated out of salsa! This is a bug: {cycle:?}"); - return Err(HandlerCancelledError::PropagatedPanic); } else if let Ok(cancelled) = panic.downcast::() { tracing::error!("Cancellation propagated out of salsa! This is a bug"); return Err(HandlerCancelledError::Inner(*cancelled));