Skip to main content

rustc_passes/
eii.rs

1//! Checks necessary for externally implementable items:
2//! Are all items implemented etc.?
3
4use std::iter;
5
6use rustc_data_structures::fx::FxIndexMap;
7use rustc_hir::attrs::{EiiDecl, EiiImpl};
8use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
9use rustc_middle::error::DuplicateEiiImpls;
10use rustc_middle::ty::TyCtxt;
11use rustc_session::config::CrateType;
12
13use crate::diagnostics::EiiWithoutImpl;
14
15#[derive(#[automatically_derived]
impl ::core::clone::Clone for CheckingMode {
    #[inline]
    fn clone(&self) -> CheckingMode { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for CheckingMode { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for CheckingMode {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                CheckingMode::CheckDuplicates => "CheckDuplicates",
                CheckingMode::CheckExistence => "CheckExistence",
            })
    }
}Debug)]
16enum CheckingMode {
17    CheckDuplicates,
18    CheckExistence,
19}
20
21fn get_checking_mode(tcx: TyCtxt<'_>) -> CheckingMode {
22    // if any of the crate types is not rlib, we must check for existence.
23    if tcx.crate_types().iter().any(|i| !#[allow(non_exhaustive_omitted_patterns)] match i {
    CrateType::Rlib => true,
    _ => false,
}matches!(i, CrateType::Rlib)) {
24        CheckingMode::CheckExistence
25    } else {
26        CheckingMode::CheckDuplicates
27    }
28}
29
30/// Checks for a given crate, what EIIs need to be generated in it.
31/// This is usually a small subset of all EIIs.
32///
33/// EII implementations come in two varieties: explicit and default.
34/// This query is called once for every crate, to check whether there aren't any duplicate explicit implementations.
35/// A duplicate may be caused by an implementation in the current crate,
36/// though it's also entirely possible that the source is two dependencies with an explicit implementation.
37/// Those work fine on their own but the combination of the two is a conflict.
38///
39/// However, if the current crate is a "root" crate, one that generates a final artifact like a binary,
40/// then we check one more thing, namely that every EII actually has an implementation, either default or not.
41/// If one EII has no implementation, that's an error at that point.
42///
43/// These two behaviors are implemented using `CheckingMode`.
44pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, (): ()) {
45    let checking_mode = get_checking_mode(tcx);
46
47    #[derive(#[automatically_derived]
impl ::core::fmt::Debug for FoundImpl {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "FoundImpl",
            "imp", &self.imp, "impl_crate", &&self.impl_crate)
    }
}Debug)]
48    struct FoundImpl {
49        imp: EiiImpl,
50        impl_crate: CrateNum,
51    }
52
53    #[derive(#[automatically_derived]
impl ::core::fmt::Debug for FoundEii {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "FoundEii",
            "decl", &self.decl, "decl_crate", &self.decl_crate, "impls",
            &&self.impls)
    }
}Debug)]
54    struct FoundEii {
55        decl: EiiDecl,
56        decl_crate: CrateNum,
57        impls: FxIndexMap<DefId, FoundImpl>,
58    }
59
60    let mut eiis = FxIndexMap::<DefId, FoundEii>::default();
61
62    // collect all the EII declarations, and possibly implementations from all descendent crates
63    for &cnum in tcx.crates(()).iter().chain(iter::once(&LOCAL_CRATE)) {
64        // get the eiis for the crate we're currently looking at
65        let crate_eiis = tcx.externally_implementable_items(cnum);
66
67        // update or insert the corresponding entries
68        for (did, (decl, impls)) in crate_eiis {
69            eiis.entry(*did)
70                .or_insert_with(|| FoundEii {
71                    decl: *decl,
72                    decl_crate: cnum,
73                    impls: Default::default(),
74                })
75                .impls
76                .extend(
77                    impls
78                        .into_iter()
79                        .map(|(did, i)| (*did, FoundImpl { imp: *i, impl_crate: cnum })),
80                );
81        }
82    }
83
84    // now we have all eiis! For each of them, choose one we want to actually generate.
85    for (foreign_item, FoundEii { decl, decl_crate, impls }) in eiis {
86        let mut default_impls = Vec::new();
87        let mut explicit_impls = Vec::new();
88
89        for (impl_did, FoundImpl { imp, impl_crate }) in impls {
90            if imp.is_default {
91                default_impls.push((impl_did, impl_crate));
92            } else {
93                explicit_impls.push((impl_did, impl_crate));
94            }
95        }
96
97        // more than one explicit implementation (across all crates)
98        // is instantly an error.
99        if explicit_impls.len() > 1 {
100            tcx.dcx().emit_err(DuplicateEiiImpls {
101                name: decl.name.name,
102                first_span: tcx.def_span(explicit_impls[0].0),
103                first_crate: tcx.crate_name(explicit_impls[0].1),
104                second_span: tcx.def_span(explicit_impls[1].0),
105                second_crate: tcx.crate_name(explicit_impls[1].1),
106
107                help: (),
108
109                additional_crates: (explicit_impls.len() > 2).then_some(()),
110                num_additional_crates: explicit_impls.len() - 2,
111                additional_crate_names: explicit_impls[2..]
112                    .iter()
113                    .map(|i| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}`", tcx.crate_name(i.1)))
    })format!("`{}`", tcx.crate_name(i.1)))
114                    .collect::<Vec<_>>()
115                    .join(", "),
116            });
117        }
118
119        if default_impls.len() > 1 {
120            let decl_span = tcx.def_ident_span(foreign_item).unwrap();
121            tcx.dcx().span_delayed_bug(decl_span, "multiple not supported right now");
122        }
123
124        let (local_impl, is_default) =
125            // note, for a single crate we never need to generate both a default and an explicit implementation.
126            // In that case, generating the explicit implementation is enough!
127            match (checking_mode, explicit_impls.first(), default_impls.first()) {
128                // If we find an explicit implementation, it's instantly the chosen implementation.
129                (_, Some((explicit, _)), _) => (explicit, false),
130                // if we find a default implementation, we can emit it but the alias should be weak
131                (_, _, Some((deflt, _))) => (deflt, true),
132
133                // if we find no explicit implementation,
134                // that's fine if we're only checking for duplicates.
135                // The existence will be checked somewhere else in a crate downstream.
136                (CheckingMode::CheckDuplicates, None, _) => continue,
137
138                // We have a target to generate, but no impl to put in it. error!
139                (CheckingMode::CheckExistence, None, None) => {
140                    tcx.dcx().emit_err(EiiWithoutImpl {
141                        current_crate_name: tcx.crate_name(LOCAL_CRATE),
142                        decl_crate_name: tcx.crate_name(decl_crate),
143                        // FIXME: shouldn't call `item_name`
144                        name: decl.name.name,
145                        kind: tcx.def_kind(decl.foreign_item).descr(decl.foreign_item),
146                        span: decl.name.span,
147                        help: (),
148                    });
149
150                    continue;
151                }
152            };
153
154        // if it's not local, who cares about generating it.
155        // That's the local crates' responsibility
156        let Some(chosen_impl) = local_impl.as_local() else {
157            continue;
158        };
159
160        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_passes/src/eii.rs:160",
                        "rustc_passes::eii", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_passes/src/eii.rs"),
                        ::tracing_core::__macro_support::Option::Some(160u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_passes::eii"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("generating EII {0:?} (default={1})",
                                                    chosen_impl, is_default) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};tracing::debug!("generating EII {chosen_impl:?} (default={is_default})");
161    }
162}