Idiomatic salsa use for enum variants query

This commit is contained in:
Lukas Wirth 2025-06-15 08:43:22 +02:00
parent f68512af65
commit aa2d234426
33 changed files with 81 additions and 77 deletions

View File

@ -134,7 +134,9 @@ pulldown-cmark-to-cmark = "10.0.4"
pulldown-cmark = { version = "0.9.6", default-features = false }
rayon = "1.10.0"
rowan = "=0.15.15"
salsa = { version = "0.22.0", default-features = false, features = ["rayon","salsa_unstable"] }
# Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work
# on impls without it
salsa = { version = "0.22.0", default-features = true, features = ["rayon","salsa_unstable", "macros"] }
salsa-macros = "0.22.0"
semver = "1.0.26"
serde = { version = "1.0.219" }

View File

@ -8,7 +8,6 @@ use hir_expand::{
use intern::{Symbol, sym};
use la_arena::ArenaMap;
use syntax::{AstPtr, ast};
use thin_vec::ThinVec;
use triomphe::Arc;
use crate::{
@ -32,9 +31,9 @@ use crate::{
diagnostics::DefDiagnostics,
},
signatures::{
ConstSignature, EnumSignature, EnumVariants, FunctionSignature, ImplSignature,
InactiveEnumVariantCode, StaticSignature, StructSignature, TraitAliasSignature,
TraitSignature, TypeAliasSignature, UnionSignature, VariantFields,
ConstSignature, EnumSignature, FunctionSignature, ImplSignature, StaticSignature,
StructSignature, TraitAliasSignature, TraitSignature, TypeAliasSignature, UnionSignature,
VariantFields,
},
tt,
visibility::{self, Visibility},
@ -121,19 +120,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase {
id: VariantId,
) -> (Arc<VariantFields>, Arc<ExpressionStoreSourceMap>);
// FIXME: Should we make this transparent? The only unstable thing in `enum_variants_with_diagnostics()`
// is ast ids, and ast ids are pretty stable now.
#[salsa::tracked]
fn enum_variants(&self, id: EnumId) -> Arc<EnumVariants> {
self.enum_variants_with_diagnostics(id).0
}
#[salsa::invoke(EnumVariants::enum_variants_query)]
fn enum_variants_with_diagnostics(
&self,
id: EnumId,
) -> (Arc<EnumVariants>, Option<Arc<ThinVec<InactiveEnumVariantCode>>>);
#[salsa::transparent]
#[salsa::invoke(ImplItems::impl_items_query)]
fn impl_items(&self, e: ImplId) -> Arc<ImplItems>;

View File

@ -137,7 +137,7 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt
let loc = variant.lookup(ctx.db);
if let Some(mut path) = find_path_inner(ctx, ItemInNs::Types(loc.parent.into()), max_len) {
path.push_segment(
ctx.db.enum_variants(loc.parent).variants[loc.index as usize].1.clone(),
loc.parent.enum_variants(ctx.db).variants[loc.index as usize].1.clone(),
);
return Some(path);
}

View File

@ -125,7 +125,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt
}
ModuleDefId::AdtId(AdtId::EnumId(e)) => {
lang_items.collect_lang_item(db, e, LangItemTarget::EnumId);
db.enum_variants(e).variants.iter().for_each(|&(id, _, _)| {
e.enum_variants(db).variants.iter().for_each(|&(id, _, _)| {
lang_items.collect_lang_item(db, id, LangItemTarget::EnumVariant);
});
}

View File

@ -51,6 +51,7 @@ pub mod visibility;
use intern::{Interned, sym};
pub use rustc_abi as layout;
use thin_vec::ThinVec;
use triomphe::Arc;
pub use crate::signatures::LocalFieldId;
@ -88,7 +89,7 @@ use crate::{
db::DefDatabase,
hir::generics::{LocalLifetimeParamId, LocalTypeOrConstParamId},
nameres::{LocalDefMap, block_def_map, crate_def_map, crate_local_def_map},
signatures::VariantFields,
signatures::{EnumVariants, InactiveEnumVariantCode, VariantFields},
};
type FxIndexMap<K, V> = indexmap::IndexMap<K, V, rustc_hash::FxBuildHasher>;
@ -253,6 +254,21 @@ impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union);
pub type EnumLoc = ItemLoc<ast::Enum>;
impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
impl EnumId {
#[inline]
pub fn enum_variants(self, db: &dyn DefDatabase) -> &EnumVariants {
&self.enum_variants_with_diagnostics(db).0
}
#[inline]
pub fn enum_variants_with_diagnostics(
self,
db: &dyn DefDatabase,
) -> &(EnumVariants, Option<ThinVec<InactiveEnumVariantCode>>) {
EnumVariants::of(db, self)
}
}
type ConstLoc = AssocItemLoc<ast::Const>;
impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);

View File

@ -995,9 +995,8 @@ impl<'db> DefCollector<'db> {
Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
cov_mark::hit!(glob_enum);
// glob import from enum => just import all the variants
let resolutions = self
.db
.enum_variants(e)
let resolutions = e
.enum_variants(self.db)
.variants
.iter()
.map(|&(variant, ref name, _)| {

View File

@ -528,8 +528,8 @@ impl DefMap {
// enum variant
cov_mark::hit!(can_import_enum_variant);
let res = db
.enum_variants(e)
let res = e
.enum_variants(db)
.variants
.iter()
.find(|(_, name, _)| name == segment)

View File

@ -172,8 +172,7 @@ fn no() {}
"ast_id_map_shim",
"parse_shim",
"real_span_map_shim",
"enum_variants_shim",
"enum_variants_with_diagnostics_shim",
"of_",
]
"#]],
expect![[r#"
@ -182,7 +181,7 @@ fn no() {}
"ast_id_map_shim",
"file_item_tree_query",
"real_span_map_shim",
"enum_variants_with_diagnostics_shim",
"of_",
]
"#]],
);

View File

@ -884,11 +884,13 @@ pub struct EnumVariants {
pub variants: Box<[(EnumVariantId, Name, FieldsShape)]>,
}
#[salsa::tracked]
impl EnumVariants {
pub(crate) fn enum_variants_query(
#[salsa::tracked(returns(ref))]
pub(crate) fn of(
db: &dyn DefDatabase,
e: EnumId,
) -> (Arc<EnumVariants>, Option<Arc<ThinVec<InactiveEnumVariantCode>>>) {
) -> (EnumVariants, Option<ThinVec<InactiveEnumVariantCode>>) {
let loc = e.lookup(db);
let source = loc.source(db);
let ast_id_map = db.ast_id_map(source.file_id);
@ -898,7 +900,7 @@ impl EnumVariants {
let cfg_options = loc.container.krate.cfg_options(db);
let mut index = 0;
let Some(variants) = source.value.variant_list() else {
return (Arc::new(EnumVariants { variants: Box::default() }), None);
return (EnumVariants { variants: Box::default() }, None);
};
let variants = variants
.variants()
@ -926,12 +928,11 @@ impl EnumVariants {
})
.collect();
(
Arc::new(EnumVariants { variants }),
diagnostics.is_empty().not().then(|| Arc::new(diagnostics)),
)
(EnumVariants { variants }, diagnostics.is_empty().not().then_some(diagnostics))
}
}
impl EnumVariants {
pub fn variant(&self, name: &Name) -> Option<EnumVariantId> {
self.variants.iter().find_map(|(v, n, _)| if n == name { Some(*v) } else { None })
}

View File

@ -823,8 +823,8 @@ pub(crate) fn adt_datum_query(
(rust_ir::AdtKind::Struct, vec![variant_id_to_fields(id.into())])
}
hir_def::AdtId::EnumId(id) => {
let variants = db
.enum_variants(id)
let variants = id
.enum_variants(db)
.variants
.iter()
.map(|&(variant_id, _, _)| variant_id_to_fields(variant_id.into()))

View File

@ -286,7 +286,7 @@ pub(crate) fn const_eval_discriminant_variant(
let value = match prev_idx {
Some(prev_idx) => {
1 + db.const_eval_discriminant(
db.enum_variants(loc.parent).variants[prev_idx as usize].0,
loc.parent.enum_variants(db).variants[prev_idx as usize].0,
)?
}
_ => 0,

View File

@ -395,7 +395,7 @@ impl<'a> DeclValidator<'a> {
/// Check incorrect names for enum variants.
fn validate_enum_variants(&mut self, enum_id: EnumId) {
let data = self.db.enum_variants(enum_id);
let data = enum_id.enum_variants(self.db);
for (variant_id, _, _) in data.variants.iter() {
self.validate_enum_variant_fields(*variant_id);

View File

@ -642,7 +642,7 @@ fn missing_match_arms<'p>(
}
let non_empty_enum = match scrut_ty.as_adt() {
Some((AdtId::EnumId(e), _)) => !cx.db.enum_variants(e).variants.is_empty(),
Some((AdtId::EnumId(e), _)) => !e.enum_variants(cx.db).variants.is_empty(),
_ => false,
};
let display_target = DisplayTarget::from_crate(cx.db, krate);

View File

@ -328,7 +328,7 @@ impl HirDisplay for Pat {
write!(
f,
"{}",
f.db.enum_variants(loc.parent).variants[loc.index as usize]
loc.parent.enum_variants(f.db).variants[loc.index as usize]
.1
.display(f.db, f.edition())
)?;

View File

@ -50,7 +50,7 @@ impl EnumVariantContiguousIndex {
}
fn to_enum_variant_id(self, db: &dyn HirDatabase, eid: EnumId) -> EnumVariantId {
db.enum_variants(eid).variants[self.0].0
eid.enum_variants(db).variants[self.0].0
}
}
@ -458,7 +458,7 @@ impl PatCx for MatchCheckCtx<'_> {
TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(),
TyKind::Array(..) | TyKind::Slice(..) => unhandled(),
&TyKind::Adt(AdtId(adt @ hir_def::AdtId::EnumId(enum_id)), ref subst) => {
let enum_data = cx.db.enum_variants(enum_id);
let enum_data = enum_id.enum_variants(cx.db);
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt);
if enum_data.variants.is_empty() && !is_declared_nonexhaustive {

View File

@ -914,7 +914,7 @@ fn render_const_scalar(
write!(
f,
"{}",
f.db.enum_variants(loc.parent).variants[loc.index as usize]
loc.parent.enum_variants(f.db).variants[loc.index as usize]
.1
.display(f.db, f.edition())
)?;
@ -1208,7 +1208,7 @@ impl HirDisplay for Ty {
write!(
f,
"{}",
db.enum_variants(loc.parent).variants[loc.index as usize]
loc.parent.enum_variants(db).variants[loc.index as usize]
.1
.display(db, f.edition())
)?

View File

@ -67,8 +67,8 @@ pub(crate) fn has_drop_glue(db: &dyn HirDatabase, ty: Ty, env: Arc<TraitEnvironm
}
// Unions cannot have fields with destructors.
AdtId::UnionId(_) => DropGlue::None,
AdtId::EnumId(id) => db
.enum_variants(id)
AdtId::EnumId(id) => id
.enum_variants(db)
.variants
.iter()
.map(|&(variant, _, _)| {

View File

@ -1673,7 +1673,7 @@ impl<'db> InferenceContext<'db> {
// If we can resolve to an enum variant, it takes priority over associated type
// of the same name.
if let Some((AdtId::EnumId(id), _)) = ty.as_adt() {
let enum_data = self.db.enum_variants(id);
let enum_data = id.enum_variants(self.db);
if let Some(variant) = enum_data.variant(current_segment.name) {
return if remaining_segments.len() == 1 {
(ty, Some(variant.into()))
@ -1792,7 +1792,7 @@ impl<'db> InferenceContext<'db> {
let segment = path.segments().last().unwrap();
// this could be an enum variant or associated type
if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
let enum_data = self.db.enum_variants(enum_id);
let enum_data = enum_id.enum_variants(self.db);
if let Some(variant) = enum_data.variant(segment) {
return (ty, Some(variant.into()));
}

View File

@ -43,7 +43,7 @@ impl CastTy {
let (AdtId::EnumId(id), _) = t.as_adt()? else {
return None;
};
let enum_data = table.db.enum_variants(id);
let enum_data = id.enum_variants(table.db);
if enum_data.is_payload_free(table.db) { Some(Self::Int(Int::CEnum)) } else { None }
}
TyKind::Raw(m, ty) => Some(Self::Ptr(ty.clone(), *m)),

View File

@ -1360,7 +1360,7 @@ impl InferenceContext<'_> {
if let Some(variant) = self.result.variant_resolution_for_pat(p) {
let adt = variant.adt_id(self.db);
let is_multivariant = match adt {
hir_def::AdtId::EnumId(e) => self.db.enum_variants(e).variants.len() != 1,
hir_def::AdtId::EnumId(e) => e.enum_variants(self.db).variants.len() != 1,
_ => false,
};
if is_multivariant {

View File

@ -397,7 +397,7 @@ impl InferenceContext<'_> {
Some((AdtId::EnumId(e), subst)) => (e, subst),
_ => return None,
};
let enum_data = self.db.enum_variants(enum_id);
let enum_data = enum_id.enum_variants(self.db);
let variant = enum_data.variant(name)?;
self.write_variant_resolution(id, variant.into());
Some((ValueNs::EnumVariantId(variant), subst.clone()))

View File

@ -113,7 +113,7 @@ impl UninhabitedFrom<'_> {
AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED,
AdtId::StructId(s) => self.visit_variant(s.into(), subst),
AdtId::EnumId(e) => {
let enum_data = self.db.enum_variants(e);
let enum_data = e.enum_variants(self.db);
for &(variant, _, _) in enum_data.variants.iter() {
let variant_inhabitedness = self.visit_variant(variant.into(), subst);

View File

@ -56,7 +56,7 @@ pub fn layout_of_adt_query(
(r, data.repr.unwrap_or_default(), false)
}
AdtId::EnumId(e) => {
let variants = db.enum_variants(e);
let variants = e.enum_variants(db);
let r = variants
.variants
.iter()
@ -82,7 +82,7 @@ pub fn layout_of_adt_query(
|min, max| repr_discr(dl, &repr, min, max).unwrap_or((Integer::I8, false)),
variants.iter_enumerated().filter_map(|(id, _)| {
let AdtId::EnumId(e) = def else { return None };
let d = db.const_eval_discriminant(db.enum_variants(e).variants[id.0].0).ok()?;
let d = db.const_eval_discriminant(e.enum_variants(db).variants[id.0].0).ok()?;
Some((id, d))
}),
// FIXME: The current code for niche-filling relies on variant indices

View File

@ -1631,7 +1631,7 @@ impl Evaluator<'_> {
Variants::Empty => unreachable!(),
Variants::Single { index } => {
let r =
self.const_eval_discriminant(self.db.enum_variants(e).variants[index.0].0)?;
self.const_eval_discriminant(e.enum_variants(self.db).variants[index.0].0)?;
Ok(r)
}
Variants::Multiple { tag, tag_encoding, variants, .. } => {
@ -1656,7 +1656,7 @@ impl Evaluator<'_> {
.unwrap_or(*untagged_variant)
.0;
let result =
self.const_eval_discriminant(self.db.enum_variants(e).variants[idx].0)?;
self.const_eval_discriminant(e.enum_variants(self.db).variants[idx].0)?;
Ok(result)
}
}
@ -2775,8 +2775,8 @@ impl Evaluator<'_> {
let name = format!(
"{}::{}",
self.db.enum_signature(loc.parent).name.display(db, edition),
self.db
.enum_variants(loc.parent)
loc.parent
.enum_variants(self.db)
.variant_name_by_id(variant)
.unwrap()
.display(db, edition),

View File

@ -1925,8 +1925,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
let name = format!(
"{}::{}",
self.db.enum_signature(loc.parent).name.display(db, edition),
self.db
.enum_variants(loc.parent)
loc.parent
.enum_variants(self.db)
.variant_name_by_id(variant)
.unwrap()
.display(db, edition),
@ -2155,7 +2155,7 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<Mi
.to_string(),
DefWithBodyId::VariantId(it) => {
let loc = it.lookup(db);
db.enum_variants(loc.parent).variants[loc.index as usize]
loc.parent.enum_variants(db).variants[loc.index as usize]
.1
.display(db, edition)
.to_string()

View File

@ -68,7 +68,8 @@ impl MirBody {
this,
"enum {}::{} = ",
db.enum_signature(loc.parent).name.display(db, edition),
db.enum_variants(loc.parent)
loc.parent
.enum_variants(db)
.variant_name_by_id(id)
.unwrap()
.display(db, edition),
@ -335,7 +336,7 @@ impl<'a> MirPrettyCtx<'a> {
w!(
this,
" as {}).{}",
this.db.enum_variants(loc.parent).variants[loc.index as usize]
loc.parent.enum_variants(this.db).variants[loc.index as usize]
.1
.display(this.db, this.display_target.edition),
name.display(this.db, this.display_target.edition)

View File

@ -479,7 +479,7 @@ pub(crate) fn visit_module(
visit_body(db, &body, cb);
}
ModuleDefId::AdtId(hir_def::AdtId::EnumId(it)) => {
db.enum_variants(it).variants.iter().for_each(|&(it, _, _)| {
it.enum_variants(db).variants.iter().for_each(|&(it, _, _)| {
let body = db.body(it.into());
cb(it.into());
visit_body(db, &body, cb);

View File

@ -109,7 +109,7 @@ impl DebugContext<'_> {
CallableDefId::StructId(s) => self.0.struct_signature(s).name.clone(),
CallableDefId::EnumVariantId(e) => {
let loc = e.lookup(self.0);
self.0.enum_variants(loc.parent).variants[loc.index as usize].1.clone()
loc.parent.enum_variants(self.0).variants[loc.index as usize].1.clone()
}
};
match def {

View File

@ -355,7 +355,7 @@ pub(crate) fn detect_variant_from_bytes<'a>(
let (var_id, var_layout) = match &layout.variants {
hir_def::layout::Variants::Empty => unreachable!(),
hir_def::layout::Variants::Single { index } => {
(db.enum_variants(e).variants[index.0].0, layout)
(e.enum_variants(db).variants[index.0].0, layout)
}
hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => {
let size = tag.size(target_data_layout).bytes_usize();
@ -365,7 +365,7 @@ pub(crate) fn detect_variant_from_bytes<'a>(
TagEncoding::Direct => {
let (var_idx, layout) =
variants.iter_enumerated().find_map(|(var_idx, v)| {
let def = db.enum_variants(e).variants[var_idx.0].0;
let def = e.enum_variants(db).variants[var_idx.0].0;
(db.const_eval_discriminant(def) == Ok(tag)).then_some((def, v))
})?;
(var_idx, layout)
@ -378,7 +378,7 @@ pub(crate) fn detect_variant_from_bytes<'a>(
.filter(|x| x != untagged_variant)
.nth(candidate_tag)
.unwrap_or(*untagged_variant);
(db.enum_variants(e).variants[variant.0].0, &variants[variant])
(e.enum_variants(db).variants[variant.0].0, &variants[variant])
}
}
}

View File

@ -213,7 +213,7 @@ impl Context<'_> {
AdtId::StructId(s) => add_constraints_from_variant(VariantId::StructId(s)),
AdtId::UnionId(u) => add_constraints_from_variant(VariantId::UnionId(u)),
AdtId::EnumId(e) => {
db.enum_variants(e).variants.iter().for_each(|&(variant, _, _)| {
e.enum_variants(db).variants.iter().for_each(|&(variant, _, _)| {
add_constraints_from_variant(VariantId::EnumVariantId(variant))
});
}

View File

@ -688,7 +688,7 @@ impl Module {
Adt::Enum(e) => {
let source_map = db.enum_signature_with_source_map(e.id).1;
expr_store_diagnostics(db, acc, &source_map);
let (variants, diagnostics) = db.enum_variants_with_diagnostics(e.id);
let (variants, diagnostics) = e.id.enum_variants_with_diagnostics(db);
let file = e.id.lookup(db).id.file_id;
let ast_id_map = db.ast_id_map(file);
if let Some(diagnostics) = &diagnostics {
@ -1504,11 +1504,11 @@ impl Enum {
}
pub fn variants(self, db: &dyn HirDatabase) -> Vec<Variant> {
db.enum_variants(self.id).variants.iter().map(|&(id, _, _)| Variant { id }).collect()
self.id.enum_variants(db).variants.iter().map(|&(id, _, _)| Variant { id }).collect()
}
pub fn num_variants(self, db: &dyn HirDatabase) -> usize {
db.enum_variants(self.id).variants.len()
self.id.enum_variants(db).variants.len()
}
pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprOptions> {
@ -1606,7 +1606,7 @@ impl Variant {
pub fn name(self, db: &dyn HirDatabase) -> Name {
let lookup = self.id.lookup(db);
let enum_ = lookup.parent;
db.enum_variants(enum_).variants[lookup.index as usize].1.clone()
enum_.enum_variants(db).variants[lookup.index as usize].1.clone()
}
pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {

View File

@ -205,7 +205,7 @@ impl ChildBySource for EnumId {
let ast_id_map = db.ast_id_map(loc.id.file_id);
db.enum_variants(*self).variants.iter().for_each(|&(variant, _, _)| {
self.enum_variants(db).variants.iter().for_each(|&(variant, _, _)| {
res[keys::ENUM_VARIANT].insert(ast_id_map.get(variant.lookup(db).id.value), variant);
});
let (_, source_map) = db.enum_signature_with_source_map(*self);

View File

@ -829,7 +829,7 @@ impl<'db> SourceAnalyzer<'db> {
handle_variants(id.into(), subst, &mut container)?
}
AdtId::EnumId(id) => {
let variants = db.enum_variants(id);
let variants = id.enum_variants(db);
let variant = variants.variant(&field_name.as_name())?;
container = Either::Left((variant, subst.clone()));
(Either::Left(Variant { id: variant }), id.into(), subst.clone())