Skip to main content

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