rapx/
def_id.rs

1use indexmap::IndexMap;
2use rustc_hir::def_id::DefId;
3use rustc_middle::ty::TyCtxt;
4use rustc_public::{CrateDef, rustc_internal};
5use std::sync::OnceLock;
6
7static INIT: OnceLock<Intrinsics> = OnceLock::new();
8
9struct Intrinsics {
10    // The key is fn path, starting from `core::` or `std::`. The value is internal def id.
11    map: IndexMap<Box<str>, DefId>,
12}
13
14pub fn init(tcx: TyCtxt) {
15    INIT.get_or_init(|| init_inner(tcx));
16}
17
18fn init_inner(tcx: TyCtxt) -> Intrinsics {
19    const CRATES: &[&str] = &["core", "std", "alloc"];
20
21    // The key is an index to INTRINSICS slice; the value means if the path is found.
22    let mut indices: IndexMap<_, _> = (0..INTRINSICS.len()).map(|idx| (idx, false)).collect();
23
24    let mut map = IndexMap::<Box<str>, DefId>::with_capacity(INTRINSICS.len());
25
26    for krate in std::iter::once(rustc_public::local_crate())
27        .chain(rustc_public::external_crates().into_iter())
28        .filter(|krate| CRATES.iter().any(|name| *name == krate.name))
29    {
30        for fn_def in krate.fn_defs() {
31            let fn_name: Box<str> = fn_def.name().into();
32            let fn_name_str: &str = &fn_name;
33            if let Some(name) = INTRINSICS.iter().enumerate().find_map(|(idx, paths)| {
34                if paths.iter().any(|&path| {
35                    path == fn_name_str
36                        || path.strip_prefix("core::").is_some_and(|s| s == fn_name_str)
37                        || path.strip_prefix("std::").is_some_and(|s| s == fn_name_str)
38                        || path.strip_prefix("alloc::").is_some_and(|s| s == fn_name_str)
39                }) {
40                    assert_eq!(
41                        indices.insert(idx, true),
42                        Some(false),
43                        "DefId for {fn_name} has been found: {:?}",
44                        map.get(&*fn_name)
45                    );
46                    Some(fn_name.clone())
47                } else {
48                    None
49                }
50            }) {
51                let def_id = rustc_internal::internal(tcx, fn_def.def_id());
52                if map.contains_key(&name) {
53                    panic!("DefId of {fn_name} has been inserted: {def_id:?}");
54                } else {
55                    map.insert(name, def_id);
56                }
57            }
58        }
59    }
60
61    map.sort_unstable_by(|a, _, b, _| a.cmp(b));
62
63    if INTRINSICS.len() != map.len() {
64        // The reason to not make this an assertion is allowing compilation on
65        // missing instrinsics, e.g. no_std crates without using alloc will never
66        // have the dealloc intrinsic.
67        // cc https://github.com/Artisan-Lab/RAPx/issues/190#issuecomment-3303049000
68        let not_found = indices
69            .iter()
70            .filter_map(|(&idx, &found)| (!found).then_some(INTRINSICS[idx]))
71            .collect::<Vec<_>>();
72        rap_warn!(
73            "Intrinsic functions is incompletely retrieved.\n\
74             {} fn ids are not found: {not_found:#?}",
75            not_found.len()
76        );
77    }
78
79    Intrinsics { map }
80}
81
82macro_rules! intrinsics {
83    ($( $id:ident : $paths:expr ,)+ ) => {
84        const INTRINSICS: &[&[&str]] = &[$( $paths ,)+];
85        $(
86            paste::paste! {
87                pub fn [<$id _opt>] () -> Option<DefId> {
88                    let map = &INIT.get().expect("Intrinsics DefIds haven't been initialized.").map;
89                    for path in $paths {
90                        match map.get(*path) {
91                            Some(id) => return Some(*id),
92                            None => ()
93                        }
94                    }
95                    None
96                }
97            }
98        )+
99    };
100}
101
102// for #![no_std] crates, intrinsics fn paths start from core instead of core.
103// cc https://github.com/Artisan-Lab/RAPx/issues/190
104intrinsics! {
105    assume_init_drop: &[
106        "std::mem::MaybeUninit::<T>::assume_init_drop",
107        "core::mem::MaybeUninit::<T>::assume_init_drop",
108        "std::mem::maybe_uninit::MaybeUninit::<T>::assume_init_drop",
109        "core::mem::maybe_uninit::MaybeUninit::<T>::assume_init_drop"
110    ],
111    call_mut: &[
112        "std::ops::FnMut::call_mut",
113        "core::ops::FnMut::call_mut",
114        "std::ops::function::FnMut::call_mut",
115        "core::ops::function::FnMut::call_mut"
116    ],
117    clone: &[
118        "std::clone::Clone::clone",
119        "core::clone::Clone::clone"
120    ],
121    copy_from: &[
122        "std::ptr::mut_ptr::<impl *mut T>::copy_from",
123        "core::ptr::mut_ptr::<impl *mut T>::copy_from"
124    ],
125    copy_from_nonoverlapping: &[
126        "std::ptr::mut_ptr::<impl *mut T>::copy_from_nonoverlapping",
127        "core::ptr::mut_ptr::<impl *mut T>::copy_from_nonoverlapping"
128    ],
129    copy_to: &[
130        "std::ptr::const_ptr::<impl *const T>::copy_to",
131        "core::ptr::const_ptr::<impl *const T>::copy_to",
132    ],
133    copy_to_nonoverlapping: &[
134        "std::ptr::const_ptr::<impl *const T>::copy_to_nonoverlapping",
135        "core::ptr::const_ptr::<impl *const T>::copy_to_nonoverlapping"
136    ],
137    dealloc: &[
138        "std::alloc::dealloc",
139        "alloc::alloc::dealloc"
140    ],
141    drop: &[
142        "std::mem::drop",
143        "core::mem::drop",
144    ],
145    drop_in_place: &[
146        "std::ptr::drop_in_place",
147        "core::ptr::drop_in_place",
148    ],
149    manually_drop: &[
150        "std::mem::ManuallyDrop::<T>::drop",
151        "core::mem::ManuallyDrop::<T>::drop",
152        "std::mem::manually_drop::ManuallyDrop::<T>::drop",
153        "core::mem::manually_drop::ManuallyDrop::<T>::drop"
154    ],
155    replace: &[
156        "std::mem::replace",
157        "core::mem::replace"
158    ],
159    take: &[
160        "std::mem::take",
161        "core::mem::take"
162    ],
163    read_via_copy: &[
164        "std::intrinsics::read_via_copy",
165        "core::intrinsics::read_via_copy"
166    ],
167    write_via_copy: &[
168        "std::intrinsics::write_via_move",
169        "core::intrinsics::write_via_move"
170    ],
171}
172
173/// rustc_public DefId to internal DefId
174pub fn to_internal<T: CrateDef>(val: &T, tcx: TyCtxt) -> DefId {
175    rustc_internal::internal(tcx, val.def_id())
176}
177
178/// Find any drop fn. Any of these drop fns can be missing, e.g. for crates like no_std without
179/// using alloc, dealloc doesn't exist.
180pub fn is_drop_fn(target: DefId) -> bool {
181    let drop_fn = [
182        drop_opt(),
183        drop_in_place_opt(),
184        manually_drop_opt(),
185        dealloc_opt(),
186    ];
187    contains(&drop_fn, target)
188}
189
190/// Is the targe DefId in the given array.
191pub fn contains(v: &[Option<DefId>], target: DefId) -> bool {
192    v.contains(&Some(target))
193}