1use macros::EnumIsA;
3
4use crate::ast::*;
5use crate::formatter::IntoFormatter;
6use crate::pretty::FmtWithCtx;
7use crate::{llbc_ast, ullbc_ast};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11#[derive(EnumIsA)]
12pub enum Safety {
13 Safe,
14 Unsafe,
15 Unknown(String),
16}
17
18impl Safety {
19 pub fn or_else(self, other: impl FnOnce() -> Safety) -> Safety {
22 match self {
23 Safety::Unsafe => Safety::Unsafe,
24 Safety::Safe => other(),
25 Safety::Unknown(reason) => match other() {
26 Safety::Unsafe => Safety::Unsafe,
27 Safety::Safe | Safety::Unknown(_) => Safety::Unknown(reason),
28 },
29 }
30 }
31}
32
33impl From<bool> for Safety {
34 fn from(is_unsafe: bool) -> Self {
35 if is_unsafe {
36 Safety::Unsafe
37 } else {
38 Safety::Safe
39 }
40 }
41}
42
43impl FromIterator<Safety> for Safety {
46 fn from_iter<I: IntoIterator<Item = Safety>>(iter: I) -> Self {
47 let mut safety = Safety::Safe;
48 for other in iter {
49 safety = safety.or_else(|| other);
50 if safety.is_unsafe() {
51 break;
52 }
53 }
54 safety
55 }
56}
57
58pub trait HasSafety {
71 fn safety(&self, krate: &TranslatedCrate) -> Safety;
72}
73
74impl<I: ?Sized, T: HasSafety> HasSafety for I
75where
76 for<'a> &'a I: IntoIterator<Item = &'a T>,
77{
78 fn safety(&self, krate: &TranslatedCrate) -> Safety {
79 self.into_iter().map(|x| x.safety(krate)).collect()
80 }
81}
82
83impl<T: HasSafety> HasSafety for RegionBinder<T> {
84 fn safety(&self, krate: &TranslatedCrate) -> Safety {
85 self.skip_binder.safety(krate)
86 }
87}
88
89impl HasSafety for FunSig {
90 fn safety(&self, _krate: &TranslatedCrate) -> Safety {
91 self.is_unsafe.into()
92 }
93}
94
95impl Place {
96 pub fn read_safety(&self, krate: &TranslatedCrate) -> Safety {
102 self.subplaces()
103 .map(|place| place.shallow_safety(krate))
104 .collect()
105 }
106
107 fn shallow_safety(&self, krate: &TranslatedCrate) -> Safety {
109 let (sub, proj) = match &self.kind {
110 PlaceKind::Global(global_ref) => {
111 return match krate.global_decls.get(global_ref.id) {
112 Some(decl) => decl.is_unsafe_to_access(krate).into(),
113 None => Safety::Unknown(format!(
114 "{} wasn't translated",
115 global_ref.with_ctx(&krate.into_fmt())
116 )),
117 };
118 }
119 PlaceKind::Local(_) => return Safety::Safe,
120 PlaceKind::Projection(sub, proj) => (sub, proj),
121 };
122 match proj {
123 ProjectionElem::Deref => (sub.ty.kind().is_raw_ptr()
125 && !matches!(sub.as_projection(), Some((_, ProjectionElem::PtrMetadata))))
126 .into(),
127 ProjectionElem::Field(None, _) => {
129 let Some(tref) = sub.ty.as_adt().filter(|tref| !tref.is_builtin()) else {
130 return Safety::Safe;
131 };
132 match krate.type_decls.get(tref.id).map(|decl| &decl.kind) {
133 Some(TypeDeclKind::Union(_)) => Safety::Unsafe,
134 Some(
135 TypeDeclKind::Struct(_) | TypeDeclKind::Enum(_) | TypeDeclKind::Alias(_),
136 ) => Safety::Safe,
137 Some(TypeDeclKind::Opaque | TypeDeclKind::Error(_)) | None => {
138 Safety::Unknown(format!(
139 "{} is opaque or wasn't translated",
140 tref.with_ctx(&krate.into_fmt())
141 ))
142 }
143 }
144 }
145 ProjectionElem::Index { .. }
146 | ProjectionElem::Subslice { .. }
147 | ProjectionElem::Offset { .. } => Safety::Unsafe,
148 ProjectionElem::Field(Some(_), _) | ProjectionElem::PtrMetadata => Safety::Safe,
149 }
150 }
151
152 pub fn write_safety(&self, krate: &TranslatedCrate) -> Safety {
155 match self.as_projection() {
156 Some((sub, ProjectionElem::Field(..))) => sub.write_safety(krate),
158 _ => self.read_safety(krate),
159 }
160 }
161}
162
163impl HasSafety for Operand {
164 fn safety(&self, krate: &TranslatedCrate) -> Safety {
165 match self {
166 Operand::Copy(place) | Operand::Move(place) => place.read_safety(krate),
167 Operand::Const(_) => Safety::Safe,
168 }
169 }
170}
171
172impl HasSafety for Rvalue {
173 fn safety(&self, krate: &TranslatedCrate) -> Safety {
174 match self {
175 Rvalue::RawPtr {
176 place,
177 ptr_metadata,
178 ..
179 } => {
180 let accessed = match &place.kind {
183 PlaceKind::Global(_) => None,
184 PlaceKind::Projection(sub, ProjectionElem::Deref) => Some(&**sub),
185 _ => place.subplaces().find(|place| {
186 !matches!(place.as_projection(), Some((_, ProjectionElem::Field(..))))
187 || !place.shallow_safety(krate).is_unsafe()
188 }),
189 };
190 accessed
191 .map_or(Safety::Safe, |place| place.read_safety(krate))
192 .or_else(|| ptr_metadata.safety(krate))
193 }
194 Rvalue::Ref {
195 place,
196 ptr_metadata,
197 ..
198 } => place
199 .read_safety(krate)
200 .or_else(|| ptr_metadata.safety(krate)),
201 Rvalue::Discriminant(place) | Rvalue::Len(place, ..) => place.read_safety(krate),
202 Rvalue::UnaryOp(unop, op) => unop.safety(krate).or_else(|| op.safety(krate)),
203 Rvalue::Use(op, _) | Rvalue::Repeat(op, ..) => op.safety(krate),
204 Rvalue::BinaryOp(binop, op1, op2) => binop
205 .safety(krate)
206 .or_else(|| op1.safety(krate).or_else(|| op2.safety(krate))),
207 Rvalue::Aggregate(_, ops) => ops.safety(krate),
208 Rvalue::NullaryOp(_) => Safety::Safe,
209 }
210 }
211}
212
213impl HasSafety for UnOp {
214 fn safety(&self, krate: &TranslatedCrate) -> Safety {
215 match self {
216 UnOp::Not => Safety::Safe,
217 UnOp::Neg(om) => om.safety(krate),
218 UnOp::Cast(CastKind::Concretize(..) | CastKind::Transmute(..)) => Safety::Unsafe,
219 UnOp::Cast(CastKind::PtrWithExposedProvenance(tgt)) if tgt.is_ref() => Safety::Unsafe,
220 UnOp::Cast(
221 CastKind::Scalar(..)
222 | CastKind::Subtype(..)
223 | CastKind::RawPtr(..)
224 | CastKind::PtrExposeProvenance
225 | CastKind::PtrWithExposedProvenance(..)
226 | CastKind::Unsize(..)
227 | CastKind::FnDefToFnPtr
228 | CastKind::FnPtrToUnsafe,
229 ) => Safety::Safe,
230 }
231 }
232}
233
234impl HasSafety for BinOp {
235 fn safety(&self, krate: &TranslatedCrate) -> Safety {
236 use BinOp::*;
237 match self {
238 Add(om) | Sub(om) | Mul(om) | Div(om) | Rem(om) | Shl(om) | Shr(om) => om.safety(krate),
239 BitXor | BitAnd | BitOr | Eq | Lt | Le | Ne | Ge | Gt | AddChecked | SubChecked
240 | MulChecked | Cmp => Safety::Safe,
241 }
242 }
243}
244
245impl HasSafety for OverflowMode {
246 fn safety(&self, _krate: &TranslatedCrate) -> Safety {
247 match self {
248 OverflowMode::UB => Safety::Unsafe,
249 OverflowMode::Panic | OverflowMode::Wrap => Safety::Safe,
250 }
251 }
252}
253
254impl HasSafety for SwitchData {
255 fn safety(&self, krate: &TranslatedCrate) -> Safety {
256 match &self.scrutinee {
257 SwitchScrutinee::Value(op) => op.safety(krate),
258 SwitchScrutinee::Discriminant(place) => place.read_safety(krate),
259 }
260 }
261}
262
263impl HasSafety for Call {
266 fn safety(&self, krate: &TranslatedCrate) -> Safety {
267 let callee_safety = match self.safety {
268 CallSafety::Inherit => self.func.safety(krate),
269 CallSafety::Safe => Safety::Safe,
270 CallSafety::Unsafe => Safety::Unsafe,
271 };
272 callee_safety
273 .or_else(|| match &self.func {
274 FnOperand::Dynamic(op, _) => op.safety(krate),
275 FnOperand::Regular(_) => Safety::Safe,
276 })
277 .or_else(|| self.args.safety(krate))
278 .or_else(|| self.dest.write_safety(krate))
279 }
280}
281
282impl HasSafety for FnOperand {
283 fn safety(&self, krate: &TranslatedCrate) -> Safety {
286 match self {
287 FnOperand::Regular(fn_ptr) => match fn_ptr.kind.as_ref() {
288 FnPtrKind::Fun(fun_id) => match krate.fun_decls.get(*fun_id) {
289 Some(decl) => decl.signature.safety(krate),
290 None => Safety::Unknown(format!(
291 "{} wasn't translated",
292 fun_id.with_ctx(&krate.into_fmt())
293 )),
294 },
295 FnPtrKind::Trait(trait_ref, method_id) => {
296 let trait_id = trait_ref.trait_id();
297 let method = krate
298 .trait_decls
299 .get(trait_id)
300 .and_then(|decl| decl.methods.get(*method_id));
301 match method {
302 Some(method) => method.skip_binder.signature.safety(krate),
303 None => Safety::Unknown(format!(
304 "the signature of {method_id:?} of {trait_id:?} wasn't translated"
305 )),
306 }
307 }
308 },
309 FnOperand::Dynamic(op, _) => op.ty().kind().as_fn_ptr().unwrap().safety(krate),
310 }
311 }
312}
313
314impl HasSafety for BorrowckStatement {
315 fn safety(&self, krate: &TranslatedCrate) -> Safety {
316 match self {
317 BorrowckStatement::FakeRead(place) => place.read_safety(krate),
318 BorrowckStatement::SetOutlives(..)
319 | BorrowckStatement::PredicateHolds(..)
320 | BorrowckStatement::SetType { .. } => Safety::Safe,
321 }
322 }
323}
324
325impl HasSafety for AbortKind {
326 fn safety(&self, _krate: &TranslatedCrate) -> Safety {
327 match self {
328 AbortKind::UndefinedBehavior => Safety::Unsafe,
329 AbortKind::Panic(..) | AbortKind::UnwindTerminate => Safety::Safe,
330 }
331 }
332}
333
334impl HasSafety for llbc_ast::Statement {
336 fn safety(&self, krate: &TranslatedCrate) -> Safety {
337 use llbc_ast::StatementKind;
338 match &self.kind {
339 StatementKind::Assign(place, rvalue) => {
340 place.write_safety(krate).or_else(|| rvalue.safety(krate))
341 }
342 StatementKind::SetDiscriminant(place, _) => place.write_safety(krate),
343 StatementKind::PlaceMention(place) | StatementKind::Drop { place, .. } => {
344 place.read_safety(krate)
345 }
346 StatementKind::Borrowck(st) => st.safety(krate),
347 StatementKind::Assert {
348 assert, on_failure, ..
349 } => assert
350 .cond
351 .safety(krate)
352 .or_else(|| on_failure.safety(krate)),
353 StatementKind::Call { call, .. } => call.safety(krate),
354 StatementKind::Switch { data, .. } => data.safety(krate),
355 StatementKind::InlineAsm { asm, .. } => asm.kind.is_asm().into(),
356 StatementKind::UndefinedBehavior => Safety::Unsafe,
357 StatementKind::StorageLive(_)
358 | StatementKind::StorageDead(_)
359 | StatementKind::Nop
360 | StatementKind::Loop(_)
361 | StatementKind::Break(_)
362 | StatementKind::Continue(_)
363 | StatementKind::Return
364 | StatementKind::Panic { .. }
365 | StatementKind::UnwindResume
366 | StatementKind::UnwindTerminate => Safety::Safe,
367 }
368 }
369}
370
371impl HasSafety for ullbc_ast::Statement {
373 fn safety(&self, krate: &TranslatedCrate) -> Safety {
374 use ullbc_ast::StatementKind;
375 match &self.kind {
376 StatementKind::Assign(place, rvalue) => {
377 place.write_safety(krate).or_else(|| rvalue.safety(krate))
378 }
379 StatementKind::SetDiscriminant(place, _) => place.write_safety(krate),
380 StatementKind::PlaceMention(place) => place.read_safety(krate),
381 StatementKind::Borrowck(st) => st.safety(krate),
382 StatementKind::Assert { assert, on_failure } => assert
383 .cond
384 .safety(krate)
385 .or_else(|| on_failure.safety(krate)),
386 StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => {
387 Safety::Safe
388 }
389 }
390 }
391}
392
393impl HasSafety for ullbc_ast::Terminator {
394 fn safety(&self, krate: &TranslatedCrate) -> Safety {
395 use ullbc_ast::TerminatorKind;
396 match &self.kind {
397 TerminatorKind::Switch { data, .. } => data.safety(krate),
398 TerminatorKind::Call { call, .. } => call.safety(krate),
399 TerminatorKind::Drop { place, .. } => place.read_safety(krate),
400 TerminatorKind::Assert { assert, .. } => assert.cond.safety(krate),
401 TerminatorKind::InlineAsm { asm, .. } => asm.kind.is_asm().into(),
402 TerminatorKind::UndefinedBehavior => Safety::Unsafe,
403 TerminatorKind::Goto { .. }
404 | TerminatorKind::Return
405 | TerminatorKind::Panic { .. }
406 | TerminatorKind::UnwindResume
407 | TerminatorKind::UnwindTerminate => Safety::Safe,
408 }
409 }
410}