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            runtime_checks,
154            item_names,
155            assoc_item_names,
156            short_names: _, // TODO
157            files: _,       // Done above
158            type_decls,
159            fun_decls,
160            global_decls,
161            trait_decls,
162            trait_impls,
163            ordered_decls: _, // Recomputed on the merged crate
164        } = krate;
165        if self.merged.translated.crate_name.is_empty() {
166            self.merged.translated.crate_name = crate_name;
167            self.merged.translated.runtime_checks = runtime_checks;
168        } else {
169            assert_eq!(
170                self.merged.translated.runtime_checks, runtime_checks,
171                "cannot merge crates with different runtime-check settings"
172            );
173        }
174        self.merged
175            .translated
176            .target_information
177            .extend(target_information);
178        self.merged.translated.item_names.extend(item_names);
179        self.merged
180            .translated
181            .assoc_item_names
182            .extend_from_other(assoc_item_names);
183        self.merged
184            .translated
185            .type_decls
186            .extend_from_other(type_decls);
187        self.merged
188            .translated
189            .fun_decls
190            .extend_from_other(fun_decls);
191        self.merged
192            .translated
193            .global_decls
194            .extend_from_other(global_decls);
195        self.merged
196            .translated
197            .trait_decls
198            .extend_from_other(trait_decls);
199        self.merged
200            .translated
201            .trait_impls
202            .extend_from_other(trait_impls);
203    }
204}
205
206// =============================================================================================
207// Step 2: Deduplicates items that don't differ across targets and create façades for
208// target-dependent functions
209// =============================================================================================
210
211generate_index_type!(TargetGroupId, "TargetGroup");
212
213/// A set of items that share the same base name and item kind.
214/// These are candidates for merging into a single cross-target item.
215struct TargetGroup {
216    ids: SeqHashMap<TargetTriple, ItemId>,
217}
218
219/// How a `TargetGroup` should be merged.
220#[derive(Debug, Copy, Clone, PartialEq, Eq)]
221enum MergeDecision {
222    /// Don't merge this group.
223    Skip,
224    /// All the items are the same; merge them into one.
225    Dedup,
226    /// Function signatures match but bodies diffe; create a façade that dispatches to the
227    /// per-target items.
228    Facade,
229}
230
231/// Compares items modulo the target-specific differences we want to ignore.
232struct ItemComparer<'a> {
233    remap: &'a HashMap<ItemId, ItemId>,
234}
235
236impl Visitor for ItemComparer<'_> {
237    type Break = ();
238}
239
240impl<'a, T: AstVisitable> derive_generic_visitor::VisitTwo<'a, T> for ItemComparer<'_> {
241    fn visit(&mut self, left: &'a T, right: &'a T) -> ControlFlow<Self::Break> {
242        ZipAst::visit(self, left, right)
243    }
244}
245
246impl ItemComparer<'_> {
247    fn compare_items(&mut self, left: ItemRef<'_>, right: ItemRef<'_>) -> ControlFlow<()> {
248        left.drive_two(&right, self)
249    }
250
251    fn compare_fun_interface(&mut self, left: &FunDecl, right: &FunDecl) -> ControlFlow<()> {
252        self.visit(&left.item_meta.name, &right.item_meta.name)?;
253        self.visit(&left.generics, &right.generics)?;
254        self.visit(&left.signature, &right.signature)
255    }
256
257    fn compare_ids<Id: Copy + Into<ItemId>>(&self, left: &Id, right: &Id) -> ControlFlow<()> {
258        let remap = |id: &Id| {
259            let id = (*id).into();
260            self.remap.get(&id).copied().unwrap_or(id)
261        };
262        if remap(left) == remap(right) {
263            ControlFlow::Continue(())
264        } else {
265            ControlFlow::Break(())
266        }
267    }
268
269    fn compare_iters<'a, T: AstVisitable + 'a>(
270        &mut self,
271        left: impl Iterator<Item = &'a T>,
272        right: impl Iterator<Item = &'a T>,
273    ) -> ControlFlow<()> {
274        derive_generic_visitor::drive_iter_two(left, right, self)
275    }
276}
277
278// Use lockstep visitation for "equality modulo" comparison.
279impl ZipAst for ItemComparer<'_> {
280    fn visit_type_decl_id(
281        &mut self,
282        left: &TypeDeclId,
283        right: &TypeDeclId,
284    ) -> ControlFlow<Self::Break> {
285        self.compare_ids(left, right)
286    }
287
288    fn visit_fun_decl_id(
289        &mut self,
290        left: &FunDeclId,
291        right: &FunDeclId,
292    ) -> ControlFlow<Self::Break> {
293        self.compare_ids(left, right)
294    }
295
296    fn visit_global_decl_id(
297        &mut self,
298        left: &GlobalDeclId,
299        right: &GlobalDeclId,
300    ) -> ControlFlow<Self::Break> {
301        self.compare_ids(left, right)
302    }
303
304    fn visit_trait_decl_id(
305        &mut self,
306        left: &TraitDeclId,
307        right: &TraitDeclId,
308    ) -> ControlFlow<Self::Break> {
309        self.compare_ids(left, right)
310    }
311
312    fn visit_trait_impl_id(
313        &mut self,
314        left: &TraitImplId,
315        right: &TraitImplId,
316    ) -> ControlFlow<Self::Break> {
317        self.compare_ids(left, right)
318    }
319
320    fn visit_name(&mut self, left: &Name, right: &Name) -> ControlFlow<Self::Break> {
321        let without_target = |elem: &&PathElem| !matches!(elem, PathElem::Target(_));
322        self.compare_iters(
323            left.name.iter().filter(without_target),
324            right.name.iter().filter(without_target),
325        )
326    }
327
328    fn visit_span(&mut self, _left: &Span, _right: &Span) -> ControlFlow<Self::Break> {
329        ControlFlow::Continue(())
330    }
331
332    fn visit_attr_info(&mut self, left: &AttrInfo, right: &AttrInfo) -> ControlFlow<Self::Break> {
333        let AttrInfo {
334            attributes: left_attributes,
335            inline: left_inline,
336            rename: left_rename,
337            public: left_public,
338        } = left;
339        let AttrInfo {
340            attributes: right_attributes,
341            inline: right_inline,
342            rename: right_rename,
343            public: right_public,
344        } = right;
345
346        let is_stable = |attr: &&Attribute| !matches!(attr, Attribute::Unknown(attr) if attr.path.starts_with("rustc_"));
347        self.compare_iters(
348            left_attributes.iter().filter(is_stable),
349            right_attributes.iter().filter(is_stable),
350        )?;
351        self.visit(left_inline, right_inline)?;
352        self.visit(left_rename, right_rename)?;
353        self.visit(left_public, right_public)
354    }
355
356    fn visit_item_meta(&mut self, left: &ItemMeta, right: &ItemMeta) -> ControlFlow<Self::Break> {
357        let ItemMeta {
358            name: left_name,
359            span: left_span,
360            // Source text isn't relevant to cross-target identity.
361            source_text: _,
362            attr_info: left_attr_info,
363            is_local: left_is_local,
364            started_from: left_started_from,
365            is_extern: left_is_extern,
366            opacity: left_opacity,
367            lang_item: left_lang_item,
368            diagnostic_item: left_diagnostic_item,
369            has_errors: left_has_errors,
370        } = left;
371        let ItemMeta {
372            name: right_name,
373            span: right_span,
374            source_text: _,
375            attr_info: right_attr_info,
376            is_local: right_is_local,
377            started_from: right_started_from,
378            is_extern: right_is_extern,
379            opacity: right_opacity,
380            lang_item: right_lang_item,
381            diagnostic_item: right_diagnostic_item,
382            has_errors: right_has_errors,
383        } = right;
384
385        self.visit(left_name, right_name)?;
386        self.visit(left_span, right_span)?;
387        self.visit(left_attr_info, right_attr_info)?;
388        self.visit(left_is_local, right_is_local)?;
389        self.visit(left_started_from, right_started_from)?;
390        self.visit(left_is_extern, right_is_extern)?;
391        self.visit(left_opacity, right_opacity)?;
392        self.visit(left_lang_item, right_lang_item)?;
393        self.visit(left_diagnostic_item, right_diagnostic_item)?;
394        self.visit(left_has_errors, right_has_errors)?;
395        ControlFlow::Continue(())
396    }
397
398    fn visit_type_decl(&mut self, left: &TypeDecl, right: &TypeDecl) -> ControlFlow<Self::Break> {
399        let TypeDecl {
400            def_id: left_def_id,
401            item_meta: left_item_meta,
402            generics: left_generics,
403            src: left_src,
404            kind: left_kind,
405            // Layouts are allowed to differ per target.
406            layout: _,
407            ptr_metadata: left_ptr_metadata,
408            marker_traits: left_marker_traits,
409        } = left;
410        let TypeDecl {
411            def_id: right_def_id,
412            item_meta: right_item_meta,
413            generics: right_generics,
414            src: right_src,
415            kind: right_kind,
416            layout: _,
417            ptr_metadata: right_ptr_metadata,
418            marker_traits: right_marker_traits,
419        } = right;
420
421        self.visit(left_def_id, right_def_id)?;
422        self.visit(left_item_meta, right_item_meta)?;
423        self.visit(left_generics, right_generics)?;
424        self.visit(left_src, right_src)?;
425        self.visit(left_kind, right_kind)?;
426        self.visit(left_ptr_metadata, right_ptr_metadata)?;
427        self.visit(left_marker_traits, right_marker_traits)
428    }
429}
430
431impl TargetGroup {
432    /// Deterministically chosen representative id.
433    fn canonical_id(&self) -> ItemId {
434        self.ids.values().next().copied().unwrap()
435    }
436
437    /// Whether this group is a group of function items.
438    fn is_function_group(&self) -> bool {
439        self.canonical_id().is_fun()
440    }
441
442    /// Compare the items of this group under the provided id mapping.
443    fn decide_merge(
444        &self,
445        krate: &TranslatedCrate,
446        remap: &HashMap<ItemId, ItemId>,
447    ) -> MergeDecision {
448        let items: Vec<Option<ItemRef<'_>>> = self
449            .ids
450            .values()
451            .map(|&id| krate.get_item(id))
452            .collect_vec();
453
454        // Items that don't exist in the crate can't be compared; if they're all missing we can
455        // still merge them tho.
456        if items.iter().all(|i| i.is_none()) {
457            return MergeDecision::Dedup;
458        }
459        let items: Vec<_> = match items.into_iter().collect::<Option<Vec<_>>>() {
460            Some(items) => items,
461            None => return MergeDecision::Skip,
462        };
463
464        let mut comparer = ItemComparer { remap };
465        if items
466            .iter()
467            .tuple_windows()
468            .all(|(&left, &right)| comparer.compare_items(left, right).is_continue())
469        {
470            MergeDecision::Dedup
471        } else if self.is_function_group()
472            && items
473                .iter()
474                .map(|item| item.as_fun().unwrap())
475                .tuple_windows()
476                .all(|(left, right)| comparer.compare_fun_interface(left, right).is_continue())
477        {
478            MergeDecision::Facade
479        } else {
480            MergeDecision::Skip
481        }
482    }
483
484    /// Yields `(non_canonical_id, canonical_id)` pairs for building an ID remap.
485    fn remap_entries<'a>(&'a self) -> impl Iterator<Item = (ItemId, ItemId)> + 'a {
486        let canonical_id = self.canonical_id();
487        self.ids.values().map(move |&id| (id, canonical_id))
488    }
489    fn into_remap_entries(self) -> impl Iterator<Item = (ItemId, ItemId)> {
490        let canonical_id = self.canonical_id();
491        self.ids.into_values().map(move |id| (id, canonical_id))
492    }
493
494    /// Build a façade `FunDecl` for a group of functions with matching signatures but different
495    /// bodies.
496    fn build_facade_decl(&self, def_id: FunDeclId, krate: &TranslatedCrate) -> FunDecl {
497        let canonical_fun_id = *self.canonical_id().as_fun().unwrap();
498        let canonical = krate.fun_decls.get(canonical_fun_id).unwrap();
499
500        let dispatch_map = self
501            .ids
502            .iter()
503            .map(|(target, &id)| {
504                let fun_decl_ref = FunDeclRef {
505                    id: *id.as_fun().unwrap(),
506                    generics: Box::new(canonical.generics.identity_args()),
507                };
508                (target.clone(), fun_decl_ref)
509            })
510            .collect();
511
512        let mut item_meta = canonical.item_meta.clone();
513        // Remove the target suffix (and do a little sanity check).
514        item_meta.name.name.pop().unwrap().as_target().unwrap();
515
516        FunDecl {
517            def_id,
518            item_meta,
519            generics: canonical.generics.clone(),
520            signature: canonical.signature.clone(),
521            src: canonical.src.clone(),
522            body: Body::TargetDispatch(dispatch_map),
523        }
524    }
525}
526
527/// Normalize a name for grouping across targets; returns the target.
528fn normalize_name_for_grouping(
529    name: &Name,
530    krate: &TranslatedCrate,
531) -> Option<(Name, TargetTriple)> {
532    let (mut name, target) = name.strip_target_suffix()?;
533    for elem in &mut name.name {
534        if let PathElem::Impl(ImplElem::Trait(id)) = elem {
535            // Replace impl block references with something that contains the implemented trait
536            // predicate instead. That way, comparing names for equality compares trait predicates
537            // instead.
538            if let Some(timpl) = krate.trait_impls.get(*id) {
539                let mut params = GenericParams::default();
540                params.trait_clauses.push(TraitParam {
541                    clause_id: TraitClauseId::ZERO,
542                    span: None,
543                    origin: PredicateOrigin::WhereClauseOnImpl,
544                    trait_: RegionBinder::empty(timpl.impl_trait.clone()),
545                });
546                *elem = PathElem::Impl(ImplElem::Ty(Box::new(Binder {
547                    params,
548                    skip_binder: Ty::mk_unit(),
549                    kind: BinderKind::Other,
550                })));
551            }
552        }
553    }
554    Some((name, target))
555}
556
557/// Orchestrates deduplication of items across compilation targets.
558struct ItemDeduplicator<'a> {
559    krate: &'a mut TranslatedCrate,
560    groups: IndexVec<TargetGroupId, TargetGroup>,
561}
562
563impl<'a> ItemDeduplicator<'a> {
564    /// Entrypoint: deduplicate items that are the same across targets.
565    pub fn dedup(krate: &'a mut TranslatedCrate, errors: &mut ErrorCtx) {
566        let groups = Self::discover_groups(krate, errors);
567        if groups.is_empty() {
568            return;
569        }
570        let mut this = Self { krate, groups };
571        let decisions = this.decide_group_mergings();
572        this.apply_merge_decisions(decisions);
573    }
574
575    /// Group items by (base_name, item_kind). Each group contains the versions of that item
576    /// across all targets where it exists.
577    fn discover_groups(
578        krate: &TranslatedCrate,
579        _errors: &mut ErrorCtx,
580    ) -> IndexVec<TargetGroupId, TargetGroup> {
581        let mut groups_map: SeqHashMap<
582            (Name, std::mem::Discriminant<ItemId>),
583            SeqHashMap<TargetTriple, ItemId>,
584        > = SeqHashMap::new();
585        for (&item_id, name) in &krate.item_names {
586            if let Some((base_name, target)) = normalize_name_for_grouping(name, krate) {
587                let key = (base_name, std::mem::discriminant(&item_id));
588                let per_target = groups_map.entry(key).or_default();
589                if per_target.contains_key(&target) {
590                    // Name collision within the same target: skip this group entirely.
591                    per_target.clear();
592                } else {
593                    per_target.insert(target, item_id);
594                }
595            }
596        }
597        // We do a fixpoint: merging a group may lead to detecting that some names are actually the
598        // same (because the names refer to impls/types).
599        loop {
600            let prev_len = groups_map.len();
601            let remap: HashMap<ItemId, ItemId> = groups_map
602                .values()
603                .filter(|ids| !ids.is_empty())
604                .cloned()
605                .map(|ids| TargetGroup { ids })
606                .flat_map(|g| g.into_remap_entries())
607                .filter(|(x, y)| x != y)
608                .collect();
609            for ((mut name, kind), ids) in mem::take(&mut groups_map) {
610                name.drive_mut(&mut IdRefMapperVisitor::new(&remap));
611                let key = (name, kind);
612                let per_target = groups_map.entry(key).or_default();
613                for (target, item_id) in ids {
614                    if per_target.contains_key(&target) {
615                        // Name collision within the same target: skip this group entirely.
616                        per_target.clear();
617                        break;
618                    } else {
619                        per_target.insert(target, item_id);
620                    }
621                }
622            }
623            // Remove empty groups (from collisions) and check for convergence.
624            groups_map.retain(|_, v| !v.is_empty());
625            if prev_len == groups_map.len() {
626                break;
627            }
628        }
629        let groups: IndexVec<TargetGroupId, TargetGroup> = groups_map
630            .into_values()
631            .map(|ids| TargetGroup { ids })
632            .collect();
633        groups
634    }
635
636    /// Decide how to merge each group. Skipped groups are not included in the output.
637    fn decide_group_mergings(&self) -> Vec<(TargetGroupId, MergeDecision)> {
638        // Start with all groups as candidates.
639        let mut candidates: Vec<(TargetGroupId, MergeDecision)> = self
640            .groups
641            .indices()
642            .map(|id| (id, MergeDecision::Skip))
643            .collect();
644
645        // Fixpoint: assume that all included groups are mapped to a single item; keep the groups
646        // that can be merged under such a mapping. Iterate until fixpoint.
647        loop {
648            let remap = self.build_remap(candidates.iter().map(|(id, _)| id));
649            let prev_len = candidates.len();
650            candidates.retain_mut(|(idx, decision)| {
651                *decision = self.groups[*idx].decide_merge(self.krate, &remap);
652                *decision != MergeDecision::Skip
653            });
654            if candidates.len() == prev_len {
655                break;
656            }
657        }
658
659        candidates
660    }
661
662    /// Build an id remap: for each candidate group, map non-canonical IDs → canonical ID.
663    fn build_remap<'b>(
664        &self,
665        candidate_indices: impl IntoIterator<Item = &'b TargetGroupId>,
666    ) -> HashMap<ItemId, ItemId> {
667        candidate_indices
668            .into_iter()
669            .flat_map(|&idx| self.groups[idx].remap_entries())
670            .filter(|(x, y)| x != y)
671            .collect()
672    }
673
674    fn apply_merge_decisions(&mut self, decisions: Vec<(TargetGroupId, MergeDecision)>) {
675        if decisions.is_empty() {
676            return;
677        }
678
679        let mut remap = HashMap::new();
680        let mut facade_decls: Vec<FunDecl> = Vec::new();
681        for &(idx, decision) in &decisions {
682            let group = &self.groups[idx];
683            let target_id = match decision {
684                MergeDecision::Skip => unreachable!(),
685                MergeDecision::Dedup => {
686                    let canonical_id = group.canonical_id();
687                    self.dedup_group(idx);
688                    canonical_id
689                }
690                MergeDecision::Facade => {
691                    let facade_id = self.krate.fun_decls.reserve_slot();
692                    // Insert facade decls later because the id remapping would mess up the
693                    // dispatch maps.
694                    facade_decls.push(group.build_facade_decl(facade_id, self.krate));
695                    // Mark per-target functions as target-dependent.
696                    for &id in group.ids.values() {
697                        let fun_id = *id.as_fun().unwrap();
698                        if let Some(fun_decl) = self.krate.fun_decls.get_mut(fun_id) {
699                            fun_decl.src = FunSource::TargetDependent {
700                                dispatcher: FunDeclRef {
701                                    id: facade_id,
702                                    generics: Box::new(fun_decl.generics.identity_args()),
703                                },
704                            };
705                        }
706                    }
707                    ItemId::Fun(facade_id)
708                }
709            };
710            let group = &self.groups[idx];
711            for &id in group.ids.values() {
712                if id != target_id {
713                    remap.insert(id, target_id);
714                }
715            }
716        }
717
718        // Remap all ids.
719        self.krate.drive_mut(&mut IdRefMapperVisitor::new(&remap));
720
721        for decl in facade_decls {
722            self.krate
723                .set_new_item_slot(ItemId::Fun(decl.def_id), ItemByVal::Fun(decl));
724        }
725    }
726
727    fn dedup_group(&mut self, idx: TargetGroupId) {
728        let group = &self.groups[idx];
729        let canonical = group.canonical_id();
730
731        // Remove the target suffix (and do a little sanity check).
732        let mut name = self.krate.item_names.get(&canonical).cloned().unwrap();
733        name.name.pop().unwrap().as_target().unwrap();
734        if let Some(mut canonical_item) = self.krate.get_item_mut(canonical) {
735            canonical_item.item_meta().name = name.clone();
736        }
737        self.krate.item_names.insert(canonical, name);
738
739        // Merge per-target layouts into the canonical type.
740        if let ItemId::Type(canonical_type_id) = canonical {
741            let layouts = group
742                .ids
743                .values()
744                .map(|&id| *id.as_type().unwrap())
745                .flat_map(|id| {
746                    self.krate
747                        .type_decls
748                        .get_mut(id)
749                        .map(|tdecl| mem::take(&mut tdecl.layout))
750                        .into_iter()
751                        .flatten()
752                })
753                .collect();
754            if let Some(dest) = self.krate.type_decls.get_mut(canonical_type_id) {
755                dest.layout = layouts;
756            }
757        }
758
759        // Remove non-canonical copies.
760        for &id in group.ids.values() {
761            if id != canonical {
762                self.krate.remove_item(id);
763            }
764        }
765    }
766}
767
768// =============================================================================================
769// Step 3: Cleanup the merged crate
770// =============================================================================================
771
772/// Recompute declaration order and run final whole-crate cleanup on the merged crate.
773fn cleanup_post_merge(ctx: &mut TransformCtx) {
774    if !ctx.options.translate_all_methods {
775        remove_unmentioned_methods(&mut ctx.translated);
776    }
777    crate::transform::add_missing_info::reorder_decls::Transform.transform_ctx(ctx);
778    if ctx.options.unbind_item_vars {
779        crate::transform::simplify_output::unbind_item_vars::Check.transform_ctx(ctx);
780    }
781}
782
783/// Emulate the behavior of our lazy method translation scheme by removing default trait methods
784/// that aren't usefully mentioned anywhere.
785fn remove_unmentioned_methods(krate: &mut TranslatedCrate) {
786    type MethodKey = (TraitDeclId, TraitMethodId);
787
788    use ReachabilityNode::*;
789    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
790    enum ReachabilityNode {
791        Root,
792        Method(MethodKey),
793        Fun(FunDeclId),
794    }
795
796    #[derive(Visitor)]
797    struct MentionedFunVisitor<F>(F);
798
799    impl<F> VisitAst for MentionedFunVisitor<F>
800    where
801        F: FnMut(ReachabilityNode),
802    {
803        fn enter_fun_decl_id(&mut self, id: &FunDeclId) {
804            (self.0)(Fun(*id));
805        }
806
807        fn enter_fn_ptr(&mut self, fn_ptr: &FnPtr) {
808            if let FnPtrKind::Trait(trait_ref, method_id) = fn_ptr.kind.as_ref() {
809                (self.0)(Method((trait_ref.trait_id(), *method_id)));
810            }
811        }
812    }
813
814    // Build a graph where the items we want to keep are reachable from the root. To start with
815    // that's all the `FunDeclId`s that aren't a method (or a target-dispatch target coming from a
816    // method), as well as all the methods without default. We end up with a graph where methods
817    // with a default may end up not reachable.
818    let graph = {
819        let mut graph: DiGraphMap<ReachabilityNode, ()> = DiGraphMap::new();
820        graph.add_node(Root);
821
822        for (fun_id, fun) in krate.fun_decls.iter_indexed() {
823            let fun_node = Fun(fun_id);
824            graph.add_node(fun_node);
825
826            if let FunSource::TraitDefault {
827                trait_ref, item_id, ..
828            }
829            | FunSource::TraitImpl {
830                trait_ref, item_id, ..
831            } = &fun.src
832            {
833                let method_key = (trait_ref.id, *item_id);
834                // The method node is reachable iff any of the corresponding function nodes is.
835                graph.add_edge(Method(method_key), fun_node, ());
836                graph.add_edge(fun_node, Method(method_key), ());
837            }
838
839            match &fun.src {
840                FunSource::TraitDefault { .. }
841                | FunSource::TraitImpl { .. }
842                | FunSource::TargetDependent { .. } => {}
843                // Functions that aren't any of the above are reachable. target-dependent functions
844                // will be reachable if their dispatcher is.
845                _ => {
846                    graph.add_edge(Root, fun_node, ());
847                }
848            }
849
850            let _ = fun.body.drive(&mut MentionedFunVisitor(|n| {
851                graph.add_edge(fun_node, n, ());
852            }));
853        }
854
855        for trait_decl in krate.trait_decls.iter() {
856            for (method_id, method) in trait_decl.methods.iter_enumerated() {
857                if method.skip_binder.default.is_none() {
858                    graph.add_edge(Root, Method((trait_decl.def_id, method_id)), ());
859                }
860            }
861        }
862
863        graph
864    };
865
866    let reachable_nodes: HashSet<_> = Dfs::new(&graph, Root).iter(&graph).collect();
867
868    let mut unused_methods: HashMap<TraitDeclId, HashSet<TraitMethodId>> = HashMap::new();
869    // Iterate over unreachable nodes.
870    for n in graph.nodes().filter(|n| !reachable_nodes.contains(n)) {
871        match n {
872            Root => {}
873            Method((trait_id, method_id)) => {
874                unused_methods
875                    .entry(trait_id)
876                    .or_default()
877                    .insert(method_id);
878            }
879            Fun(fun_id) => {
880                // Remove unreachable functions.
881                krate.remove_item(ItemId::Fun(fun_id));
882            }
883        }
884    }
885    if unused_methods.is_empty() {
886        return;
887    }
888
889    // Remove unreachable methods from both decls and impls.
890    for trait_impl in krate.trait_impls.iter_mut() {
891        let trait_id = trait_impl.impl_trait.id;
892        if let Some(unused_methods) = unused_methods.get(&trait_id) {
893            trait_impl
894                .methods
895                .retain(|method_id, _| !unused_methods.contains(&method_id));
896        }
897    }
898    for (trait_id, unused_methods) in unused_methods {
899        if let Some(trait_decl) = krate.trait_decls.get_mut(trait_id) {
900            trait_decl
901                .methods
902                .retain(|method_id, _| !unused_methods.contains(&method_id));
903        }
904    }
905}
906
907// =============================================================================================
908// Utilities
909// =============================================================================================
910
911/// Visitor that remaps references to the given items.
912#[derive(Visitor)]
913struct IdRefMapperVisitor<'a> {
914    map: &'a HashMap<ItemId, ItemId>,
915}
916
917impl<'a> IdRefMapperVisitor<'a> {
918    fn new(remap: &'a HashMap<ItemId, ItemId>) -> Self {
919        Self { map: remap }
920    }
921
922    fn map<Id>(&self, id: &mut Id)
923    where
924        Id: Copy,
925        Id: Into<ItemId>,
926        ItemId: TryInto<Id, Error: Debug>,
927    {
928        if let Some(&new) = self.map.get(&(*id).into()) {
929            *id = new.try_into().unwrap();
930        }
931    }
932}
933
934impl VisitAstMut for IdRefMapperVisitor<'_> {
935    fn enter_type_decl_ref(&mut self, x: &mut TypeDeclRef) {
936        self.map(&mut x.id);
937    }
938    fn enter_fun_decl_ref(&mut self, x: &mut FunDeclRef) {
939        self.map(&mut x.id);
940    }
941    fn enter_global_decl_ref(&mut self, x: &mut GlobalDeclRef) {
942        self.map(&mut x.id);
943    }
944    fn enter_trait_decl_ref(&mut self, x: &mut TraitDeclRef) {
945        self.map(&mut x.id);
946    }
947    fn enter_trait_impl_ref(&mut self, x: &mut TraitImplRef) {
948        self.map(&mut x.id);
949    }
950
951    fn enter_fn_ptr(&mut self, x: &mut FnPtr) {
952        if let FnPtrKind::Fun(id) = x.kind.as_mut() {
953            self.map(id)
954        }
955    }
956    fn enter_impl_elem(&mut self, x: &mut ImplElem) {
957        if let ImplElem::Trait(id) = x {
958            self.map(id);
959        }
960    }
961    fn enter_binder<T: AstVisitable>(&mut self, x: &mut Binder<T>) {
962        match &mut x.kind {
963            BinderKind::TraitType(trait_id, _) | BinderKind::TraitMethod(trait_id, _) => {
964                self.map(trait_id);
965            }
966            BinderKind::InherentImplBlock | BinderKind::Dyn | BinderKind::Other => {}
967        }
968    }
969}