1use std::iter::Enumerate;
5
6use base_db::target::{self, TargetData};
7use hir_def::{
8 EnumId, EnumVariantId, FunctionId, Lookup, TraitId, lang_item::LangItems,
9 signatures::FunctionSignature,
10};
11use rustc_abi::TargetDataLayout;
12use span::Edition;
13
14use crate::{
15 TargetFeatures,
16 db::HirDatabase,
17 layout::{Layout, TagEncoding},
18 lower::SupertraitsInfo,
19 mir::pad16,
20};
21
22pub(crate) fn fn_traits(lang_items: &LangItems) -> impl Iterator<Item = TraitId> + '_ {
23 [lang_items.Fn, lang_items.FnMut, lang_items.FnOnce].into_iter().flatten()
24}
25
26pub fn direct_super_traits(db: &dyn HirDatabase, trait_: TraitId) -> &[TraitId] {
28 &SupertraitsInfo::query(db, trait_).direct_supertraits
29}
30
31pub fn all_super_traits(db: &dyn HirDatabase, trait_: TraitId) -> &[TraitId] {
33 &SupertraitsInfo::query(db, trait_).all_supertraits
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum Unsafety {
38 Safe,
39 Unsafe,
40 DeprecatedSafe2024,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum TargetFeatureIsSafeInTarget {
46 No,
47 Yes,
48}
49
50pub fn target_feature_is_safe_in_target(target: &TargetData) -> TargetFeatureIsSafeInTarget {
51 match target.arch {
52 target::Arch::Wasm32 | target::Arch::Wasm64 => TargetFeatureIsSafeInTarget::Yes,
53 _ => TargetFeatureIsSafeInTarget::No,
54 }
55}
56
57pub fn is_fn_unsafe_to_call(
58 db: &dyn HirDatabase,
59 func: FunctionId,
60 caller_target_features: &TargetFeatures<'_>,
61 call_edition: Edition,
62 target_feature_is_safe: TargetFeatureIsSafeInTarget,
63) -> Unsafety {
64 let data = FunctionSignature::of(db, func);
65 if data.is_unsafe() {
66 return Unsafety::Unsafe;
67 }
68
69 if data.has_target_feature() && target_feature_is_safe == TargetFeatureIsSafeInTarget::No {
70 let callee_target_features = TargetFeatures::from_fn_no_implications(db, func);
72 if !caller_target_features.enabled.is_superset(&callee_target_features.enabled) {
73 return Unsafety::Unsafe;
74 }
75 }
76
77 if data.is_deprecated_safe_2024() {
78 if call_edition.at_least_2024() {
79 return Unsafety::Unsafe;
80 } else {
81 return Unsafety::DeprecatedSafe2024;
82 }
83 }
84
85 let loc = func.lookup(db);
86 match loc.container {
87 hir_def::ItemContainerId::ExternBlockId(_) => {
88 if data.is_safe() { Unsafety::Safe } else { Unsafety::Unsafe }
91 }
92 _ => Unsafety::Safe,
93 }
94}
95
96pub(crate) fn detect_variant_from_bytes<'a>(
97 layout: &'a Layout,
98 db: &dyn HirDatabase,
99 target_data_layout: &TargetDataLayout,
100 b: &[u8],
101 e: EnumId,
102) -> Option<(EnumVariantId, &'a Layout)> {
103 let (var_id, var_layout) = match &layout.variants {
104 hir_def::layout::Variants::Empty => unreachable!(),
105 hir_def::layout::Variants::Single { index } => {
106 (e.enum_variants(db).variants[index.0].0, layout)
107 }
108 hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => {
109 let size = tag.size(target_data_layout).bytes_usize();
110 let offset = layout.fields.offset(0).bytes_usize(); let tag = i128::from_le_bytes(pad16(&b[offset..offset + size], false));
112 match tag_encoding {
113 TagEncoding::Direct => {
114 let (var_idx, layout) =
115 variants.iter_enumerated().find_map(|(var_idx, v)| {
116 let def = e.enum_variants(db).variants[var_idx.0].0;
117 (db.const_eval_discriminant(def) == Ok(tag)).then_some((def, v))
118 })?;
119 (var_idx, layout)
120 }
121 TagEncoding::Niche { untagged_variant, niche_start, .. } => {
122 let candidate_tag = tag.wrapping_sub(*niche_start as i128) as usize;
123 let variant = variants
124 .iter_enumerated()
125 .map(|(x, _)| x)
126 .filter(|x| x != untagged_variant)
127 .nth(candidate_tag)
128 .unwrap_or(*untagged_variant);
129 (e.enum_variants(db).variants[variant.0].0, &variants[variant])
130 }
131 }
132 }
133 };
134 Some((var_id, var_layout))
135}
136
137pub(crate) struct EnumerateAndAdjust<I> {
138 enumerate: Enumerate<I>,
139 gap_pos: usize,
140 gap_len: usize,
141}
142
143impl<I> Iterator for EnumerateAndAdjust<I>
144where
145 I: Iterator,
146{
147 type Item = (usize, <I as Iterator>::Item);
148
149 fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
150 self.enumerate
151 .next()
152 .map(|(i, elem)| (if i < self.gap_pos { i } else { i + self.gap_len }, elem))
153 }
154
155 fn size_hint(&self) -> (usize, Option<usize>) {
156 self.enumerate.size_hint()
157 }
158}
159
160pub(crate) trait EnumerateAndAdjustIterator {
161 fn enumerate_and_adjust(
162 self,
163 expected_len: usize,
164 gap_pos: Option<u32>,
165 ) -> EnumerateAndAdjust<Self>
166 where
167 Self: Sized;
168}
169
170impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T {
171 fn enumerate_and_adjust(
172 self,
173 expected_len: usize,
174 gap_pos: Option<u32>,
175 ) -> EnumerateAndAdjust<Self>
176 where
177 Self: Sized,
178 {
179 let actual_len = self.len();
180 EnumerateAndAdjust {
181 enumerate: self.enumerate(),
182 gap_pos: gap_pos.map(|it| it as usize).unwrap_or(expected_len),
183 gap_len: expected_len - actual_len,
184 }
185 }
186}