Skip to main content

rapx/verify/
api_classify.rs

1//! Standard-library API call classification helpers.
2//!
3//! Each classifier answers "what kind of operation is this call?" (pointer
4//! arithmetic, raw memory access, ownership transfer, …). Callers — the VM,
5//! the alias/hazard scanner, and the call-summary registry — use these to pick
6//! a modelling strategy or discharge a safety obligation.
7//!
8//! # Matching mechanism
9//!
10//! Every classifier takes a `DefId` — `fn(Option<DefId>) -> bool` for callee
11//! matching, or `fn(DefId) -> bool` for ADT *type* matching — and answers via
12//! exact set membership in [`crate::def_id`] rather than substring-matching a
13//! `def_path_str`. [`crate::def_id`] resolves the well-known `std`/`core`/`alloc`
14//! items (by lang/diagnostic item, explicit path, or — for the open-ended
15//! groups and the std-challenge suites' local re-implementations — by
16//! name-scanning `fn_defs()` at init), so a call site is matched by identity
17//! without the false positives of per-call-site name matching.
18
19use rustc_hir::def_id::DefId;
20use rustc_middle::ty::{Ty, TyKind};
21use rustc_middle::ty::TyCtxt;
22
23/// Whether `callee` is `Some` and matches any item in the (Option<DefId>) list.
24fn any_of(callee: Option<DefId>, items: &[Option<DefId>]) -> bool {
25    callee.is_some_and(|c| items.contains(&Some(c)))
26}
27
28/// Whether `callee` is `Some` and matches any resolved DefId in the slice.
29fn any_fn(callee: Option<DefId>, fns: &[DefId]) -> bool {
30    callee.is_some_and(|c| fns.contains(&c))
31}
32
33// ── Ownership reconstruction ──────────────────────────────────────
34
35/// Whether `callee` reconstructs an owned value, taking ownership of the
36/// pointed-to memory: from a single raw pointer (`Box::from_raw`,
37/// `CString::from_raw`, `Arc::from_raw`, `Rc::from_raw`) or from a `Vec<u8>`
38/// (`CString::from_vec_with_nul_unchecked`). Distinct from
39/// [`is_from_raw_parts`], which builds a slice/`Vec` from `(ptr, len[, cap])`.
40pub fn is_ownership_reconstruction(callee: Option<DefId>) -> bool {
41    any_of(
42        callee,
43        &[
44            crate::def_id::box_from_raw(),
45            crate::def_id::cstring_from_raw(),
46            crate::def_id::arc_from_raw(),
47            crate::def_id::rc_from_raw(),
48            crate::def_id::box_from_raw_in(),
49            crate::def_id::arc_from_raw_in(),
50            crate::def_id::rc_from_raw_in(),
51            crate::def_id::cstring_from_vec_with_nul_unchecked(),
52        ],
53    )
54}
55
56/// Whether `callee` is `ManuallyDrop::<T>::drop` — the *manual* drop that frees
57/// the pointee's allocation without the `ManuallyDrop` wrapper's own (no-op)
58/// drop glue.
59pub fn is_manually_drop_drop(callee: Option<DefId>) -> bool {
60    any_of(callee, &[crate::def_id::manually_drop()])
61}
62
63/// Whether `callee` is `std::mem::drop` / `core::mem::drop` — the value drop
64/// that frees the argument's heap allocation.
65pub fn is_std_drop(callee: Option<DefId>) -> bool {
66    any_of(callee, &[crate::def_id::drop()])
67}
68
69/// Whether `callee` is `drop_in_place::<T>` — the MIR drop shim that drops the
70/// pointee in place and (for an owning pointee like `Box`/`Vec`) frees its
71/// heap allocation.
72pub fn is_drop_in_place(callee: Option<DefId>) -> bool {
73    any_of(callee, &[crate::def_id::drop_in_place()])
74}
75
76// ── Pointer extraction / cast ─────────────────────────────────────
77
78/// Whether `callee` produces a raw pointer (or `NonNull`) alias of its first
79/// argument: `as_ptr`/`as_mut_ptr`, `into_raw`, and pointer `cast` (incl.
80/// `cast_mut`/`cast_const`). `as_ptr_range`/`as_mut_ptr_range` are excluded
81/// because they return a `Range` of two pointers rather than a single pointer;
82/// `NonNull::new_unchecked`/`as_ref`/`as_mut` are excluded because they do not
83/// produce a raw pointer (and `new_unchecked` must not mark its result
84/// non-null, or it would hide `new_unchecked(null)` unsoundness).
85pub fn is_as_ptr(callee: Option<DefId>) -> bool {
86    any_of(
87        callee,
88        &[
89            crate::def_id::slice_as_ptr(),
90            crate::def_id::slice_as_mut_ptr(),
91            crate::def_id::str_as_ptr(),
92            crate::def_id::str_as_mut_ptr(),
93            crate::def_id::vec_as_ptr(),
94            crate::def_id::vec_as_mut_ptr(),
95            crate::def_id::cstr_as_ptr(),
96            crate::def_id::nonnull_as_ptr(),
97            crate::def_id::const_ptr_slice_as_ptr(),
98            crate::def_id::mut_ptr_slice_as_mut_ptr(),
99            crate::def_id::nonnull_slice_as_mut_ptr(),
100            crate::def_id::box_as_ptr(),
101            crate::def_id::box_as_mut_ptr(),
102            crate::def_id::maybe_uninit_as_ptr(),
103            crate::def_id::maybe_uninit_as_mut_ptr(),
104            crate::def_id::arc_as_ptr(),
105            crate::def_id::rc_as_ptr(),
106            crate::def_id::const_ptr_cast(),
107            crate::def_id::const_ptr_cast_mut(),
108            crate::def_id::const_ptr_cast_array(),
109            crate::def_id::mut_ptr_cast(),
110            crate::def_id::mut_ptr_cast_const(),
111            crate::def_id::mut_ptr_cast_array(),
112            crate::def_id::nonnull_cast(),
113            crate::def_id::box_into_raw(),
114            crate::def_id::cstring_into_raw(),
115            crate::def_id::arc_into_raw(),
116            crate::def_id::rc_into_raw(),
117        ],
118    )
119}
120
121/// Whether `callee` is a raw-pointer `cast`/`cast_mut`/`cast_const`. These only
122/// *reinterpret* the address — they preserve null-ness and provenance, but do
123/// not establish that the result is non-null, aligned, or points at initialized
124/// memory. Distinguished from [`is_as_ptr`] so [`is_as_ptr_valid`] can keep
125/// them on the MIR-inlining path rather than the `ReturnPointerFromArg` model
126/// (which asserts those facts).
127pub(crate) fn is_raw_ptr_cast(callee: Option<DefId>) -> bool {
128    any_of(
129        callee,
130        &[
131            crate::def_id::const_ptr_cast(),
132            crate::def_id::const_ptr_cast_mut(),
133            crate::def_id::const_ptr_cast_array(),
134            crate::def_id::mut_ptr_cast(),
135            crate::def_id::mut_ptr_cast_const(),
136            crate::def_id::mut_ptr_cast_array(),
137        ],
138    )
139}
140
141/// [`is_as_ptr`] restricted to the *pointer-validity-establishing* subset:
142/// `as_ptr`/`as_mut_ptr`, `into_raw`, and `NonNull::cast` — which expose a
143/// non-null, aligned, initialized backing pointer. Raw-pointer `cast`/
144/// `cast_mut`/`cast_const` are excluded because they only reinterpret the
145/// address (preserving null-ness) and are left to MIR inlining.
146pub fn is_as_ptr_valid(callee: Option<DefId>) -> bool {
147    is_as_ptr(callee) && !is_raw_ptr_cast(callee)
148}
149
150/// Whether `callee` is an `as_ptr`/`as_mut_ptr` method on a recognized
151/// pointer-container ADT (`Vec`/`NonNull`/`Box`/`MaybeUninit`/`CString`).
152///
153/// Unlike [`is_as_ptr`] (which matches the std functions by exact full path),
154/// this matches by the *self type* — resolved from the method's impl — so it
155/// also catches the std-challenge suites' local re-implementations without the
156/// broad `::as_ptr` name suffix.
157pub fn is_container_as_ptr(tcx: TyCtxt<'_>, callee: DefId) -> bool {
158    let name = tcx.item_name(callee);
159    if name.as_str() != "as_ptr" && name.as_str() != "as_mut_ptr" {
160        return false;
161    }
162    let Some(self_ty) = crate::helpers::name::get_struct_self_ty(tcx, callee) else {
163        return false;
164    };
165    let TyKind::Adt(adt, _) = self_ty.kind() else {
166        return false;
167    };
168    is_std_vec(adt.did())
169        || is_std_nonnull(adt.did())
170        || is_std_box(adt.did())
171        || is_maybe_uninit_type(adt.did())
172        || is_std_cstring(adt.did())
173}
174
175/// `str::as_bytes`: reinterprets `&str` as `&[u8]` — same data pointer and
176/// byte length, so the result aliases the argument.
177pub fn is_str_as_bytes(callee: Option<DefId>) -> bool {
178    any_of(callee, &[crate::def_id::str_as_bytes()])
179}
180
181// ── Pointer arithmetic ────────────────────────────────────────────
182// Direction (`add` vs `sub`) and granularity (`element` vs `byte`) are two
183// orthogonal axes. Each of the four combinations is a first-class classifier
184// below; the aggregate predicates at the end are unions over a single axis,
185// for callers that only care about one dimension.
186
187/// Element-strided `add`/`wrapping_add` and signed `offset`/`wrapping_offset`
188/// (stride = `size_of::<T>()`). `offset_from`/`offset_from_unsigned` are *not*
189/// matched (they subtract two pointers into an `isize`).
190pub(crate) fn is_element_ptr_add(callee: Option<DefId>) -> bool {
191    any_of(
192        callee,
193        &[
194            crate::def_id::const_ptr_add(),
195            crate::def_id::const_ptr_wrapping_add(),
196            crate::def_id::const_ptr_offset(),
197            crate::def_id::const_ptr_wrapping_offset(),
198            crate::def_id::mut_ptr_add(),
199            crate::def_id::mut_ptr_wrapping_add(),
200            crate::def_id::mut_ptr_offset(),
201            crate::def_id::mut_ptr_wrapping_offset(),
202            crate::def_id::nonnull_add(),
203            crate::def_id::nonnull_offset(),
204        ],
205    )
206}
207
208/// Element-strided `sub`/`wrapping_sub` (stride = `size_of::<T>()`).
209pub(crate) fn is_element_ptr_sub(callee: Option<DefId>) -> bool {
210    any_of(
211        callee,
212        &[
213            crate::def_id::const_ptr_sub(),
214            crate::def_id::const_ptr_wrapping_sub(),
215            crate::def_id::mut_ptr_sub(),
216            crate::def_id::mut_ptr_wrapping_sub(),
217            crate::def_id::nonnull_sub(),
218        ],
219    )
220}
221
222/// Byte-granular `byte_add`/`wrapping_byte_add` and signed
223/// `byte_offset`/`wrapping_byte_offset` (stride 1).
224pub(crate) fn is_byte_ptr_add(callee: Option<DefId>) -> bool {
225    any_of(
226        callee,
227        &[
228            crate::def_id::const_ptr_byte_add(),
229            crate::def_id::const_ptr_wrapping_byte_add(),
230            crate::def_id::const_ptr_byte_offset(),
231            crate::def_id::const_ptr_wrapping_byte_offset(),
232            crate::def_id::mut_ptr_byte_add(),
233            crate::def_id::mut_ptr_wrapping_byte_add(),
234            crate::def_id::mut_ptr_byte_offset(),
235            crate::def_id::mut_ptr_wrapping_byte_offset(),
236            crate::def_id::nonnull_byte_add(),
237            crate::def_id::nonnull_byte_offset(),
238        ],
239    )
240}
241
242/// Byte-granular `byte_sub`/`wrapping_byte_sub` (stride 1).
243pub(crate) fn is_byte_ptr_sub(callee: Option<DefId>) -> bool {
244    any_of(
245        callee,
246        &[
247            crate::def_id::const_ptr_byte_sub(),
248            crate::def_id::const_ptr_wrapping_byte_sub(),
249            crate::def_id::mut_ptr_byte_sub(),
250            crate::def_id::mut_ptr_wrapping_byte_sub(),
251            crate::def_id::nonnull_byte_sub(),
252        ],
253    )
254}
255
256/// Any pointer `add` (element or byte). `offset`/`byte_offset` take a signed
257/// `isize`, so a negative offset is still classified here (the sign lives in
258/// the argument); see [`is_pointer_sub`] for the positive-count `sub` family.
259pub fn is_pointer_add(callee: Option<DefId>) -> bool {
260    is_element_ptr_add(callee) || is_byte_ptr_add(callee)
261}
262
263/// Any pointer `sub` (element or byte): a positive count, `base - count * stride`.
264pub fn is_pointer_sub(callee: Option<DefId>) -> bool {
265    is_element_ptr_sub(callee) || is_byte_ptr_sub(callee)
266}
267
268/// Any byte-granular pointer arithmetic (stride 1), regardless of direction.
269pub fn is_byte_ptr_arith(callee: Option<DefId>) -> bool {
270    is_byte_ptr_add(callee) || is_byte_ptr_sub(callee)
271}
272
273// ── Layout constants ──────────────────────────────────────────────
274
275/// Whether `callee` is the compile-time layout constant `size_of::<T>()` or
276/// `align_of::<T>()`. The runtime intrinsics (`size_of_val`, `align_of_val`,
277/// `pref_align_of`, `*_val_raw`, …) are *not* classified here.
278pub fn is_layout_constant(callee: Option<DefId>) -> bool {
279    any_of(
280        callee,
281        &[
282            crate::def_id::mem_size_of(),
283            crate::def_id::mem_align_of(),
284            crate::def_id::intrinsics_size_of(),
285            crate::def_id::intrinsics_align_of(),
286        ],
287    )
288}
289
290/// Whether `callee` is `ptr::align_offset` / `NonNull::align_offset` /
291/// `*const T::align_offset` / `*mut T::align_offset`.
292pub fn is_align_offset(callee: Option<DefId>) -> bool {
293    any_of(
294        callee,
295        &[
296            crate::def_id::ptr_align_offset(),
297            crate::def_id::nonnull_align_offset(),
298            crate::def_id::const_ptr_align_offset(),
299            crate::def_id::mut_ptr_align_offset(),
300        ],
301    )
302}
303
304// ── Raw pointer read / write ──────────────────────────────────────
305
306/// Whether `callee` writes through a raw pointer to its first argument
307/// (`ptr::write`, `write_bytes`, `write_unaligned`, `write_volatile`).
308pub fn is_ptr_write(callee: Option<DefId>) -> bool {
309    any_of(
310        callee,
311        &[
312            crate::def_id::ptr_write(),
313            crate::def_id::ptr_write_unaligned(),
314            crate::def_id::ptr_write_volatile(),
315            crate::def_id::ptr_write_bytes(),
316        ],
317    )
318}
319
320/// Whether `callee` reads through a raw pointer or copies memory
321/// (`ptr::read`/`read_unaligned`/`read_volatile`, `copy_to`/`copy_from`,
322/// `MaybeUninit::assume_init_read`, and intrinsics `copy`/`copy_nonoverlapping`).
323pub fn is_ptr_read(callee: Option<DefId>) -> bool {
324    any_of(
325        callee,
326        &[
327            crate::def_id::ptr_read(),
328            crate::def_id::ptr_read_unaligned(),
329            crate::def_id::ptr_read_volatile(),
330            crate::def_id::copy_to(),
331            crate::def_id::copy_to_nonoverlapping(),
332            crate::def_id::copy_from(),
333            crate::def_id::copy_from_nonoverlapping(),
334            crate::def_id::assume_init_read(),
335            crate::def_id::intrinsics_copy(),
336            crate::def_id::intrinsics_copy_nonoverlapping(),
337        ],
338    )
339}
340
341// ── MaybeUninit ───────────────────────────────────────────────────
342
343/// Whether `callee` is `MaybeUninit::write`, which initializes the slot (unlike
344/// raw `ptr::write`, handled by [`is_mem_copy_or_write`]).
345pub fn is_maybe_uninit_write(callee: Option<DefId>) -> bool {
346    any_of(callee, &[crate::def_id::maybe_uninit_write()])
347}
348
349/// Whether `callee` is `MaybeUninit::uninit` (a new uninitialized slot).
350pub fn is_maybe_uninit_uninit(callee: Option<DefId>) -> bool {
351    any_of(callee, &[crate::def_id::maybe_uninit_uninit()])
352}
353
354/// Whether `callee` is a `MaybeUninit` "assume initialized" accessor
355/// (`assume_init`, `assume_init_read`, `assume_init_ref`, `assume_init_mut`).
356pub fn is_maybe_uninit_assume_init(callee: Option<DefId>) -> bool {
357    any_of(
358        callee,
359        &[
360            crate::def_id::maybe_uninit_assume_init(),
361            crate::def_id::assume_init_read(),
362            crate::def_id::maybe_uninit_assume_init_ref(),
363            crate::def_id::maybe_uninit_assume_init_mut(),
364        ],
365    )
366}
367
368/// Memory copy/write intrinsics that legitimately write through a raw pointer
369/// without requiring the target bytes to be pre-initialized (e.g. `ptr::write`,
370/// `write_bytes`, `copy_nonoverlapping`, `ptr::copy`). Used by the checker to
371/// discharge `Init`/`Typed` obligations on `MaybeUninit` targets.
372pub fn is_mem_copy_or_write(callee: Option<DefId>) -> bool {
373    any_of(
374        callee,
375        &[
376            crate::def_id::intrinsics_copy(),
377            crate::def_id::intrinsics_copy_nonoverlapping(),
378            crate::def_id::copy_from_nonoverlapping(),
379            crate::def_id::copy_to_nonoverlapping(),
380            crate::def_id::ptr_write(),
381            crate::def_id::ptr_write_bytes(),
382        ],
383    )
384}
385
386// ── Queries and unwrap ────────────────────────────────────────────
387
388/// Whether `callee` is a `len` query method. Matched by `DefId` via
389/// [`crate::def_id::len_fns`], which resolves every `::len` `fn_def` in the std
390/// crates *and* the local crate — so the std-challenge suites' re-implemented
391/// `len` methods are modelled too, without substring-matching a `def_path_str`.
392pub fn is_len(callee: Option<DefId>) -> bool {
393    any_fn(callee, crate::def_id::len_fns())
394}
395
396/// Whether `callee` is a `capacity` query method.
397pub fn is_capacity(callee: Option<DefId>) -> bool {
398    any_fn(callee, crate::def_id::capacity_fns())
399}
400
401pub fn is_unwrap(callee: Option<DefId>) -> bool {
402    any_of(
403        callee,
404        &[
405            crate::def_id::option_unwrap(),
406            crate::def_id::option_expect(),
407            crate::def_id::option_unwrap_unchecked(),
408            crate::def_id::result_unwrap(),
409            crate::def_id::result_unwrap_err(),
410            crate::def_id::result_expect(),
411            crate::def_id::result_expect_err(),
412            crate::def_id::result_unwrap_unchecked(),
413        ],
414    )
415}
416
417// ── Slice / C-string ──────────────────────────────────────────────
418
419/// Whether `callee` is a `from_raw_parts` constructor (`slice`/`str`/`ptr`/
420/// `String`/`Vec`/`NonNull`). Matched by `DefId` via
421/// [`crate::def_id::from_raw_parts_fns`] (resolved from `fn_defs()`, including
422/// local re-implementations), instead of substring-matching `def_path_str`.
423pub fn is_from_raw_parts(callee: Option<DefId>) -> bool {
424    any_fn(callee, crate::def_id::from_raw_parts_fns())
425}
426
427/// Whether `callee` is a `from_raw_parts_mut` constructor.
428pub fn is_from_raw_parts_mut(callee: Option<DefId>) -> bool {
429    any_fn(callee, crate::def_id::from_raw_parts_mut_fns())
430}
431
432pub fn is_cstr_from_ptr(callee: Option<DefId>) -> bool {
433    any_of(callee, &[crate::def_id::cstr_from_ptr()])
434}
435
436/// `_unchecked` C-string constructors whose caller must guarantee NUL
437/// termination (`CStr::from_bytes_with_nul_unchecked`,
438/// `CString::from_vec_with_nul_unchecked`).
439pub fn is_cstr_unchecked_constructor(callee: Option<DefId>) -> bool {
440    any_of(
441        callee,
442        &[
443            crate::def_id::cstr_from_bytes_with_nul_unchecked(),
444            crate::def_id::cstring_from_vec_with_nul_unchecked(),
445        ],
446    )
447}
448
449// ── Vec constructors / methods ────────────────────────────────────
450
451pub fn is_vec_push_or_reserve(callee: Option<DefId>) -> bool {
452    any_of(
453        callee,
454        &[
455            crate::def_id::vec_push(),
456            crate::def_id::vec_reserve(),
457            crate::def_id::vec_reserve_exact(),
458        ],
459    )
460}
461pub fn is_vec_alloc_constructor(callee: Option<DefId>) -> bool {
462    any_of(callee, &[crate::def_id::vec_from_elem()])
463}
464pub fn is_vec_from_box(callee: Option<DefId>) -> bool {
465    any_of(
466        callee,
467        &[
468            crate::def_id::slice_into_vec(),
469            #[cfg(rapx_ge_99)]
470            crate::def_id::box_assume_init_into_vec_unsafe(),
471        ],
472    )
473}
474/// `alloc::alloc::exchange_malloc` (`Box::new`'s allocator on some toolchains).
475pub fn is_exchange_malloc(callee: Option<DefId>) -> bool {
476    callee.is_some_and(|c| crate::def_id::exchange_malloc() == Some(c))
477}
478/// `slice::to_vec` (`<[T]>::to_vec` via `to_vec_in::ConvertVec::to_vec`) —
479/// allocates a fresh buffer and copies the slice's elements.
480pub fn is_slice_to_vec(callee: Option<DefId>) -> bool {
481    any_of(callee, &[crate::def_id::slice_to_vec()])
482}
483/// `Vec::with_capacity` — matched by `DefId` via
484/// [`crate::def_id::with_capacity_fns`].
485pub fn is_vec_with_capacity(callee: Option<DefId>) -> bool {
486    any_fn(callee, crate::def_id::with_capacity_fns())
487}
488/// `Box::new` / `new_in` / `new_uninit` / `new_uninit_in` (and `try_` variants)
489/// — fresh heap allocation constructors.
490pub fn is_box_alloc_ctor(callee: Option<DefId>) -> bool {
491    any_fn(callee, crate::def_id::box_alloc_ctors())
492}
493pub fn is_into_boxed_slice(callee: Option<DefId>) -> bool {
494    any_of(callee, &[crate::def_id::vec_into_boxed_slice()])
495}
496
497// ── Alias-hazard classification ───────────────────────────────────
498// These are the single home for "what does this raw-pointer API do" used by the
499// alias/hazard scanner. `is_ownership_transfer` is the raw-pointer subset of
500// [`is_ownership_reconstruction`]: it excludes `from_vec_with_nul_unchecked`
501// (which consumes a `Vec<u8>` rather than a raw pointer). `Vec::from_raw_parts`
502// / `from_parts` ownership transfer is matched separately by
503// [`is_vec_ownership_transfer`].
504
505pub fn is_ownership_transfer(callee: Option<DefId>) -> bool {
506    let Some(callee) = callee else { return false };
507    is_ownership_reconstruction(Some(callee))
508        && !crate::def_id::contains(
509            &[crate::def_id::cstring_from_vec_with_nul_unchecked()],
510            callee,
511        )
512}
513
514pub fn is_vec_ownership_transfer(callee: Option<DefId>) -> bool {
515    any_fn(callee, crate::def_id::vec_ownership_transfer_fns())
516}
517
518/// Whether `callee` is `NonNull::new` (the null-checked constructor).
519pub(crate) fn is_nonnull_checked_new(callee: Option<DefId>) -> bool {
520    any_of(callee, &[crate::def_id::nonnull_new()])
521}
522
523/// Whether `callee` is `NonNull::new_unchecked` (the unchecked transparent
524/// wrapper). Modeled as a provenance-preserving alias so the pointer's element
525/// offset survives inlined iterator bodies (`post_inc_start`'s
526/// `new_unchecked(ptr.add(1))`); non-nullness is inherited from the source, not
527/// asserted, so `new_unchecked(null)` unsoundness is still caught.
528pub(crate) fn is_nonnull_new_unchecked(callee: Option<DefId>) -> bool {
529    any_of(callee, &[crate::def_id::nonnull_new_unchecked()])
530}
531
532/// Whether `callee` is `NonNull::as_ref` or `NonNull::as_mut`.
533pub fn is_nonnull_as_ref_as_mut(callee: Option<DefId>) -> bool {
534    any_of(
535        callee,
536        &[
537            crate::def_id::nonnull_as_ref(),
538            crate::def_id::nonnull_as_mut(),
539        ],
540    )
541}
542
543/// Whether `callee` is `NonNull::as_mut` (produces an exclusive `&mut`).
544pub fn is_nonnull_as_mut(callee: Option<DefId>) -> bool {
545    any_of(callee, &[crate::def_id::nonnull_as_mut()])
546}
547
548/// Whether `callee` is `select_unpredictable` (the intrinsic or its
549/// `hint::`/`intrinsics::` wrappers): returns one of two candidate values.
550pub(crate) fn is_select_unpredictable(callee: Option<DefId>) -> bool {
551    any_of(
552        callee,
553        &[
554            crate::def_id::select_unpredictable(),
555            crate::def_id::hint_select_unpredictable(),
556        ],
557    )
558}
559
560/// Whether `callee` is a `Vec` method that may reallocate (invalidating any
561/// outstanding raw pointers derived from it).
562pub fn is_vec_invalidating_method(callee: Option<DefId>) -> bool {
563    any_of(
564        callee,
565        &[
566            crate::def_id::vec_push(),
567            crate::def_id::vec_reserve(),
568            crate::def_id::vec_reserve_exact(),
569            crate::def_id::vec_shrink_to_fit(),
570            crate::def_id::vec_shrink_to(),
571            crate::def_id::vec_insert(),
572            crate::def_id::vec_remove(),
573            crate::def_id::vec_clear(),
574            crate::def_id::vec_truncate(),
575            crate::def_id::vec_set_len(),
576        ],
577    )
578}
579
580/// Whether `callee` returns ownership of an allocation as a raw pointer
581/// (`Box::into_raw`, `CString::into_raw`, `Arc::into_raw`, `Rc::into_raw`, ...).
582pub fn is_ownership_return(callee: Option<DefId>) -> bool {
583    any_of(
584        callee,
585        &[
586            crate::def_id::box_into_raw(),
587            crate::def_id::cstring_into_raw(),
588            crate::def_id::arc_into_raw(),
589            crate::def_id::rc_into_raw(),
590        ],
591    )
592}
593
594/// Whether `callee` is a benign, read-only use of a raw-pointer origin
595/// (`len`, `is_empty`, `is_null`, `addr`, `as_ptr`/`as_mut_ptr`, `cast`).
596pub fn is_benign_origin_use(callee: Option<DefId>) -> bool {
597    any_of(
598        callee,
599        &[
600            crate::def_id::const_ptr_is_null(),
601            crate::def_id::const_ptr_addr(),
602            crate::def_id::const_ptr_cast(),
603            crate::def_id::const_ptr_cast_mut(),
604            crate::def_id::const_ptr_slice_is_empty(),
605            crate::def_id::const_ptr_slice_len(),
606            crate::def_id::const_ptr_slice_as_ptr(),
607            crate::def_id::mut_ptr_is_null(),
608            crate::def_id::mut_ptr_addr(),
609            crate::def_id::mut_ptr_cast(),
610            crate::def_id::mut_ptr_cast_const(),
611            crate::def_id::mut_ptr_slice_is_empty(),
612            crate::def_id::mut_ptr_slice_len(),
613            crate::def_id::mut_ptr_slice_as_mut_ptr(),
614            crate::def_id::nonnull_addr(),
615            crate::def_id::nonnull_cast(),
616            crate::def_id::nonnull_as_ptr(),
617            crate::def_id::nonnull_slice_is_empty(),
618            crate::def_id::nonnull_slice_len(),
619            crate::def_id::nonnull_slice_as_mut_ptr(),
620            crate::def_id::slice_len(),
621            crate::def_id::slice_is_empty(),
622            crate::def_id::slice_as_ptr(),
623            crate::def_id::slice_as_mut_ptr(),
624            crate::def_id::str_len(),
625            crate::def_id::str_is_empty(),
626            crate::def_id::str_as_ptr(),
627            crate::def_id::str_as_mut_ptr(),
628            crate::def_id::vec_len(),
629            crate::def_id::vec_is_empty(),
630            crate::def_id::vec_as_ptr(),
631            crate::def_id::vec_as_mut_ptr(),
632            crate::def_id::string_len(),
633            crate::def_id::string_is_empty(),
634            crate::def_id::cstr_as_ptr(),
635            crate::def_id::cstr_is_empty(),
636        ],
637    )
638}
639
640// ── ADT type-name classifiers ─────────────────────────────────────
641// *Shape* recognizers used by the VM to model repr(transparent) wrappers and
642// fixed field layouts (`Vec` = ptr/cap/len, `slice::Iter` = ptr/end, …).
643// Matched by `DefId` (resolved in [`crate::def_id`]).
644
645pub fn is_std_vec(def_id: DefId) -> bool {
646    crate::def_id::vec_types().contains(&def_id)
647}
648pub fn is_std_box(def_id: DefId) -> bool {
649    crate::def_id::box_types().contains(&def_id)
650}
651pub fn is_std_cstring(def_id: DefId) -> bool {
652    crate::def_id::cstring_types().contains(&def_id)
653}
654pub fn is_std_nonnull(def_id: DefId) -> bool {
655    crate::def_id::nonnull_types().contains(&def_id)
656}
657pub fn is_maybe_uninit_type(def_id: DefId) -> bool {
658    crate::def_id::maybe_uninit_types().contains(&def_id)
659}
660
661/// Whether `ty` (peeling through `&` / `*mut` / `*const` / `[T]` / `[T; N]`) is
662/// `MaybeUninit<...>`, i.e. carries no validity invariant (any bit pattern is a
663/// valid value).  Shared by the VM (`init_parameters`) and the `Typed` checker.
664pub fn is_maybe_uninit_ty(ty: Ty<'_>) -> bool {
665    use rustc_middle::ty::TyKind;
666    let mut t = ty;
667    loop {
668        match t.kind() {
669            TyKind::Slice(e) | TyKind::Array(e, _) => t = *e,
670            TyKind::RawPtr(e, _) | TyKind::Ref(_, e, _) => t = *e,
671            TyKind::Adt(adt, _) => return is_maybe_uninit_type(adt.did()),
672            _ => return false,
673        }
674    }
675}
676pub fn is_std_iter_or_itermut(def_id: DefId) -> bool {
677    crate::def_id::iter_types().contains(&def_id)
678}
679pub fn is_std_ordering(def_id: DefId) -> bool {
680    crate::def_id::ordering_types().contains(&def_id)
681}
682
683// ── Arithmetic / collection-operation classifiers ─────────────────
684// Matched by `DefId` via [`crate::def_id::OP_FNS`] (resolved from `fn_defs()`).
685// These were previously name-based in the call-summary registry; see
686// [`crate::def_id`] for the exact method-name patterns each group collects.
687
688pub fn is_min_like(callee: Option<DefId>) -> bool {
689    any_fn(callee, crate::def_id::min_like_fns())
690}
691pub fn is_max(callee: Option<DefId>) -> bool {
692    any_fn(callee, crate::def_id::max_fns())
693}
694pub fn is_clamp(callee: Option<DefId>) -> bool {
695    any_fn(callee, crate::def_id::clamp_fns())
696}
697pub fn is_abs(callee: Option<DefId>) -> bool {
698    any_fn(callee, crate::def_id::abs_fns())
699}
700pub fn is_neg(callee: Option<DefId>) -> bool {
701    any_fn(callee, crate::def_id::neg_fns())
702}
703pub fn is_sat_unchecked_add(callee: Option<DefId>) -> bool {
704    any_fn(callee, crate::def_id::sat_unchecked_add_fns())
705}
706pub fn is_sat_unchecked_mul(callee: Option<DefId>) -> bool {
707    any_fn(callee, crate::def_id::sat_unchecked_mul_fns())
708}
709pub fn is_checked_add(callee: Option<DefId>) -> bool {
710    any_fn(callee, crate::def_id::checked_add_fns())
711}
712pub fn is_checked_mul(callee: Option<DefId>) -> bool {
713    any_fn(callee, crate::def_id::checked_mul_fns())
714}
715pub fn is_overflowing_abs_neg(callee: Option<DefId>) -> bool {
716    any_fn(callee, crate::def_id::overflowing_nz_fns())
717}
718pub fn is_bit_preserving_nz(callee: Option<DefId>) -> bool {
719    any_fn(callee, crate::def_id::bit_preserving_nz_fns())
720}
721pub fn is_checked_nonzero_iff(callee: Option<DefId>) -> bool {
722    any_fn(callee, crate::def_id::checked_nonzero_iff_fns())
723}
724pub fn is_checked_next_pow2(callee: Option<DefId>) -> bool {
725    any_fn(callee, crate::def_id::checked_next_pow2_fns())
726}
727pub fn is_layout_align(callee: Option<DefId>) -> bool {
728    any_fn(callee, crate::def_id::layout_align_fns())
729}
730pub fn is_split_at(callee: Option<DefId>) -> bool {
731    any_fn(callee, crate::def_id::split_at_fns())
732}
733
734// ── Open-ended operation classifiers ──────────────────────────────
735// These match the std-challenge suites' local `_ext` re-implementations and
736// generic trait-method patterns. They cannot be tied to a *closed* hard-coded
737// set, so they are resolved by name-scanning `fn_defs()` in [`crate::def_id`]
738// (which also collects the local crate's re-implementations) and matched by
739// `DefId` like the other classifiers.
740
741pub fn is_align_to_local(callee: Option<DefId>) -> bool {
742    any_fn(callee, crate::def_id::align_to_local_fns())
743}
744pub fn is_iter_position(callee: Option<DefId>) -> bool {
745    any_fn(callee, crate::def_id::iter_position_fns())
746}
747pub fn is_strlen(callee: Option<DefId>) -> bool {
748    any_fn(callee, crate::def_id::strlen_fns())
749}
750pub fn is_slice_get_unchecked(callee: Option<DefId>) -> bool {
751    any_fn(callee, crate::def_id::slice_get_unchecked_fns())
752}
753
754/// Whether `callee` is `SliceIndex::get_unchecked`/`get_unchecked_mut` (the
755/// trait method, whose receiver is the *index* and whose first argument is the
756/// slice pointer). Distinct from [`is_slice_get_unchecked`] (the slice-side
757/// methods whose receiver is the slice): the result aliases argument 1, not
758/// argument 0.
759pub fn is_sliceindex_get_unchecked(callee: Option<DefId>) -> bool {
760    any_fn(callee, crate::def_id::sliceindex_get_unchecked_fns())
761}
762
763/// Whether `callee` is `slice::range(range, bounds)` — the range normalizer that
764/// returns `Range { start, end }` with `0 <= start <= end <= bounds.end`.
765pub fn is_slice_range(callee: Option<DefId>) -> bool {
766    any_fn(callee, crate::def_id::slice_range_fns())
767}
768
769/// Whether `callee` is `mem::replace(dest, src)` — returns `*dest` (the old
770/// value), so the summary must deref the reference argument.
771pub fn is_mem_replace(callee: Option<DefId>) -> bool {
772    any_of(callee, &[crate::def_id::replace()])
773}