//! Helper functions for working with def, which don't need to be a separate //! query, but can't be computed directly from `*Data` (ie, which need a `db`). use base_db::target::{self, TargetData}; use hir_def::{ EnumId, EnumVariantId, FunctionId, Lookup, TraitId, attrs::AttrFlags, lang_item::LangItems, }; use intern::sym; use rustc_abi::TargetDataLayout; use span::Edition; use crate::{ TargetFeatures, db::HirDatabase, layout::{Layout, TagEncoding}, lower::SupertraitsInfo, mir::pad16, }; /// SAFETY: `old_pointer` must be valid for unique writes pub(crate) unsafe fn unsafe_update_eq(old_pointer: *mut T, new_value: T) -> bool where T: PartialEq, { // SAFETY: Caller obligation let old_ref: &mut T = unsafe { &mut *old_pointer }; if *old_ref != new_value { *old_ref = new_value; true } else { // Subtle but important: Eq impls can be buggy or define equality // in surprising ways. If it says that the value has not changed, // we do not modify the existing value, and thus do not have to // update the revision, as downstream code will not see the new value. false } } pub(crate) fn fn_traits(lang_items: &LangItems) -> impl Iterator + '_ { [lang_items.Fn, lang_items.FnMut, lang_items.FnOnce].into_iter().flatten() } /// Returns an iterator over the direct super traits (including the trait itself). pub fn direct_super_traits(db: &dyn HirDatabase, trait_: TraitId) -> &[TraitId] { &SupertraitsInfo::query(db, trait_).direct_supertraits } /// Returns the whole super trait hierarchy (including the trait itself). pub fn all_super_traits(db: &dyn HirDatabase, trait_: TraitId) -> &[TraitId] { &SupertraitsInfo::query(db, trait_).all_supertraits } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Unsafety { Safe, Unsafe, /// A lint. DeprecatedSafe2024, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TargetFeatureIsSafeInTarget { No, Yes, } pub fn target_feature_is_safe_in_target(target: &TargetData) -> TargetFeatureIsSafeInTarget { match target.arch { target::Arch::Wasm32 | target::Arch::Wasm64 => TargetFeatureIsSafeInTarget::Yes, _ => TargetFeatureIsSafeInTarget::No, } } pub fn is_fn_unsafe_to_call( db: &dyn HirDatabase, func: FunctionId, caller_target_features: &TargetFeatures<'_>, call_edition: Edition, target_feature_is_safe: TargetFeatureIsSafeInTarget, ) -> Unsafety { let data = db.function_signature(func); if data.is_unsafe() { return Unsafety::Unsafe; } if data.has_target_feature() && target_feature_is_safe == TargetFeatureIsSafeInTarget::No { // RFC 2396 . let callee_target_features = TargetFeatures::from_fn_no_implications(db, func); if !caller_target_features.enabled.is_superset(&callee_target_features.enabled) { return Unsafety::Unsafe; } } if data.is_deprecated_safe_2024() { if call_edition.at_least_2024() { return Unsafety::Unsafe; } else { return Unsafety::DeprecatedSafe2024; } } let loc = func.lookup(db); match loc.container { hir_def::ItemContainerId::ExternBlockId(block) => { let is_intrinsic_block = block.abi(db) == Some(sym::rust_dash_intrinsic); if is_intrinsic_block { // legacy intrinsics // extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute if AttrFlags::query(db, func.into()).contains(AttrFlags::RUSTC_SAFE_INTRINSIC) { Unsafety::Safe } else { Unsafety::Unsafe } } else { // Function in an `extern` block are always unsafe to call, except when // it is marked as `safe`. if data.is_safe() { Unsafety::Safe } else { Unsafety::Unsafe } } } _ => Unsafety::Safe, } } pub(crate) fn detect_variant_from_bytes<'a>( layout: &'a Layout, db: &dyn HirDatabase, target_data_layout: &TargetDataLayout, b: &[u8], e: EnumId, ) -> Option<(EnumVariantId, &'a Layout)> { let (var_id, var_layout) = match &layout.variants { hir_def::layout::Variants::Empty => unreachable!(), hir_def::layout::Variants::Single { index } => { (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(); let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field let tag = i128::from_le_bytes(pad16(&b[offset..offset + size], false)); match tag_encoding { TagEncoding::Direct => { let (var_idx, layout) = variants.iter_enumerated().find_map(|(var_idx, v)| { 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) } TagEncoding::Niche { untagged_variant, niche_start, .. } => { let candidate_tag = tag.wrapping_sub(*niche_start as i128) as usize; let variant = variants .iter_enumerated() .map(|(x, _)| x) .filter(|x| x != untagged_variant) .nth(candidate_tag) .unwrap_or(*untagged_variant); (e.enum_variants(db).variants[variant.0].0, &variants[variant]) } } } }; Some((var_id, var_layout)) }