Skip to content

Commit 4234adf

Browse files
committed
Auto merge of #52546 - nikomatsakis:issue-52050, r=pnkfelix
do not overwrite child def-id in place but rather remove/insert When inserting a node N into the tree of impls, we sometimes find than an existing node C should be replaced with N. We used to overwrite C in place with the new def-id N -- but since the lists of def-ids are separated by simplified type, that could lead to N being inserted in the wrong place. This meant we might miss conflicts. We are now not trying to be so smart -- we remove C and then add N later. Fixes #52050 r? @aturon -- do you still remember this code at all? :)
2 parents d754582 + 4fd5aed commit 4234adf

File tree

3 files changed

+121
-22
lines changed

3 files changed

+121
-22
lines changed

src/librustc/traits/coherence.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ where
5959
F1: FnOnce(OverlapResult<'_>) -> R,
6060
F2: FnOnce() -> R,
6161
{
62-
debug!("impl_can_satisfy(\
62+
debug!("overlapping_impls(\
6363
impl1_def_id={:?}, \
6464
impl2_def_id={:?},
6565
intercrate_mode={:?})",

src/librustc/traits/specialize/specialization_graph.rs

+78-21
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ enum Inserted {
7373
/// The impl was inserted as a new child in this group of children.
7474
BecameNewSibling(Option<OverlapError>),
7575

76-
/// The impl replaced an existing impl that specializes it.
77-
Replaced(DefId),
76+
/// The impl should replace an existing impl X, because the impl specializes X.
77+
ReplaceChild(DefId),
7878

7979
/// The impl is a specialization of an existing child.
8080
ShouldRecurseOn(DefId),
@@ -94,12 +94,34 @@ impl<'a, 'gcx, 'tcx> Children {
9494
impl_def_id: DefId) {
9595
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
9696
if let Some(sty) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) {
97+
debug!("insert_blindly: impl_def_id={:?} sty={:?}", impl_def_id, sty);
9798
self.nonblanket_impls.entry(sty).or_insert(vec![]).push(impl_def_id)
9899
} else {
100+
debug!("insert_blindly: impl_def_id={:?} sty=None", impl_def_id);
99101
self.blanket_impls.push(impl_def_id)
100102
}
101103
}
102104

105+
/// Remove an impl from this set of children. Used when replacing
106+
/// an impl with a parent. The impl must be present in the list of
107+
/// children already.
108+
fn remove_existing(&mut self,
109+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
110+
impl_def_id: DefId) {
111+
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
112+
let vec: &mut Vec<DefId>;
113+
if let Some(sty) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) {
114+
debug!("remove_existing: impl_def_id={:?} sty={:?}", impl_def_id, sty);
115+
vec = self.nonblanket_impls.get_mut(&sty).unwrap();
116+
} else {
117+
debug!("remove_existing: impl_def_id={:?} sty=None", impl_def_id);
118+
vec = &mut self.blanket_impls;
119+
}
120+
121+
let index = vec.iter().position(|d| *d == impl_def_id).unwrap();
122+
vec.remove(index);
123+
}
124+
103125
/// Attempt to insert an impl into this set of children, while comparing for
104126
/// specialization relationships.
105127
fn insert(&mut self,
@@ -110,11 +132,22 @@ impl<'a, 'gcx, 'tcx> Children {
110132
{
111133
let mut last_lint = None;
112134

113-
for slot in match simplified_self {
114-
Some(sty) => self.filtered_mut(sty),
115-
None => self.iter_mut(),
135+
debug!(
136+
"insert(impl_def_id={:?}, simplified_self={:?})",
137+
impl_def_id,
138+
simplified_self,
139+
);
140+
141+
for possible_sibling in match simplified_self {
142+
Some(sty) => self.filtered(sty),
143+
None => self.iter(),
116144
} {
117-
let possible_sibling = *slot;
145+
debug!(
146+
"insert: impl_def_id={:?}, simplified_self={:?}, possible_sibling={:?}",
147+
impl_def_id,
148+
simplified_self,
149+
possible_sibling,
150+
);
118151

119152
let overlap_error = |overlap: traits::coherence::OverlapResult| {
120153
// overlap, but no specialization; error out
@@ -168,9 +201,7 @@ impl<'a, 'gcx, 'tcx> Children {
168201
debug!("placing as parent of TraitRef {:?}",
169202
tcx.impl_trait_ref(possible_sibling).unwrap());
170203

171-
// possible_sibling specializes the impl
172-
*slot = impl_def_id;
173-
return Ok(Inserted::Replaced(possible_sibling));
204+
return Ok(Inserted::ReplaceChild(possible_sibling));
174205
} else {
175206
if !tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) {
176207
traits::overlapping_impls(
@@ -193,15 +224,14 @@ impl<'a, 'gcx, 'tcx> Children {
193224
Ok(Inserted::BecameNewSibling(last_lint))
194225
}
195226

196-
fn iter_mut(&'a mut self) -> Box<dyn Iterator<Item = &'a mut DefId> + 'a> {
197-
let nonblanket = self.nonblanket_impls.iter_mut().flat_map(|(_, v)| v.iter_mut());
198-
Box::new(self.blanket_impls.iter_mut().chain(nonblanket))
227+
fn iter(&mut self) -> Box<dyn Iterator<Item = DefId> + '_> {
228+
let nonblanket = self.nonblanket_impls.iter_mut().flat_map(|(_, v)| v.iter());
229+
Box::new(self.blanket_impls.iter().chain(nonblanket).cloned())
199230
}
200231

201-
fn filtered_mut(&'a mut self, sty: SimplifiedType)
202-
-> Box<dyn Iterator<Item = &'a mut DefId> + 'a> {
203-
let nonblanket = self.nonblanket_impls.entry(sty).or_insert(vec![]).iter_mut();
204-
Box::new(self.blanket_impls.iter_mut().chain(nonblanket))
232+
fn filtered(&mut self, sty: SimplifiedType) -> Box<dyn Iterator<Item = DefId> + '_> {
233+
let nonblanket = self.nonblanket_impls.entry(sty).or_insert(vec![]).iter();
234+
Box::new(self.blanket_impls.iter().chain(nonblanket).cloned())
205235
}
206236
}
207237

@@ -259,11 +289,38 @@ impl<'a, 'gcx, 'tcx> Graph {
259289
last_lint = opt_lint;
260290
break;
261291
}
262-
Replaced(new_child) => {
263-
self.parent.insert(new_child, impl_def_id);
264-
let mut new_children = Children::new();
265-
new_children.insert_blindly(tcx, new_child);
266-
self.children.insert(impl_def_id, new_children);
292+
ReplaceChild(grand_child_to_be) => {
293+
// We currently have
294+
//
295+
// P
296+
// |
297+
// G
298+
//
299+
// and we are inserting the impl N. We want to make it:
300+
//
301+
// P
302+
// |
303+
// N
304+
// |
305+
// G
306+
307+
// Adjust P's list of children: remove G and then add N.
308+
{
309+
let siblings = self.children
310+
.get_mut(&parent)
311+
.unwrap();
312+
siblings.remove_existing(tcx, grand_child_to_be);
313+
siblings.insert_blindly(tcx, impl_def_id);
314+
}
315+
316+
// Set G's parent to N and N's parent to P
317+
self.parent.insert(grand_child_to_be, impl_def_id);
318+
self.parent.insert(impl_def_id, parent);
319+
320+
// Add G as N's child.
321+
let mut grand_children = Children::new();
322+
grand_children.insert_blindly(tcx, grand_child_to_be);
323+
self.children.insert(impl_def_id, grand_children);
267324
break;
268325
}
269326
ShouldRecurseOn(new_parent) => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(specialization)]
12+
13+
// Regression test for #52050: when inserting the blanket impl `I`
14+
// into the tree, we had to replace the child node for `Foo`, which
15+
// led to the struture of the tree being messed up.
16+
17+
use std::iter::Iterator;
18+
19+
trait IntoPyDictPointer { }
20+
21+
struct Foo { }
22+
23+
impl Iterator for Foo {
24+
type Item = ();
25+
fn next(&mut self) -> Option<()> {
26+
None
27+
}
28+
}
29+
30+
impl IntoPyDictPointer for Foo { }
31+
32+
impl<I> IntoPyDictPointer for I
33+
where
34+
I: Iterator,
35+
{
36+
}
37+
38+
impl IntoPyDictPointer for () //~ ERROR conflicting implementations
39+
{
40+
}
41+
42+
fn main() { }

0 commit comments

Comments
 (0)