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