1#[cfg(rapx_has_attr_ir)]
18use rustc_attr_ir::LangItem;
19#[cfg(all(not(rapx_has_attr_ir), not(rapx_ge_100)))]
20use rustc_hir::LangItem;
21#[cfg(all(not(rapx_has_attr_ir), rapx_ge_100))]
22use rustc_hir::attrs::lang_items::LangItem;
23use rustc_hir::def::DefKind;
24use rustc_hir::def_id::DefId;
25use rustc_middle::ty::{ClauseKind, GenericArgKind, ParamTy, Ty, TyCtxt, TyKind};
26
27use crate::compat::FxHashMap;
28use crate::helpers::mir_scan::{Checkpoint, has_atomic_call, has_raw_ptr_write};
29use crate::verify::vm::state::VmState;
30use crate::verify::{
31 contract::{Property, PropertyArg, PropertyKind},
32 report::{CheckResult, UnknownReason},
33 target::get_struct_invariants_from_annotation,
34};
35
36use super::PropertyChecker;
37
38#[derive(Clone, Copy, PartialEq, Eq, Debug)]
42enum Contains {
43 Yes,
44 No,
45 Maybe,
46}
47
48impl Contains {
49 fn join(self, other: Contains) -> Contains {
51 match (self, other) {
52 (Contains::Yes, _) | (_, Contains::Yes) => Contains::Yes,
53 (Contains::Maybe, _) | (_, Contains::Maybe) => Contains::Maybe,
54 (Contains::No, Contains::No) => Contains::No,
55 }
56 }
57
58 fn to_check(self) -> CheckResult {
60 match self {
61 Contains::Yes => CheckResult::Failed,
62 Contains::Maybe => CheckResult::Unknown(UnknownReason::Unimplemented),
63 Contains::No => CheckResult::ProvedByRule,
64 }
65 }
66}
67
68impl PropertyChecker {
69 pub(super) fn check_contain_no_type<'z3, 'tcx>(
72 &self,
73 vm_state: &VmState<'z3, 'tcx>,
74 checkpoint: &Checkpoint<'tcx>,
75 property: &Property<'tcx>,
76 ) -> CheckResult {
77 let Some(ty) = Self::ty_arg(property, 0) else {
78 return CheckResult::Unknown(UnknownReason::Unimplemented);
79 };
80 let negatives: Vec<String> = property.args()[1..]
81 .iter()
82 .filter_map(|a| match a {
83 PropertyArg::Ident(name) => Some(name.clone()),
84 _ => None,
85 })
86 .collect();
87 if negatives.is_empty() {
88 return CheckResult::Unknown(UnknownReason::Unimplemented);
89 }
90 contain_no_type_check(vm_state.tcx, ty, &negatives, checkpoint.caller, false)
91 }
92
93 pub(super) fn check_no_raw_ptr<'z3, 'tcx>(
95 &self,
96 vm_state: &VmState<'z3, 'tcx>,
97 checkpoint: &Checkpoint<'tcx>,
98 property: &Property<'tcx>,
99 ) -> CheckResult {
100 let Some(ty) = Self::ty_arg(property, 0) else {
101 return CheckResult::Unknown(UnknownReason::Unimplemented);
102 };
103 no_raw_ptr_check(vm_state.tcx, ty, checkpoint.caller, false)
104 }
105
106 pub(super) fn check_no_internal_mut<'z3, 'tcx>(
108 &self,
109 vm_state: &VmState<'z3, 'tcx>,
110 property: &Property<'tcx>,
111 ) -> CheckResult {
112 let Some(ty) = Self::ty_arg(property, 0) else {
113 return CheckResult::Unknown(UnknownReason::Unimplemented);
114 };
115 no_internal_mut_check(vm_state.tcx, ty)
116 }
117
118 pub(super) fn check_uni_internal_mut<'z3, 'tcx>(
121 &self,
122 vm_state: &VmState<'z3, 'tcx>,
123 property: &Property<'tcx>,
124 ) -> CheckResult {
125 let Some(ty) = Self::ty_arg(property, 0) else {
126 return CheckResult::Unknown(UnknownReason::Unimplemented);
127 };
128 uni_internal_mut_check(vm_state.tcx, ty)
129 }
130
131 pub(super) fn check_atomic_update<'z3, 'tcx>(
135 &self,
136 vm_state: &VmState<'z3, 'tcx>,
137 checkpoint: &Checkpoint<'tcx>,
138 property: &Property<'tcx>,
139 ) -> CheckResult {
140 let Some(ty) = Self::ty_arg(property, 0) else {
141 return CheckResult::Unknown(UnknownReason::Unimplemented);
142 };
143 atomic_update_check(vm_state.tcx, ty, checkpoint.caller, false)
144 }
145
146 pub(super) fn check_ref_send<'z3, 'tcx>(
149 &self,
150 vm_state: &VmState<'z3, 'tcx>,
151 checkpoint: &Checkpoint<'tcx>,
152 property: &Property<'tcx>,
153 ) -> CheckResult {
154 let Some(ty) = Self::ty_arg(property, 0) else {
155 return CheckResult::Unknown(UnknownReason::Unimplemented);
156 };
157 ref_send_check(vm_state.tcx, ty, checkpoint.caller, true)
158 }
159}
160
161pub(crate) fn contain_no_type_check<'tcx>(
165 tcx: TyCtxt<'tcx>,
166 ty: Ty<'tcx>,
167 negatives: &[String],
168 impl_def_id: DefId,
169 is_sync: bool,
170) -> CheckResult {
171 let mut defs: Vec<DefId> = Vec::new();
172 for name in negatives {
173 defs.extend_from_slice(crate::def_id::negative_type_defs(name));
174 }
175 type_structurally_contains(tcx, ty, &defs, impl_def_id, is_sync).to_check()
176}
177
178pub(crate) fn no_raw_ptr_check<'tcx>(
180 tcx: TyCtxt<'tcx>,
181 ty: Ty<'tcx>,
182 impl_def_id: DefId,
183 is_sync: bool,
184) -> CheckResult {
185 find_raw_ptr(tcx, ty, impl_def_id, is_sync).to_check()
186}
187
188pub(crate) fn no_internal_mut_check<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> CheckResult {
192 if has_raw_ptr_writes(tcx, ty) || has_atomic_ptr_updates(tcx, ty) {
193 CheckResult::Failed
194 } else {
195 CheckResult::ProvedByRule
196 }
197}
198
199pub(crate) fn uni_internal_mut_check<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> CheckResult {
203 if (has_raw_ptr_writes(tcx, ty) || has_atomic_ptr_updates(tcx, ty))
204 && !type_implements_clone(tcx, ty)
205 {
206 CheckResult::ProvedByRule
207 } else {
208 CheckResult::Failed
209 }
210}
211
212pub(crate) fn atomic_update_check<'tcx>(
222 tcx: TyCtxt<'tcx>,
223 ty: Ty<'tcx>,
224 impl_def_id: DefId,
225 is_sync: bool,
226) -> CheckResult {
227 match find_unsynchronized_mutation(tcx, ty, impl_def_id, is_sync) {
228 Contains::No => CheckResult::ProvedByRule,
229 Contains::Maybe => CheckResult::Unknown(UnknownReason::Unimplemented),
230 Contains::Yes => {
231 if has_atomic_ptr_updates(tcx, ty) {
232 CheckResult::ProvedByRule
233 } else {
234 CheckResult::Failed
235 }
236 }
237 }
238}
239
240pub(crate) fn field_invariant_check<'tcx>(
248 tcx: TyCtxt<'tcx>,
249 ty: Ty<'tcx>,
250 kind: PropertyKind,
251 field: Option<&str>,
252 invariant_results: &FxHashMap<DefId, CheckResult>,
253) -> CheckResult {
254 let TyKind::Adt(adt_def, _) = ty.kind() else {
255 return CheckResult::Failed;
256 };
257 let adt_def_id = adt_def.did();
258
259 let invariants = get_struct_invariants_from_annotation(tcx, adt_def_id, adt_def_id);
260 let matched = invariants.iter().any(|p| {
261 p.kind() == Some(kind)
262 && field.is_none_or(|f| {
263 p.args()
264 .first()
265 .and_then(|a| {
266 crate::verify::contract::place::field_name_from_arg(tcx, adt_def_id, a)
267 })
268 .as_deref()
269 == Some(f)
270 })
271 });
272 if !matched {
273 return CheckResult::Failed;
274 }
275
276 if let Some(result) = invariant_results.get(&adt_def_id) {
278 if *result != CheckResult::ProvedByRule {
279 return CheckResult::Failed;
280 }
281 }
282 CheckResult::ProvedByRule
283}
284
285pub(crate) fn ref_send_check<'tcx>(
287 tcx: TyCtxt<'tcx>,
288 ty: Ty<'tcx>,
289 impl_def_id: DefId,
290 is_sync: bool,
291) -> CheckResult {
292 find_unsynchronized_mutation(tcx, ty, impl_def_id, is_sync).to_check()
293}
294
295fn type_implements_clone<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
298 let Some(clone_did) = tcx.lang_items().clone_trait() else {
299 return false;
300 };
301 tcx.all_impls(clone_did)
302 .any(|impl_did| tcx.impl_trait_ref(impl_did).skip_binder().self_ty() == ty)
303}
304
305fn param_bound_is_satisfied(
308 tcx: TyCtxt<'_>,
309 impl_def_id: DefId,
310 param_ty: ParamTy,
311 is_sync: bool,
312) -> bool {
313 let trait_did = if is_sync {
314 tcx.get_diagnostic_item(rustc_span::sym::Sync)
315 } else {
316 tcx.get_diagnostic_item(rustc_span::sym::Send)
317 };
318 let Some(trait_did) = trait_did else {
319 return false;
320 };
321 let predicates = crate::compat::predicates_of(tcx, impl_def_id);
322 #[cfg(not(rapx_ge_100))]
323 let iter = predicates.predicates.iter();
324 #[cfg(rapx_ge_100)]
325 let iter = predicates.clauses.iter();
326 for (pred, _) in iter {
327 if let ClauseKind::Trait(trait_ref) = pred.kind().skip_binder() {
328 if trait_ref.def_id() == trait_did {
329 if let TyKind::Param(p) = trait_ref.self_ty().kind() {
330 if p.index == param_ty.index {
331 return true;
332 }
333 }
334 }
335 }
336 }
337 false
338}
339
340fn has_raw_ptr_writes<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
342 let TyKind::Adt(adt_def, _) = ty.kind() else {
343 return false;
344 };
345 let adt_def_id = adt_def.did();
346 tcx.inherent_impls(adt_def_id).iter().any(|impl_id| {
347 tcx.associated_item_def_ids(*impl_id).iter().any(|item| {
348 matches!(tcx.def_kind(*item), DefKind::Fn | DefKind::AssocFn)
349 && has_raw_ptr_write(tcx, *item)
350 })
351 })
352}
353
354fn has_atomic_ptr_updates<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
358 let TyKind::Adt(adt_def, _) = ty.kind() else {
359 return false;
360 };
361 let adt_def_id = adt_def.did();
362 tcx.inherent_impls(adt_def_id).iter().any(|impl_id| {
363 tcx.associated_item_def_ids(*impl_id).iter().any(|item| {
364 matches!(tcx.def_kind(*item), DefKind::Fn | DefKind::AssocFn)
365 && has_atomic_call(tcx, *item)
366 })
367 })
368}
369
370fn find_raw_ptr<'tcx>(
374 tcx: TyCtxt<'tcx>,
375 ty: Ty<'tcx>,
376 impl_def_id: DefId,
377 is_sync: bool,
378) -> Contains {
379 match ty.kind() {
380 TyKind::RawPtr(..) => Contains::Yes,
381 TyKind::Pat(inner, _) => find_raw_ptr(tcx, *inner, impl_def_id, is_sync),
382 TyKind::Adt(adt_def, substs) => {
383 let mut result = Contains::No;
384 for field in adt_def.all_fields() {
385 let field_ty = crate::helpers::mir_utils::field_ty(tcx, field, substs);
386 result = result.join(find_raw_ptr(tcx, field_ty, impl_def_id, is_sync));
387 if result == Contains::Yes {
388 return Contains::Yes;
389 }
390 }
391 for subst in substs.iter() {
392 if let GenericArgKind::Type(subst_ty) = subst.kind() {
393 result = result.join(find_raw_ptr(tcx, subst_ty, impl_def_id, is_sync));
394 if result == Contains::Yes {
395 return Contains::Yes;
396 }
397 }
398 }
399 result
400 }
401 TyKind::Ref(_, inner, _) | TyKind::Slice(inner) | TyKind::Array(inner, _) => {
402 find_raw_ptr(tcx, *inner, impl_def_id, is_sync)
403 }
404 TyKind::Tuple(tys) => tys.iter().fold(Contains::No, |acc, t| {
405 acc.join(find_raw_ptr(tcx, t, impl_def_id, is_sync))
406 }),
407 TyKind::Param(param_ty) => {
408 if param_bound_is_satisfied(tcx, impl_def_id, *param_ty, is_sync) {
409 Contains::No
410 } else {
411 Contains::Maybe
412 }
413 }
414 _ => Contains::No,
415 }
416}
417
418fn type_structurally_contains<'tcx>(
423 tcx: TyCtxt<'tcx>,
424 ty: Ty<'tcx>,
425 negative_defs: &[DefId],
426 impl_def_id: DefId,
427 is_sync: bool,
428) -> Contains {
429 match ty.kind() {
430 TyKind::Adt(adt_def, substs) => {
431 if negative_defs.contains(&adt_def.did()) {
432 return Contains::Yes;
433 }
434 if crate::def_id::sync_primitive_types().contains(&adt_def.did()) {
435 return Contains::No;
436 }
437 let mut result = Contains::No;
438 for field in adt_def.all_fields() {
439 let field_ty = crate::helpers::mir_utils::field_ty(tcx, field, substs);
440 result = result.join(type_structurally_contains(
441 tcx,
442 field_ty,
443 negative_defs,
444 impl_def_id,
445 is_sync,
446 ));
447 if result == Contains::Yes {
448 return Contains::Yes;
449 }
450 }
451 for subst in substs.iter() {
452 if let GenericArgKind::Type(subst_ty) = subst.kind() {
453 result = result.join(type_structurally_contains(
454 tcx,
455 subst_ty,
456 negative_defs,
457 impl_def_id,
458 is_sync,
459 ));
460 if result == Contains::Yes {
461 return Contains::Yes;
462 }
463 }
464 }
465 result
466 }
467 TyKind::Ref(_, inner, _) | TyKind::Slice(inner) | TyKind::Array(inner, _) => {
468 type_structurally_contains(tcx, *inner, negative_defs, impl_def_id, is_sync)
469 }
470 TyKind::Tuple(tys) => tys.iter().fold(Contains::No, |acc, t| {
471 acc.join(type_structurally_contains(
472 tcx,
473 t,
474 negative_defs,
475 impl_def_id,
476 is_sync,
477 ))
478 }),
479 TyKind::Param(param_ty) => {
480 if param_bound_is_satisfied(tcx, impl_def_id, *param_ty, is_sync) {
481 Contains::No
482 } else {
483 Contains::Maybe
484 }
485 }
486 _ => Contains::No,
487 }
488}
489
490fn find_unsynchronized_mutation<'tcx>(
494 tcx: TyCtxt<'tcx>,
495 ty: Ty<'tcx>,
496 impl_def_id: DefId,
497 is_sync: bool,
498) -> Contains {
499 match ty.kind() {
500 TyKind::RawPtr(..) => Contains::Yes,
501 TyKind::Pat(inner, _) => find_unsynchronized_mutation(tcx, *inner, impl_def_id, is_sync),
502 TyKind::Adt(adt_def, substs) => {
503 let did = adt_def.did();
504 if crate::def_id::sync_primitive_types().contains(&did) {
505 return Contains::No;
506 }
507 if tcx.is_lang_item(did, LangItem::UnsafeCell) {
508 return Contains::Yes;
509 }
510 let mut result = Contains::No;
511 for field in adt_def.all_fields() {
512 let field_ty = crate::helpers::mir_utils::field_ty(tcx, field, substs);
513 result = result.join(find_unsynchronized_mutation(
514 tcx,
515 field_ty,
516 impl_def_id,
517 is_sync,
518 ));
519 if result == Contains::Yes {
520 return Contains::Yes;
521 }
522 }
523 for subst in substs.iter() {
524 if let GenericArgKind::Type(subst_ty) = subst.kind() {
525 result = result.join(find_unsynchronized_mutation(
526 tcx,
527 subst_ty,
528 impl_def_id,
529 is_sync,
530 ));
531 if result == Contains::Yes {
532 return Contains::Yes;
533 }
534 }
535 }
536 result
537 }
538 TyKind::Ref(_, inner, _) | TyKind::Slice(inner) | TyKind::Array(inner, _) => {
539 find_unsynchronized_mutation(tcx, *inner, impl_def_id, is_sync)
540 }
541 TyKind::Tuple(tys) => tys.iter().fold(Contains::No, |acc, t| {
542 acc.join(find_unsynchronized_mutation(tcx, t, impl_def_id, is_sync))
543 }),
544 TyKind::Param(param_ty) => {
545 if param_bound_is_satisfied(tcx, impl_def_id, *param_ty, is_sync) {
546 Contains::No
547 } else {
548 Contains::Maybe
549 }
550 }
551 _ => Contains::No,
552 }
553}