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 { .. } | ProjectionElem::Subslice { .. } => Safety::Unsafe,
146 ProjectionElem::Field(Some(_), _) | ProjectionElem::PtrMetadata => Safety::Safe,
147 }
148 }
149
150 pub fn write_safety(&self, krate: &TranslatedCrate) -> Safety {
153 match self.as_projection() {
154 Some((sub, ProjectionElem::Field(..))) => sub.write_safety(krate),
156 _ => self.read_safety(krate),
157 }
158 }
159}
160
161impl HasSafety for Operand {
162 fn safety(&self, krate: &TranslatedCrate) -> Safety {
163 match self {
164 Operand::Copy(place) | Operand::Move(place) => place.read_safety(krate),
165 Operand::Const(_) => Safety::Safe,
166 }
167 }
168}
169
170impl HasSafety for Rvalue {
171 fn safety(&self, krate: &TranslatedCrate) -> Safety {
172 match self {
173 Rvalue::RawPtr {
174 place,
175 ptr_metadata,
176 ..
177 } => {
178 let accessed = match &place.kind {
181 PlaceKind::Global(_) => None,
182 PlaceKind::Projection(sub, ProjectionElem::Deref) => Some(&**sub),
183 _ => place.subplaces().find(|place| {
184 !matches!(place.as_projection(), Some((_, ProjectionElem::Field(..))))
185 || !place.shallow_safety(krate).is_unsafe()
186 }),
187 };
188 accessed
189 .map_or(Safety::Safe, |place| place.read_safety(krate))
190 .or_else(|| ptr_metadata.safety(krate))
191 }
192 Rvalue::Ref {
193 place,
194 ptr_metadata,
195 ..
196 } => place
197 .read_safety(krate)
198 .or_else(|| ptr_metadata.safety(krate)),
199 Rvalue::Discriminant(place) | Rvalue::Len(place, ..) => place.read_safety(krate),
200 Rvalue::UnaryOp(unop, op) => unop.safety(krate).or_else(|| op.safety(krate)),
201 Rvalue::Use(op, _) | Rvalue::Repeat(op, ..) => op.safety(krate),
202 Rvalue::BinaryOp(binop, op1, op2) => binop
203 .safety(krate)
204 .or_else(|| op1.safety(krate).or_else(|| op2.safety(krate))),
205 Rvalue::Aggregate(_, ops) => ops.safety(krate),
206 Rvalue::NullaryOp(_) => Safety::Safe,
207 }
208 }
209}
210
211impl HasSafety for UnOp {
212 fn safety(&self, krate: &TranslatedCrate) -> Safety {
213 match self {
214 UnOp::Not => Safety::Safe,
215 UnOp::Neg(om) => om.safety(krate),
216 UnOp::Cast(CastKind::Concretize(..) | CastKind::Transmute(..)) => Safety::Unsafe,
217 UnOp::Cast(CastKind::PtrWithExposedProvenance(_, tgt)) if tgt.is_ref() => {
218 Safety::Unsafe
219 }
220 UnOp::Cast(_) => Safety::Safe,
221 }
222 }
223}
224
225impl HasSafety for BinOp {
226 fn safety(&self, krate: &TranslatedCrate) -> Safety {
227 use BinOp::*;
228 match self {
229 Add(om) | Sub(om) | Mul(om) | Div(om) | Rem(om) | Shl(om) | Shr(om) => om.safety(krate),
230 BitXor | BitAnd | BitOr | Eq | Lt | Le | Ne | Ge | Gt | AddChecked | SubChecked
231 | MulChecked | Cmp => Safety::Safe,
232 Offset => Safety::Unsafe,
233 }
234 }
235}
236
237impl HasSafety for OverflowMode {
238 fn safety(&self, _krate: &TranslatedCrate) -> Safety {
239 match self {
240 OverflowMode::UB => Safety::Unsafe,
241 OverflowMode::Panic | OverflowMode::Wrap => Safety::Safe,
242 }
243 }
244}
245
246impl HasSafety for SwitchData {
247 fn safety(&self, krate: &TranslatedCrate) -> Safety {
248 match &self.scrutinee {
249 SwitchScrutinee::Value(op) => op.safety(krate),
250 SwitchScrutinee::Discriminant(place) => place.read_safety(krate),
251 }
252 }
253}
254
255impl HasSafety for Call {
258 fn safety(&self, krate: &TranslatedCrate) -> Safety {
259 let callee_safety = match self.safety {
260 CallSafety::Inherit => self.func.safety(krate),
261 CallSafety::Safe => Safety::Safe,
262 CallSafety::Unsafe => Safety::Unsafe,
263 };
264 callee_safety
265 .or_else(|| match &self.func {
266 FnOperand::Dynamic(op) => op.safety(krate),
267 FnOperand::Regular(_) => Safety::Safe,
268 })
269 .or_else(|| self.args.safety(krate))
270 .or_else(|| self.dest.write_safety(krate))
271 }
272}
273
274impl HasSafety for FnOperand {
275 fn safety(&self, krate: &TranslatedCrate) -> Safety {
278 match self {
279 FnOperand::Regular(fn_ptr) => match fn_ptr.kind.as_ref() {
280 FnPtrKind::Fun(fun_id) => match krate.fun_decls.get(*fun_id) {
281 Some(decl) => decl.signature.safety(krate),
282 None => Safety::Unknown(format!(
283 "{} wasn't translated",
284 fun_id.with_ctx(&krate.into_fmt())
285 )),
286 },
287 FnPtrKind::Trait(trait_ref, method_id) => {
288 let trait_id = trait_ref.trait_decl_ref.skip_binder.id;
289 let method = krate
290 .trait_decls
291 .get(trait_id)
292 .and_then(|decl| decl.methods.get(*method_id));
293 match method {
294 Some(method) => method.skip_binder.signature.safety(krate),
295 None => Safety::Unknown(format!(
296 "the signature of {method_id:?} of {trait_id:?} wasn't translated"
297 )),
298 }
299 }
300 },
301 FnOperand::Dynamic(op) => op.ty().kind().as_fn_ptr().unwrap().safety(krate),
302 }
303 }
304}
305
306impl HasSafety for BorrowckStatement {
307 fn safety(&self, krate: &TranslatedCrate) -> Safety {
308 match self {
309 BorrowckStatement::FakeRead(place) => place.read_safety(krate),
310 BorrowckStatement::SetOutlives(..)
311 | BorrowckStatement::PredicateHolds(..)
312 | BorrowckStatement::SetType { .. } => Safety::Safe,
313 }
314 }
315}
316
317impl HasSafety for AbortKind {
318 fn safety(&self, _krate: &TranslatedCrate) -> Safety {
319 match self {
320 AbortKind::UndefinedBehavior => Safety::Unsafe,
321 AbortKind::Panic(..) | AbortKind::UnwindTerminate => Safety::Safe,
322 }
323 }
324}
325
326impl HasSafety for llbc_ast::Statement {
328 fn safety(&self, krate: &TranslatedCrate) -> Safety {
329 use llbc_ast::StatementKind;
330 match &self.kind {
331 StatementKind::Assign(place, rvalue) => {
332 place.write_safety(krate).or_else(|| rvalue.safety(krate))
333 }
334 StatementKind::SetDiscriminant(place, _) => place.write_safety(krate),
335 StatementKind::PlaceMention(place) | StatementKind::Drop { place, .. } => {
336 place.read_safety(krate)
337 }
338 StatementKind::Borrowck(st) => st.safety(krate),
339 StatementKind::Assert {
340 assert, on_failure, ..
341 } => assert
342 .cond
343 .safety(krate)
344 .or_else(|| on_failure.safety(krate)),
345 StatementKind::Call { call, .. } => call.safety(krate),
346 StatementKind::Switch { data, .. } => data.safety(krate),
347 StatementKind::InlineAsm { asm, .. } => asm.kind.is_asm().into(),
348 StatementKind::UndefinedBehavior => Safety::Unsafe,
349 StatementKind::StorageLive(_)
350 | StatementKind::StorageDead(_)
351 | StatementKind::Nop
352 | StatementKind::Loop(_)
353 | StatementKind::Break(_)
354 | StatementKind::Continue(_)
355 | StatementKind::Return
356 | StatementKind::Panic { .. }
357 | StatementKind::UnwindResume
358 | StatementKind::UnwindTerminate => Safety::Safe,
359 }
360 }
361}
362
363impl HasSafety for ullbc_ast::Statement {
365 fn safety(&self, krate: &TranslatedCrate) -> Safety {
366 use ullbc_ast::StatementKind;
367 match &self.kind {
368 StatementKind::Assign(place, rvalue) => {
369 place.write_safety(krate).or_else(|| rvalue.safety(krate))
370 }
371 StatementKind::SetDiscriminant(place, _) => place.write_safety(krate),
372 StatementKind::PlaceMention(place) => place.read_safety(krate),
373 StatementKind::Borrowck(st) => st.safety(krate),
374 StatementKind::Assert { assert, on_failure } => assert
375 .cond
376 .safety(krate)
377 .or_else(|| on_failure.safety(krate)),
378 StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => {
379 Safety::Safe
380 }
381 }
382 }
383}
384
385impl HasSafety for ullbc_ast::Terminator {
386 fn safety(&self, krate: &TranslatedCrate) -> Safety {
387 use ullbc_ast::TerminatorKind;
388 match &self.kind {
389 TerminatorKind::Switch { data, .. } => data.safety(krate),
390 TerminatorKind::Call { call, .. } => call.safety(krate),
391 TerminatorKind::Drop { place, .. } => place.read_safety(krate),
392 TerminatorKind::Assert { assert, .. } => assert.cond.safety(krate),
393 TerminatorKind::InlineAsm { asm, .. } => asm.kind.is_asm().into(),
394 TerminatorKind::UndefinedBehavior => Safety::Unsafe,
395 TerminatorKind::Goto { .. }
396 | TerminatorKind::Return
397 | TerminatorKind::Panic { .. }
398 | TerminatorKind::UnwindResume
399 | TerminatorKind::UnwindTerminate => Safety::Safe,
400 }
401 }
402}