1use 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
42pub 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
53pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> {
56 let mut result = smallvec![trait_];
60 let mut i = 0;
61 while let Some(&t) = result.get(i) {
62 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
74pub(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 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
227pub(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 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 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 if db.attrs(func.into()).by_key(sym::rustc_safe_intrinsic).exists() {
302 Unsafety::Safe
303 } else {
304 Unsafety::Unsafe
305 }
306 } else {
307 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(); 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}