1use rustc_hir::{
2 ItemKind,
3 def_id::{DefId, LocalDefId},
4};
5#[cfg(not(rapx_ge_100))]
6use rustc_hir::LangItem;
7#[cfg(rapx_ge_100)]
8use rustc_hir::attrs::lang_items::LangItem;
9use rustc_middle::{
10 mir::{BasicBlock, ConstValue, Local, Operand, Place, Rvalue, StatementKind, TerminatorKind},
11 mir::interpret::{AllocId, GlobalAlloc},
12 ty::{ConstKind, GenericArgKind, PseudoCanonicalInput, Ty, TyCtxt, TyKind, TypingEnv},
13};
14use rustc_span::Symbol;
15
16use std::collections::{HashMap, HashSet};
17
18use crate::{
19 analysis::alias::{collect_local_origins, LocalOriginMap},
20 helpers::mir_scan::Checkpoint,
21 verify::def_use::PlaceKey,
22};
23
24pub(crate) fn pointee_ty<'tcx>(ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
25 match ty.kind() {
26 TyKind::RawPtr(ty, _) | TyKind::Ref(_, ty, _) => Some(*ty),
27 _ => None,
28 }
29}
30
31pub(crate) fn dep_callee_def_id(func: &Operand<'_>) -> Option<DefId> {
32 let Operand::Constant(func_constant) = func else { return None };
33 let TyKind::FnDef(def_id, _) = func_constant.const_.ty().kind() else { return None };
34 Some(*def_id)
35}
36
37pub fn collect_return_block_indices(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<BasicBlock> {
39 let mut blocks = Vec::new();
40 if !tcx.is_mir_available(def_id) {
41 return blocks;
42 }
43 let body = tcx.optimized_mir(def_id);
44 for (bb, data) in body.basic_blocks.iter_enumerated() {
45 if matches!(data.terminator().kind, TerminatorKind::Return) {
46 blocks.push(bb);
47 }
48 }
49 blocks
50}
51
52pub fn callee_is_linear(tcx: TyCtxt<'_>, def_id: DefId, max_blocks: usize) -> bool {
56 if !tcx.is_mir_available(def_id) {
57 return false;
58 }
59 let body = tcx.optimized_mir(def_id);
60 body.basic_blocks.len() <= max_blocks
61 && !body
62 .basic_blocks
63 .iter()
64 .any(|bb| matches!(bb.terminator().kind, TerminatorKind::SwitchInt { .. }))
65 && body
66 .basic_blocks
67 .iter()
68 .filter(|bb| matches!(bb.terminator().kind, TerminatorKind::Return))
69 .count()
70 <= 1
71}
72
73pub fn callee_param_index_for_local(tcx: TyCtxt<'_>, callee: DefId, local: usize) -> Option<usize> {
79 let arg_count = if tcx.is_mir_available(callee) {
80 tcx.optimized_mir(callee).arg_count
81 } else {
82 tcx.fn_sig(callee)
83 .skip_binder()
84 .inputs()
85 .skip_binder()
86 .len()
87 };
88 arg_of_local(Local::from_usize(local), arg_count)
89}
90
91pub fn is_std_crate_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
92 matches!(
93 tcx.crate_name(def_id.krate).as_str(),
94 "core" | "std" | "alloc"
95 )
96}
97
98pub fn is_trait_unsafe(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
99 let Some(local_id) = trait_def_id.as_local() else {
100 return false;
101 };
102 let item = tcx.hir_expect_item(local_id);
103
104 #[cfg(not(rapx_ge_99))]
105 if let ItemKind::Trait(_, _, unsafety, _, _, _, _) = &item.kind {
106 return matches!(unsafety, rustc_hir::Safety::Unsafe);
107 }
108 #[cfg(rapx_ge_99)]
109 if let ItemKind::Trait { safety, .. } = &item.kind {
110 return matches!(safety, rustc_hir::Safety::Unsafe);
111 }
112
113 false
114}
115
116pub fn resolve_impl_self_ty_def_id(item: &rustc_hir::Item<'_>) -> Option<DefId> {
117 let ItemKind::Impl(rustc_hir::Impl { self_ty, .. }) = &item.kind else {
118 return None;
119 };
120 match &self_ty.kind {
121 rustc_hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)) => match path.res {
122 rustc_hir::def::Res::Def(
123 rustc_hir::def::DefKind::Struct
124 | rustc_hir::def::DefKind::Enum
125 | rustc_hir::def::DefKind::Union,
126 def_id,
127 ) => Some(def_id),
128 _ => None,
129 },
130 _ => None,
131 }
132}
133
134pub fn has_rapx_verify_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
135 let hir_id = tcx.local_def_id_to_hir_id(def_id);
136
137 let rapx = Symbol::intern("rapx");
138 let verify = Symbol::intern("verify");
139
140 let attrs = tcx.hir_attrs(hir_id);
141
142 attrs.iter().any(|attr| {
143 if attr.is_doc_comment().is_some() {
144 return false;
145 }
146
147 let path = attr.path();
148
149 path.len() == 2 && path[0] == rapx && path[1] == verify
150 })
151}
152
153pub fn get_owner_struct_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
154 let assoc_item = tcx.opt_associated_item(def_id)?;
155 let impl_id = assoc_item.impl_container(tcx)?;
156 let self_ty = tcx.type_of(impl_id).skip_binder();
157
158 match self_ty.kind() {
159 TyKind::Adt(adt_def, _) => Some(adt_def.did()),
160 _ => None,
161 }
162}
163
164pub(crate) fn ty_has_param_const(ty: Ty<'_>) -> bool {
167 for arg in ty.walk() {
168 match arg.kind() {
169 GenericArgKind::Const(c) if matches!(c.kind(), ConstKind::Param(_)) => return true,
170 GenericArgKind::Type(inner_ty) if matches!(inner_ty.kind(), TyKind::Alias(..)) => {
171 return true;
172 }
173 _ => {}
174 }
175 }
176 false
177}
178
179pub(crate) fn catch_panic<T>(f: impl FnOnce() -> T) -> Result<T, String> {
182 std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).map_err(|e| {
183 e.downcast_ref::<String>()
184 .cloned()
185 .or_else(|| e.downcast_ref::<&str>().map(|s| s.to_string()))
186 .unwrap_or_else(|| "<rustc ICE>".to_string())
187 })
188}
189
190pub fn call_name(tcx: TyCtxt<'_>, func: &Operand<'_>) -> String {
192 dep_callee_def_id(func)
193 .map(|def_id| tcx.def_path_str(def_id))
194 .unwrap_or_else(|| format!("{func:?}"))
195}
196
197pub fn arg_of_local(local: Local, arg_count: usize) -> Option<usize> {
201 let i = local.as_usize();
202 if i >= 1 && i <= arg_count {
203 Some(i - 1)
204 } else {
205 None
206 }
207}
208
209pub fn has_crate(tcx: TyCtxt<'_>, name: &str) -> bool {
210 for num in tcx.crates(()) {
211 if tcx.crate_name(*num) == Symbol::intern(name) {
212 return true;
213 }
214 }
215 false
216}
217
218pub fn rvalue_source_place<'a, 'tcx>(rvalue: &'a Rvalue<'tcx>) -> Option<&'a rustc_middle::mir::Place<'tcx>> {
221 use rustc_middle::mir::{Operand, Rvalue};
222 match rvalue {
223 Rvalue::Use(Operand::Copy(place), ..)
224 | Rvalue::Use(Operand::Move(place), ..)
225 | Rvalue::Cast(_, Operand::Copy(place), _)
226 | Rvalue::Cast(_, Operand::Move(place), _)
227 | Rvalue::Ref(_, _, place)
228 | Rvalue::RawPtr(_, place)
229 | Rvalue::CopyForDeref(place) => Some(place),
230 _ => None,
231 }
232}
233
234pub fn operand_place(operand: &Operand<'_>) -> Option<PlaceKey> {
238 match operand {
239 Operand::Copy(place) | Operand::Move(place) => Some(PlaceKey::from_mir_place(place)),
240 Operand::Constant(_) => None,
241 #[cfg(rapx_ge_99)]
242 Operand::RuntimeChecks(_) => None,
243 }
244}
245
246pub fn operand_mir_place<'a, 'tcx>(operand: &'a Operand<'tcx>) -> Option<&'a Place<'tcx>> {
248 match operand {
249 Operand::Copy(place) | Operand::Move(place) => Some(place),
250 _ => None,
251 }
252}
253
254pub fn call_destination<'tcx>(
256 tcx: TyCtxt<'tcx>,
257 checkpoint: &Checkpoint<'tcx>,
258) -> Option<Local> {
259 if checkpoint.kind == crate::helpers::mir_scan::CheckpointKind::RawPtrDeref {
260 return checkpoint.destination;
261 }
262 let body = tcx.optimized_mir(checkpoint.caller);
263 let terminator = body.basic_blocks[checkpoint.block].terminator();
264 let TerminatorKind::Call { destination, .. } = &terminator.kind else {
265 return None;
266 };
267 Some(destination.local)
268}
269
270pub fn deep_resolve_place(
275 mut local: usize,
276 origins: &LocalOriginMap,
277) -> (usize, Vec<usize>) {
278 let mut seen = HashSet::new();
279 let mut all_fields: Vec<usize> = Vec::new();
280 loop {
281 if !seen.insert(local) {
282 return (local, all_fields);
283 }
284 match origins.get(&local) {
285 Some((l, fields)) => {
286 let mut combined = fields.clone();
287 combined.extend(all_fields.iter());
288 all_fields = combined;
289 if *l == 1 {
290 return (1, all_fields);
291 }
292 local = *l;
293 }
294 None => {
295 return (local, all_fields);
296 }
297 }
298 }
299}
300
301pub fn trace_raw_ptr_through_call(
304 tcx: TyCtxt<'_>,
305 caller: DefId,
306 checkpoint_block: BasicBlock,
307 raw_ptr: Local,
308) -> Option<PlaceKey> {
309 let body = tcx.optimized_mir(caller);
310 let mut block = checkpoint_block;
311 let mut visited = HashSet::new();
312 loop {
313 if !visited.insert(block) {
314 break;
315 }
316 for statement in body.basic_blocks[block].statements.iter().rev() {
317 let StatementKind::Assign(assign) = &statement.kind else {
318 continue;
319 };
320 let (target, _rvalue) = assign.as_ref();
321 if target.local != raw_ptr {
322 continue;
323 }
324 break;
325 }
326 let predecessors = &body.basic_blocks.predecessors()[block];
327 if predecessors.len() != 1 {
328 break;
329 }
330 let prev = predecessors[0];
331 let terminator = body.basic_blocks[prev].terminator();
332 if let TerminatorKind::Call {
333 func,
334 args,
335 destination,
336 ..
337 } = &terminator.kind
338 {
339 if destination.local == raw_ptr {
340 let callee_name = call_name(tcx, func);
341 if callee_name.contains("::get_unchecked") {
342 if let Some(slice) = args.get(1) {
343 return operand_place(&slice.node);
344 }
345 }
346 break;
347 }
348 }
349 block = prev;
350 }
351 None
352}
353
354pub fn blocks_reachable_after_call(
358 tcx: TyCtxt<'_>,
359 caller: DefId,
360 call_block: BasicBlock,
361) -> HashSet<BasicBlock> {
362 let body = tcx.optimized_mir(caller);
363 let mut starts = Vec::new();
364 if let TerminatorKind::Call { target, .. } = &body.basic_blocks[call_block].terminator().kind
365 && let Some(target) = target
366 {
367 starts.push(*target);
368 }
369
370 let mut seen = HashSet::new();
371 let mut stack = starts;
372 while let Some(block) = stack.pop() {
373 if !seen.insert(block) {
374 continue;
375 }
376 let terminator = body.basic_blocks[block].terminator();
377 for successor in terminator.successors() {
378 stack.push(successor);
379 }
380 }
381 seen
382}
383
384pub fn collect_place_aliases(
388 tcx: TyCtxt<'_>,
389 def_id: DefId,
390) -> HashMap<Local, PlaceKey> {
391 collect_local_origins(tcx, def_id)
392 .into_iter()
393 .map(|(local, (origin_local, fields))| {
394 (
395 Local::from_usize(local),
396 PlaceKey::from_origin(origin_local, fields),
397 )
398 })
399 .collect()
400}
401
402pub fn resolve_mir_place<'tcx>(
404 _tcx: TyCtxt<'tcx>,
405 place: &Place<'tcx>,
406 aliases: &HashMap<Local, PlaceKey>,
407) -> PlaceKey {
408 let key = PlaceKey::from_mir_place(place);
409 if !key.fields.is_empty() {
410 return key;
411 }
412 aliases.get(&place.local).cloned().unwrap_or(key)
413}
414
415pub fn rvalue_any_place_matching<'tcx>(
419 rvalue: &Rvalue<'tcx>,
420 pred: &mut impl FnMut(&Place<'tcx>) -> bool,
421) -> bool {
422 match rvalue {
423 Rvalue::Aggregate(_, operands) => operands.iter().any(|operand| match operand {
424 Operand::Copy(place) | Operand::Move(place) => pred(place),
425 Operand::Constant(_) => false,
426 #[cfg(rapx_ge_99)]
427 Operand::RuntimeChecks(_) => false,
428 }),
429 _ => rvalue_source_place(rvalue)
430 .map_or(false, |place| pred(place)),
431 }
432}
433
434pub fn trace_place_root(
438 origins: &LocalOriginMap,
439 place: &PlaceKey,
440) -> Option<(usize, Vec<usize>)> {
441 let Some(local) = place.local() else {
442 return None;
443 };
444 let (root_local, root_fields) = deep_resolve_place(local.as_usize(), origins);
445 Some((root_local, root_fields))
446}
447
448pub fn const_value_bytes<'tcx>(
450 tcx: TyCtxt<'tcx>,
451 value: ConstValue,
452 depth: usize,
453) -> Option<Vec<u8>> {
454 if depth > 4 {
455 return None;
456 }
457 match value {
458 ConstValue::Slice { alloc_id, .. } => alloc_id_bytes(tcx, alloc_id, depth),
459 ConstValue::Scalar(scalar) => {
460 #[cfg(rapx_scalar_to_pointer_interp_result)]
461 let ptr = scalar.to_pointer(&tcx).discard_err()?;
462 #[cfg(not(rapx_scalar_to_pointer_interp_result))]
463 let ptr = scalar.to_pointer(&tcx);
464 let alloc_id = ptr.provenance?.alloc_id();
465 alloc_id_bytes(tcx, alloc_id, depth)
466 }
467 ConstValue::Indirect { alloc_id, .. } => alloc_id_bytes(tcx, alloc_id, depth),
468 _ => None,
469 }
470}
471
472pub fn alloc_id_bytes<'tcx>(
474 tcx: TyCtxt<'tcx>,
475 alloc_id: AllocId,
476 depth: usize,
477) -> Option<Vec<u8>> {
478 if depth > 4 {
479 return None;
480 }
481 let alloc = match tcx.global_alloc(alloc_id) {
482 GlobalAlloc::Memory(alloc) => alloc,
483 GlobalAlloc::Static(def_id) => tcx.eval_static_initializer(def_id).ok()?,
484 _ => return None,
485 };
486 let alloc = alloc.inner();
487 let provenance = alloc.provenance().ptrs();
488 if let Some((_, prov)) = provenance.iter().next() {
489 return alloc_id_bytes(tcx, prov.alloc_id(), depth + 1);
490 }
491 Some(
492 alloc
493 .inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())
494 .to_vec(),
495 )
496}
497
498pub(crate) fn offset_of_container<'tcx>(
507 tcx: TyCtxt<'tcx>,
508 constant: &rustc_middle::mir::Const<'tcx>,
509) -> Option<Ty<'tcx>> {
510 let rustc_middle::mir::Const::Unevaluated(uneval, _) = constant else {
511 return None;
512 };
513 if !is_const_def_kind(tcx, uneval.def) {
516 return None;
517 }
518 if !uneval.def.is_local() {
523 return None;
524 }
525 let body = tcx.mir_for_ctfe(uneval.def);
526 for bb in body.basic_blocks.iter() {
527 if let Some(term) = &bb.terminator
528 && let TerminatorKind::Call { func, .. } = &term.kind
529 && let Some(ty) = offset_of_ty_from_func(tcx, func)
530 {
531 return Some(ty);
532 }
533 }
534 None
535}
536
537fn is_const_def_kind(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
539 use rustc_hir::def::DefKind;
540 #[cfg(rapx_ge_99)]
541 let base = matches!(
542 tcx.def_kind(def_id),
543 DefKind::Const { .. }
544 | DefKind::Static { .. }
545 | DefKind::AssocConst { .. }
546 | DefKind::AnonConst
547 );
548 #[cfg(not(rapx_ge_99))]
549 let base = matches!(
550 tcx.def_kind(def_id),
551 DefKind::Const | DefKind::Static { .. } | DefKind::AssocConst | DefKind::AnonConst
552 );
553 #[cfg(rapx_ge_99)]
554 {
555 base
556 }
557 #[cfg(not(rapx_ge_99))]
558 {
559 base || matches!(tcx.def_kind(def_id), DefKind::InlineConst)
560 }
561}
562
563fn offset_of_ty_from_func<'tcx>(
564 tcx: TyCtxt<'tcx>,
565 func: &Operand<'tcx>,
566) -> Option<Ty<'tcx>> {
567 let Operand::Constant(c) = func else { return None };
568 let TyKind::FnDef(def_id, args) = c.const_.ty().kind() else { return None };
569 if !tcx.is_lang_item(*def_id, LangItem::OffsetOf) {
570 return None;
571 }
572 args.iter().find_map(|a| {
573 #[cfg(rapx_ge_99)]
574 let a = a.skip_binder();
575 match a.kind() {
576 GenericArgKind::Type(t) => Some(t),
577 _ => None,
578 }
579 })
580}
581
582pub fn type_layout<'tcx>(tcx: TyCtxt<'tcx>, caller: DefId, ty: Ty<'tcx>) -> Option<(u64, u64)> {
583 if ty_has_param_const(ty) { return None }
584 match layout_of_ty(tcx, caller, ty) {
585 Some(l) => Some((l.align.abi.bytes(), l.size.bytes())),
586 None if matches!(ty.kind(), TyKind::Param(_)) => Some((0, 0)),
587 None => None,
588 }
589}
590
591pub fn layout_of_ty<'tcx>(
595 tcx: TyCtxt<'tcx>,
596 caller: DefId,
597 ty: Ty<'tcx>,
598) -> Option<rustc_abi::TyAndLayout<'tcx, Ty<'tcx>>> {
599 let env = TypingEnv::post_analysis(tcx, caller);
600 catch_panic(|| tcx.layout_of(PseudoCanonicalInput { typing_env: env, value: ty }))
601 .ok()
602 .and_then(|r| r.ok())
603}
604
605pub fn field_offset_in_bytes<'tcx>(
607 tcx: TyCtxt<'tcx>,
608 caller: DefId,
609 ty: Ty<'tcx>,
610 field_idx: usize,
611) -> u64 {
612 let Some(layout) = layout_of_ty(tcx, caller, ty) else { return 0 };
613 match layout.fields {
614 rustc_abi::FieldsShape::Arbitrary { ref offsets, .. } => {
615 let idx = rustc_abi::FieldIdx::from_usize(field_idx);
616 if idx.as_usize() < offsets.len() { return offsets[idx].bytes(); }
617 }
618 _ => {}
619 }
620 0
621}
622
623pub fn destination_stride<'tcx>(
624 tcx: TyCtxt<'tcx>, caller: DefId, dest: Option<Local>,
625) -> Option<u64> {
626 let d = dest?;
627 let pointee = pointee_ty(tcx.optimized_mir(caller).local_decls[d].ty)?;
628 type_layout(tcx, caller, pointee).map(|(_, s)| s)
629}
630
631pub fn pointee_alignment<'tcx>(
632 tcx: TyCtxt<'tcx>, caller: DefId, dest: Option<Local>,
633) -> Option<(u64, String)> {
634 let d = dest?;
635 let ty = tcx.optimized_mir(caller).local_decls[d].ty;
636 let pointee = pointee_ty(ty).or(Some(ty))?;
637 if let Some((a, _)) = type_layout(tcx, caller, pointee) {
638 return Some((a, format!("{pointee:?}")));
639 }
640 if let TyKind::Array(e, _) = pointee.kind()
641 && let Some((a, _)) = type_layout(tcx, caller, *e)
642 {
643 return Some((a, format!("{pointee:?}")));
644 }
645 Some((0, format!("{pointee:?}")))
646}
647
648pub fn nonnull_inner_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
649 let TyKind::Adt(def, args) = ty.kind() else { return None };
650 if !tcx.def_path_str(def.did()).contains("ptr::non_null::NonNull") { return None }
651 args.iter().find_map(|a| match a.kind() { GenericArgKind::Type(t) => Some(t), _ => None })
652}
653
654pub fn slice_element_size(
655 tcx: TyCtxt<'_>, caller: DefId, dest: Option<Local>,
656) -> u64 {
657 let d = match dest {
658 Some(d) => d,
659 None => return 1,
660 };
661 let ty = tcx.optimized_mir(caller).local_decls[d].ty;
662 let elem = match ty.kind() {
663 TyKind::Ref(_, inner, _) => match inner.kind() {
664 TyKind::Slice(e) => *e,
665 _ => return 1,
666 },
667 TyKind::RawPtr(inner, _) => match inner.kind() {
668 TyKind::Slice(e) => *e,
669 _ => return 1,
670 },
671 _ => return 1,
672 };
673 type_layout(tcx, caller, elem)
674 .map(|(_, s)| s)
675 .unwrap_or(1)
676}
677
678pub fn vec_element_size(tcx: TyCtxt<'_>, caller: DefId, dest: Option<Local>) -> u64 {
679 let d = match dest {
680 Some(d) => d,
681 None => return 1,
682 };
683 let ty = tcx.optimized_mir(caller).local_decls[d].ty;
684 vec_elem_ty(tcx, ty)
685 .and_then(|elem_ty| type_layout(tcx, caller, elem_ty).map(|(_, s)| s))
686 .unwrap_or(1)
687}
688
689pub fn const_int_from_debug(text: &str) -> Option<u64> {
694 if let Ok(v) = text.parse::<u64>() {
695 return Some(v);
696 }
697 if let Some(start) = text.find("0x") {
698 let hex_part = &text[start..];
699 let end = hex_part
700 .find(|c: char| !c.is_ascii_hexdigit() && c != 'x')
701 .unwrap_or(hex_part.len());
702 u64::from_str_radix(&hex_part[2..end], 16).ok()
703 } else if let Some(start) = text.find("Value(") {
704 let inner = &text[start + 6..];
705 if let Some(end) = inner.find(')') {
706 inner[..end].parse::<u64>().ok()
707 } else {
708 None
709 }
710 } else {
711 None
712 }
713}
714
715pub fn const_scalar_int<'tcx>(
724 tcx: TyCtxt<'tcx>,
725 constant: &rustc_middle::mir::Const<'tcx>,
726 text: &str,
727) -> Option<i128> {
728 if let Some(v) = const_int_from_debug(text) {
729 return Some(v as i128);
730 }
731 let is_num_bound =
734 text.contains("::BITS") || text.contains("::MAX") || text.contains("::MIN");
735 if !is_num_bound && offset_of_container(tcx, constant).is_none() {
736 return None;
737 }
738 let typing_env = TypingEnv::fully_monomorphized();
739 let val = constant.eval(tcx, typing_env, rustc_span::DUMMY_SP).ok()?;
740 let scalar = val.try_to_scalar_int()?;
741 let bits = scalar.size().bits() as u32;
742 let raw = scalar.to_bits(scalar.size()) as i128;
743 if raw > u32::MAX as i128 {
746 return None;
747 }
748 let ty = constant.ty();
750 if let TyKind::Int(_) = ty.kind() {
751 let sign = 1i128 << (bits - 1);
752 if raw >= sign {
753 Some(raw - (1i128 << bits))
754 } else {
755 Some(raw)
756 }
757 } else {
758 Some(raw)
759 }
760}
761
762pub fn extract_const_bytes_from_operand<'tcx>(
766 tcx: TyCtxt<'tcx>,
767 operand: &Operand<'tcx>,
768) -> Option<Vec<u8>> {
769 let constant = match operand {
770 Operand::Constant(c) => c,
771 _ => return None,
772 };
773 let ty = constant.const_.ty();
774 let (inner_ty, _is_ref) = match ty.kind() {
775 TyKind::Ref(_, inner, _) => (*inner, true),
776 _ => return None,
777 };
778 let inner_ty = if let TyKind::Ref(_, innermost, _) = inner_ty.kind() {
780 *innermost
781 } else {
782 inner_ty
783 };
784 let _elem_ty = match inner_ty.kind() {
785 TyKind::Array(elem, _) | TyKind::Slice(elem) => *elem,
786 _ => return None,
787 };
788
789 let typing_env = TypingEnv::fully_monomorphized();
791 let value = constant
792 .const_
793 .eval(tcx, typing_env, rustc_span::DUMMY_SP)
794 .ok()?;
795
796 const_value_bytes(tcx, value, 0)
797}
798
799pub fn extract_local(operand: &Operand<'_>) -> Option<Local> {
801 match operand {
802 Operand::Copy(place) | Operand::Move(place)
803 if place.projection.is_empty() => Some(place.local),
804 _ => None,
805 }
806}
807
808pub fn extract_operand_const(operand: &Operand<'_>) -> Option<u64> {
810 match operand {
811 Operand::Constant(constant) => {
812 let text = format!("{:?}", constant.const_);
813 const_int_from_debug(&text)
814 }
815 _ => None,
816 }
817}
818
819pub fn is_u8_array_or_slice(ty: Ty<'_>) -> bool {
821 match ty.kind() {
822 TyKind::Array(elem_ty, _) => {
823 matches!(elem_ty.kind(), TyKind::Uint(rustc_middle::ty::UintTy::U8))
824 }
825 TyKind::Slice(elem_ty) => {
826 matches!(elem_ty.kind(), TyKind::Uint(rustc_middle::ty::UintTy::U8))
827 }
828 _ => false,
829 }
830}
831
832pub fn type_contains_reference(ty: Ty<'_>) -> bool {
834 match ty.kind() {
835 TyKind::Ref(..) => true,
836 TyKind::Adt(_, substs) => substs.types().any(type_contains_reference),
837 _ => false,
838 }
839}
840
841pub fn vec_elem_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
843 if let TyKind::Adt(adt_def, substs) = ty.kind() {
844 let name = tcx.def_path_str(adt_def.did());
845 if crate::helpers::api_classify::is_std_vec(&name) {
846 return substs.first().and_then(|s| s.as_type());
847 }
848 }
849 None
850}
851
852pub fn size_of_generic_param<'tcx>(tcx: TyCtxt<'tcx>, caller: DefId, ty: Ty<'tcx>) -> u64 {
855 match ty.kind() {
856 TyKind::Param(_) => {}
857 _ => return 0,
858 };
859 let param_env = tcx.param_env(caller);
860 let typing_env = TypingEnv::post_analysis(tcx, caller);
861 for clause in param_env.caller_bounds() {
862 let Some(trait_clause) = clause.as_trait_clause() else { continue };
863 let self_ty = trait_clause.self_ty().skip_binder();
864 if self_ty != ty {
865 continue;
866 }
867 let trait_def_id = trait_clause.def_id();
868 let mut max_size: u64 = 0;
869 for impl_def_id in tcx.all_impls(trait_def_id) {
870 let impl_ty = tcx.type_of(impl_def_id).skip_binder();
871 if ty_has_param_const(impl_ty) {
872 continue;
873 }
874 let layout = match catch_panic(|| {
875 tcx.layout_of(PseudoCanonicalInput { typing_env, value: impl_ty })
876 }) {
877 Ok(Ok(l)) => l,
878 _ => continue,
879 };
880 max_size = max_size.max(layout.size.bytes());
881 }
882 return max_size;
883 }
884 0
885}
886
887pub fn min_align_of_generic_param<'tcx>(tcx: TyCtxt<'tcx>, caller: DefId, ty: Ty<'tcx>) -> u64 {
890 match ty.kind() {
891 TyKind::Param(_) => {}
892 _ => return 0,
893 };
894 let param_env = tcx.param_env(caller);
895 let typing_env = TypingEnv::post_analysis(tcx, caller);
896 for clause in param_env.caller_bounds() {
897 let Some(trait_clause) = clause.as_trait_clause() else { continue };
898 let self_ty = trait_clause.self_ty().skip_binder();
899 if self_ty != ty {
900 continue;
901 }
902 let trait_def_id = trait_clause.def_id();
903 let mut min_align: u64 = u64::MAX;
904 for impl_def_id in tcx.all_impls(trait_def_id) {
905 let impl_ty = tcx.type_of(impl_def_id).skip_binder();
906 if ty_has_param_const(impl_ty) {
907 continue;
908 }
909 let layout = match catch_panic(|| {
910 tcx.layout_of(PseudoCanonicalInput { typing_env, value: impl_ty })
911 }) {
912 Ok(Ok(l)) => l,
913 _ => continue,
914 };
915 min_align = min_align.min(layout.align.abi.bytes());
916 }
917 return if min_align == u64::MAX { 0 } else { min_align };
918 }
919 0
920}