Skip to content

Commit 99e3fca

Browse files
committed
Auto merge of #54906 - qnighy:fix-issue-50452, r=nikomatsakis
Reattach all grandchildren when constructing specialization graph. Specialization graphs are constructed by incrementally adding impls in the order of declaration. If the impl being added has its specializations in the graph already, they should be reattached under the impl. However, the current implementation only reattaches the one found first. Therefore, in the following specialization graph, ``` Tr1 | I3 / \ I1 I2 ``` If `I1`, `I2`, and `I3` are declared in this order, the compiler mistakenly constructs the following graph: ``` Tr1 / \ I3 I2 | I1 ``` This patch fixes the reattach procedure to include all specializing grandchildren-to-be. Fixes #50452.
2 parents 4ec0ba9 + 3a8e0af commit 99e3fca

File tree

3 files changed

+80
-8
lines changed

3 files changed

+80
-8
lines changed

Diff for: src/librustc/traits/specialize/specialization_graph.rs

+19-8
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 should replace an existing impl X, because the impl specializes X.
77-
ReplaceChild(DefId),
76+
/// The impl should replace existing impls [X1, ..], because the impl specializes X1, X2, etc.
77+
ReplaceChildren(Vec<DefId>),
7878

7979
/// The impl is a specialization of an existing child.
8080
ShouldRecurseOn(DefId),
@@ -124,6 +124,7 @@ impl<'a, 'gcx, 'tcx> Children {
124124
-> Result<Inserted, OverlapError>
125125
{
126126
let mut last_lint = None;
127+
let mut replace_children = Vec::new();
127128

128129
debug!(
129130
"insert(impl_def_id={:?}, simplified_self={:?})",
@@ -194,7 +195,7 @@ impl<'a, 'gcx, 'tcx> Children {
194195
debug!("placing as parent of TraitRef {:?}",
195196
tcx.impl_trait_ref(possible_sibling).unwrap());
196197

197-
return Ok(Inserted::ReplaceChild(possible_sibling));
198+
replace_children.push(possible_sibling);
198199
} else {
199200
if !tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) {
200201
traits::overlapping_impls(
@@ -211,6 +212,10 @@ impl<'a, 'gcx, 'tcx> Children {
211212
}
212213
}
213214

215+
if !replace_children.is_empty() {
216+
return Ok(Inserted::ReplaceChildren(replace_children));
217+
}
218+
214219
// no overlap with any potential siblings, so add as a new sibling
215220
debug!("placing as new sibling");
216221
self.insert_blindly(tcx, impl_def_id);
@@ -282,7 +287,7 @@ impl<'a, 'gcx, 'tcx> Graph {
282287
last_lint = opt_lint;
283288
break;
284289
}
285-
ReplaceChild(grand_child_to_be) => {
290+
ReplaceChildren(grand_children_to_be) => {
286291
// We currently have
287292
//
288293
// P
@@ -302,17 +307,23 @@ impl<'a, 'gcx, 'tcx> Graph {
302307
let siblings = self.children
303308
.get_mut(&parent)
304309
.unwrap();
305-
siblings.remove_existing(tcx, grand_child_to_be);
310+
for &grand_child_to_be in &grand_children_to_be {
311+
siblings.remove_existing(tcx, grand_child_to_be);
312+
}
306313
siblings.insert_blindly(tcx, impl_def_id);
307314
}
308315

309316
// Set G's parent to N and N's parent to P
310-
self.parent.insert(grand_child_to_be, impl_def_id);
317+
for &grand_child_to_be in &grand_children_to_be {
318+
self.parent.insert(grand_child_to_be, impl_def_id);
319+
}
311320
self.parent.insert(impl_def_id, parent);
312321

313322
// Add G as N's child.
314-
self.children.entry(impl_def_id).or_default()
315-
.insert_blindly(tcx, grand_child_to_be);
323+
for &grand_child_to_be in &grand_children_to_be {
324+
self.children.entry(impl_def_id).or_default()
325+
.insert_blindly(tcx, grand_child_to_be);
326+
}
316327
break;
317328
}
318329
ShouldRecurseOn(new_parent) => {

Diff for: src/test/compile-fail/specialization/issue-50452.rs

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2018 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+
// compile-fail
12+
13+
#![feature(specialization)]
14+
15+
pub trait Foo {
16+
fn foo();
17+
}
18+
19+
impl Foo for i32 {}
20+
impl Foo for i64 {
21+
fn foo() {}
22+
//~^ERROR `foo` specializes an item from a parent `impl`
23+
}
24+
impl<T> Foo for T {
25+
fn foo() {}
26+
}
27+
28+
fn main() {
29+
i32::foo();
30+
i64::foo();
31+
u8::foo();
32+
}

Diff for: src/test/run-pass/specialization/issue-50452.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2018 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+
// run-pass
12+
13+
#![feature(specialization)]
14+
15+
pub trait Foo {
16+
fn foo();
17+
}
18+
19+
impl Foo for i32 {}
20+
impl Foo for i64 {}
21+
impl<T> Foo for T {
22+
fn foo() {}
23+
}
24+
25+
fn main() {
26+
i32::foo();
27+
i64::foo();
28+
u8::foo();
29+
}

0 commit comments

Comments
 (0)