Split enum variants out of enum_data query

This commit is contained in:
Lukas Wirth 2025-02-26 08:02:53 +01:00
parent 1835bc2a87
commit 176981ef49
21 changed files with 74 additions and 52 deletions

View file

@ -56,12 +56,16 @@ bitflags! {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumData {
pub name: Name,
pub variants: Box<[(EnumVariantId, Name)]>,
pub repr: Option<ReprOptions>,
pub visibility: RawVisibility,
pub rustc_has_incoherent_inherent_impls: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumVariants {
pub variants: Box<[(EnumVariantId, Name)]>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumVariantData {
pub name: Name,
@ -203,28 +207,16 @@ impl StructData {
}
}
impl EnumData {
pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
impl EnumVariants {
pub(crate) fn enum_variants_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumVariants> {
let loc = e.lookup(db);
let krate = loc.container.krate;
let item_tree = loc.id.item_tree(db);
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
let rustc_has_incoherent_inherent_impls = item_tree
.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
.by_key(&sym::rustc_has_incoherent_inherent_impls)
.exists();
let enum_ = &item_tree[loc.id.value];
Arc::new(EnumData {
name: enum_.name.clone(),
Arc::new(EnumVariants {
variants: loc.container.def_map(db).enum_definitions[&e]
.iter()
.map(|&id| (id, item_tree[id.lookup(db).id.value].name.clone()))
.collect(),
repr,
visibility: item_tree[enum_.visibility].clone(),
rustc_has_incoherent_inherent_impls,
})
}
@ -233,13 +225,6 @@ impl EnumData {
Some(id)
}
pub fn variant_body_type(&self) -> IntegerType {
match self.repr {
Some(ReprOptions { int: Some(builtin), .. }) => builtin,
_ => IntegerType::Pointer(true),
}
}
// [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448)
pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool {
self.variants.iter().all(|(v, _)| {
@ -262,6 +247,35 @@ impl EnumData {
}
}
impl EnumData {
pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
let loc = e.lookup(db);
let krate = loc.container.krate;
let item_tree = loc.id.item_tree(db);
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
let rustc_has_incoherent_inherent_impls = item_tree
.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
.by_key(&sym::rustc_has_incoherent_inherent_impls)
.exists();
let enum_ = &item_tree[loc.id.value];
Arc::new(EnumData {
name: enum_.name.clone(),
repr,
visibility: item_tree[enum_.visibility].clone(),
rustc_has_incoherent_inherent_impls,
})
}
pub fn variant_body_type(&self) -> IntegerType {
match self.repr {
Some(ReprOptions { int: Some(builtin), .. }) => builtin,
_ => IntegerType::Pointer(true),
}
}
}
impl EnumVariantData {
#[inline]
pub(crate) fn enum_variant_data_query(

View file

@ -11,7 +11,7 @@ use triomphe::Arc;
use crate::{
attr::{Attrs, AttrsWithOwner},
data::{
adt::{EnumData, EnumVariantData, StructData, VariantData},
adt::{EnumData, EnumVariantData, EnumVariants, StructData, VariantData},
ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData,
ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData,
},
@ -167,6 +167,9 @@ pub trait DefDatabase:
#[salsa::invoke_actual(EnumData::enum_data_query)]
fn enum_data(&self, e: EnumId) -> Arc<EnumData>;
#[salsa::invoke_actual(EnumVariants::enum_variants_query)]
fn enum_variants(&self, e: EnumId) -> Arc<EnumVariants>;
#[salsa::transparent]
#[salsa::invoke_actual(EnumVariantData::enum_variant_data_query)]
fn enum_variant_data(&self, id: EnumVariantId) -> Arc<EnumVariantData>;

View file

@ -801,7 +801,7 @@ pub(crate) fn adt_datum_query(
}
hir_def::AdtId::EnumId(id) => {
let variants = db
.enum_data(id)
.enum_variants(id)
.variants
.iter()
.map(|&(variant_id, _)| variant_id_to_fields(variant_id.into()))

View file

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

View file

@ -394,7 +394,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_data(enum_id);
let data = self.db.enum_variants(enum_id);
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_data(e).variants.is_empty(),
Some((AdtId::EnumId(e), _)) => !cx.db.enum_variants(e).variants.is_empty(),
_ => false,
};
let display_target = DisplayTarget::from_crate(cx.db, krate);

View file

@ -49,7 +49,7 @@ impl EnumVariantContiguousIndex {
}
fn to_enum_variant_id(self, db: &dyn HirDatabase, eid: EnumId) -> EnumVariantId {
db.enum_data(eid).variants[self.0].0
db.enum_variants(eid).variants[self.0].0
}
}
@ -449,7 +449,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_data(enum_id);
let enum_data = cx.db.enum_variants(enum_id);
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt);
if enum_data.variants.is_empty() && !is_declared_nonexhaustive {

View file

@ -72,7 +72,7 @@ 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_data(id)
.enum_variants(id)
.variants
.iter()
.map(|&(variant, _)| {

View file

@ -1587,7 +1587,7 @@ impl<'a> InferenceContext<'a> {
// 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_data(id);
let enum_data = self.db.enum_variants(id);
if let Some(variant) = enum_data.variant(current_segment.name) {
return if remaining_segments.len() == 1 {
(ty, Some(variant.into()))
@ -1701,7 +1701,7 @@ impl<'a> InferenceContext<'a> {
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_data(enum_id);
let enum_data = self.db.enum_variants(enum_id);
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_data(id);
let enum_data = table.db.enum_variants(id);
if enum_data.is_payload_free(table.db.upcast()) {
Some(Self::Int(Int::CEnum))
} else {

View file

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

View file

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

View file

@ -98,7 +98,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_data(e);
let enum_data = self.db.enum_variants(e);
for &(variant, _) in enum_data.variants.iter() {
let variant_inhabitedness = self.visit_variant(variant.into(), subst);

View file

@ -54,13 +54,13 @@ pub fn layout_of_adt_query(
(r, data.repr.unwrap_or_default())
}
AdtId::EnumId(e) => {
let data = db.enum_data(e);
let r = data
let variants = db.enum_variants(e);
let r = variants
.variants
.iter()
.map(|&(v, _)| handle_variant(v.into(), &db.enum_variant_data(v).variant_data))
.collect::<Result<SmallVec<_>, _>>()?;
(r, data.repr.unwrap_or_default())
(r, db.enum_data(e).repr.unwrap_or_default())
}
};
let variants = variants
@ -80,7 +80,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_data(e).variants[id.0].0).ok()?;
let d = db.const_eval_discriminant(db.enum_variants(e).variants[id.0].0).ok()?;
Some((id, d))
}),
// FIXME: The current code for niche-filling relies on variant indices

View file

@ -1641,7 +1641,8 @@ impl Evaluator<'_> {
match &layout.variants {
Variants::Empty => unreachable!(),
Variants::Single { index } => {
let r = self.const_eval_discriminant(self.db.enum_data(e).variants[index.0].0)?;
let r =
self.const_eval_discriminant(self.db.enum_variants(e).variants[index.0].0)?;
Ok(r)
}
Variants::Multiple { tag, tag_encoding, variants, .. } => {
@ -1666,7 +1667,7 @@ impl Evaluator<'_> {
.unwrap_or(*untagged_variant)
.0;
let result =
self.const_eval_discriminant(self.db.enum_data(e).variants[idx].0)?;
self.const_eval_discriminant(self.db.enum_variants(e).variants[idx].0)?;
Ok(result)
}
}

View file

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

View file

@ -369,7 +369,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_data(e).variants[index.0].0, layout)
(db.enum_variants(e).variants[index.0].0, layout)
}
hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => {
let size = tag.size(target_data_layout).bytes_usize();
@ -379,7 +379,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_data(e).variants[var_idx.0].0;
let def = db.enum_variants(e).variants[var_idx.0].0;
(db.const_eval_discriminant(def) == Ok(tag)).then_some((def, v))
})?;
(var_idx, layout)
@ -392,7 +392,7 @@ pub(crate) fn detect_variant_from_bytes<'a>(
.filter(|x| x != untagged_variant)
.nth(candidate_tag)
.unwrap_or(*untagged_variant);
(db.enum_data(e).variants[variant.0].0, &variants[variant])
(db.enum_variants(e).variants[variant.0].0, &variants[variant])
}
}
}

View file

@ -206,7 +206,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_data(e).variants.iter().for_each(|&(variant, _)| {
db.enum_variants(e).variants.iter().for_each(|&(variant, _)| {
add_constraints_from_variant(VariantId::EnumVariantId(variant))
});
}

View file

@ -1519,7 +1519,11 @@ impl Enum {
}
pub fn variants(self, db: &dyn HirDatabase) -> Vec<Variant> {
db.enum_data(self.id).variants.iter().map(|&(id, _)| Variant { id }).collect()
db.enum_variants(self.id).variants.iter().map(|&(id, _)| Variant { id }).collect()
}
pub fn num_variants(self, db: &dyn HirDatabase) -> usize {
db.enum_variants(self.id).variants.len()
}
pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprOptions> {

View file

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

View file

@ -1,6 +1,6 @@
//! Completes constants and paths in unqualified patterns.
use hir::{db::DefDatabase, AssocItem, ScopeDef};
use hir::{AssocItem, ScopeDef};
use ide_db::syntax_helpers::suggest_name;
use syntax::ast::Pat;
@ -60,7 +60,7 @@ pub(crate) fn complete_pattern(
}
let refutable = pattern_ctx.refutability == PatternRefutability::Refutable;
let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1;
let single_variant_enum = |enum_: hir::Enum| enum_.num_variants(ctx.db) == 1;
if let Some(hir::Adt::Enum(e)) =
ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())