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