1 use super::OverlapError;
2
3 use crate::traits;
4 use rustc_errors::ErrorGuaranteed;
5 use rustc_hir::def_id::DefId;
6 use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams};
7 use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
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<'tcx> {
19 pub error: OverlapError<'tcx>,
20 pub kind: FutureCompatOverlapErrorKind,
21 }
22
23 /// The result of attempting to insert an impl into a group of children.
24 #[derive(Debug)]
25 enum Inserted<'tcx> {
26 /// The impl was inserted as a new child in this group of children.
27 BecameNewSibling(Option<FutureCompatOverlapError<'tcx>>),
28
29 /// The impl should replace existing impls [X1, ..], because the impl specializes X1, X2, etc.
30 ReplaceChildren(Vec<DefId>),
31
32 /// The impl is a specialization of an existing child.
33 ShouldRecurseOn(DefId),
34 }
35
36 trait ChildrenExt<'tcx> {
insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId)37 fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId);
remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId)38 fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId);
39
insert( &mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId, simplified_self: Option<SimplifiedType>, overlap_mode: OverlapMode, ) -> Result<Inserted<'tcx>, OverlapError<'tcx>>40 fn insert(
41 &mut self,
42 tcx: TyCtxt<'tcx>,
43 impl_def_id: DefId,
44 simplified_self: Option<SimplifiedType>,
45 overlap_mode: OverlapMode,
46 ) -> Result<Inserted<'tcx>, OverlapError<'tcx>>;
47 }
48
49 impl<'tcx> ChildrenExt<'tcx> for Children {
50 /// Insert an impl into this set of children without comparing to any existing impls.
insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId)51 fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
52 let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder();
53 if let Some(st) =
54 fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsCandidateKey)
55 {
56 debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st);
57 self.non_blanket_impls.entry(st).or_default().push(impl_def_id)
58 } else {
59 debug!("insert_blindly: impl_def_id={:?} st=None", impl_def_id);
60 self.blanket_impls.push(impl_def_id)
61 }
62 }
63
64 /// Removes an impl from this set of children. Used when replacing
65 /// an impl with a parent. The impl must be present in the list of
66 /// children already.
remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId)67 fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
68 let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder();
69 let vec: &mut Vec<DefId>;
70 if let Some(st) =
71 fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsCandidateKey)
72 {
73 debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st);
74 vec = self.non_blanket_impls.get_mut(&st).unwrap();
75 } else {
76 debug!("remove_existing: impl_def_id={:?} st=None", impl_def_id);
77 vec = &mut self.blanket_impls;
78 }
79
80 let index = vec.iter().position(|d| *d == impl_def_id).unwrap();
81 vec.remove(index);
82 }
83
84 /// Attempt to insert an impl into this set of children, while comparing for
85 /// specialization relationships.
86 #[instrument(level = "debug", skip(self, tcx), ret)]
insert( &mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId, simplified_self: Option<SimplifiedType>, overlap_mode: OverlapMode, ) -> Result<Inserted<'tcx>, OverlapError<'tcx>>87 fn insert(
88 &mut self,
89 tcx: TyCtxt<'tcx>,
90 impl_def_id: DefId,
91 simplified_self: Option<SimplifiedType>,
92 overlap_mode: OverlapMode,
93 ) -> Result<Inserted<'tcx>, OverlapError<'tcx>> {
94 let mut last_lint = None;
95 let mut replace_children = Vec::new();
96
97 let possible_siblings = match simplified_self {
98 Some(st) => PotentialSiblings::Filtered(filtered_children(self, st)),
99 None => PotentialSiblings::Unfiltered(iter_children(self)),
100 };
101
102 for possible_sibling in possible_siblings {
103 debug!(?possible_sibling);
104
105 let create_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>| {
106 let trait_ref = overlap.impl_header.trait_ref.unwrap();
107 let self_ty = trait_ref.self_ty();
108
109 OverlapError {
110 with_impl: possible_sibling,
111 trait_ref,
112 // Only report the `Self` type if it has at least
113 // some outer concrete shell; otherwise, it's
114 // not adding much information.
115 self_ty: self_ty.has_concrete_skeleton().then_some(self_ty),
116 intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
117 involves_placeholder: overlap.involves_placeholder,
118 }
119 };
120
121 let report_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>,
122 last_lint: &mut _| {
123 // Found overlap, but no specialization; error out or report future-compat warning.
124
125 // Do we *still* get overlap if we disable the future-incompatible modes?
126 let should_err = traits::overlapping_impls(
127 tcx,
128 possible_sibling,
129 impl_def_id,
130 traits::SkipLeakCheck::default(),
131 overlap_mode,
132 )
133 .is_some();
134
135 let error = create_overlap_error(overlap);
136
137 if should_err {
138 Err(error)
139 } else {
140 *last_lint = Some(FutureCompatOverlapError {
141 error,
142 kind: FutureCompatOverlapErrorKind::LeakCheck,
143 });
144
145 Ok((false, false))
146 }
147 };
148
149 let last_lint_mut = &mut last_lint;
150 let (le, ge) = traits::overlapping_impls(
151 tcx,
152 possible_sibling,
153 impl_def_id,
154 traits::SkipLeakCheck::Yes,
155 overlap_mode,
156 )
157 .map_or(Ok((false, false)), |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 { report_overlap_error(overlap, last_lint_mut) } else { Ok((le, ge)) }
178 })?;
179
180 if le && !ge {
181 debug!(
182 "descending as child of TraitRef {:?}",
183 tcx.impl_trait_ref(possible_sibling).unwrap().subst_identity()
184 );
185
186 // The impl specializes `possible_sibling`.
187 return Ok(Inserted::ShouldRecurseOn(possible_sibling));
188 } else if ge && !le {
189 debug!(
190 "placing as parent of TraitRef {:?}",
191 tcx.impl_trait_ref(possible_sibling).unwrap().subst_identity()
192 );
193
194 replace_children.push(possible_sibling);
195 } else {
196 // Either there's no overlap, or the overlap was already reported by
197 // `overlap_error`.
198 }
199 }
200
201 if !replace_children.is_empty() {
202 return Ok(Inserted::ReplaceChildren(replace_children));
203 }
204
205 // No overlap with any potential siblings, so add as a new sibling.
206 debug!("placing as new sibling");
207 self.insert_blindly(tcx, impl_def_id);
208 Ok(Inserted::BecameNewSibling(last_lint))
209 }
210 }
211
iter_children(children: &mut Children) -> impl Iterator<Item = DefId> + '_212 fn iter_children(children: &mut Children) -> impl Iterator<Item = DefId> + '_ {
213 let nonblanket = children.non_blanket_impls.iter().flat_map(|(_, v)| v.iter());
214 children.blanket_impls.iter().chain(nonblanket).cloned()
215 }
216
filtered_children( children: &mut Children, st: SimplifiedType, ) -> impl Iterator<Item = DefId> + '_217 fn filtered_children(
218 children: &mut Children,
219 st: SimplifiedType,
220 ) -> impl Iterator<Item = DefId> + '_ {
221 let nonblanket = children.non_blanket_impls.entry(st).or_default().iter();
222 children.blanket_impls.iter().chain(nonblanket).cloned()
223 }
224
225 // A custom iterator used by Children::insert
226 enum PotentialSiblings<I, J>
227 where
228 I: Iterator<Item = DefId>,
229 J: Iterator<Item = DefId>,
230 {
231 Unfiltered(I),
232 Filtered(J),
233 }
234
235 impl<I, J> Iterator for PotentialSiblings<I, J>
236 where
237 I: Iterator<Item = DefId>,
238 J: Iterator<Item = DefId>,
239 {
240 type Item = DefId;
241
next(&mut self) -> Option<Self::Item>242 fn next(&mut self) -> Option<Self::Item> {
243 match *self {
244 PotentialSiblings::Unfiltered(ref mut iter) => iter.next(),
245 PotentialSiblings::Filtered(ref mut iter) => iter.next(),
246 }
247 }
248 }
249
250 pub trait GraphExt<'tcx> {
251 /// Insert a local impl into the specialization graph. If an existing impl
252 /// conflicts with it (has overlap, but neither specializes the other),
253 /// information about the area of overlap is returned in the `Err`.
insert( &mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId, overlap_mode: OverlapMode, ) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>>254 fn insert(
255 &mut self,
256 tcx: TyCtxt<'tcx>,
257 impl_def_id: DefId,
258 overlap_mode: OverlapMode,
259 ) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>>;
260
261 /// Insert cached metadata mapping from a child impl back to its parent.
record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId)262 fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId);
263 }
264
265 impl<'tcx> GraphExt<'tcx> for Graph {
266 /// Insert a local impl into the specialization graph. If an existing impl
267 /// conflicts with it (has overlap, but neither specializes the other),
268 /// information about the area of overlap is returned in the `Err`.
insert( &mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId, overlap_mode: OverlapMode, ) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>>269 fn insert(
270 &mut self,
271 tcx: TyCtxt<'tcx>,
272 impl_def_id: DefId,
273 overlap_mode: OverlapMode,
274 ) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>> {
275 assert!(impl_def_id.is_local());
276
277 // FIXME: use `EarlyBinder` in `self.children`
278 let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder();
279 let trait_def_id = trait_ref.def_id;
280
281 debug!(
282 "insert({:?}): inserting TraitRef {:?} into specialization graph",
283 impl_def_id, trait_ref
284 );
285
286 // If the reference itself contains an earlier error (e.g., due to a
287 // resolution failure), then we just insert the impl at the top level of
288 // the graph and claim that there's no overlap (in order to suppress
289 // bogus errors).
290 if trait_ref.references_error() {
291 debug!(
292 "insert: inserting dummy node for erroneous TraitRef {:?}, \
293 impl_def_id={:?}, trait_def_id={:?}",
294 trait_ref, impl_def_id, trait_def_id
295 );
296
297 self.parent.insert(impl_def_id, trait_def_id);
298 self.children.entry(trait_def_id).or_default().insert_blindly(tcx, impl_def_id);
299 return Ok(None);
300 }
301
302 let mut parent = trait_def_id;
303 let mut last_lint = None;
304 let simplified =
305 fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsCandidateKey);
306
307 // Descend the specialization tree, where `parent` is the current parent node.
308 loop {
309 use self::Inserted::*;
310
311 let insert_result = self.children.entry(parent).or_default().insert(
312 tcx,
313 impl_def_id,
314 simplified,
315 overlap_mode,
316 )?;
317
318 match insert_result {
319 BecameNewSibling(opt_lint) => {
320 last_lint = opt_lint;
321 break;
322 }
323 ReplaceChildren(grand_children_to_be) => {
324 // We currently have
325 //
326 // P
327 // |
328 // G
329 //
330 // and we are inserting the impl N. We want to make it:
331 //
332 // P
333 // |
334 // N
335 // |
336 // G
337
338 // Adjust P's list of children: remove G and then add N.
339 {
340 let siblings = self.children.get_mut(&parent).unwrap();
341 for &grand_child_to_be in &grand_children_to_be {
342 siblings.remove_existing(tcx, grand_child_to_be);
343 }
344 siblings.insert_blindly(tcx, impl_def_id);
345 }
346
347 // Set G's parent to N and N's parent to P.
348 for &grand_child_to_be in &grand_children_to_be {
349 self.parent.insert(grand_child_to_be, impl_def_id);
350 }
351 self.parent.insert(impl_def_id, parent);
352
353 // Add G as N's child.
354 for &grand_child_to_be in &grand_children_to_be {
355 self.children
356 .entry(impl_def_id)
357 .or_default()
358 .insert_blindly(tcx, grand_child_to_be);
359 }
360 break;
361 }
362 ShouldRecurseOn(new_parent) => {
363 parent = new_parent;
364 }
365 }
366 }
367
368 self.parent.insert(impl_def_id, parent);
369 Ok(last_lint)
370 }
371
372 /// Insert cached metadata mapping from a child impl back to its parent.
record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId)373 fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId) {
374 if self.parent.insert(child, parent).is_some() {
375 bug!(
376 "When recording an impl from the crate store, information about its parent \
377 was already present."
378 );
379 }
380
381 self.children.entry(parent).or_default().insert_blindly(tcx, child);
382 }
383 }
384
385 /// Locate the definition of an associated type in the specialization hierarchy,
386 /// starting from the given impl.
assoc_def( tcx: TyCtxt<'_>, impl_def_id: DefId, assoc_def_id: DefId, ) -> Result<LeafDef, ErrorGuaranteed>387 pub(crate) fn assoc_def(
388 tcx: TyCtxt<'_>,
389 impl_def_id: DefId,
390 assoc_def_id: DefId,
391 ) -> Result<LeafDef, ErrorGuaranteed> {
392 let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
393 let trait_def = tcx.trait_def(trait_def_id);
394
395 // This function may be called while we are still building the
396 // specialization graph that is queried below (via TraitDef::ancestors()),
397 // so, in order to avoid unnecessary infinite recursion, we manually look
398 // for the associated item at the given impl.
399 // If there is no such item in that impl, this function will fail with a
400 // cycle error if the specialization graph is currently being built.
401 if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_def_id) {
402 let item = tcx.associated_item(impl_item_id);
403 let impl_node = Node::Impl(impl_def_id);
404 return Ok(LeafDef {
405 item,
406 defining_node: impl_node,
407 finalizing_node: if item.defaultness(tcx).is_default() {
408 None
409 } else {
410 Some(impl_node)
411 },
412 });
413 }
414
415 let ancestors = trait_def.ancestors(tcx, impl_def_id)?;
416 if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) {
417 Ok(assoc_item)
418 } else {
419 // This is saying that neither the trait nor
420 // the impl contain a definition for this
421 // associated type. Normally this situation
422 // could only arise through a compiler bug --
423 // if the user wrote a bad item name, it
424 // should have failed in astconv.
425 bug!(
426 "No associated type `{}` for {}",
427 tcx.item_name(assoc_def_id),
428 tcx.def_path_str(impl_def_id)
429 )
430 }
431 }
432