1#[cfg(rapx_has_attr_ir)]
2use rustc_attr_ir::LangItem;
3#[cfg(all(not(rapx_has_attr_ir), not(rapx_ge_100)))]
4use rustc_hir::LangItem;
5#[cfg(all(not(rapx_has_attr_ir), rapx_ge_100))]
6use rustc_hir::attrs::lang_items::LangItem;
7use rustc_hir::{
8 ItemKind,
9 def_id::{DefId, LocalDefId},
10};
11#[cfg(rapx_const_ext)]
12use rustc_middle::ty::consts::ConstExt;
13use rustc_middle::{
14 mir::interpret::{AllocId, GlobalAlloc},
15 mir::{
16 BasicBlock, Body, ConstValue, Local, Operand, Place, Rvalue, StatementKind, SwitchTargets,
17 TerminatorKind,
18 },
19 ty::{
20 ConstKind, FieldDef, GenericArgKind, GenericArgsRef, PseudoCanonicalInput, Ty, TyCtxt,
21 TyKind, TypingEnv,
22 },
23};
24use rustc_span::{DUMMY_SP, Symbol};
25
26use std::collections::HashSet;
27
28#[cfg(not(rapx_has_skip_norm_wip))]
29use crate::compat::SkipNormWip;
30
31use crate::{compat::FxHashMap, helpers::mir_scan::Checkpoint};
32
33use super::def_use::PlaceKey;
34
35pub(crate) fn pointee_ty<'tcx>(ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
36 match ty.kind() {
37 TyKind::RawPtr(ty, _) | TyKind::Ref(_, ty, _) => Some(*ty),
38 TyKind::Adt(adt, args) if crate::def_id::nonnull_types().contains(&adt.did()) => {
41 args.types().next()
42 }
43 _ => None,
44 }
45}
46
47pub(crate) fn dep_callee_def_id(func: &Operand<'_>) -> Option<DefId> {
48 let Operand::Constant(c) = func else {
49 return None;
50 };
51 let TyKind::FnDef(def_id, _) = c.const_.ty().kind() else {
52 return None;
53 };
54 Some(*def_id)
55}
56
57pub(crate) fn is_eq_call(tcx: TyCtxt<'_>, func: &Operand<'_>) -> bool {
60 let Some(def_id) = dep_callee_def_id(func) else {
61 return false;
62 };
63 let Some(assoc) = tcx.opt_associated_item(def_id) else {
64 return false;
65 };
66 if assoc.name().as_str() != "eq" {
67 return false;
68 }
69 let Some(trait_id) = assoc.trait_container(tcx) else {
71 return false;
72 };
73 tcx.def_path_str(trait_id).ends_with("PartialEq")
74}
75
76pub(crate) fn is_drop_in_place(def_id: DefId) -> bool {
78 crate::def_id::drop_in_place() == Some(def_id)
79}
80
81fn is_diverging_call(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
84 tcx.is_lang_item(def_id, LangItem::Panic)
85 || tcx.is_lang_item(def_id, LangItem::PanicNounwind)
86 || tcx.is_lang_item(def_id, LangItem::PanicFmt)
87 || tcx.is_lang_item(def_id, LangItem::PanicDisplay)
88 || tcx.is_lang_item(def_id, LangItem::ConstPanicFmt)
89 || tcx.is_lang_item(def_id, LangItem::PanicBoundsCheck)
90 || tcx.is_lang_item(def_id, LangItem::PanicMisalignedPointerDereference)
91 || tcx.intrinsic(def_id).is_some_and(|i| {
92 i.name == rustc_span::sym::unreachable || i.name == rustc_span::sym::abort
93 })
94}
95
96pub(crate) fn switch_targets_unreachable<'tcx>(
101 tcx: TyCtxt<'tcx>,
102 body: &Body<'tcx>,
103 targets: &SwitchTargets,
104) -> bool {
105 targets.iter().all(|(_, target)| {
106 let mut cur = target;
107 let mut seen = HashSet::new();
108 loop {
109 if !seen.insert(cur) {
110 return false;
111 }
112 let bb = &body.basic_blocks[cur];
113 let term = bb.terminator();
114 match &term.kind {
115 TerminatorKind::Unreachable => return true,
116 TerminatorKind::Call { func, .. } => {
117 let Some(callee) = dep_callee_def_id(func) else {
118 return false;
119 };
120 return is_diverging_call(tcx, callee);
121 }
122 TerminatorKind::Goto { target: next } => {
123 cur = *next;
124 }
125 TerminatorKind::Return => return bb.statements.is_empty(),
130 _ => return false,
131 }
132 }
133 })
134}
135
136pub(crate) fn switch_is_debug_assert<'tcx>(
141 tcx: TyCtxt<'tcx>,
142 body: &Body<'tcx>,
143 bb: BasicBlock,
144) -> bool {
145 let TerminatorKind::SwitchInt { discr, targets } = &body.basic_blocks[bb].terminator().kind
146 else {
147 return false;
148 };
149 let discr_is_const = match discr {
154 Operand::Constant(_) => true,
155 #[cfg(rapx_ge_95)]
160 Operand::RuntimeChecks(_) => true,
161 Operand::Copy(p) | Operand::Move(p) => {
162 body.basic_blocks.iter().any(|bbd| {
163 bbd.statements.iter().any(|stmt| {
164 let StatementKind::Assign(assign) = &stmt.kind else {
165 return false;
166 };
167 let (dest, rvalue) = &**assign;
168 if dest != p {
169 return false;
170 }
171 match rvalue {
172 #[cfg(rapx_rvalue_use_with_retag)]
173 Rvalue::Use(Operand::Constant(_), _) => true,
174 #[cfg(not(rapx_rvalue_use_with_retag))]
175 Rvalue::Use(Operand::Constant(_)) => true,
176 _ => rvalue_runtime_checks_value(rvalue).is_some(),
177 }
178 })
179 })
180 }
181 #[allow(unreachable_patterns)]
182 _ => false,
183 };
184 if discr_is_const {
185 return true;
186 }
187 switch_targets_unreachable(tcx, body, targets)
188}
189
190pub(crate) fn rvalue_runtime_checks_value(rvalue: &Rvalue<'_>) -> Option<u64> {
197 #[cfg(rapx_rvalue_has_nullary_op)]
198 {
199 if let Rvalue::NullaryOp(rustc_middle::mir::NullOp::RuntimeChecks(_)) = rvalue {
200 return Some(0);
201 }
202 }
203 #[cfg(not(rapx_rvalue_has_nullary_op))]
204 {
205 #[cfg(rapx_rvalue_use_with_retag)]
206 if let Rvalue::Use(Operand::RuntimeChecks(_), _) = rvalue {
207 return Some(0);
208 }
209 #[cfg(not(rapx_rvalue_use_with_retag))]
210 if let Rvalue::Use(Operand::RuntimeChecks(_)) = rvalue {
211 return Some(0);
212 }
213 }
214 None
215}
216
217pub(crate) enum RangeKind {
219 RangeTo,
220 RangeFrom,
221 Range,
222 RangeInclusive,
223 Other,
224}
225
226pub(crate) fn range_kind(tcx: TyCtxt<'_>, def_id: DefId) -> RangeKind {
228 if tcx.is_lang_item(def_id, LangItem::RangeTo) {
229 RangeKind::RangeTo
230 } else if tcx.is_lang_item(def_id, LangItem::RangeFrom) {
231 RangeKind::RangeFrom
232 } else if tcx.is_lang_item(def_id, LangItem::RangeInclusiveStruct) {
233 RangeKind::RangeInclusive
234 } else if tcx.is_lang_item(def_id, LangItem::Range) {
235 RangeKind::Range
236 } else {
237 RangeKind::Other
238 }
239}
240
241pub(crate) fn is_range_type(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
243 !matches!(range_kind(tcx, def_id), RangeKind::Other)
244 || tcx.is_lang_item(def_id, LangItem::RangeToInclusive)
245 || tcx.is_lang_item(def_id, LangItem::RangeFull)
246}
247
248pub(crate) fn is_index_method(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
250 let Some(assoc) = tcx.opt_associated_item(def_id) else {
251 return false;
252 };
253 let name = assoc.name();
254 if name.as_str() != "index" && name.as_str() != "index_mut" {
255 return false;
256 }
257 let Some(trait_id) = assoc.trait_container(tcx) else {
258 return false;
259 };
260 (tcx.is_lang_item(trait_id, LangItem::Index) && name.as_str() == "index")
261 || (tcx.is_lang_item(trait_id, LangItem::IndexMut) && name.as_str() == "index_mut")
262}
263
264pub(crate) fn is_post_inc_start(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
268 let name = tcx.item_name(def_id);
269 name.as_str() == "post_inc_start"
270}
271
272pub(crate) fn is_pre_dec_end(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
275 let name = tcx.item_name(def_id);
276 name.as_str() == "pre_dec_end"
277}
278
279pub(crate) fn is_iter_ptr_adj(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
281 let name = tcx.item_name(def_id);
282 let n = name.as_str();
283 n == "post_inc_start" || n == "pre_dec_end"
284}
285
286pub(crate) fn resolve_callee_impl<'tcx>(
292 tcx: TyCtxt<'tcx>,
293 caller_def_id: DefId,
294 callee_def_id: DefId,
295 callee_args: GenericArgsRef<'tcx>,
296) -> Option<DefId> {
297 let assoc = tcx.opt_associated_item(callee_def_id)?;
298 if assoc.trait_container(tcx).is_none() {
299 return None;
300 }
301 let typing_env = TypingEnv::post_analysis(tcx, caller_def_id);
302 let instance =
303 rustc_middle::ty::Instance::try_resolve(tcx, typing_env, callee_def_id, callee_args)
304 .ok()
305 .flatten()?;
306 let resolved = match instance.def {
307 rustc_middle::ty::InstanceKind::Item(def_id) => def_id,
308 _ => return None,
309 };
310 if resolved == callee_def_id {
311 None
312 } else {
313 Some(resolved)
314 }
315}
316
317pub(crate) fn dep_callee_resolved_def_id<'tcx>(
321 tcx: TyCtxt<'tcx>,
322 caller: DefId,
323 func: &Operand<'tcx>,
324) -> Option<DefId> {
325 let Operand::Constant(c) = func else {
326 return None;
327 };
328 let TyKind::FnDef(def_id, callee_args) = c.const_.ty().kind() else {
329 return None;
330 };
331 let callee_def_id = *def_id;
332 #[cfg(rapx_ge_99)]
333 let callee_args = callee_args.skip_binder();
334 resolve_callee_impl(tcx, caller, callee_def_id, callee_args).or(Some(callee_def_id))
335}
336
337pub fn collect_return_block_indices(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<BasicBlock> {
339 let mut blocks = Vec::new();
340 if !tcx.is_mir_available(def_id) {
341 return blocks;
342 }
343 let body = tcx.optimized_mir(def_id);
344 for (bb, data) in body.basic_blocks.iter_enumerated() {
345 if matches!(data.terminator().kind, TerminatorKind::Return) {
346 blocks.push(bb);
347 }
348 }
349 blocks
350}
351
352pub fn is_return_block(tcx: TyCtxt<'_>, def_id: DefId, block: BasicBlock) -> bool {
354 if !tcx.is_mir_available(def_id) {
355 return false;
356 }
357 let body = tcx.optimized_mir(def_id);
358 body.basic_blocks
359 .get(block)
360 .is_some_and(|data| matches!(data.terminator().kind, TerminatorKind::Return))
361}
362
363pub fn callee_param_index_for_local(tcx: TyCtxt<'_>, callee: DefId, local: usize) -> Option<usize> {
369 let arg_count = if tcx.is_mir_available(callee) {
370 tcx.optimized_mir(callee).arg_count
371 } else {
372 tcx.fn_sig(callee)
373 .skip_binder()
374 .inputs()
375 .skip_binder()
376 .len()
377 };
378 arg_of_local(Local::from_usize(local), arg_count)
379}
380
381pub fn is_std_crate_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
382 matches!(
383 tcx.crate_name(def_id.krate).as_str(),
384 "core" | "std" | "alloc"
385 )
386}
387
388pub fn is_trait_unsafe(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
389 let Some(local_id) = trait_def_id.as_local() else {
390 return false;
391 };
392 let item = tcx.hir_expect_item(local_id);
393
394 #[cfg(not(rapx_ge_99))]
395 if let ItemKind::Trait(_, _, unsafety, _, _, _, _) = &item.kind {
396 return matches!(unsafety, rustc_hir::Safety::Unsafe);
397 }
398 #[cfg(rapx_ge_99)]
399 if let ItemKind::Trait { safety, .. } = &item.kind {
400 return matches!(safety, rustc_hir::Safety::Unsafe);
401 }
402
403 false
404}
405
406pub fn resolve_impl_self_ty_def_id(item: &rustc_hir::Item<'_>) -> Option<DefId> {
407 let ItemKind::Impl(rustc_hir::Impl { self_ty, .. }) = &item.kind else {
408 return None;
409 };
410 match &self_ty.kind {
411 rustc_hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)) => match path.res {
412 rustc_hir::def::Res::Def(
413 rustc_hir::def::DefKind::Struct
414 | rustc_hir::def::DefKind::Enum
415 | rustc_hir::def::DefKind::Union,
416 def_id,
417 ) => Some(def_id),
418 _ => None,
419 },
420 _ => None,
421 }
422}
423
424pub(crate) fn has_rapx_attr(tcx: TyCtxt<'_>, def_id: LocalDefId, name: Symbol) -> bool {
426 let hir_id = tcx.local_def_id_to_hir_id(def_id);
427
428 let rapx = Symbol::intern("rapx");
429 let attrs = tcx.hir_attrs(hir_id);
430
431 attrs.iter().any(|attr| {
432 if attr.is_doc_comment().is_some() {
433 return false;
434 }
435
436 let path = attr.path();
437
438 path.len() == 2 && path[0] == rapx && path[1] == name
439 })
440}
441
442pub fn has_rapx_verify_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
443 has_rapx_attr(tcx, def_id, Symbol::intern("verify"))
444}
445
446fn ty_has_param_const(ty: Ty<'_>) -> bool {
449 for arg in ty.walk() {
450 match arg.kind() {
451 GenericArgKind::Const(c) if matches!(c.kind(), ConstKind::Param(_)) => return true,
452 GenericArgKind::Type(inner_ty) if matches!(inner_ty.kind(), TyKind::Alias(..)) => {
453 return true;
454 }
455 _ => {}
456 }
457 }
458 false
459}
460
461pub(crate) fn catch_panic<T>(f: impl FnOnce() -> T) -> Result<T, String> {
464 std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).map_err(|e| {
465 e.downcast_ref::<String>()
466 .cloned()
467 .or_else(|| e.downcast_ref::<&str>().map(|s| s.to_string()))
468 .unwrap_or_else(|| "<rustc ICE>".to_string())
469 })
470}
471
472pub fn call_name(tcx: TyCtxt<'_>, func: &Operand<'_>) -> String {
474 dep_callee_def_id(func)
475 .map(|def_id| tcx.def_path_str(def_id))
476 .unwrap_or_else(|| format!("{func:?}"))
477}
478
479pub fn arg_of_local(local: Local, arg_count: usize) -> Option<usize> {
483 let i = local.as_usize();
484 if i >= 1 && i <= arg_count {
485 Some(i - 1)
486 } else {
487 None
488 }
489}
490
491pub fn has_crate(tcx: TyCtxt<'_>, name: &str) -> bool {
492 for num in tcx.crates(()) {
493 if tcx.crate_name(*num) == Symbol::intern(name) {
494 return true;
495 }
496 }
497 false
498}
499
500pub fn rvalue_source_place<'a, 'tcx>(
503 rvalue: &'a Rvalue<'tcx>,
504) -> Option<&'a rustc_middle::mir::Place<'tcx>> {
505 use rustc_middle::mir::{Operand, Rvalue};
506 match rvalue {
507 Rvalue::Use(Operand::Copy(place), ..)
508 | Rvalue::Use(Operand::Move(place), ..)
509 | Rvalue::Cast(_, Operand::Copy(place), _)
510 | Rvalue::Cast(_, Operand::Move(place), _)
511 | Rvalue::Ref(_, _, place)
512 | Rvalue::RawPtr(_, place)
513 | Rvalue::CopyForDeref(place) => Some(place),
514 _ => None,
515 }
516}
517
518pub fn operand_place(operand: &Operand<'_>) -> Option<PlaceKey> {
522 operand_mir_place(operand).map(PlaceKey::from_mir_place)
523}
524
525pub fn operand_mir_place<'a, 'tcx>(operand: &'a Operand<'tcx>) -> Option<&'a Place<'tcx>> {
527 match operand {
528 Operand::Copy(place) | Operand::Move(place) => Some(place),
529 _ => None,
530 }
531}
532
533pub fn call_destination<'tcx>(tcx: TyCtxt<'tcx>, checkpoint: &Checkpoint<'tcx>) -> Option<Local> {
535 if checkpoint.kind == crate::helpers::mir_scan::CheckpointKind::RawPtrDeref {
536 return checkpoint.destination;
537 }
538 let body = tcx.optimized_mir(checkpoint.caller);
539 let terminator = body.basic_blocks[checkpoint.block].terminator();
540 let TerminatorKind::Call { destination, .. } = &terminator.kind else {
541 return None;
542 };
543 Some(destination.local)
544}
545
546pub fn blocks_reachable_after_call(
554 tcx: TyCtxt<'_>,
555 caller: DefId,
556 call_block: BasicBlock,
557) -> HashSet<BasicBlock> {
558 let body = tcx.optimized_mir(caller);
559 let mut starts = Vec::new();
560 if let TerminatorKind::Call { target, .. } = &body.basic_blocks[call_block].terminator().kind
561 && let Some(target) = target
562 {
563 starts.push(*target);
564 } else {
565 starts.extend(body.basic_blocks[call_block].terminator().successors());
566 }
567
568 let mut seen = HashSet::new();
569 let mut stack = starts;
570 while let Some(block) = stack.pop() {
571 if !seen.insert(block) {
572 continue;
573 }
574 let terminator = body.basic_blocks[block].terminator();
575 for successor in terminator.successors() {
576 stack.push(successor);
577 }
578 }
579 seen
580}
581
582pub fn rvalue_any_place_matching<'tcx>(
588 rvalue: &Rvalue<'tcx>,
589 pred: &mut impl FnMut(&Place<'tcx>) -> bool,
590) -> bool {
591 match rvalue {
592 Rvalue::Aggregate(_, operands) => operands.iter().any(|operand| match operand {
593 Operand::Copy(place) | Operand::Move(place) => pred(place),
594 Operand::Constant(_) => false,
595 #[cfg(rapx_ge_95)]
596 Operand::RuntimeChecks(_) => false,
597 }),
598 _ => rvalue_source_place(rvalue).is_some_and(pred),
599 }
600}
601
602pub(crate) fn resolve_const_item_value<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> Option<u128> {
608 for item_id in tcx.hir_crate_items(()).free_items() {
609 let def_id = item_id.owner_id.to_def_id();
610 let Some(item_name) = tcx.opt_item_name(def_id) else {
611 continue;
612 };
613 if item_name.as_str() != name {
614 continue;
615 }
616 if !matches!(tcx.def_kind(def_id), rustc_hir::def::DefKind::Const { .. }) {
617 continue;
618 }
619 let instance = rustc_middle::ty::Instance::mono(tcx, def_id);
620 let cid = rustc_middle::mir::interpret::GlobalId {
621 instance,
622 promoted: None,
623 };
624 let Ok(val) = tcx.const_eval_global_id(TypingEnv::fully_monomorphized(), cid, DUMMY_SP)
625 else {
626 continue;
627 };
628 let Some(scalar) = val.try_to_scalar_int() else {
629 continue;
630 };
631 return Some(scalar.to_bits(scalar.size()));
632 }
633 None
634}
635
636fn const_value_bytes<'tcx>(tcx: TyCtxt<'tcx>, value: ConstValue, depth: usize) -> Option<Vec<u8>> {
638 if depth > 4 {
639 return None;
640 }
641 match value {
642 ConstValue::Slice { alloc_id, .. } => alloc_id_bytes(tcx, alloc_id, depth),
643 ConstValue::Scalar(scalar) => {
644 #[cfg(rapx_scalar_to_pointer_interp_result)]
645 let ptr = scalar.to_pointer(&tcx).discard_err()?;
646 #[cfg(not(rapx_scalar_to_pointer_interp_result))]
647 let ptr = scalar.to_pointer(&tcx);
648 let alloc_id = ptr.provenance?.alloc_id();
649 alloc_id_bytes(tcx, alloc_id, depth)
650 }
651 ConstValue::Indirect { alloc_id, .. } => alloc_id_bytes(tcx, alloc_id, depth),
652 _ => None,
653 }
654}
655
656fn alloc_id_bytes<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, depth: usize) -> Option<Vec<u8>> {
658 if depth > 4 {
659 return None;
660 }
661 let alloc = match tcx.global_alloc(alloc_id) {
662 GlobalAlloc::Memory(alloc) => alloc,
663 GlobalAlloc::Static(def_id) => tcx.eval_static_initializer(def_id).ok()?,
664 _ => return None,
665 };
666 let alloc = alloc.inner();
667 let provenance = alloc.provenance().ptrs();
668 if let Some((_, prov)) = provenance.iter().next() {
669 return alloc_id_bytes(tcx, prov.alloc_id(), depth + 1);
670 }
671 Some(
672 alloc
673 .inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())
674 .to_vec(),
675 )
676}
677
678pub(crate) fn offset_of_container<'tcx>(
687 tcx: TyCtxt<'tcx>,
688 constant: &rustc_middle::mir::Const<'tcx>,
689) -> Option<Ty<'tcx>> {
690 let rustc_middle::mir::Const::Unevaluated(uneval, _) = constant else {
691 return None;
692 };
693 if !is_const_def_kind(tcx, uneval.def) {
696 return None;
697 }
698 if !uneval.def.is_local() {
703 return None;
704 }
705 let body = tcx.mir_for_ctfe(uneval.def);
706 for bb in body.basic_blocks.iter() {
707 if let Some(term) = &bb.terminator
708 && let TerminatorKind::Call { func, .. } = &term.kind
709 && let Some(ty) = offset_of_ty_from_func(tcx, func)
710 {
711 return Some(ty);
712 }
713 }
714 None
715}
716
717fn is_const_def_kind(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
719 use rustc_hir::def::DefKind;
720 #[cfg(rapx_ge_99)]
721 let base = matches!(
722 tcx.def_kind(def_id),
723 DefKind::Const { .. }
724 | DefKind::Static { .. }
725 | DefKind::AssocConst { .. }
726 | DefKind::AnonConst
727 );
728 #[cfg(not(rapx_ge_99))]
729 let base = matches!(
730 tcx.def_kind(def_id),
731 DefKind::Const | DefKind::Static { .. } | DefKind::AssocConst | DefKind::AnonConst
732 );
733 #[cfg(rapx_ge_99)]
734 {
735 base
736 }
737 #[cfg(not(rapx_ge_99))]
738 {
739 base || matches!(tcx.def_kind(def_id), DefKind::InlineConst)
740 }
741}
742
743pub(crate) fn fn_def_first_type_arg<'tcx>(func: &Operand<'tcx>) -> Option<Ty<'tcx>> {
748 let Operand::Constant(c) = func else {
749 return None;
750 };
751 let TyKind::FnDef(_, args) = c.const_.ty().kind() else {
752 return None;
753 };
754 args.iter().find_map(|a| {
755 #[cfg(rapx_ge_99)]
756 let a = a.skip_binder();
757 match a.kind() {
758 GenericArgKind::Type(t) => Some(t),
759 _ => None,
760 }
761 })
762}
763
764fn offset_of_ty_from_func<'tcx>(tcx: TyCtxt<'tcx>, func: &Operand<'tcx>) -> Option<Ty<'tcx>> {
765 let Operand::Constant(c) = func else {
766 return None;
767 };
768 let TyKind::FnDef(def_id, _) = c.const_.ty().kind() else {
769 return None;
770 };
771 if !tcx.is_lang_item(*def_id, LangItem::OffsetOf) {
772 return None;
773 }
774 fn_def_first_type_arg(func)
775}
776
777pub fn type_layout<'tcx>(tcx: TyCtxt<'tcx>, caller: DefId, ty: Ty<'tcx>) -> Option<(u64, u64)> {
778 if ty_has_param_const(ty) {
779 return None;
780 }
781 match layout_of_ty(tcx, caller, ty) {
782 Some(l) => Some((l.align.abi.bytes(), l.size.bytes())),
783 None if matches!(ty.kind(), TyKind::Param(_)) => Some((0, 0)),
784 None => None,
785 }
786}
787
788pub fn layout_of_ty<'tcx>(
792 tcx: TyCtxt<'tcx>,
793 caller: DefId,
794 ty: Ty<'tcx>,
795) -> Option<rustc_abi::TyAndLayout<'tcx, Ty<'tcx>>> {
796 let env = TypingEnv::post_analysis(tcx, caller);
797 catch_panic(|| {
798 tcx.layout_of(PseudoCanonicalInput {
799 typing_env: env,
800 value: ty,
801 })
802 })
803 .ok()
804 .and_then(|r| r.ok())
805}
806
807pub fn field_offset_in_bytes<'tcx>(
809 tcx: TyCtxt<'tcx>,
810 caller: DefId,
811 ty: Ty<'tcx>,
812 field_idx: usize,
813) -> u64 {
814 let Some(layout) = layout_of_ty(tcx, caller, ty) else {
815 return 0;
816 };
817 if let rustc_abi::FieldsShape::Arbitrary { ref offsets, .. } = layout.fields {
818 let idx = rustc_abi::FieldIdx::from_usize(field_idx);
819 if idx.as_usize() < offsets.len() {
820 return offsets[idx].bytes();
821 }
822 }
823 0
824}
825
826pub fn destination_stride<'tcx>(
827 tcx: TyCtxt<'tcx>,
828 caller: DefId,
829 dest: Option<Local>,
830) -> Option<u64> {
831 let d = dest?;
832 let pointee = pointee_ty(tcx.optimized_mir(caller).local_decls[d].ty)?;
833 let (_, s) = type_layout(tcx, caller, pointee)?;
834 if s == 0 && ty_has_type_param(pointee) {
838 return None;
839 }
840 Some(s)
841}
842
843pub(crate) fn ty_has_type_param(ty: Ty<'_>) -> bool {
846 ty.walk().any(|t| {
847 matches!(
848 t.kind(),
849 GenericArgKind::Type(inner) if matches!(inner.kind(), TyKind::Param(_))
850 )
851 })
852}
853
854pub fn pointee_alignment<'tcx>(
855 tcx: TyCtxt<'tcx>,
856 caller: DefId,
857 dest: Option<Local>,
858) -> Option<(u64, String)> {
859 let d = dest?;
860 let ty = tcx.optimized_mir(caller).local_decls[d].ty;
861 let pointee = pointee_ty(ty).or(Some(ty))?;
862 if let Some((a, _)) = type_layout(tcx, caller, pointee) {
863 return Some((a, format!("{pointee:?}")));
864 }
865 if let TyKind::Array(e, _) = pointee.kind()
866 && let Some((a, _)) = type_layout(tcx, caller, *e)
867 {
868 return Some((a, format!("{pointee:?}")));
869 }
870 Some((0, format!("{pointee:?}")))
871}
872
873pub fn const_int_from_debug(text: &str) -> Option<u64> {
878 if let Ok(v) = text.parse::<u64>() {
879 return Some(v);
880 }
881 if let Some(start) = text.find("0x") {
882 let hex_part = &text[start..];
883 let end = hex_part
884 .find(|c: char| !c.is_ascii_hexdigit() && c != 'x')
885 .unwrap_or(hex_part.len());
886 u64::from_str_radix(&hex_part[2..end], 16).ok()
887 } else if let Some(start) = text.find("Value(") {
888 let inner = &text[start + 6..];
889 if let Some(end) = inner.find(')') {
890 inner[..end].parse::<u64>().ok()
891 } else {
892 None
893 }
894 } else {
895 None
896 }
897}
898
899pub fn eval_const_scalar_int<'tcx>(
908 tcx: TyCtxt<'tcx>,
909 constant: &rustc_middle::mir::Const<'tcx>,
910 text: &str,
911) -> Option<i128> {
912 if let Some(v) = const_int_from_debug(text) {
913 return Some(v as i128);
914 }
915 let is_num_bound = text.contains("::BITS") || text.contains("::MAX") || text.contains("::MIN");
918 let is_unevaluated = matches!(*constant, rustc_middle::mir::Const::Unevaluated(..));
922 if !is_num_bound && !is_unevaluated && offset_of_container(tcx, constant).is_none() {
923 return None;
924 }
925 let typing_env = TypingEnv::fully_monomorphized();
926 let val = constant.eval(tcx, typing_env, rustc_span::DUMMY_SP).ok()?;
927 let scalar = val.try_to_scalar_int()?;
928 let bits = scalar.size().bits() as u32;
929 let raw = scalar.to_bits(scalar.size()) as i128;
930 if raw > u32::MAX as i128 {
933 return None;
934 }
935 let ty = constant.ty();
937 if let TyKind::Int(_) = ty.kind() {
938 let sign = 1i128 << (bits - 1);
939 if raw >= sign {
940 Some(raw - (1i128 << bits))
941 } else {
942 Some(raw)
943 }
944 } else {
945 Some(raw)
946 }
947}
948
949pub(crate) fn eval_array_len<'tcx>(
955 tcx: TyCtxt<'tcx>,
956 c: &rustc_middle::ty::Const<'tcx>,
957) -> Option<u64> {
958 if let Some(v) = c.try_to_target_usize(tcx) {
959 return Some(v);
960 }
961 #[cfg(rapx_constkind_alias)]
966 let def_id = {
967 let ConstKind::Alias(_, alias_const) = c.kind() else {
968 return None;
969 };
970 #[cfg(not(rapx_alias_const_inherent_self))]
971 let def_id = alias_const.kind.opt_def_id()?;
972 #[cfg(rapx_alias_const_inherent_self)]
973 let def_id = {
974 use rustc_middle::ty::AliasConstKind;
975 match alias_const.kind {
976 AliasConstKind::Projection { def_id } => def_id.into(),
977 AliasConstKind::InherentSelf { def_id } => def_id.into(),
978 AliasConstKind::InherentImpl { def_id } => def_id.into(),
979 AliasConstKind::Free { def_id } => def_id.into(),
980 AliasConstKind::Anon { def_id } => def_id.into(),
981 }
982 };
983 def_id
984 };
985 #[cfg(not(rapx_constkind_alias))]
986 let def_id = {
987 let ConstKind::Unevaluated(uneval) = c.kind() else {
988 return None;
989 };
990 uneval.def
991 };
992 let instance = rustc_middle::ty::Instance::mono(tcx, def_id);
993 let cid = rustc_middle::mir::interpret::GlobalId {
994 instance,
995 promoted: None,
996 };
997 if let Ok(val) = tcx.const_eval_global_id(TypingEnv::fully_monomorphized(), cid, DUMMY_SP) {
998 if let Some(scalar) = val.try_to_scalar_int() {
999 return Some(scalar.to_target_usize(tcx));
1000 }
1001 }
1002 None
1003}
1004
1005pub fn const_operand_bytes<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>) -> Option<Vec<u8>> {
1009 let constant = match operand {
1010 Operand::Constant(c) => c,
1011 _ => return None,
1012 };
1013 let ty = constant.const_.ty();
1014 let inner_ty = match ty.kind() {
1015 TyKind::Ref(_, inner, _) => *inner,
1016 _ => return None,
1017 };
1018 let inner_ty = if let TyKind::Ref(_, innermost, _) = inner_ty.kind() {
1020 *innermost
1021 } else {
1022 inner_ty
1023 };
1024 if !matches!(inner_ty.kind(), TyKind::Array(..) | TyKind::Slice(..)) {
1025 return None;
1026 }
1027
1028 let typing_env = TypingEnv::fully_monomorphized();
1030 let value = constant
1031 .const_
1032 .eval(tcx, typing_env, rustc_span::DUMMY_SP)
1033 .ok()?;
1034
1035 const_value_bytes(tcx, value, 0)
1036}
1037
1038pub fn extract_local(operand: &Operand<'_>) -> Option<Local> {
1040 operand_mir_place(operand)
1041 .filter(|place| place.projection.is_empty())
1042 .map(|place| place.local)
1043}
1044
1045pub fn operand_const_u64(operand: &Operand<'_>) -> Option<u64> {
1047 operand_scalar_int(operand).map(|v| v as u64)
1048}
1049
1050pub fn is_u8_array_or_slice(ty: Ty<'_>) -> bool {
1052 match ty.kind() {
1053 TyKind::Array(elem_ty, _) => {
1054 matches!(elem_ty.kind(), TyKind::Uint(rustc_middle::ty::UintTy::U8))
1055 }
1056 TyKind::Slice(elem_ty) => {
1057 matches!(elem_ty.kind(), TyKind::Uint(rustc_middle::ty::UintTy::U8))
1058 }
1059 _ => false,
1060 }
1061}
1062
1063pub fn type_contains_reference(ty: Ty<'_>) -> bool {
1069 match ty.kind() {
1070 TyKind::Ref(..) => true,
1071 TyKind::Adt(_, substs) => substs.types().any(type_contains_reference),
1072 _ => false,
1073 }
1074}
1075
1076pub fn type_contains_raw_ptr<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
1081 match ty.kind() {
1082 TyKind::RawPtr(_, _) => true,
1083 TyKind::Tuple(elems) => elems.iter().any(|t| type_contains_raw_ptr(tcx, t)),
1084 TyKind::Adt(def, args) => {
1085 if args.iter().any(|arg| {
1086 if let Some(t) = arg.as_type() {
1087 type_contains_raw_ptr(tcx, t)
1088 } else {
1089 false
1090 }
1091 }) {
1092 return true;
1093 }
1094 let adt = tcx.adt_def(def.did());
1095 adt.all_fields()
1096 .any(|field| type_contains_raw_ptr(tcx, field_ty(tcx, field, args)))
1097 }
1098 _ => false,
1099 }
1100}
1101
1102pub fn field_ty<'tcx>(tcx: TyCtxt<'tcx>, field: &FieldDef, args: GenericArgsRef<'tcx>) -> Ty<'tcx> {
1108 field.ty(tcx, args).skip_norm_wip()
1109}
1110
1111pub fn is_raw_ptr_wrapper<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
1116 let adt = tcx.adt_def(def_id);
1117 let variant = adt.non_enum_variant();
1118 if variant.fields.len() != 1 {
1119 return false;
1120 }
1121 let args = rustc_middle::ty::GenericArgs::identity_for_item(tcx, def_id);
1122 let field = variant.fields.iter().next().unwrap();
1123 let field_ty = field_ty(tcx, field, args);
1124 matches!(field_ty.kind(), TyKind::RawPtr(..))
1125}
1126
1127fn generic_param_impl_layouts<'tcx>(
1130 tcx: TyCtxt<'tcx>,
1131 caller: DefId,
1132 ty: Ty<'tcx>,
1133) -> Vec<rustc_abi::TyAndLayout<'tcx, Ty<'tcx>>> {
1134 if !matches!(ty.kind(), TyKind::Param(_)) {
1135 return Vec::new();
1136 }
1137 let param_env = tcx.param_env(caller);
1138 let typing_env = TypingEnv::post_analysis(tcx, caller);
1139 for clause in param_env.caller_bounds() {
1140 let Some(trait_clause) = clause.as_trait_clause() else {
1141 continue;
1142 };
1143 let self_ty = trait_clause.self_ty().skip_binder();
1144 if self_ty != ty {
1145 continue;
1146 }
1147 let mut layouts = Vec::new();
1148 for impl_def_id in tcx.all_impls(trait_clause.def_id()) {
1149 let impl_ty = tcx.type_of(impl_def_id).skip_binder();
1150 if ty_has_param_const(impl_ty) {
1151 continue;
1152 }
1153 let Ok(Ok(layout)) = catch_panic(|| {
1154 tcx.layout_of(PseudoCanonicalInput {
1155 typing_env,
1156 value: impl_ty,
1157 })
1158 }) else {
1159 continue;
1160 };
1161 layouts.push(layout);
1162 }
1163 return layouts;
1164 }
1165 Vec::new()
1166}
1167
1168pub fn size_of_generic_param<'tcx>(tcx: TyCtxt<'tcx>, caller: DefId, ty: Ty<'tcx>) -> u64 {
1171 generic_param_impl_layouts(tcx, caller, ty)
1172 .into_iter()
1173 .map(|l| l.size.bytes())
1174 .max()
1175 .unwrap_or(0)
1176}
1177
1178pub fn min_align_of_generic_param<'tcx>(tcx: TyCtxt<'tcx>, caller: DefId, ty: Ty<'tcx>) -> u64 {
1181 generic_param_impl_layouts(tcx, caller, ty)
1182 .into_iter()
1183 .map(|l| l.align.abi.bytes())
1184 .min()
1185 .unwrap_or(0)
1186}
1187
1188pub fn max_align_of_generic_param<'tcx>(tcx: TyCtxt<'tcx>, caller: DefId, ty: Ty<'tcx>) -> u64 {
1192 generic_param_impl_layouts(tcx, caller, ty)
1193 .into_iter()
1194 .map(|l| l.align.abi.bytes())
1195 .max()
1196 .unwrap_or(0)
1197}
1198
1199pub fn follow_parents(parents: &FxHashMap<Local, Local>, start: Local) -> Local {
1202 let mut current = start;
1203 let mut seen = std::collections::HashSet::new();
1204 while seen.insert(current) {
1205 let Some(next) = parents.get(¤t) else {
1206 break;
1207 };
1208 current = *next;
1209 }
1210 current
1211}
1212
1213pub fn resolve_through_casts<'tcx>(body: &Body<'tcx>, local: Local) -> Local {
1215 let mut current = local;
1216 let mut seen = std::collections::HashSet::new();
1217 while seen.insert(current) {
1218 let found = body.basic_blocks.iter().any(|data| {
1219 data.statements.iter().any(|stmt| {
1220 let StatementKind::Assign(assign) = &stmt.kind else {
1221 return false;
1222 };
1223 let (target, rvalue) = assign.as_ref();
1224 if target.local != current || !target.projection.is_empty() {
1225 return false;
1226 }
1227 if let Rvalue::Cast(_, operand, _) = rvalue {
1228 #[allow(unreachable_patterns)]
1229 match operand {
1230 Operand::Copy(p) | Operand::Move(p) if p.projection.is_empty() => {
1231 current = p.local;
1232 return true;
1233 }
1234 _ => {}
1235 }
1236 }
1237 false
1238 })
1239 });
1240 if !found {
1241 break;
1242 }
1243 }
1244 current
1245}
1246
1247fn operand_scalar_int(operand: &Operand<'_>) -> Option<u128> {
1250 let constant = match operand {
1251 Operand::Constant(c) => c,
1252 _ => return None,
1253 };
1254 constant
1255 .const_
1256 .try_to_scalar_int()
1257 .map(|s| s.to_uint(s.size()))
1258 .or_else(|| const_int_from_debug(&format!("{:?}", constant.const_)).map(|v| v as u128))
1259}
1260
1261fn is_as_ptr_or_as_method(name: &str) -> bool {
1262 name.contains("as_ptr") || name.contains("::as_")
1263}
1264
1265fn rvalue_const_bytes<'tcx>(tcx: TyCtxt<'tcx>, rvalue: &Rvalue<'tcx>) -> Option<Vec<u8>> {
1266 let constant = match rvalue {
1267 Rvalue::Use(Operand::Constant(constant), ..)
1268 | Rvalue::Cast(_, Operand::Constant(constant), _) => constant,
1269 _ => return None,
1270 };
1271 let value = constant
1272 .const_
1273 .eval(tcx, TypingEnv::fully_monomorphized(), DUMMY_SP)
1274 .ok()?;
1275 const_value_bytes(tcx, value, 0)
1276}
1277
1278pub fn collect_all_const_bytes_worklist<'tcx>(
1279 tcx: TyCtxt<'tcx>,
1280 body: &Body<'tcx>,
1281 root: Local,
1282) -> Vec<Vec<u8>> {
1283 let mut results: Vec<Vec<u8>> = Vec::new();
1284 let mut worklist: Vec<Local> = vec![root];
1285 let mut visited: std::collections::HashSet<Local> = std::collections::HashSet::new();
1286
1287 while let Some(local) = worklist.pop() {
1288 if !visited.insert(local) {
1289 continue;
1290 }
1291
1292 for data in body.basic_blocks.iter() {
1293 for statement in &data.statements {
1294 let StatementKind::Assign(assign) = &statement.kind else {
1295 continue;
1296 };
1297 let (target, rvalue) = assign.as_ref();
1298 if target.local != local || !target.projection.is_empty() {
1299 continue;
1300 }
1301
1302 if let Rvalue::Ref(_, _, place) = rvalue {
1303 if let Some(bytes) = const_bytes_for_local(tcx, body, place.local) {
1304 results.push(bytes);
1305 }
1306 continue;
1307 }
1308
1309 if let Rvalue::Use(operand, ..) = rvalue {
1310 #[allow(unreachable_patterns)]
1311 match operand {
1312 Operand::Copy(p) | Operand::Move(p) => {
1313 worklist.push(p.local);
1314 if let Some(bytes) = const_bytes_for_local(tcx, body, p.local) {
1315 results.push(bytes);
1316 }
1317 continue;
1318 }
1319 Operand::Constant(_) => {}
1320 _ => continue,
1321 }
1322 }
1323
1324 if let Some(bytes) = rvalue_const_bytes(tcx, rvalue) {
1325 results.push(bytes);
1326 }
1327 }
1328 }
1329
1330 for data in body.basic_blocks.iter() {
1331 if let Some(terminator) = &data.terminator {
1332 if let TerminatorKind::Call {
1333 destination,
1334 func,
1335 args,
1336 ..
1337 } = &terminator.kind
1338 {
1339 let dlocal = destination.local;
1340 if dlocal != local {
1341 continue;
1342 }
1343 if !destination.projection.is_empty() {
1344 continue;
1345 }
1346 let name = call_name(tcx, func);
1347 if is_as_ptr_or_as_method(&name) {
1348 for arg in args {
1349 if let Some(bytes) =
1350 trace_const_bytes_from_operand(tcx, body, &arg.node)
1351 {
1352 results.push(bytes);
1353 }
1354 }
1355 }
1356 if name.contains("::add") {
1357 if let Some(offset) = args.get(1).and_then(|a| operand_scalar_int(&a.node))
1358 {
1359 if let Some(base) = args.first() {
1360 if let Some(bytes) =
1361 trace_const_bytes_from_operand(tcx, body, &base.node)
1362 {
1363 let start = offset as usize;
1364 if start < bytes.len() {
1365 results.push(bytes[start..].to_vec());
1366 }
1367 }
1368 }
1369 }
1370 }
1371 if name.contains("box_assume_init_into_vec_unsafe") {
1372 if let Some(box_op) = args.first() {
1373 if let Operand::Copy(p) | Operand::Move(p) = &box_op.node {
1374 if p.projection.is_empty() {
1375 worklist.push(p.local);
1376 }
1377 }
1378 }
1379 }
1380 }
1381 }
1382 }
1383 }
1384
1385 collect_aggregate_const_bytes(tcx, body, &mut results);
1386 collect_as_ptr_const_bytes(tcx, body, &mut results);
1387
1388 results
1389}
1390
1391fn collect_aggregate_const_bytes<'tcx>(
1392 tcx: TyCtxt<'tcx>,
1393 body: &Body<'tcx>,
1394 results: &mut Vec<Vec<u8>>,
1395) {
1396 for data in body.basic_blocks.iter() {
1397 for statement in &data.statements {
1398 let StatementKind::Assign(assign) = &statement.kind else {
1399 continue;
1400 };
1401 let (_, rvalue) = assign.as_ref();
1402 let Rvalue::Aggregate(_, operands) = rvalue else {
1403 continue;
1404 };
1405 if operands.len() < 2 {
1406 continue;
1407 }
1408 let last_op = operands.iter().last().unwrap();
1409 if !is_constant_zero(last_op) {
1410 continue;
1411 }
1412 let mut all_nonzero = true;
1413 for op in operands.iter().take(operands.len() - 1) {
1414 if !aggregate_op_is_nonzero(tcx, body, op) {
1415 all_nonzero = false;
1416 break;
1417 }
1418 }
1419 if all_nonzero {
1420 let len = operands.len();
1421 let mut bytes = Vec::with_capacity(len);
1422 for _ in 0..len - 1 {
1423 bytes.push(b'x');
1424 }
1425 bytes.push(0);
1426 results.push(bytes);
1427 }
1428 }
1429 }
1430}
1431
1432fn collect_as_ptr_const_bytes<'tcx>(
1433 tcx: TyCtxt<'tcx>,
1434 body: &Body<'tcx>,
1435 results: &mut Vec<Vec<u8>>,
1436) {
1437 for data in body.basic_blocks.iter() {
1438 if let Some(terminator) = &data.terminator {
1439 if let TerminatorKind::Call { func, args, .. } = &terminator.kind {
1440 let name = call_name(tcx, func);
1441 if is_as_ptr_or_as_method(&name) {
1442 for arg in args {
1443 if let Some(bytes) = trace_const_bytes_from_operand(tcx, body, &arg.node) {
1444 results.push(bytes);
1445 }
1446 }
1447 }
1448 }
1449 }
1450 }
1451}
1452
1453fn trace_const_bytes_from_operand<'tcx>(
1454 tcx: TyCtxt<'tcx>,
1455 body: &Body<'tcx>,
1456 operand: &Operand<'tcx>,
1457) -> Option<Vec<u8>> {
1458 if let Some(bytes) = const_operand_bytes(tcx, operand) {
1459 return Some(bytes);
1460 }
1461 match operand {
1462 Operand::Copy(p) | Operand::Move(p) if p.projection.is_empty() => {
1463 if let Some(bytes) = const_bytes_for_local(tcx, body, p.local) {
1464 return Some(bytes);
1465 }
1466 const_bytes_from_call_dest(tcx, body, p.local)
1467 }
1468 _ => None,
1469 }
1470}
1471
1472fn const_bytes_from_call_dest<'tcx>(
1473 tcx: TyCtxt<'tcx>,
1474 body: &Body<'tcx>,
1475 local: Local,
1476) -> Option<Vec<u8>> {
1477 for data in body.basic_blocks.iter() {
1478 if let Some(terminator) = &data.terminator {
1479 if let TerminatorKind::Call {
1480 destination,
1481 func,
1482 args,
1483 ..
1484 } = &terminator.kind
1485 {
1486 if destination.local != local || !destination.projection.is_empty() {
1487 continue;
1488 }
1489 let name = call_name(tcx, func);
1490 if is_as_ptr_or_as_method(&name) {
1491 for arg in args {
1492 if let Some(bytes) = trace_const_bytes_from_operand(tcx, body, &arg.node) {
1493 return Some(bytes);
1494 }
1495 }
1496 }
1497 }
1498 }
1499 }
1500 None
1501}
1502
1503pub fn const_bytes_for_local<'tcx>(
1504 tcx: TyCtxt<'tcx>,
1505 body: &Body<'tcx>,
1506 root: Local,
1507) -> Option<Vec<u8>> {
1508 for data in body.basic_blocks.iter() {
1509 for statement in &data.statements {
1510 let StatementKind::Assign(assign) = &statement.kind else {
1511 continue;
1512 };
1513 let (target, rvalue) = assign.as_ref();
1514 if target.local != root || !target.projection.is_empty() {
1515 continue;
1516 }
1517 if let Rvalue::Ref(_, _, place) = rvalue {
1518 let deref_local = place.local;
1519 if let Some(bytes) = const_bytes_for_local(tcx, body, deref_local) {
1520 return Some(bytes);
1521 }
1522 continue;
1523 }
1524 if let Rvalue::Use(operand, ..) = rvalue {
1525 #[allow(unreachable_patterns)]
1526 match operand {
1527 Operand::Copy(p) | Operand::Move(p) => {
1528 if let Some(bytes) = const_bytes_for_local(tcx, body, p.local) {
1529 return Some(bytes);
1530 }
1531 if let Some(bytes) = const_bytes_from_call_dest(tcx, body, p.local) {
1532 return Some(bytes);
1533 }
1534 continue;
1535 }
1536 Operand::Constant(_) => {}
1537 _ => continue,
1538 }
1539 }
1540 if let Rvalue::Cast(_, operand, _) = rvalue {
1541 if let Operand::Copy(p) | Operand::Move(p) = operand {
1542 if p.projection.is_empty() {
1543 if let Some(bytes) = const_bytes_for_local(tcx, body, p.local) {
1544 return Some(bytes);
1545 }
1546 }
1547 }
1548 continue;
1549 }
1550 return rvalue_const_bytes(tcx, rvalue);
1551 }
1552 }
1553 None
1554}
1555
1556fn aggregate_op_is_nonzero<'tcx>(
1557 tcx: TyCtxt<'tcx>,
1558 body: &Body<'tcx>,
1559 operand: &Operand<'tcx>,
1560) -> bool {
1561 if is_constant_zero(operand) {
1562 return false;
1563 }
1564 if const_operand_bytes(tcx, operand).is_some() {
1565 return true;
1566 }
1567 match operand {
1568 Operand::Copy(p) | Operand::Move(p) if p.projection.is_empty() => {
1569 for data in body.basic_blocks.iter() {
1570 if let Some(terminator) = &data.terminator {
1571 if let TerminatorKind::Call {
1572 destination, func, ..
1573 } = &terminator.kind
1574 {
1575 if destination.local == p.local && destination.projection.is_empty() {
1576 return fn_always_returns_nonzero(tcx, func);
1577 }
1578 }
1579 }
1580 }
1581 false
1582 }
1583 Operand::Constant(_) => operand_scalar_int(operand).is_some_and(|v| v != 0),
1584 _ => false,
1585 }
1586}
1587
1588fn is_constant_zero(operand: &Operand<'_>) -> bool {
1589 operand_scalar_int(operand) == Some(0)
1590}
1591
1592fn fn_always_returns_nonzero<'tcx>(tcx: TyCtxt<'tcx>, func: &Operand<'tcx>) -> bool {
1593 let Some(fn_def_id) = dep_callee_def_id(func) else {
1594 return false;
1595 };
1596 let callee_body = tcx.optimized_mir(fn_def_id);
1597
1598 let mut has_return = false;
1599 for bb_data in callee_body.basic_blocks.iter() {
1600 if let Some(terminator) = &bb_data.terminator {
1601 if matches!(terminator.kind, TerminatorKind::Return) {
1602 has_return = true;
1603 }
1604 }
1605 for stmt in &bb_data.statements {
1606 let StatementKind::Assign(assign) = &stmt.kind else {
1607 continue;
1608 };
1609 let (target, rvalue) = assign.as_ref();
1610 if target.local != Local::from_usize(0) || !target.projection.is_empty() {
1611 continue;
1612 }
1613 if !rvalue_is_nonzero(rvalue) {
1614 return false;
1615 }
1616 }
1617 }
1618
1619 has_return
1620}
1621
1622fn rvalue_is_nonzero(rvalue: &Rvalue<'_>) -> bool {
1623 match rvalue {
1624 #[allow(unreachable_patterns)]
1625 Rvalue::Use(operand, ..) => match operand {
1626 Operand::Constant(_) => operand_scalar_int(operand).is_some_and(|v| v != 0),
1627 Operand::Copy(_) | Operand::Move(_) => true,
1628 _ => false,
1629 },
1630 _ => false,
1631 }
1632}