Unnamed repository; edit this file 'description' to name the repository.
Adapt for new cycle handling changing in Salsa
Chayim Refael Friedman 2025-04-22
parent db72e2f · commit 57c019a
-rw-r--r--crates/hir-def/src/lib.rs2
-rw-r--r--crates/hir-ty/src/consteval.rs10
-rw-r--r--crates/hir-ty/src/db.rs29
-rw-r--r--crates/hir-ty/src/drop.rs3
-rw-r--r--crates/hir-ty/src/layout.rs6
-rw-r--r--crates/hir-ty/src/layout/adt.rs4
-rw-r--r--crates/hir-ty/src/lower.rs23
-rw-r--r--crates/hir-ty/src/mir.rs4
-rw-r--r--crates/hir-ty/src/mir/lower.rs5
-rw-r--r--crates/hir-ty/src/mir/monomorphization.rs5
-rw-r--r--crates/hir-ty/src/variance.rs14
-rw-r--r--crates/query-group-macro/src/lib.rs71
-rw-r--r--crates/query-group-macro/src/queries.rs24
-rw-r--r--crates/query-group-macro/tests/cycle.rs265
-rw-r--r--crates/rust-analyzer/src/handlers/dispatch.rs7
15 files changed, 133 insertions, 339 deletions
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<i128> {
}
}
-pub(crate) fn const_eval_recover(
+pub(crate) fn const_eval_cycle_result(
_: &dyn HirDatabase,
- _: &Cycle,
_: GeneralConstId,
_: Substitution,
_: Option<Arc<TraitEnvironment>>,
@@ -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<Const, ConstEvalError> {
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<i128, ConstEvalError> {
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<Arc<MirBody>, MirLowerError>;
#[salsa::invoke(crate::mir::mir_body_for_closure_query)]
fn mir_body_for_closure(&self, def: InternedClosureId) -> Result<Arc<MirBody>, 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<Arc<[BorrowckResult]>, 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<Const, ConstEvalError>;
#[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<Const, ConstEvalError>;
#[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<i128, ConstEvalError>;
#[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<Arc<Layout>, 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<TraitEnvironment>) -> Result<Arc<Layout>, 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<Ty>;
- #[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<Ty>, 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<Binders<Ty>>;
#[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<Ty>, 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<Arc<Binders<ImplTraits>>>;
#[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<TraitEnvironment>;
#[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<Arc<[crate::variance::Variance]>>;
#[salsa::invoke(chalk_db::associated_ty_value_query)]
@@ -317,7 +320,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
) -> chalk_ir::ProgramClauses<Interner>;
#[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<TraitEnvironment>) -> 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<TraitEnvironment>) -> 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<TraitEnvironment>,
) -> 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<TraitEnvironment>,
) -> Result<Arc<Layout>, 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<u128>,
(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<TraitEnvironment>,
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<Name>,
@@ -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<Ty> {
type_for_adt(db, adt)
}
-pub(crate) fn type_for_adt_recovery(
- db: &dyn HirDatabase,
- _cycle: &salsa::Cycle,
- adt: AdtId,
-) -> Binders<Ty> {
+fn type_for_adt_cycle_result(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
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<Ty>, 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<Ty>, 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<Arc<Mi
Ok(Arc::new(result))
}
-pub(crate) fn mir_body_recover(
+pub(crate) fn mir_body_cycle_result(
_db: &dyn HirDatabase,
- _cycle: &Cycle,
_def: DefWithBodyId,
) -> Result<Arc<MirBody>> {
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<crate::TraitEnvironment>,
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<Ar
variances.is_empty().not().then(|| Arc::from_iter(variances))
}
-pub(crate) fn variances_of_cycle(
+pub(crate) fn variances_of_cycle_fn(
+ _db: &dyn HirDatabase,
+ result: &Option<Arc<[Variance]>>,
+ _count: u32,
+ _def: GenericDefId,
+) -> CycleRecoveryAction<Option<Arc<[Variance]>>> {
+ CycleRecoveryAction::Fallback(result.clone())
+}
+
+pub(crate) fn variances_of_cycle_initial(
db: &dyn HirDatabase,
- _cycle: &Cycle,
def: GenericDefId,
) -> Option<Arc<[Variance]>> {
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<Self> {
+ let options = Punctuated::<Option, Token![,]>::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<Self> {
+ let name = input.parse()?;
+ input.parse::<Token![=]>()?;
+ 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::<Parenthesized<Path>>(tts)?;
- cycle = Some(path.0.clone())
+ let c = syn::parse::<Parenthesized<Cycle>>(tts)?;
+ cycle = Some(c.0);
}
"input" => {
if !pat_and_tys.is_empty() {
@@ -415,7 +478,7 @@ impl<T> syn::parse::Parse for Parenthesized<T>
where
T: syn::parse::Parse,
{
- fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
+ fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let content;
syn::parenthesized!(content in input);
content.parse::<T>().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<PatType>,
pub(crate) invoke: Option<Path>,
pub(crate) default: Option<syn::Block>,
- pub(crate) cycle: Option<Path>,
+ pub(crate) cycle: Option<Cycle>,
pub(crate) lru: Option<u32>,
pub(crate) generated_struct: Option<GeneratedInputStruct>,
}
@@ -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::<salsa::Cycle>() {
- return cycle.clone();
- }
- }
- panic!("unexpected value: {:?}", v)
-}
-
-#[derive(PartialEq, Eq, Hash, Clone, Debug)]
-struct Error {
- cycle: Vec<String>,
-}
-
-#[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::<Cycle>() {
- tracing::error!("Cycle propagated out of salsa! This is a bug: {cycle:?}");
- return Err(HandlerCancelledError::PropagatedPanic);
} else if let Ok(cancelled) = panic.downcast::<Cancelled>() {
tracing::error!("Cancellation propagated out of salsa! This is a bug");
return Err(HandlerCancelledError::Inner(*cancelled));