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