]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
New upstream version 1.48.0~beta.8+dfsg1
[rustc.git] / compiler / rustc_trait_selection / src / traits / specialize / specialization_graph.rs
1 use super::OverlapError;
2
3 use crate::traits;
4 use rustc_hir::def_id::DefId;
5 use rustc_middle::ty::fast_reject::{self, SimplifiedType};
6 use rustc_middle::ty::print::with_no_trimmed_paths;
7 use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
8
9 pub use rustc_middle::traits::specialization_graph::*;
10
11 #[derive(Copy, Clone, Debug)]
12 pub enum FutureCompatOverlapErrorKind {
13 Issue33140,
14 LeakCheck,
15 }
16
17 #[derive(Debug)]
18 pub struct FutureCompatOverlapError {
19 pub error: OverlapError,
20 pub kind: FutureCompatOverlapErrorKind,
21 }
22
23 /// The result of attempting to insert an impl into a group of children.
24 enum Inserted {
25 /// The impl was inserted as a new child in this group of children.
26 BecameNewSibling(Option<FutureCompatOverlapError>),
27
28 /// The impl should replace existing impls [X1, ..], because the impl specializes X1, X2, etc.
29 ReplaceChildren(Vec<DefId>),
30
31 /// The impl is a specialization of an existing child.
32 ShouldRecurseOn(DefId),
33 }
34
35 trait ChildrenExt {
36 fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId);
37 fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId);
38
39 fn insert(
40 &mut self,
41 tcx: TyCtxt<'tcx>,
42 impl_def_id: DefId,
43 simplified_self: Option<SimplifiedType>,
44 ) -> Result<Inserted, OverlapError>;
45 }
46
47 impl ChildrenExt for Children {
48 /// Insert an impl into this set of children without comparing to any existing impls.
49 fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
50 let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
51 if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) {
52 debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st);
53 self.nonblanket_impls.entry(st).or_default().push(impl_def_id)
54 } else {
55 debug!("insert_blindly: impl_def_id={:?} st=None", impl_def_id);
56 self.blanket_impls.push(impl_def_id)
57 }
58 }
59
60 /// Removes an impl from this set of children. Used when replacing
61 /// an impl with a parent. The impl must be present in the list of
62 /// children already.
63 fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
64 let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
65 let vec: &mut Vec<DefId>;
66 if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) {
67 debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st);
68 vec = self.nonblanket_impls.get_mut(&st).unwrap();
69 } else {
70 debug!("remove_existing: impl_def_id={:?} st=None", impl_def_id);
71 vec = &mut self.blanket_impls;
72 }
73
74 let index = vec.iter().position(|d| *d == impl_def_id).unwrap();
75 vec.remove(index);
76 }
77
78 /// Attempt to insert an impl into this set of children, while comparing for
79 /// specialization relationships.
80 fn insert(
81 &mut self,
82 tcx: TyCtxt<'tcx>,
83 impl_def_id: DefId,
84 simplified_self: Option<SimplifiedType>,
85 ) -> Result<Inserted, OverlapError> {
86 let mut last_lint = None;
87 let mut replace_children = Vec::new();
88
89 debug!("insert(impl_def_id={:?}, simplified_self={:?})", impl_def_id, simplified_self,);
90
91 let possible_siblings = match simplified_self {
92 Some(st) => PotentialSiblings::Filtered(filtered_children(self, st)),
93 None => PotentialSiblings::Unfiltered(iter_children(self)),
94 };
95
96 for possible_sibling in possible_siblings {
97 debug!(
98 "insert: impl_def_id={:?}, simplified_self={:?}, possible_sibling={:?}",
99 impl_def_id, simplified_self, possible_sibling,
100 );
101
102 let create_overlap_error = |overlap: traits::coherence::OverlapResult<'_>| {
103 let trait_ref = overlap.impl_header.trait_ref.unwrap();
104 let self_ty = trait_ref.self_ty();
105
106 // FIXME: should postpone string formatting until we decide to actually emit.
107 with_no_trimmed_paths(|| OverlapError {
108 with_impl: possible_sibling,
109 trait_desc: trait_ref.print_only_trait_path().to_string(),
110 // Only report the `Self` type if it has at least
111 // some outer concrete shell; otherwise, it's
112 // not adding much information.
113 self_desc: if self_ty.has_concrete_skeleton() {
114 Some(self_ty.to_string())
115 } else {
116 None
117 },
118 intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
119 involves_placeholder: overlap.involves_placeholder,
120 })
121 };
122
123 let report_overlap_error = |overlap: traits::coherence::OverlapResult<'_>,
124 last_lint: &mut _| {
125 // Found overlap, but no specialization; error out or report future-compat warning.
126
127 // Do we *still* get overlap if we disable the future-incompatible modes?
128 let should_err = traits::overlapping_impls(
129 tcx,
130 possible_sibling,
131 impl_def_id,
132 traits::SkipLeakCheck::default(),
133 |_| true,
134 || false,
135 );
136
137 let error = create_overlap_error(overlap);
138
139 if should_err {
140 Err(error)
141 } else {
142 *last_lint = Some(FutureCompatOverlapError {
143 error,
144 kind: FutureCompatOverlapErrorKind::LeakCheck,
145 });
146
147 Ok((false, false))
148 }
149 };
150
151 let last_lint_mut = &mut last_lint;
152 let (le, ge) = traits::overlapping_impls(
153 tcx,
154 possible_sibling,
155 impl_def_id,
156 traits::SkipLeakCheck::Yes,
157 |overlap| {
158 if let Some(overlap_kind) =
159 tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling)
160 {
161 match overlap_kind {
162 ty::ImplOverlapKind::Permitted { marker: _ } => {}
163 ty::ImplOverlapKind::Issue33140 => {
164 *last_lint_mut = Some(FutureCompatOverlapError {
165 error: create_overlap_error(overlap),
166 kind: FutureCompatOverlapErrorKind::Issue33140,
167 });
168 }
169 }
170
171 return Ok((false, false));
172 }
173
174 let le = tcx.specializes((impl_def_id, possible_sibling));
175 let ge = tcx.specializes((possible_sibling, impl_def_id));
176
177 if le == ge {
178 report_overlap_error(overlap, last_lint_mut)
179 } else {
180 Ok((le, ge))
181 }
182 },
183 || Ok((false, false)),
184 )?;
185
186 if le && !ge {
187 debug!(
188 "descending as child of TraitRef {:?}",
189 tcx.impl_trait_ref(possible_sibling).unwrap()
190 );
191
192 // The impl specializes `possible_sibling`.
193 return Ok(Inserted::ShouldRecurseOn(possible_sibling));
194 } else if ge && !le {
195 debug!(
196 "placing as parent of TraitRef {:?}",
197 tcx.impl_trait_ref(possible_sibling).unwrap()
198 );
199
200 replace_children.push(possible_sibling);
201 } else {
202 // Either there's no overlap, or the overlap was already reported by
203 // `overlap_error`.
204 }
205 }
206
207 if !replace_children.is_empty() {
208 return Ok(Inserted::ReplaceChildren(replace_children));
209 }
210
211 // No overlap with any potential siblings, so add as a new sibling.
212 debug!("placing as new sibling");
213 self.insert_blindly(tcx, impl_def_id);
214 Ok(Inserted::BecameNewSibling(last_lint))
215 }
216 }
217
218 fn iter_children(children: &mut Children) -> impl Iterator<Item = DefId> + '_ {
219 let nonblanket = children.nonblanket_impls.iter_mut().flat_map(|(_, v)| v.iter());
220 children.blanket_impls.iter().chain(nonblanket).cloned()
221 }
222
223 fn filtered_children(
224 children: &mut Children,
225 st: SimplifiedType,
226 ) -> impl Iterator<Item = DefId> + '_ {
227 let nonblanket = children.nonblanket_impls.entry(st).or_default().iter();
228 children.blanket_impls.iter().chain(nonblanket).cloned()
229 }
230
231 // A custom iterator used by Children::insert
232 enum PotentialSiblings<I, J>
233 where
234 I: Iterator<Item = DefId>,
235 J: Iterator<Item = DefId>,
236 {
237 Unfiltered(I),
238 Filtered(J),
239 }
240
241 impl<I, J> Iterator for PotentialSiblings<I, J>
242 where
243 I: Iterator<Item = DefId>,
244 J: Iterator<Item = DefId>,
245 {
246 type Item = DefId;
247
248 fn next(&mut self) -> Option<Self::Item> {
249 match *self {
250 PotentialSiblings::Unfiltered(ref mut iter) => iter.next(),
251 PotentialSiblings::Filtered(ref mut iter) => iter.next(),
252 }
253 }
254 }
255
256 pub trait GraphExt {
257 /// Insert a local impl into the specialization graph. If an existing impl
258 /// conflicts with it (has overlap, but neither specializes the other),
259 /// information about the area of overlap is returned in the `Err`.
260 fn insert(
261 &mut self,
262 tcx: TyCtxt<'tcx>,
263 impl_def_id: DefId,
264 ) -> Result<Option<FutureCompatOverlapError>, OverlapError>;
265
266 /// Insert cached metadata mapping from a child impl back to its parent.
267 fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId);
268 }
269
270 impl GraphExt for Graph {
271 /// Insert a local impl into the specialization graph. If an existing impl
272 /// conflicts with it (has overlap, but neither specializes the other),
273 /// information about the area of overlap is returned in the `Err`.
274 fn insert(
275 &mut self,
276 tcx: TyCtxt<'tcx>,
277 impl_def_id: DefId,
278 ) -> Result<Option<FutureCompatOverlapError>, OverlapError> {
279 assert!(impl_def_id.is_local());
280
281 let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
282 let trait_def_id = trait_ref.def_id;
283
284 debug!(
285 "insert({:?}): inserting TraitRef {:?} into specialization graph",
286 impl_def_id, trait_ref
287 );
288
289 // If the reference itself contains an earlier error (e.g., due to a
290 // resolution failure), then we just insert the impl at the top level of
291 // the graph and claim that there's no overlap (in order to suppress
292 // bogus errors).
293 if trait_ref.references_error() {
294 debug!(
295 "insert: inserting dummy node for erroneous TraitRef {:?}, \
296 impl_def_id={:?}, trait_def_id={:?}",
297 trait_ref, impl_def_id, trait_def_id
298 );
299
300 self.parent.insert(impl_def_id, trait_def_id);
301 self.children.entry(trait_def_id).or_default().insert_blindly(tcx, impl_def_id);
302 return Ok(None);
303 }
304
305 let mut parent = trait_def_id;
306 let mut last_lint = None;
307 let simplified = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false);
308
309 // Descend the specialization tree, where `parent` is the current parent node.
310 loop {
311 use self::Inserted::*;
312
313 let insert_result =
314 self.children.entry(parent).or_default().insert(tcx, impl_def_id, simplified)?;
315
316 match insert_result {
317 BecameNewSibling(opt_lint) => {
318 last_lint = opt_lint;
319 break;
320 }
321 ReplaceChildren(grand_children_to_be) => {
322 // We currently have
323 //
324 // P
325 // |
326 // G
327 //
328 // and we are inserting the impl N. We want to make it:
329 //
330 // P
331 // |
332 // N
333 // |
334 // G
335
336 // Adjust P's list of children: remove G and then add N.
337 {
338 let siblings = self.children.get_mut(&parent).unwrap();
339 for &grand_child_to_be in &grand_children_to_be {
340 siblings.remove_existing(tcx, grand_child_to_be);
341 }
342 siblings.insert_blindly(tcx, impl_def_id);
343 }
344
345 // Set G's parent to N and N's parent to P.
346 for &grand_child_to_be in &grand_children_to_be {
347 self.parent.insert(grand_child_to_be, impl_def_id);
348 }
349 self.parent.insert(impl_def_id, parent);
350
351 // Add G as N's child.
352 for &grand_child_to_be in &grand_children_to_be {
353 self.children
354 .entry(impl_def_id)
355 .or_default()
356 .insert_blindly(tcx, grand_child_to_be);
357 }
358 break;
359 }
360 ShouldRecurseOn(new_parent) => {
361 parent = new_parent;
362 }
363 }
364 }
365
366 self.parent.insert(impl_def_id, parent);
367 Ok(last_lint)
368 }
369
370 /// Insert cached metadata mapping from a child impl back to its parent.
371 fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId) {
372 if self.parent.insert(child, parent).is_some() {
373 bug!(
374 "When recording an impl from the crate store, information about its parent \
375 was already present."
376 );
377 }
378
379 self.children.entry(parent).or_default().insert_blindly(tcx, child);
380 }
381 }