1use crate::analysis::utils::fn_info::{
10 access_ident_recursive, match_ty_with_ident, parse_expr_into_local_and_ty,
11 parse_expr_into_number,
12};
13use rustc_hir::def_id::DefId;
14use rustc_middle::mir::BinOp as MirBinOp;
15use rustc_middle::ty::Ty;
16use rustc_middle::ty::TyCtxt;
17use safety_parser::syn::{BinOp as SynBinOp, Expr, Lit, UnOp};
18
19#[derive(Clone, Debug)]
21pub enum PlaceBase {
22 Return,
24 Arg(usize),
26 Local(usize),
28}
29
30#[derive(Clone, Debug)]
32pub enum ContractProjection<'tcx> {
33 Field { index: usize, ty: Option<Ty<'tcx>> },
35}
36
37#[derive(Clone, Debug)]
39pub struct ContractPlace<'tcx> {
40 pub base: PlaceBase,
41 pub projections: Vec<ContractProjection<'tcx>>,
42}
43
44impl<'tcx> ContractPlace<'tcx> {
45 pub fn local(base: usize, fields: Vec<(usize, Ty<'tcx>)>) -> Self {
47 Self {
48 base: if base == 0 {
49 PlaceBase::Return
50 } else {
51 PlaceBase::Local(base)
52 },
53 projections: fields
54 .into_iter()
55 .map(|(index, ty)| ContractProjection::Field {
56 index,
57 ty: Some(ty),
58 })
59 .collect(),
60 }
61 }
62
63 pub fn arg(index: usize) -> Self {
65 Self {
66 base: PlaceBase::Arg(index),
67 projections: Vec::new(),
68 }
69 }
70
71 pub fn local_base(&self) -> Option<usize> {
73 match self.base {
74 PlaceBase::Return => Some(0),
75 PlaceBase::Local(local) => Some(local),
76 PlaceBase::Arg(_) => None,
77 }
78 }
79
80 pub fn field_indices(&self) -> Vec<usize> {
82 self.projections
83 .iter()
84 .map(|projection| match projection {
85 ContractProjection::Field { index, .. } => *index,
86 })
87 .collect()
88 }
89}
90
91#[derive(Clone, Copy, Debug)]
93pub enum NumericOp {
94 Add,
95 Sub,
96 Mul,
97 Div,
98 Rem,
99 BitAnd,
100 BitOr,
101 BitXor,
102}
103
104impl NumericOp {
105 fn from_syn(op: &SynBinOp) -> Option<Self> {
107 match op {
108 SynBinOp::Add(_) => Some(Self::Add),
109 SynBinOp::Sub(_) => Some(Self::Sub),
110 SynBinOp::Mul(_) => Some(Self::Mul),
111 SynBinOp::Div(_) => Some(Self::Div),
112 SynBinOp::Rem(_) => Some(Self::Rem),
113 SynBinOp::BitAnd(_) => Some(Self::BitAnd),
114 SynBinOp::BitOr(_) => Some(Self::BitOr),
115 SynBinOp::BitXor(_) => Some(Self::BitXor),
116 _ => None,
117 }
118 }
119}
120
121#[derive(Clone, Copy, Debug)]
123pub enum NumericUnaryOp {
124 Not,
125 Neg,
126}
127
128impl NumericUnaryOp {
129 fn from_syn(op: &UnOp) -> Option<Self> {
131 match op {
132 UnOp::Not(_) => Some(Self::Not),
133 UnOp::Neg(_) => Some(Self::Neg),
134 _ => None,
135 }
136 }
137}
138
139#[derive(Clone, Debug)]
141pub enum ContractExpr<'tcx> {
142 Place(ContractPlace<'tcx>),
144 Const(u128),
146 SizeOf(Ty<'tcx>),
148 AlignOf(Ty<'tcx>),
150 Binary {
152 op: NumericOp,
153 lhs: Box<ContractExpr<'tcx>>,
154 rhs: Box<ContractExpr<'tcx>>,
155 },
156 Unary {
158 op: NumericUnaryOp,
159 expr: Box<ContractExpr<'tcx>>,
160 },
161 Unknown,
163}
164
165impl<'tcx> ContractExpr<'tcx> {
166 pub fn new_var(base: usize) -> Self {
168 Self::Place(ContractPlace::local(base, Vec::new()))
169 }
170
171 pub fn new_value(value: usize) -> Self {
173 Self::Const(value as u128)
174 }
175
176 pub fn new_unknown() -> Self {
178 Self::Unknown
179 }
180
181 pub fn get_var_base(&self) -> Option<usize> {
183 match self {
184 Self::Place(place) => place.local_base(),
185 _ => None,
186 }
187 }
188}
189
190#[derive(Clone, Copy, Debug)]
192pub enum RelOp {
193 Eq,
194 Ne,
195 Lt,
196 Le,
197 Gt,
198 Ge,
199}
200
201impl RelOp {
202 pub fn from_mir(op: MirBinOp) -> Option<Self> {
204 match op {
205 MirBinOp::Eq => Some(Self::Eq),
206 MirBinOp::Ne => Some(Self::Ne),
207 MirBinOp::Lt => Some(Self::Lt),
208 MirBinOp::Le => Some(Self::Le),
209 MirBinOp::Gt => Some(Self::Gt),
210 MirBinOp::Ge => Some(Self::Ge),
211 _ => None,
212 }
213 }
214
215 fn from_syn(op: &SynBinOp) -> Option<Self> {
217 match op {
218 SynBinOp::Eq(_) => Some(Self::Eq),
219 SynBinOp::Ne(_) => Some(Self::Ne),
220 SynBinOp::Lt(_) => Some(Self::Lt),
221 SynBinOp::Le(_) => Some(Self::Le),
222 SynBinOp::Gt(_) => Some(Self::Gt),
223 SynBinOp::Ge(_) => Some(Self::Ge),
224 _ => None,
225 }
226 }
227
228 pub fn reversed(self) -> Self {
230 match self {
231 Self::Eq => Self::Eq,
232 Self::Ne => Self::Ne,
233 Self::Lt => Self::Gt,
234 Self::Le => Self::Ge,
235 Self::Gt => Self::Lt,
236 Self::Ge => Self::Le,
237 }
238 }
239}
240
241#[derive(Clone, Debug)]
243pub struct NumericPredicate<'tcx> {
244 pub lhs: ContractExpr<'tcx>,
245 pub op: RelOp,
246 pub rhs: ContractExpr<'tcx>,
247}
248
249impl<'tcx> NumericPredicate<'tcx> {
250 pub fn new(lhs: ContractExpr<'tcx>, op: RelOp, rhs: ContractExpr<'tcx>) -> Self {
252 Self { lhs, op, rhs }
253 }
254
255 pub fn from_mir_locals(lhs: usize, rhs: usize, op: MirBinOp) -> Option<Self> {
257 RelOp::from_mir(op)
258 .map(|rel| Self::new(ContractExpr::new_var(lhs), rel, ContractExpr::new_var(rhs)))
259 }
260}
261
262#[derive(Clone, Debug)]
267pub enum PropertyContract<'tcx> {
268 Align(Ty<'tcx>),
270 Size(),
272 NoPadding,
274 NonNull,
276 Allocated(Ty<'tcx>, ContractExpr<'tcx>),
278 InBound(Ty<'tcx>, ContractExpr<'tcx>),
280 NonOverlap,
282 ValidNum(Vec<NumericPredicate<'tcx>>),
284 ValidString,
286 ValidCStr,
288 Init(Ty<'tcx>, ContractExpr<'tcx>),
290 Unwrap,
292 Typed(Ty<'tcx>),
294 Owning,
296 Alias,
298 Alive,
300 Pinned,
302 NonVolatile,
304 Opened,
306 Trait,
308 Unreachable,
310 ValidPtr(Ty<'tcx>, ContractExpr<'tcx>),
312 Deref,
314 Ptr2Ref,
316 Layout,
318 Unknown,
320}
321
322impl<'tcx> PropertyContract<'tcx> {
323 pub fn new(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str, exprs: &Vec<Expr>) -> Self {
325 match name {
326 "Align" => {
327 Self::check_arg_length(exprs.len(), 2, "Align");
328 let ty = Self::parse_type(tcx, def_id, &exprs[1], "Align");
329 Self::Align(ty)
330 }
331 "Size" => Self::Size(),
332 "NoPadding" => Self::NoPadding,
333 "NonNull" => Self::NonNull,
334 "Allocated" => {
335 Self::check_arg_length(exprs.len(), 3, "Allocated");
336 let ty = Self::parse_type(tcx, def_id, &exprs[1], "Allocated");
337 let length = Self::parse_contract_expr(tcx, def_id, &exprs[2], "Allocated");
338 Self::Allocated(ty, length)
339 }
340 "InBound" | "InBounded" => match exprs.as_slice() {
341 [_target, ty_expr, len_expr] => {
342 let ty = Self::parse_type(tcx, def_id, ty_expr, "InBound");
343 let length = Self::parse_contract_expr(tcx, def_id, len_expr, "InBound");
344 Self::InBound(ty, length)
345 }
346 [target, len_expr] => {
347 let Some(ty) = Self::parse_target_type(tcx, def_id, target) else {
348 return Self::Unknown;
349 };
350 let length = Self::parse_contract_expr(tcx, def_id, len_expr, "InBound");
351 Self::InBound(ty, length)
352 }
353 _ => {
354 Self::check_arg_length(exprs.len(), 3, "InBound");
355 Self::Unknown
356 }
357 },
358 "NonOverlap" => Self::NonOverlap,
359 "ValidNum" => {
360 let predicates = Self::parse_valid_num(tcx, def_id, exprs);
361 if predicates.is_empty() {
362 Self::Unknown
363 } else {
364 Self::ValidNum(predicates)
365 }
366 }
367 "ValidString" => Self::ValidString,
368 "ValidCStr" => Self::ValidCStr,
369 "Init" => {
370 Self::check_arg_length(exprs.len(), 3, "Init");
371 let ty = Self::parse_type(tcx, def_id, &exprs[1], "Init");
372 let length = Self::parse_contract_expr(tcx, def_id, &exprs[2], "Init");
373 Self::Init(ty, length)
374 }
375 "Unwrap" => Self::Unwrap,
376 "Typed" => {
377 Self::check_arg_length(exprs.len(), 2, "Typed");
378 let ty = Self::parse_type(tcx, def_id, &exprs[1], "Typed");
379 Self::Typed(ty)
380 }
381 "Owning" => Self::Owning,
382 "Alias" => Self::Alias,
383 "Alive" => Self::Alive,
384 "Pinned" => Self::Pinned,
385 "NonVolatile" => Self::NonVolatile,
386 "Opened" => Self::Opened,
387 "Trait" => Self::Trait,
388 "Unreachable" => Self::Unreachable,
389 "ValidPtr" => {
390 Self::check_arg_length(exprs.len(), 3, "ValidPtr");
391 let ty = Self::parse_type(tcx, def_id, &exprs[1], "ValidPtr");
392 let length = Self::parse_contract_expr(tcx, def_id, &exprs[2], "ValidPtr");
393 Self::ValidPtr(ty, length)
394 }
395 "Deref" => Self::Deref,
396 "Ptr2Ref" | "ValidPtr2Ref" => Self::Ptr2Ref,
397 "Layout" => Self::Layout,
398 _ => Self::Unknown,
399 }
400 }
401
402 pub fn new_partial_order(lhs: usize, rhs: usize, op: MirBinOp) -> Self {
404 if let Some(predicate) = NumericPredicate::from_mir_locals(lhs, rhs, op) {
405 Self::ValidNum(vec![predicate])
406 } else {
407 Self::Unknown
408 }
409 }
410
411 pub fn new_obj_boundary(ty: Ty<'tcx>, len: ContractExpr<'tcx>) -> Self {
413 Self::InBound(ty, len)
414 }
415
416 fn check_arg_length(expr_len: usize, required_len: usize, sp: &str) -> bool {
418 if expr_len != required_len {
419 panic!("Wrong args length for {:?} Tag!", sp);
420 }
421 true
422 }
423
424 fn parse_type(tcx: TyCtxt<'tcx>, def_id: DefId, expr: &Expr, sp: &str) -> Ty<'tcx> {
426 let ty_ident_full = access_ident_recursive(expr);
427 if ty_ident_full.is_none() {
428 rap_error!("Incorrect expression for the type of {:?} Tag!", sp);
429 }
430 let ty_ident = ty_ident_full.unwrap().0;
431 let ty = match_ty_with_ident(tcx, def_id, ty_ident);
432 if ty.is_none() {
433 rap_error!("Cannot get type in {:?} Tag!", sp);
434 }
435 ty.unwrap()
436 }
437
438 fn parse_target_type(tcx: TyCtxt<'tcx>, def_id: DefId, expr: &Expr) -> Option<Ty<'tcx>> {
440 parse_expr_into_local_and_ty(tcx, def_id, expr).map(|(_, _, ty)| ty)
441 }
442
443 fn parse_contract_expr(
445 tcx: TyCtxt<'tcx>,
446 def_id: DefId,
447 expr: &Expr,
448 sp: &str,
449 ) -> ContractExpr<'tcx> {
450 match expr {
451 Expr::Paren(paren) => Self::parse_contract_expr(tcx, def_id, &paren.expr, sp),
452 Expr::Group(group) => Self::parse_contract_expr(tcx, def_id, &group.expr, sp),
453 Expr::Lit(expr_lit) => match &expr_lit.lit {
454 Lit::Int(lit_int) => lit_int
455 .base10_parse::<u128>()
456 .map(ContractExpr::Const)
457 .unwrap_or(ContractExpr::Unknown),
458 _ => ContractExpr::Unknown,
459 },
460 Expr::Unary(expr_unary) => {
461 let Some(op) = NumericUnaryOp::from_syn(&expr_unary.op) else {
462 return ContractExpr::Unknown;
463 };
464 ContractExpr::Unary {
465 op,
466 expr: Box::new(Self::parse_contract_expr(tcx, def_id, &expr_unary.expr, sp)),
467 }
468 }
469 Expr::Binary(expr_binary) => {
470 let Some(op) = NumericOp::from_syn(&expr_binary.op) else {
471 return ContractExpr::Unknown;
472 };
473 ContractExpr::Binary {
474 op,
475 lhs: Box::new(Self::parse_contract_expr(
476 tcx,
477 def_id,
478 &expr_binary.left,
479 sp,
480 )),
481 rhs: Box::new(Self::parse_contract_expr(
482 tcx,
483 def_id,
484 &expr_binary.right,
485 sp,
486 )),
487 }
488 }
489 _ => {
490 if let Some(place) = Self::parse_contract_place(tcx, def_id, expr) {
491 ContractExpr::Place(place)
492 } else if let Some(value) = parse_expr_into_number(expr) {
493 ContractExpr::new_value(value)
494 } else {
495 rap_debug!(
496 "Numeric expression in {:?} could not be resolved: {:?}",
497 sp,
498 expr
499 );
500 ContractExpr::Unknown
501 }
502 }
503 }
504 }
505
506 fn parse_contract_place(
508 tcx: TyCtxt<'tcx>,
509 def_id: DefId,
510 expr: &Expr,
511 ) -> Option<ContractPlace<'tcx>> {
512 if let Some((base, fields, _ty)) = parse_expr_into_local_and_ty(tcx, def_id, expr) {
513 return Some(ContractPlace::local(base, fields));
514 }
515 Self::parse_arg_place(expr)
516 }
517
518 fn parse_arg_place(expr: &Expr) -> Option<ContractPlace<'tcx>> {
520 if let Expr::Path(expr_path) = expr {
521 if let Some(ident) = expr_path.path.get_ident() {
522 let s = ident.to_string();
523
524 if let Some(num_str) = s.strip_prefix("Arg_") {
525 if let Ok(idx) = num_str.parse::<usize>() {
526 return Some(ContractPlace::arg(idx));
527 }
528 }
529 }
530 }
531 None
532 }
533
534 fn parse_valid_num(
536 tcx: TyCtxt<'tcx>,
537 def_id: DefId,
538 exprs: &Vec<Expr>,
539 ) -> Vec<NumericPredicate<'tcx>> {
540 match exprs.as_slice() {
541 [] => Vec::new(),
542 [expr] => Self::parse_numeric_predicate(tcx, def_id, expr)
543 .into_iter()
544 .collect(),
545 [value, range, ..] => {
546 if let Some(predicates) = Self::parse_interval_predicates(tcx, def_id, value, range)
547 {
548 predicates
549 } else {
550 Self::parse_numeric_predicate(tcx, def_id, value)
551 .into_iter()
552 .collect()
553 }
554 }
555 }
556 }
557
558 fn parse_numeric_predicate(
560 tcx: TyCtxt<'tcx>,
561 def_id: DefId,
562 expr: &Expr,
563 ) -> Option<NumericPredicate<'tcx>> {
564 if let Expr::Binary(expr_binary) = expr {
565 if let Some(op) = RelOp::from_syn(&expr_binary.op) {
566 return Some(NumericPredicate::new(
567 Self::parse_contract_expr(tcx, def_id, &expr_binary.left, "ValidNum"),
568 op,
569 Self::parse_contract_expr(tcx, def_id, &expr_binary.right, "ValidNum"),
570 ));
571 }
572 }
573
574 Some(NumericPredicate::new(
575 Self::parse_contract_expr(tcx, def_id, expr, "ValidNum"),
576 RelOp::Ne,
577 ContractExpr::Const(0),
578 ))
579 }
580
581 fn parse_interval_predicates(
583 tcx: TyCtxt<'tcx>,
584 def_id: DefId,
585 value: &Expr,
586 range: &Expr,
587 ) -> Option<Vec<NumericPredicate<'tcx>>> {
588 match range {
589 Expr::Array(array) if array.elems.len() == 2 => {
590 let mut elems = array.elems.iter();
591 let lower = elems.next().unwrap();
592 let upper = elems.next().unwrap();
593 Some(Self::build_interval_predicates(
594 tcx, def_id, value, lower, true, upper, true,
595 ))
596 }
597 Expr::Lit(expr_lit) => {
598 let Lit::Str(range_lit) = &expr_lit.lit else {
599 return None;
600 };
601 Self::parse_string_interval(tcx, def_id, value, &range_lit.value())
602 }
603 _ => None,
604 }
605 }
606
607 fn parse_string_interval(
609 tcx: TyCtxt<'tcx>,
610 def_id: DefId,
611 value: &Expr,
612 raw_range: &str,
613 ) -> Option<Vec<NumericPredicate<'tcx>>> {
614 let trimmed = raw_range.trim();
615 if trimmed.len() < 5 {
616 return None;
617 }
618
619 let lower_inclusive = trimmed.starts_with('[');
620 let upper_inclusive = trimmed.ends_with(']');
621 if !(lower_inclusive || trimmed.starts_with('('))
622 || !(upper_inclusive || trimmed.ends_with(')'))
623 {
624 return None;
625 }
626
627 let body = &trimmed[1..trimmed.len() - 1];
628 let (lower_raw, upper_raw) = body.split_once(',')?;
629 let lower = safety_parser::syn::parse_str::<Expr>(lower_raw.trim()).ok()?;
630 let upper = safety_parser::syn::parse_str::<Expr>(upper_raw.trim()).ok()?;
631
632 Some(Self::build_interval_predicates(
633 tcx,
634 def_id,
635 value,
636 &lower,
637 lower_inclusive,
638 &upper,
639 upper_inclusive,
640 ))
641 }
642
643 fn build_interval_predicates(
645 tcx: TyCtxt<'tcx>,
646 def_id: DefId,
647 value: &Expr,
648 lower: &Expr,
649 lower_inclusive: bool,
650 upper: &Expr,
651 upper_inclusive: bool,
652 ) -> Vec<NumericPredicate<'tcx>> {
653 let value_expr = Self::parse_contract_expr(tcx, def_id, value, "ValidNum");
654 let lower_expr = Self::parse_contract_expr(tcx, def_id, lower, "ValidNum");
655 let upper_expr = Self::parse_contract_expr(tcx, def_id, upper, "ValidNum");
656 vec![
657 NumericPredicate::new(
658 lower_expr,
659 if lower_inclusive {
660 RelOp::Le
661 } else {
662 RelOp::Lt
663 },
664 value_expr.clone(),
665 ),
666 NumericPredicate::new(
667 value_expr,
668 if upper_inclusive {
669 RelOp::Le
670 } else {
671 RelOp::Lt
672 },
673 upper_expr,
674 ),
675 ]
676 }
677}
678
679#[derive(Debug, Clone)]
681pub struct ContractFactSet<'tcx> {
682 pub contracts: Vec<PropertyContract<'tcx>>,
683 pub predicates: Vec<NumericPredicate<'tcx>>,
684}
685
686impl<'tcx> ContractFactSet<'tcx> {
687 pub fn new_default() -> Self {
689 Self {
690 contracts: Vec::new(),
691 predicates: Vec::new(),
692 }
693 }
694
695 pub fn add_contract(&mut self, contract: PropertyContract<'tcx>) {
697 if let PropertyContract::ValidNum(predicates) = &contract {
698 self.predicates.extend(predicates.iter().cloned());
699 }
700 self.contracts.push(contract);
701 }
702
703 pub fn add_predicate(&mut self, predicate: NumericPredicate<'tcx>) {
705 self.predicates.push(predicate.clone());
706 self.contracts
707 .push(PropertyContract::ValidNum(vec![predicate]));
708 }
709
710 pub fn get_contracts_length(&self) -> usize {
712 self.contracts.len()
713 }
714}
715
716#[derive(Debug, Clone, PartialEq, Eq)]
718pub enum AlignState<'tcx> {
719 Aligned(Ty<'tcx>),
721 Unaligned(Ty<'tcx>),
723 Unknown,
725}
726
727impl<'tcx> AlignState<'tcx> {
728 pub fn merge(&self, other: &Self) -> Self {
730 if self == other {
731 return other.clone();
732 }
733 match (self, other) {
734 (AlignState::Aligned(t1), AlignState::Aligned(t2)) => {
735 if t1 == t2 {
736 AlignState::Aligned(*t1)
737 } else {
738 AlignState::Unknown
739 }
740 }
741 (AlignState::Unaligned(_), AlignState::Unaligned(_)) => AlignState::Unknown,
742 _ => AlignState::Unknown,
743 }
744 }
745}