Skip to main content

charon_lib/export/
multi_target.rs

1//! Merging multiple [`CrateData`]s from different compilation targets into one.
2use std::cell::RefCell;
3use std::collections::{HashMap, HashSet};
4use std::fmt::Debug;
5use std::mem;
6
7use itertools::Itertools;
8use petgraph::prelude::DiGraphMap;
9use petgraph::visit::{Dfs, Walker};
10
11use crate::errors::ErrorCtx;
12use crate::ids::IndexVec;
13use crate::options::TranslateOptions;
14use crate::transform::TransformCtx;
15use crate::transform::ctx::TransformPass;
16use crate::{ast::*, options::CliOpts};
17
18use super::{CharonVersion, CrateData};
19
20/// Merge per-target [`CrateData`]s into a single [`CrateData`].
21pub fn merge(options: CliOpts, krates: Vec<CrateData>) -> CrateData {
22    let mut error_ctx = ErrorCtx::new();
23    let tr_options = TranslateOptions::new(&mut error_ctx, &options);
24
25    let mut merged = CrateMerger::process(options, krates);
26
27    ItemDeduplicator::dedup(&mut merged.translated, &mut error_ctx);
28
29    let mut ctx = TransformCtx {
30        options: tr_options,
31        translated: merged.translated,
32        errors: RefCell::new(error_ctx),
33    };
34    cleanup_post_merge(&mut ctx);
35    merged.translated = ctx.translated;
36
37    merged
38}
39
40// =============================================================================================
41// Step 1: Merge a set of crates into one, remembering the source target in the names.
42// =============================================================================================
43
44struct CrateMerger {
45    merged: CrateData,
46    file_name_to_id: HashMap<FileName, FileId>,
47}
48
49impl CrateMerger {
50    fn process(options: CliOpts, krates: Vec<CrateData>) -> CrateData {
51        let mut translated = TranslatedCrate::default();
52        translated.options = options;
53        let mut merger = CrateMerger {
54            merged: CrateData {
55                charon_version: CharonVersion(crate::VERSION.to_owned()),
56                translated,
57                has_errors: false,
58            },
59            file_name_to_id: HashMap::new(),
60        };
61        for krate in krates {
62            merger.add_one(krate);
63        }
64
65        merger.merged
66    }
67
68    fn add_one(&mut self, krate: CrateData) {
69        let CrateData {
70            charon_version: _, // Checked by deserialization already
71            translated: mut krate,
72            has_errors,
73        } = krate;
74        self.merged.has_errors |= has_errors;
75        let target = krate
76            .target_information
77            .keys()
78            .exactly_one()
79            .ok()
80            .unwrap()
81            .clone();
82
83        // Remap all ids inside `krate`.
84        krate.drive_mut(&mut {
85            let file_id_map = krate.files.map_ref(|file| {
86                if let Some(&existing_id) = self.file_name_to_id.get(&file.name) {
87                    existing_id
88                } else {
89                    let new_id = self.merged.translated.files.push_with(|new_id| {
90                        let mut file = file.clone();
91                        file.id = new_id;
92                        file
93                    });
94                    self.file_name_to_id.insert(file.name.clone(), new_id);
95                    new_id
96                }
97            });
98
99            #[derive(Visitor)]
100            struct RemapIdsVisitor {
101                target: TargetTriple,
102                file_id_map: IndexVec<FileId, FileId>,
103                type_offset: usize,
104                fun_offset: usize,
105                global_offset: usize,
106                trait_decl_offset: usize,
107                trait_impl_offset: usize,
108            }
109
110            impl VisitAstMut for RemapIdsVisitor {
111                fn enter_file_id(&mut self, id: &mut FileId) {
112                    *id = self.file_id_map[*id];
113                }
114                fn enter_type_decl_id(&mut self, id: &mut TypeDeclId) {
115                    *id += self.type_offset;
116                }
117                fn enter_fun_decl_id(&mut self, id: &mut FunDeclId) {
118                    *id += self.fun_offset;
119                }
120                fn enter_global_decl_id(&mut self, id: &mut GlobalDeclId) {
121                    *id += self.global_offset;
122                }
123                fn enter_trait_decl_id(&mut self, id: &mut TraitDeclId) {
124                    *id += self.trait_decl_offset;
125                }
126                fn enter_trait_impl_id(&mut self, id: &mut TraitImplId) {
127                    *id += self.trait_impl_offset;
128                }
129                fn visit_abort_kind(&mut self, _x: &mut AbortKind) -> ControlFlow<Self::Break> {
130                    // Don't modify the name found there
131                    ControlFlow::Continue(())
132                }
133                fn enter_name(&mut self, name: &mut Name) {
134                    name.name.push(PathElem::Target(self.target.clone()));
135                }
136            }
137
138            RemapIdsVisitor {
139                target,
140                file_id_map,
141                type_offset: self.merged.translated.type_decls.slot_count(),
142                fun_offset: self.merged.translated.fun_decls.slot_count(),
143                global_offset: self.merged.translated.global_decls.slot_count(),
144                trait_decl_offset: self.merged.translated.trait_decls.slot_count(),
145                trait_impl_offset: self.merged.translated.trait_impls.slot_count(),
146            }
147        });
148
149        let TranslatedCrate {
150            crate_name,
151            options: _, // We discard the per-target options we made
152            target_information,
153            item_names,
154            assoc_item_names,
155            short_names: _, // TODO
156            files: _,       // Done above
157            type_decls,
158            fun_decls,
159            global_decls,
160            trait_decls,
161            trait_impls,
162            ordered_decls: _, // Recomputed on the merged crate
163        } = krate;
164        if self.merged.translated.crate_name.is_empty() {
165            self.merged.translated.crate_name = crate_name;
166        }
167        self.merged
168            .translated
169            .target_information
170            .extend(target_information);
171        self.merged.translated.item_names.extend(item_names);
172        self.merged
173            .translated
174            .assoc_item_names
175            .extend_from_other(assoc_item_names);
176        self.merged
177            .translated
178            .type_decls
179            .extend_from_other(type_decls);
180        self.merged
181            .translated
182            .fun_decls
183            .extend_from_other(fun_decls);
184        self.merged
185            .translated
186            .global_decls
187            .extend_from_other(global_decls);
188        self.merged
189            .translated
190            .trait_decls
191            .extend_from_other(trait_decls);
192        self.merged
193            .translated
194            .trait_impls
195            .extend_from_other(trait_impls);
196    }
197}
198
199// =============================================================================================
200// Step 2: Deduplicates items that don't differ across targets and create façades for
201// target-dependent functions
202// =============================================================================================
203
204generate_index_type!(TargetGroupId, "TargetGroup");
205
206/// A set of items that share the same base name and item kind.
207/// These are candidates for merging into a single cross-target item.
208struct TargetGroup {
209    ids: SeqHashMap<TargetTriple, ItemId>,
210}
211
212/// How a `TargetGroup` should be merged.
213#[derive(Debug, Clone, Copy, PartialEq, Eq)]
214enum MergeDecision {
215    /// Don't merge this group.
216    Skip,
217    /// All the items are the same; merge them into one.
218    Dedup,
219    /// Function signatures match but bodies diffe; create a façade that dispatches to the
220    /// per-target items.
221    Facade,
222}
223
224impl TargetGroup {
225    /// Deterministically chosen representative id.
226    fn canonical_id(&self) -> ItemId {
227        self.ids.values().next().copied().unwrap()
228    }
229
230    /// Whether this group is a group of function items.
231    fn is_function_group(&self) -> bool {
232        self.canonical_id().is_fun()
233    }
234
235    /// Compare the items of this group under the provided id mapping.
236    fn decide_merge(
237        &self,
238        krate: &TranslatedCrate,
239        remap: &HashMap<ItemId, ItemId>,
240    ) -> MergeDecision {
241        let canonical_id = self.canonical_id();
242        let items: Vec<Option<ItemByVal>> = self
243            .ids
244            .values()
245            .map(|&id| {
246                krate
247                    .get_item(id)
248                    .map(|item| normalize_item(item.to_owned(), canonical_id, remap))
249            })
250            .collect_vec();
251
252        // Items that don't exist in the crate can't be compared; if they're all missing we can
253        // still merge them tho.
254        if items.iter().all(|i| i.is_none()) {
255            return MergeDecision::Dedup;
256        }
257        let items: Vec<_> = match items.into_iter().collect::<Option<Vec<_>>>() {
258            Some(items) => items,
259            None => return MergeDecision::Skip,
260        };
261
262        if items.iter().all_equal() {
263            MergeDecision::Dedup
264        } else if self.is_function_group()
265            && items
266                .iter()
267                .map(|item| item.as_fun().unwrap())
268                .map(|d| (&d.item_meta.name, &d.generics, &d.signature))
269                .all_equal()
270        {
271            MergeDecision::Facade
272        } else {
273            MergeDecision::Skip
274        }
275    }
276
277    /// Yields `(non_canonical_id, canonical_id)` pairs for building an ID remap.
278    fn remap_entries<'a>(&'a self) -> impl Iterator<Item = (ItemId, ItemId)> + 'a {
279        self.ids.values().map(|&id| (id, self.canonical_id()))
280    }
281    fn into_remap_entries(self) -> impl Iterator<Item = (ItemId, ItemId)> {
282        let canonical_id = self.canonical_id();
283        self.ids.into_values().map(move |id| (id, canonical_id))
284    }
285
286    /// Build a façade `FunDecl` for a group of functions with matching signatures but different
287    /// bodies. The `def_id` is set to a placeholder and must be fixed up on insertion.
288    fn build_facade_decl(&self, def_id: FunDeclId, krate: &TranslatedCrate) -> FunDecl {
289        let canonical_fun_id = *self.canonical_id().as_fun().unwrap();
290        let canonical = krate.fun_decls.get(canonical_fun_id).unwrap();
291
292        let dispatch_map = self
293            .ids
294            .iter()
295            .map(|(target, &id)| {
296                let fun_decl_ref = FunDeclRef {
297                    id: *id.as_fun().unwrap(),
298                    generics: Box::new(canonical.generics.identity_args()),
299                };
300                (target.clone(), fun_decl_ref)
301            })
302            .collect();
303
304        let mut item_meta = canonical.item_meta.clone();
305        // Remove the target suffix (and do a little sanity check).
306        item_meta.name.name.pop().unwrap().as_target().unwrap();
307
308        FunDecl {
309            def_id,
310            item_meta,
311            generics: canonical.generics.clone(),
312            signature: canonical.signature.clone(),
313            src: canonical.src.clone(),
314            is_global_initializer: canonical.is_global_initializer,
315            body: Body::TargetDispatch(dispatch_map),
316        }
317    }
318}
319
320/// Normalize a name for grouping across targets; returns the target.
321fn normalize_name_for_grouping(
322    name: &Name,
323    krate: &TranslatedCrate,
324) -> Option<(Name, TargetTriple)> {
325    let (mut name, target) = name.strip_target_suffix()?;
326    for elem in &mut name.name {
327        if let PathElem::Impl(ImplElem::Trait(id)) = elem {
328            // Replace ipl block references with something that contains the implemented trait
329            // predicate instead. That way, comparing names for equality compares trait predicates
330            // instead.
331            if let Some(timpl) = krate.trait_impls.get(*id) {
332                let mut params = GenericParams::default();
333                params.trait_clauses.push(TraitParam {
334                    clause_id: TraitClauseId::ZERO,
335                    span: None,
336                    origin: PredicateOrigin::WhereClauseOnImpl,
337                    trait_: RegionBinder::empty(timpl.impl_trait.clone()),
338                });
339                *elem = PathElem::Impl(ImplElem::Ty(Box::new(Binder {
340                    params,
341                    skip_binder: Ty::mk_unit(),
342                    kind: BinderKind::Other,
343                })));
344            }
345        }
346    }
347    Some((name, target))
348}
349
350/// Orchestrates deduplication of items across compilation targets.
351struct ItemDeduplicator<'a> {
352    krate: &'a mut TranslatedCrate,
353    groups: IndexVec<TargetGroupId, TargetGroup>,
354}
355
356impl<'a> ItemDeduplicator<'a> {
357    /// Entrypoint: deduplicate items that are the same across targets.
358    pub fn dedup(krate: &'a mut TranslatedCrate, errors: &mut ErrorCtx) {
359        let groups = Self::discover_groups(krate, errors);
360        if groups.is_empty() {
361            return;
362        }
363        let mut this = Self { krate, groups };
364        let decisions = this.decide_group_mergings();
365        this.apply_merge_decisions(decisions);
366    }
367
368    /// Group items by (base_name, item_kind). Each group contains the versions of that item
369    /// across all targets where it exists.
370    fn discover_groups(
371        krate: &TranslatedCrate,
372        _errors: &mut ErrorCtx,
373    ) -> IndexVec<TargetGroupId, TargetGroup> {
374        let mut groups_map: SeqHashMap<
375            (Name, std::mem::Discriminant<ItemId>),
376            SeqHashMap<TargetTriple, ItemId>,
377        > = SeqHashMap::new();
378        for (&item_id, name) in &krate.item_names {
379            if let Some((base_name, target)) = normalize_name_for_grouping(name, krate) {
380                let key = (base_name, std::mem::discriminant(&item_id));
381                let per_target = groups_map.entry(key).or_default();
382                if per_target.contains_key(&target) {
383                    // Name collision within the same target: skip this group entirely.
384                    per_target.clear();
385                } else {
386                    per_target.insert(target, item_id);
387                }
388            }
389        }
390        // We do a fixpoint: merging a group may lead to detecting that some names are actually the
391        // same (because the names refer to impls/types).
392        loop {
393            let prev_len = groups_map.len();
394            let remap: HashMap<ItemId, ItemId> = groups_map
395                .values()
396                .filter(|ids| !ids.is_empty())
397                .cloned()
398                .map(|ids| TargetGroup { ids })
399                .flat_map(|g| g.into_remap_entries())
400                .filter(|(x, y)| x != y)
401                .collect();
402            for ((mut name, kind), ids) in mem::take(&mut groups_map) {
403                name.drive_mut(&mut IdRefMapperVisitor::new(&remap));
404                let key = (name, kind);
405                let per_target = groups_map.entry(key).or_default();
406                for (target, item_id) in ids {
407                    if per_target.contains_key(&target) {
408                        // Name collision within the same target: skip this group entirely.
409                        per_target.clear();
410                        break;
411                    } else {
412                        per_target.insert(target, item_id);
413                    }
414                }
415            }
416            // Remove empty groups (from collisions) and check for convergence.
417            groups_map.retain(|_, v| !v.is_empty());
418            if prev_len == groups_map.len() {
419                break;
420            }
421        }
422        let groups: IndexVec<TargetGroupId, TargetGroup> = groups_map
423            .into_values()
424            .map(|ids| TargetGroup { ids })
425            .collect();
426        groups
427    }
428
429    /// Decide how to merge each group. Skipped groups are not included in the output.
430    fn decide_group_mergings(&self) -> Vec<(TargetGroupId, MergeDecision)> {
431        // Start with all groups as candidates.
432        let mut candidates: Vec<(TargetGroupId, MergeDecision)> = self
433            .groups
434            .indices()
435            .map(|id| (id, MergeDecision::Skip))
436            .collect();
437
438        // Fixpoint: assume that all included groups are mapped to a single item; keep the groups
439        // that can be merged under such a mapping. Iterate until fixpoint.
440        loop {
441            let remap = self.build_remap(candidates.iter().map(|(id, _)| id));
442            let prev_len = candidates.len();
443            candidates.retain_mut(|(idx, decision)| {
444                *decision = self.groups[*idx].decide_merge(self.krate, &remap);
445                *decision != MergeDecision::Skip
446            });
447            if candidates.len() == prev_len {
448                break;
449            }
450        }
451
452        candidates
453    }
454
455    /// Build an id remap: for each candidate group, map non-canonical IDs → canonical ID.
456    fn build_remap<'b>(
457        &self,
458        candidate_indices: impl IntoIterator<Item = &'b TargetGroupId>,
459    ) -> HashMap<ItemId, ItemId> {
460        candidate_indices
461            .into_iter()
462            .flat_map(|&idx| self.groups[idx].remap_entries())
463            .filter(|(x, y)| x != y)
464            .collect()
465    }
466
467    fn apply_merge_decisions(&mut self, decisions: Vec<(TargetGroupId, MergeDecision)>) {
468        if decisions.is_empty() {
469            return;
470        }
471
472        let mut remap = HashMap::new();
473        let mut facade_decls: Vec<FunDecl> = Vec::new();
474        for &(idx, decision) in &decisions {
475            let mut group = &self.groups[idx];
476            let target_id = match decision {
477                MergeDecision::Skip => unreachable!(),
478                MergeDecision::Dedup => {
479                    self.dedup_group(idx); // takes mutable borrow; invalidates `group`
480                    group = &self.groups[idx];
481                    group.canonical_id()
482                }
483                MergeDecision::Facade => {
484                    let facade_id = self.krate.fun_decls.reserve_slot();
485                    // Insert facade decls later because the id remapping would mess up the
486                    // dispatch maps.
487                    facade_decls.push(group.build_facade_decl(facade_id, self.krate));
488                    // Mark per-target functions as target-dependent.
489                    for &id in group.ids.values() {
490                        let fun_id = *id.as_fun().unwrap();
491                        if let Some(fun_decl) = self.krate.fun_decls.get_mut(fun_id) {
492                            fun_decl.src = ItemSource::TargetDependent {
493                                dispatcher: FunDeclRef {
494                                    id: facade_id,
495                                    generics: Box::new(fun_decl.generics.identity_args()),
496                                },
497                            };
498                        }
499                    }
500                    ItemId::Fun(facade_id)
501                }
502            };
503            for &id in group.ids.values() {
504                if id != target_id {
505                    remap.insert(id, target_id);
506                }
507            }
508        }
509
510        // Remap all ids.
511        self.krate.drive_mut(&mut IdRefMapperVisitor::new(&remap));
512
513        for decl in facade_decls {
514            self.krate
515                .set_new_item_slot(ItemId::Fun(decl.def_id), ItemByVal::Fun(decl));
516        }
517    }
518
519    fn dedup_group(&mut self, idx: TargetGroupId) {
520        let group = &self.groups[idx];
521        let canonical = group.canonical_id();
522
523        // Remove the target suffix (and do a little sanity check).
524        let mut name = self.krate.item_names.get(&canonical).cloned().unwrap();
525        name.name.pop().unwrap().as_target().unwrap();
526        if let Some(mut canonical_item) = self.krate.get_item_mut(canonical) {
527            canonical_item.item_meta().name = name.clone();
528        }
529        self.krate.item_names.insert(canonical, name);
530
531        // Merge per-target layouts into the canonical type.
532        if let ItemId::Type(canonical_type_id) = canonical {
533            let layouts = group
534                .ids
535                .values()
536                .map(|&id| *id.as_type().unwrap())
537                .flat_map(|id| {
538                    self.krate
539                        .type_decls
540                        .get_mut(id)
541                        .map(|tdecl| mem::take(&mut tdecl.layout))
542                        .into_iter()
543                        .flatten()
544                })
545                .collect();
546            if let Some(dest) = self.krate.type_decls.get_mut(canonical_type_id) {
547                dest.layout = layouts;
548            }
549        }
550
551        // Remove non-canonical copies.
552        for &id in group.ids.values() {
553            if id != canonical {
554                self.krate.remove_item(id);
555            }
556        }
557    }
558}
559
560// =============================================================================================
561// Step 3: Cleanup the merged crate
562// =============================================================================================
563
564/// Recompute declaration order and run final whole-crate cleanup on the merged crate.
565fn cleanup_post_merge(ctx: &mut TransformCtx) {
566    if !ctx.options.translate_all_methods {
567        remove_unmentioned_methods(&mut ctx.translated);
568    }
569    crate::transform::add_missing_info::reorder_decls::Transform.transform_ctx(ctx);
570    if ctx.options.unbind_item_vars {
571        crate::transform::simplify_output::unbind_item_vars::Check.transform_ctx(ctx);
572    }
573}
574
575/// Emulate the behavior of our lazy method translation scheme by removing default trait methods
576/// that aren't usefully mentioned anywhere.
577fn remove_unmentioned_methods(krate: &mut TranslatedCrate) {
578    type MethodKey = (TraitDeclId, TraitMethodId);
579
580    use ReachabilityNode::*;
581    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
582    enum ReachabilityNode {
583        Root,
584        Method(MethodKey),
585        Fun(FunDeclId),
586    }
587
588    #[derive(Visitor)]
589    struct MentionedFunVisitor<F>(F);
590
591    impl<F> VisitAst for MentionedFunVisitor<F>
592    where
593        F: FnMut(ReachabilityNode),
594    {
595        fn enter_fun_decl_id(&mut self, id: &FunDeclId) {
596            (self.0)(Fun(*id));
597        }
598
599        fn enter_fn_ptr(&mut self, fn_ptr: &FnPtr) {
600            if let FnPtrKind::Trait(trait_ref, method_id) = fn_ptr.kind.as_ref() {
601                (self.0)(Method((trait_ref.trait_id(), *method_id)));
602            }
603        }
604    }
605
606    // Build a graph where the items we want to keep are reachable from the root. To start with
607    // that's all the `FunDeclId`s that aren't a method (or a target-dispatch target coming from a
608    // method), as well as all the methods without default. We end up with a graph where methods
609    // with a default may end up not reachable.
610    let graph = {
611        let mut graph: DiGraphMap<ReachabilityNode, ()> = DiGraphMap::new();
612        graph.add_node(Root);
613
614        for (fun_id, fun) in krate.fun_decls.iter_indexed() {
615            let fun_node = Fun(fun_id);
616            graph.add_node(fun_node);
617
618            if let ItemSource::TraitDecl {
619                trait_ref, item_id, ..
620            }
621            | ItemSource::TraitImpl {
622                trait_ref, item_id, ..
623            } = &fun.src
624                && let Some(&method_id) = item_id.as_method()
625            {
626                let method_key = (trait_ref.id, method_id);
627                // The method node is reachable iff any of the corresponding function nodes is.
628                graph.add_edge(Method(method_key), fun_node, ());
629                graph.add_edge(fun_node, Method(method_key), ());
630            }
631
632            match &fun.src {
633                ItemSource::TraitDecl {
634                    item_id: AssocItemId::Method(_),
635                    ..
636                }
637                | ItemSource::TraitImpl {
638                    item_id: AssocItemId::Method(_),
639                    ..
640                }
641                | ItemSource::TargetDependent { .. } => {}
642                // Functions that aren't any of the above are reachable. target-dependent functions
643                // will be reachable if their dispatcher is.
644                _ => {
645                    graph.add_edge(Root, fun_node, ());
646                }
647            }
648
649            let _ = fun.body.drive(&mut MentionedFunVisitor(|n| {
650                graph.add_edge(fun_node, n, ());
651            }));
652        }
653
654        graph
655    };
656
657    let reachable_nodes: HashSet<_> = Dfs::new(&graph, Root).iter(&graph).collect();
658
659    let mut unused_methods: HashMap<TraitDeclId, HashSet<TraitMethodId>> = HashMap::new();
660    // Iterate over unreachable nodes.
661    for n in graph.nodes().filter(|n| !reachable_nodes.contains(n)) {
662        match n {
663            Root => {}
664            Method((trait_id, method_id)) => {
665                unused_methods
666                    .entry(trait_id)
667                    .or_default()
668                    .insert(method_id);
669            }
670            Fun(fun_id) => {
671                // Remove unreachable functions.
672                krate.remove_item(ItemId::Fun(fun_id));
673            }
674        }
675    }
676    if unused_methods.is_empty() {
677        return;
678    }
679
680    // Remove unreachable methods from both decls and impls.
681    for trait_impl in krate.trait_impls.iter_mut() {
682        let trait_id = trait_impl.impl_trait.id;
683        if let Some(unused_methods) = unused_methods.get(&trait_id) {
684            trait_impl
685                .methods
686                .retain(|method_id, _| !unused_methods.contains(&method_id));
687        }
688    }
689    for (trait_id, unused_methods) in unused_methods {
690        if let Some(trait_decl) = krate.trait_decls.get_mut(trait_id) {
691            trait_decl
692                .methods
693                .retain(|method_id, _| !unused_methods.contains(&method_id));
694        }
695    }
696}
697
698// =============================================================================================
699// Utilities
700// =============================================================================================
701
702/// Normalize an item for cross-target comparison.
703fn normalize_item(
704    mut item: ItemByVal,
705    canonical_id: ItemId,
706    remap: &HashMap<ItemId, ItemId>,
707) -> ItemByVal {
708    item.as_mut().drive_mut(&mut IdRefMapperVisitor::new(remap));
709    item.as_mut().set_id(canonical_id);
710    item.as_mut().dyn_visit_mut(|name: &mut Name| {
711        name.name
712            .retain(|elem| !matches!(elem, PathElem::Target(_)))
713    });
714
715    strip_unstable_attributes(&mut item);
716    // Ignore source text and spans: if the items are otherwise identical, it's ok to just pick one
717    // of the identical instances wrt spans/source.
718    item.as_mut()
719        .dyn_visit_mut(|span: &mut Span| *span = Span::dummy());
720    item.as_mut().item_meta().source_text = None;
721    if let ItemByVal::Type(ty_decl) = &mut item {
722        // Layouts are allowed to differ per-target.
723        ty_decl.layout.clear();
724    }
725    item
726}
727
728/// Strip attributes such as `rustc_diagnostic_item` whose arguments can vary across targets in a
729/// way that doesn't matter to us.
730fn strip_unstable_attributes(item: &mut ItemByVal) {
731    fn strip_in_vec(attr_info: &mut AttrInfo) {
732        attr_info.attributes.retain(
733            |attr| !matches!(attr, Attribute::Unknown(attr) if attr.path.starts_with("rustc_")),
734        );
735    }
736
737    strip_in_vec(&mut item.as_mut().item_meta().attr_info);
738
739    if let ItemByVal::TraitDecl(d) = item {
740        for method in &mut d.methods {
741            strip_in_vec(&mut method.skip_binder.item_meta.attr_info);
742        }
743        for cst in &mut d.consts {
744            strip_in_vec(&mut cst.attr_info);
745        }
746        for ty in &mut d.types {
747            strip_in_vec(&mut ty.skip_binder.attr_info);
748        }
749    }
750}
751
752/// Visitor that remaps references to the given items.
753#[derive(Visitor)]
754struct IdRefMapperVisitor<'a> {
755    map: &'a HashMap<ItemId, ItemId>,
756}
757
758impl<'a> IdRefMapperVisitor<'a> {
759    fn new(remap: &'a HashMap<ItemId, ItemId>) -> Self {
760        Self { map: remap }
761    }
762
763    fn map<Id>(&self, id: &mut Id)
764    where
765        Id: Copy,
766        Id: Into<ItemId>,
767        ItemId: TryInto<Id, Error: Debug>,
768    {
769        if let Some(&new) = self.map.get(&(*id).into()) {
770            *id = new.try_into().unwrap();
771        }
772    }
773}
774
775impl VisitAstMut for IdRefMapperVisitor<'_> {
776    fn enter_type_decl_ref(&mut self, x: &mut TypeDeclRef) {
777        if let TypeId::Adt(id) = &mut x.id {
778            self.map(id);
779        }
780    }
781    fn enter_fun_decl_ref(&mut self, x: &mut FunDeclRef) {
782        self.map(&mut x.id);
783    }
784    fn enter_global_decl_ref(&mut self, x: &mut GlobalDeclRef) {
785        self.map(&mut x.id);
786    }
787    fn enter_trait_decl_ref(&mut self, x: &mut TraitDeclRef) {
788        self.map(&mut x.id);
789    }
790    fn enter_trait_impl_ref(&mut self, x: &mut TraitImplRef) {
791        self.map(&mut x.id);
792    }
793
794    fn enter_projection_elem(&mut self, x: &mut ProjectionElem) {
795        if let ProjectionElem::Field(proj, _) = x
796            && let FieldProjKind::Adt(id, _) = proj
797        {
798            self.map(id);
799        }
800    }
801    fn enter_fn_ptr(&mut self, x: &mut FnPtr) {
802        if let FnPtrKind::Fun(FunId::Regular(id)) = x.kind.as_mut() {
803            self.map(id)
804        }
805    }
806    fn enter_fun_decl(&mut self, x: &mut FunDecl) {
807        if let Some(id) = &mut x.is_global_initializer {
808            self.map(id);
809        }
810    }
811    fn enter_impl_elem(&mut self, x: &mut ImplElem) {
812        if let ImplElem::Trait(id) = x {
813            self.map(id);
814        }
815    }
816    fn enter_binder<T: AstVisitable>(&mut self, x: &mut Binder<T>) {
817        match &mut x.kind {
818            BinderKind::TraitType(trait_id, _) | BinderKind::TraitMethod(trait_id, _) => {
819                self.map(trait_id);
820            }
821            BinderKind::InherentImplBlock | BinderKind::Dyn | BinderKind::Other => {}
822        }
823    }
824}