ra_ap_hir_ty/
utils.rs

1//! Helper functions for working with def, which don't need to be a separate
2//! query, but can't be computed directly from `*Data` (ie, which need a `db`).
3
4use std::{cell::LazyCell, iter};
5
6use base_db::Crate;
7use chalk_ir::{
8    DebruijnIndex,
9    fold::{FallibleTypeFolder, Shift},
10};
11use hir_def::{
12    EnumId, EnumVariantId, FunctionId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId,
13    db::DefDatabase,
14    hir::generics::WherePredicate,
15    lang_item::LangItem,
16    resolver::{HasResolver, TypeNs},
17    type_ref::{TraitBoundModifier, TypeRef},
18};
19use hir_expand::name::Name;
20use intern::sym;
21use rustc_abi::TargetDataLayout;
22use rustc_hash::FxHashSet;
23use smallvec::{SmallVec, smallvec};
24use span::Edition;
25use stdx::never;
26
27use crate::{
28    ChalkTraitId, Const, ConstScalar, GenericArg, Interner, Substitution, TargetFeatures, TraitRef,
29    TraitRefExt, Ty, WhereClause,
30    consteval::unknown_const,
31    db::HirDatabase,
32    layout::{Layout, TagEncoding},
33    mir::pad16,
34};
35
36pub(crate) fn fn_traits(db: &dyn DefDatabase, krate: Crate) -> impl Iterator<Item = TraitId> + '_ {
37    [LangItem::Fn, LangItem::FnMut, LangItem::FnOnce]
38        .into_iter()
39        .filter_map(move |lang| lang.resolve_trait(db, krate))
40}
41
42/// Returns an iterator over the direct super traits (including the trait itself).
43pub fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> {
44    let mut result = smallvec![trait_];
45    direct_super_traits_cb(db, trait_, |tt| {
46        if !result.contains(&tt) {
47            result.push(tt);
48        }
49    });
50    result
51}
52
53/// Returns an iterator over the whole super trait hierarchy (including the
54/// trait itself).
55pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> {
56    // we need to take care a bit here to avoid infinite loops in case of cycles
57    // (i.e. if we have `trait A: B; trait B: A;`)
58
59    let mut result = smallvec![trait_];
60    let mut i = 0;
61    while let Some(&t) = result.get(i) {
62        // yeah this is quadratic, but trait hierarchies should be flat
63        // enough that this doesn't matter
64        direct_super_traits_cb(db, t, |tt| {
65            if !result.contains(&tt) {
66                result.push(tt);
67            }
68        });
69        i += 1;
70    }
71    result
72}
73
74/// Given a trait ref (`Self: Trait`), builds all the implied trait refs for
75/// super traits. The original trait ref will be included. So the difference to
76/// `all_super_traits` is that we keep track of type parameters; for example if
77/// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get
78/// `Self: OtherTrait<i32>`.
79pub(super) fn all_super_trait_refs<T>(
80    db: &dyn HirDatabase,
81    trait_ref: TraitRef,
82    cb: impl FnMut(TraitRef) -> Option<T>,
83) -> Option<T> {
84    let seen = iter::once(trait_ref.trait_id).collect();
85    SuperTraits { db, seen, stack: vec![trait_ref] }.find_map(cb)
86}
87
88struct SuperTraits<'a> {
89    db: &'a dyn HirDatabase,
90    stack: Vec<TraitRef>,
91    seen: FxHashSet<ChalkTraitId>,
92}
93
94impl SuperTraits<'_> {
95    fn elaborate(&mut self, trait_ref: &TraitRef) {
96        direct_super_trait_refs(self.db, trait_ref, |trait_ref| {
97            if !self.seen.contains(&trait_ref.trait_id) {
98                self.stack.push(trait_ref);
99            }
100        });
101    }
102}
103
104impl Iterator for SuperTraits<'_> {
105    type Item = TraitRef;
106
107    fn next(&mut self) -> Option<Self::Item> {
108        if let Some(next) = self.stack.pop() {
109            self.elaborate(&next);
110            Some(next)
111        } else {
112            None
113        }
114    }
115}
116
117pub(super) fn elaborate_clause_supertraits(
118    db: &dyn HirDatabase,
119    clauses: impl Iterator<Item = WhereClause>,
120) -> ClauseElaborator<'_> {
121    let mut elaborator = ClauseElaborator { db, stack: Vec::new(), seen: FxHashSet::default() };
122    elaborator.extend_deduped(clauses);
123
124    elaborator
125}
126
127pub(super) struct ClauseElaborator<'a> {
128    db: &'a dyn HirDatabase,
129    stack: Vec<WhereClause>,
130    seen: FxHashSet<WhereClause>,
131}
132
133impl ClauseElaborator<'_> {
134    fn extend_deduped(&mut self, clauses: impl IntoIterator<Item = WhereClause>) {
135        self.stack.extend(clauses.into_iter().filter(|c| self.seen.insert(c.clone())))
136    }
137
138    fn elaborate_supertrait(&mut self, clause: &WhereClause) {
139        if let WhereClause::Implemented(trait_ref) = clause {
140            direct_super_trait_refs(self.db, trait_ref, |t| {
141                let clause = WhereClause::Implemented(t);
142                if self.seen.insert(clause.clone()) {
143                    self.stack.push(clause);
144                }
145            });
146        }
147    }
148}
149
150impl Iterator for ClauseElaborator<'_> {
151    type Item = WhereClause;
152
153    fn next(&mut self) -> Option<Self::Item> {
154        if let Some(next) = self.stack.pop() {
155            self.elaborate_supertrait(&next);
156            Some(next)
157        } else {
158            None
159        }
160    }
161}
162
163fn direct_super_traits_cb(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(TraitId)) {
164    let resolver = LazyCell::new(|| trait_.resolver(db));
165    let (generic_params, store) = db.generic_params_and_store(trait_.into());
166    let trait_self = generic_params.trait_self_param();
167    generic_params
168        .where_predicates()
169        .iter()
170        .filter_map(|pred| match pred {
171            WherePredicate::ForLifetime { target, bound, .. }
172            | WherePredicate::TypeBound { target, bound } => {
173                let is_trait = match &store[*target] {
174                    TypeRef::Path(p) => p.is_self_type(),
175                    TypeRef::TypeParam(p) => Some(p.local_id()) == trait_self,
176                    _ => false,
177                };
178                match is_trait {
179                    true => bound.as_path(&store),
180                    false => None,
181                }
182            }
183            WherePredicate::Lifetime { .. } => None,
184        })
185        .filter(|(_, bound_modifier)| matches!(bound_modifier, TraitBoundModifier::None))
186        .filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path) {
187            Some(TypeNs::TraitId(t)) => Some(t),
188            _ => None,
189        })
190        .for_each(cb);
191}
192
193fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef, cb: impl FnMut(TraitRef)) {
194    let generic_params = db.generic_params(trait_ref.hir_trait_id().into());
195    let trait_self = match generic_params.trait_self_param() {
196        Some(p) => TypeOrConstParamId { parent: trait_ref.hir_trait_id().into(), local_id: p },
197        None => return,
198    };
199    db.generic_predicates_for_param(trait_self.parent, trait_self, None)
200        .iter()
201        .filter_map(|pred| {
202            pred.as_ref().filter_map(|pred| match pred.skip_binders() {
203                // FIXME: how to correctly handle higher-ranked bounds here?
204                WhereClause::Implemented(tr) => Some(
205                    tr.clone()
206                        .shifted_out_to(Interner, DebruijnIndex::ONE)
207                        .expect("FIXME unexpected higher-ranked trait bound"),
208                ),
209                _ => None,
210            })
211        })
212        .map(|pred| pred.substitute(Interner, &trait_ref.substitution))
213        .for_each(cb);
214}
215
216pub(super) fn associated_type_by_name_including_super_traits(
217    db: &dyn HirDatabase,
218    trait_ref: TraitRef,
219    name: &Name,
220) -> Option<(TraitRef, TypeAliasId)> {
221    all_super_trait_refs(db, trait_ref, |t| {
222        let assoc_type = t.hir_trait_id().trait_items(db).associated_type_by_name(name)?;
223        Some((t, assoc_type))
224    })
225}
226
227/// It is a bit different from the rustc equivalent. Currently it stores:
228/// - 0..n-1: generics of the parent
229/// - n: the function signature, encoded as a function pointer type
230///
231/// and it doesn't store the closure types and fields.
232///
233/// Codes should not assume this ordering, and should always use methods available
234/// on this struct for retrieving, and `TyBuilder::substs_for_closure` for creating.
235pub(crate) struct ClosureSubst<'a>(pub(crate) &'a Substitution);
236
237impl<'a> ClosureSubst<'a> {
238    pub(crate) fn parent_subst(&self) -> &'a [GenericArg] {
239        match self.0.as_slice(Interner) {
240            [x @ .., _] => x,
241            _ => {
242                never!("Closure missing parameter");
243                &[]
244            }
245        }
246    }
247
248    pub(crate) fn sig_ty(&self) -> &'a Ty {
249        match self.0.as_slice(Interner) {
250            [.., x] => x.assert_ty_ref(Interner),
251            _ => {
252                unreachable!("Closure missing sig_ty parameter");
253            }
254        }
255    }
256}
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq)]
259pub enum Unsafety {
260    Safe,
261    Unsafe,
262    /// A lint.
263    DeprecatedSafe2024,
264}
265
266pub fn is_fn_unsafe_to_call(
267    db: &dyn HirDatabase,
268    func: FunctionId,
269    caller_target_features: &TargetFeatures,
270    call_edition: Edition,
271) -> Unsafety {
272    let data = db.function_signature(func);
273    if data.is_unsafe() {
274        return Unsafety::Unsafe;
275    }
276
277    if data.has_target_feature() {
278        // RFC 2396 <https://rust-lang.github.io/rfcs/2396-target-feature-1.1.html>.
279        let callee_target_features =
280            TargetFeatures::from_attrs_no_implications(&db.attrs(func.into()));
281        if !caller_target_features.enabled.is_superset(&callee_target_features.enabled) {
282            return Unsafety::Unsafe;
283        }
284    }
285
286    if data.is_deprecated_safe_2024() {
287        if call_edition.at_least_2024() {
288            return Unsafety::Unsafe;
289        } else {
290            return Unsafety::DeprecatedSafe2024;
291        }
292    }
293
294    let loc = func.lookup(db);
295    match loc.container {
296        hir_def::ItemContainerId::ExternBlockId(block) => {
297            let is_intrinsic_block = block.abi(db) == Some(sym::rust_dash_intrinsic);
298            if is_intrinsic_block {
299                // legacy intrinsics
300                // extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute
301                if db.attrs(func.into()).by_key(sym::rustc_safe_intrinsic).exists() {
302                    Unsafety::Safe
303                } else {
304                    Unsafety::Unsafe
305                }
306            } else {
307                // Function in an `extern` block are always unsafe to call, except when
308                // it is marked as `safe`.
309                if data.is_safe() { Unsafety::Safe } else { Unsafety::Unsafe }
310            }
311        }
312        _ => Unsafety::Safe,
313    }
314}
315
316pub(crate) struct UnevaluatedConstEvaluatorFolder<'a> {
317    pub(crate) db: &'a dyn HirDatabase,
318}
319
320impl FallibleTypeFolder<Interner> for UnevaluatedConstEvaluatorFolder<'_> {
321    type Error = ();
322
323    fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = ()> {
324        self
325    }
326
327    fn interner(&self) -> Interner {
328        Interner
329    }
330
331    fn try_fold_const(
332        &mut self,
333        constant: Const,
334        _outer_binder: DebruijnIndex,
335    ) -> Result<Const, Self::Error> {
336        if let chalk_ir::ConstValue::Concrete(c) = &constant.data(Interner).value
337            && let ConstScalar::UnevaluatedConst(id, subst) = &c.interned
338        {
339            if let Ok(eval) = self.db.const_eval(*id, subst.clone(), None) {
340                return Ok(eval);
341            } else {
342                return Ok(unknown_const(constant.data(Interner).ty.clone()));
343            }
344        }
345        Ok(constant)
346    }
347}
348
349pub(crate) fn detect_variant_from_bytes<'a>(
350    layout: &'a Layout,
351    db: &dyn HirDatabase,
352    target_data_layout: &TargetDataLayout,
353    b: &[u8],
354    e: EnumId,
355) -> Option<(EnumVariantId, &'a Layout)> {
356    let (var_id, var_layout) = match &layout.variants {
357        hir_def::layout::Variants::Empty => unreachable!(),
358        hir_def::layout::Variants::Single { index } => {
359            (e.enum_variants(db).variants[index.0].0, layout)
360        }
361        hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => {
362            let size = tag.size(target_data_layout).bytes_usize();
363            let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
364            let tag = i128::from_le_bytes(pad16(&b[offset..offset + size], false));
365            match tag_encoding {
366                TagEncoding::Direct => {
367                    let (var_idx, layout) =
368                        variants.iter_enumerated().find_map(|(var_idx, v)| {
369                            let def = e.enum_variants(db).variants[var_idx.0].0;
370                            (db.const_eval_discriminant(def) == Ok(tag)).then_some((def, v))
371                        })?;
372                    (var_idx, layout)
373                }
374                TagEncoding::Niche { untagged_variant, niche_start, .. } => {
375                    let candidate_tag = tag.wrapping_sub(*niche_start as i128) as usize;
376                    let variant = variants
377                        .iter_enumerated()
378                        .map(|(x, _)| x)
379                        .filter(|x| x != untagged_variant)
380                        .nth(candidate_tag)
381                        .unwrap_or(*untagged_variant);
382                    (e.enum_variants(db).variants[variant.0].0, &variants[variant])
383                }
384            }
385        }
386    };
387    Some((var_id, var_layout))
388}