Skip to content

Commit

Permalink
Auto merge of #52546 - nikomatsakis:issue-52050, r=pnkfelix
Browse files Browse the repository at this point in the history
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? :)
  • Loading branch information
bors committed Jul 28, 2018
2 parents d754582 + 4fd5aed commit 4234adf
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/librustc/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ where
F1: FnOnce(OverlapResult<'_>) -> R,
F2: FnOnce() -> R,
{
debug!("impl_can_satisfy(\
debug!("overlapping_impls(\
impl1_def_id={:?}, \
impl2_def_id={:?},
intercrate_mode={:?})",
Expand Down
99 changes: 78 additions & 21 deletions src/librustc/traits/specialize/specialization_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ enum Inserted {
/// The impl was inserted as a new child in this group of children.
BecameNewSibling(Option<OverlapError>),

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

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

/// Remove an impl from this set of children. Used when replacing
/// an impl with a parent. The impl must be present in the list of
/// children already.
fn remove_existing(&mut self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
impl_def_id: DefId) {
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
let vec: &mut Vec<DefId>;
if let Some(sty) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false) {
debug!("remove_existing: impl_def_id={:?} sty={:?}", impl_def_id, sty);
vec = self.nonblanket_impls.get_mut(&sty).unwrap();
} else {
debug!("remove_existing: impl_def_id={:?} sty=None", impl_def_id);
vec = &mut self.blanket_impls;
}

let index = vec.iter().position(|d| *d == impl_def_id).unwrap();
vec.remove(index);
}

/// Attempt to insert an impl into this set of children, while comparing for
/// specialization relationships.
fn insert(&mut self,
Expand All @@ -110,11 +132,22 @@ impl<'a, 'gcx, 'tcx> Children {
{
let mut last_lint = None;

for slot in match simplified_self {
Some(sty) => self.filtered_mut(sty),
None => self.iter_mut(),
debug!(
"insert(impl_def_id={:?}, simplified_self={:?})",
impl_def_id,
simplified_self,
);

for possible_sibling in match simplified_self {
Some(sty) => self.filtered(sty),
None => self.iter(),
} {
let possible_sibling = *slot;
debug!(
"insert: impl_def_id={:?}, simplified_self={:?}, possible_sibling={:?}",
impl_def_id,
simplified_self,
possible_sibling,
);

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

// possible_sibling specializes the impl
*slot = impl_def_id;
return Ok(Inserted::Replaced(possible_sibling));
return Ok(Inserted::ReplaceChild(possible_sibling));
} else {
if !tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) {
traits::overlapping_impls(
Expand All @@ -193,15 +224,14 @@ impl<'a, 'gcx, 'tcx> Children {
Ok(Inserted::BecameNewSibling(last_lint))
}

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

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

Expand Down Expand Up @@ -259,11 +289,38 @@ impl<'a, 'gcx, 'tcx> Graph {
last_lint = opt_lint;
break;
}
Replaced(new_child) => {
self.parent.insert(new_child, impl_def_id);
let mut new_children = Children::new();
new_children.insert_blindly(tcx, new_child);
self.children.insert(impl_def_id, new_children);
ReplaceChild(grand_child_to_be) => {
// We currently have
//
// P
// |
// G
//
// and we are inserting the impl N. We want to make it:
//
// P
// |
// N
// |
// G

// Adjust P's list of children: remove G and then add N.
{
let siblings = self.children
.get_mut(&parent)
.unwrap();
siblings.remove_existing(tcx, grand_child_to_be);
siblings.insert_blindly(tcx, impl_def_id);
}

// Set G's parent to N and N's parent to P
self.parent.insert(grand_child_to_be, impl_def_id);
self.parent.insert(impl_def_id, parent);

// Add G as N's child.
let mut grand_children = Children::new();
grand_children.insert_blindly(tcx, grand_child_to_be);
self.children.insert(impl_def_id, grand_children);
break;
}
ShouldRecurseOn(new_parent) => {
Expand Down
42 changes: 42 additions & 0 deletions src/test/compile-fail/specialization/issue-52050.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(specialization)]

// Regression test for #52050: when inserting the blanket impl `I`
// into the tree, we had to replace the child node for `Foo`, which
// led to the struture of the tree being messed up.

use std::iter::Iterator;

trait IntoPyDictPointer { }

struct Foo { }

impl Iterator for Foo {
type Item = ();
fn next(&mut self) -> Option<()> {
None
}
}

impl IntoPyDictPointer for Foo { }

impl<I> IntoPyDictPointer for I
where
I: Iterator,
{
}

impl IntoPyDictPointer for () //~ ERROR conflicting implementations
{
}

fn main() { }

0 comments on commit 4234adf

Please sign in to comment.