rapx/
def_id.rs

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