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