diff --git a/crates/hir-def/src/adt.rs b/crates/hir-def/src/adt.rs index b336f59ffee..7a5025aa6a5 100644 --- a/crates/hir-def/src/adt.rs +++ b/crates/hir-def/src/adt.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use base_db::CrateId; +use bitflags::bitflags; use cfg::CfgOptions; use either::Either; @@ -20,6 +21,7 @@ builtin_type::{BuiltinInt, BuiltinUint}, db::DefDatabase, item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId}, + lang_item::LangItem, layout::{Align, ReprFlags, ReprOptions}, nameres::diagnostics::DefDiagnostic, src::HasChildSource, @@ -39,8 +41,26 @@ pub struct StructData { pub variant_data: Arc, pub repr: Option, pub visibility: RawVisibility, - pub rustc_has_incoherent_inherent_impls: bool, - pub fundamental: bool, + pub flags: StructFlags, +} + +bitflags! { + pub struct StructFlags: u8 { + const NO_FLAGS = 0; + /// Indicates whether the struct is `PhantomData`. + const IS_PHANTOM_DATA = 1 << 2; + /// Indicates whether the struct has a `#[fundamental]` attribute. + const IS_FUNDAMENTAL = 1 << 3; + // FIXME: should this be a flag? + /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute. + const IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL = 1 << 4; + /// Indicates whether this struct is `Box`. + const IS_BOX = 1 << 5; + /// Indicates whether this struct is `ManuallyDrop`. + const IS_MANUALLY_DROP = 1 << 6; + /// Indicates whether this struct is `UnsafeCell`. + const IS_UNSAFE_CELL = 1 << 6; + } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -174,10 +194,25 @@ pub(crate) fn struct_data_with_diagnostics_query( let item_tree = loc.id.item_tree(db); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); + let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()); - let rustc_has_incoherent_inherent_impls = - attrs.by_key("rustc_has_incoherent_inherent_impls").exists(); - let fundamental = attrs.by_key("fundamental").exists(); + + let mut flags = StructFlags::NO_FLAGS; + if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() { + flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL; + } + if attrs.by_key("fundamental").exists() { + flags |= StructFlags::IS_FUNDAMENTAL; + } + if let Some(lang) = attrs.lang_item() { + match lang { + LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA, + LangItem::OwnedBox => flags |= StructFlags::IS_BOX, + LangItem::ManuallyDrop => flags |= StructFlags::IS_MANUALLY_DROP, + LangItem::UnsafeCell => flags |= StructFlags::IS_UNSAFE_CELL, + _ => (), + } + } let strukt = &item_tree[loc.id.value]; let (variant_data, diagnostics) = lower_fields( @@ -196,8 +231,7 @@ pub(crate) fn struct_data_with_diagnostics_query( variant_data: Arc::new(variant_data), repr, visibility: item_tree[strukt.visibility].clone(), - rustc_has_incoherent_inherent_impls, - fundamental, + flags, }), diagnostics.into(), ) @@ -218,9 +252,13 @@ pub(crate) fn union_data_with_diagnostics_query( let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()); - let rustc_has_incoherent_inherent_impls = - attrs.by_key("rustc_has_incoherent_inherent_impls").exists(); - let fundamental = attrs.by_key("fundamental").exists(); + let mut flags = StructFlags::NO_FLAGS; + if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() { + flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL; + } + if attrs.by_key("fundamental").exists() { + flags |= StructFlags::IS_FUNDAMENTAL; + } let union = &item_tree[loc.id.value]; let (variant_data, diagnostics) = lower_fields( @@ -239,8 +277,7 @@ pub(crate) fn union_data_with_diagnostics_query( variant_data: Arc::new(variant_data), repr, visibility: item_tree[union.visibility].clone(), - rustc_has_incoherent_inherent_impls, - fundamental, + flags, }), diagnostics.into(), ) diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index e4d3636ce5e..860df1b68b2 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -20,6 +20,7 @@ use crate::{ db::DefDatabase, item_tree::{AttrOwner, Fields, ItemTreeId, ItemTreeNode}, + lang_item::LangItem, nameres::{ModuleOrigin, ModuleSource}, src::{HasChildSource, HasSource}, AdtId, AttrDefId, EnumId, GenericParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroId, @@ -177,13 +178,13 @@ pub(crate) fn fields_attrs_query( Arc::new(res) } - - pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { - AttrQuery { attrs: self, key } - } } impl Attrs { + pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { + AttrQuery { attrs: self, key } + } + pub fn cfg(&self) -> Option { let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse); let first = cfgs.next()?; @@ -206,6 +207,10 @@ pub fn lang(&self) -> Option<&SmolStr> { self.by_key("lang").string_value() } + pub fn lang_item(&self) -> Option { + self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it)) + } + pub fn docs(&self) -> Option { let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value()); let indent = doc_indent(self); diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs index c749bf570c3..e23ae750dd5 100644 --- a/crates/hir-ty/src/autoderef.rs +++ b/crates/hir-ty/src/autoderef.rs @@ -4,10 +4,7 @@ //! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs). use chalk_ir::cast::Cast; -use hir_def::{ - lang_item::{LangItem, LangItemTarget}, - AdtId, -}; +use hir_def::lang_item::LangItem; use hir_expand::name::name; use limit::Limit; @@ -90,13 +87,8 @@ pub(crate) fn builtin_deref<'ty>( TyKind::Ref(.., ty) => Some(ty), // FIXME: Maybe accept this but diagnose if its not explicit? TyKind::Raw(.., ty) if explicit => Some(ty), - &TyKind::Adt(chalk_ir::AdtId(AdtId::StructId(strukt)), ref substs) => { - if Some(strukt) - == table - .db - .lang_item(table.trait_env.krate, LangItem::OwnedBox) - .and_then(LangItemTarget::as_struct) - { + &TyKind::Adt(chalk_ir::AdtId(adt), ref substs) => { + if crate::lang_items::is_box(table.db, adt) { substs.at(Interner, 0).ty(Interner) } else { None diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs index 859a37804ae..fe1cda71dd5 100644 --- a/crates/hir-ty/src/diagnostics/match_check.rs +++ b/crates/hir-ty/src/diagnostics/match_check.rs @@ -379,7 +379,7 @@ fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { } PatKind::Deref { subpattern } => { match self.ty.kind(Interner) { - TyKind::Adt(adt, _) if is_box(adt.0, f.db) => write!(f, "box ")?, + TyKind::Adt(adt, _) if is_box(f.db, adt.0) => write!(f, "box ")?, &TyKind::Ref(mutbl, ..) => { write!(f, "&{}", if mutbl == Mutability::Mut { "mut " } else { "" })? } diff --git a/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs index d130827a77e..b103fdc709d 100644 --- a/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs +++ b/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs @@ -384,7 +384,7 @@ pub(super) fn arity(&self, pcx: PatCtxt<'_, '_>) -> usize { TyKind::Tuple(arity, ..) => arity, TyKind::Ref(..) => 1, TyKind::Adt(adt, ..) => { - if is_box(adt.0, pcx.cx.db) { + if is_box(pcx.cx.db, adt.0) { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. 1 @@ -800,7 +800,7 @@ pub(crate) fn wildcards( } TyKind::Ref(.., rty) => Fields::wildcards_from_tys(cx, once(rty.clone())), &TyKind::Adt(AdtId(adt), ref substs) => { - if is_box(adt, cx.db) { + if is_box(cx.db, adt) { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); @@ -905,7 +905,7 @@ pub(crate) fn from_pat(cx: &MatchCheckCtx<'_, 'p>, pat: &Pat) -> Self { } fields = Fields::from_iter(cx, wilds) } - TyKind::Adt(adt, substs) if is_box(adt.0, cx.db) => { + TyKind::Adt(adt, substs) if is_box(cx.db, adt.0) => { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_, @@ -992,7 +992,7 @@ pub(crate) fn to_pat(&self, cx: &MatchCheckCtx<'_, 'p>) -> Pat { }) .collect(), }, - TyKind::Adt(adt, _) if is_box(adt.0, cx.db) => { + TyKind::Adt(adt, _) if is_box(cx.db, adt.0) => { // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside // of `std`). So this branch is only reachable when the feature is enabled and // the pattern is a box pattern. diff --git a/crates/hir-ty/src/lang_items.rs b/crates/hir-ty/src/lang_items.rs index 28e5f6df063..b0ad4fc946a 100644 --- a/crates/hir-ty/src/lang_items.rs +++ b/crates/hir-ty/src/lang_items.rs @@ -1,22 +1,18 @@ //! Functions to detect special lang items -use hir_def::{lang_item::LangItem, AdtId, HasModule}; +use hir_def::{adt::StructFlags, lang_item::LangItem, AdtId}; use hir_expand::name::Name; use crate::db::HirDatabase; -pub fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool { - let krate = adt.module(db.upcast()).krate(); - let box_adt = - db.lang_item(krate, LangItem::OwnedBox).and_then(|it| it.as_struct()).map(AdtId::from); - Some(adt) == box_adt +pub fn is_box(db: &dyn HirDatabase, adt: AdtId) -> bool { + let AdtId::StructId(id) = adt else { return false }; + db.struct_data(id).flags.contains(StructFlags::IS_UNSAFE_CELL) } -pub fn is_unsafe_cell(adt: AdtId, db: &dyn HirDatabase) -> bool { - let krate = adt.module(db.upcast()).krate(); - let box_adt = - db.lang_item(krate, LangItem::UnsafeCell).and_then(|it| it.as_struct()).map(AdtId::from); - Some(adt) == box_adt +pub fn is_unsafe_cell(db: &dyn HirDatabase, adt: AdtId) -> bool { + let AdtId::StructId(id) = adt else { return false }; + db.struct_data(id).flags.contains(StructFlags::IS_UNSAFE_CELL) } pub fn lang_items_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, LangItem)> { diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index b22d0fe8ded..18199f327d1 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -71,7 +71,7 @@ pub fn layout_of_adt_query( &repr, &variants, is_enum, - is_unsafe_cell(def, db), + is_unsafe_cell(db, def), layout_scalar_valid_range(db, def), |min, max| Integer::repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)), variants.iter_enumerated().filter_map(|(id, _)| { diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index e08c44f0a04..d253b4cc03e 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -7,8 +7,9 @@ use base_db::{CrateId, Edition}; use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause}; use hir_def::{ - data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId, - FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId, + adt::StructFlags, data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, + ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, + TraitId, }; use hir_expand::name::Name; use rustc_hash::{FxHashMap, FxHashSet}; @@ -405,12 +406,14 @@ pub fn def_crates( match ty.kind(Interner) { &TyKind::Adt(AdtId(def_id), _) => { let rustc_has_incoherent_inherent_impls = match def_id { - hir_def::AdtId::StructId(id) => { - db.struct_data(id).rustc_has_incoherent_inherent_impls - } - hir_def::AdtId::UnionId(id) => { - db.union_data(id).rustc_has_incoherent_inherent_impls - } + hir_def::AdtId::StructId(id) => db + .struct_data(id) + .flags + .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL), + hir_def::AdtId::UnionId(id) => db + .union_data(id) + .flags + .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL), hir_def::AdtId::EnumId(id) => db.enum_data(id).rustc_has_incoherent_inherent_impls, }; Some(if rustc_has_incoherent_inherent_impls { @@ -808,12 +811,14 @@ fn is_inherent_impl_coherent( | TyKind::Scalar(_) => true, &TyKind::Adt(AdtId(adt), _) => match adt { - hir_def::AdtId::StructId(it) => { - db.struct_data(it).rustc_has_incoherent_inherent_impls - } - hir_def::AdtId::UnionId(it) => { - db.union_data(it).rustc_has_incoherent_inherent_impls - } + hir_def::AdtId::StructId(id) => db + .struct_data(id) + .flags + .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL), + hir_def::AdtId::UnionId(id) => db + .union_data(id) + .flags + .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL), hir_def::AdtId::EnumId(it) => db.enum_data(it).rustc_has_incoherent_inherent_impls, }, TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| {