Fix and test variance of BTreeMap and its companion structs.

This commit is contained in:
Jonathan S 2016-01-17 09:47:21 -06:00
parent bff52927f5
commit fae75c93b3
4 changed files with 111 additions and 17 deletions

View File

@ -12,6 +12,7 @@ use core::cmp::Ordering;
use core::fmt::Debug; use core::fmt::Debug;
use core::hash::{Hash, Hasher}; use core::hash::{Hash, Hasher};
use core::iter::FromIterator; use core::iter::FromIterator;
use core::marker::PhantomData;
use core::ops::Index; use core::ops::Index;
use core::{fmt, intrinsics, mem, ptr}; use core::{fmt, intrinsics, mem, ptr};
@ -158,7 +159,8 @@ impl<K, Q: ?Sized> super::Recover<Q> for BTreeMap<K, ()>
Found(handle) => { Found(handle) => {
Some(OccupiedEntry { Some(OccupiedEntry {
handle: handle, handle: handle,
length: &mut self.length length: &mut self.length,
_marker: PhantomData,
}.remove_kv().0) }.remove_kv().0)
}, },
GoDown(_) => None GoDown(_) => None
@ -172,7 +174,8 @@ impl<K, Q: ?Sized> super::Recover<Q> for BTreeMap<K, ()>
VacantEntry { VacantEntry {
key: key, key: key,
handle: handle, handle: handle,
length: &mut self.length length: &mut self.length,
_marker: PhantomData,
}.insert(()); }.insert(());
None None
} }
@ -223,7 +226,10 @@ pub struct Range<'a, K: 'a, V: 'a> {
/// A mutable iterator over a sub-range of BTreeMap's entries. /// A mutable iterator over a sub-range of BTreeMap's entries.
pub struct RangeMut<'a, K: 'a, V: 'a> { pub struct RangeMut<'a, K: 'a, V: 'a> {
front: Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>, front: Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>,
back: Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge> back: Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>,
// Be invariant in `K` and `V`
_marker: PhantomData<&'a mut (K, V)>,
} }
/// A view into a single entry in a map, which may either be vacant or occupied. /// A view into a single entry in a map, which may either be vacant or occupied.
@ -247,7 +253,10 @@ pub enum Entry<'a, K: 'a, V: 'a> {
pub struct VacantEntry<'a, K: 'a, V: 'a> { pub struct VacantEntry<'a, K: 'a, V: 'a> {
key: K, key: K,
handle: Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>, handle: Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>,
length: &'a mut usize length: &'a mut usize,
// Be invariant in `K` and `V`
_marker: PhantomData<&'a mut (K, V)>,
} }
/// An occupied Entry. /// An occupied Entry.
@ -259,7 +268,10 @@ pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
marker::LeafOrInternal marker::LeafOrInternal
>, marker::KV>, >, marker::KV>,
length: &'a mut usize length: &'a mut usize,
// Be invariant in `K` and `V`
_marker: PhantomData<&'a mut (K, V)>,
} }
impl<K: Ord, V> BTreeMap<K, V> { impl<K: Ord, V> BTreeMap<K, V> {
@ -415,7 +427,8 @@ impl<K: Ord, V> BTreeMap<K, V> {
Found(handle) => { Found(handle) => {
Some(OccupiedEntry { Some(OccupiedEntry {
handle: handle, handle: handle,
length: &mut self.length length: &mut self.length,
_marker: PhantomData,
}.remove()) }.remove())
}, },
GoDown(_) => None GoDown(_) => None
@ -568,7 +581,8 @@ impl<K: Ord, V> BTreeMap<K, V> {
RangeMut { RangeMut {
front: front, front: front,
back: back back: back,
_marker: PhantomData
} }
} }
@ -593,12 +607,14 @@ impl<K: Ord, V> BTreeMap<K, V> {
match search::search_tree(self.root.as_mut(), &key) { match search::search_tree(self.root.as_mut(), &key) {
Found(handle) => Occupied(OccupiedEntry { Found(handle) => Occupied(OccupiedEntry {
handle: handle, handle: handle,
length: &mut self.length length: &mut self.length,
_marker: PhantomData,
}), }),
GoDown(handle) => Vacant(VacantEntry { GoDown(handle) => Vacant(VacantEntry {
key: key, key: key,
handle: handle, handle: handle,
length: &mut self.length length: &mut self.length,
_marker: PhantomData,
}) })
} }
} }
@ -1235,6 +1251,7 @@ impl<K, V> BTreeMap<K, V> {
range: RangeMut { range: RangeMut {
front: first_leaf_edge(root1), front: first_leaf_edge(root1),
back: last_leaf_edge(root2), back: last_leaf_edge(root2),
_marker: PhantomData,
}, },
length: self.length length: self.length
} }

View File

@ -97,13 +97,13 @@ impl<K, V> BoxedNode<K, V> {
} }
} }
unsafe fn from_ptr(ptr: NonZero<*mut LeafNode<K, V>>) -> Self { unsafe fn from_ptr(ptr: NonZero<*const LeafNode<K, V>>) -> Self {
BoxedNode { ptr: Unique::new(*ptr) } BoxedNode { ptr: Unique::new(*ptr as *mut LeafNode<K, V>) }
} }
fn as_ptr(&self) -> NonZero<*mut LeafNode<K, V>> { fn as_ptr(&self) -> NonZero<*const LeafNode<K, V>> {
unsafe { unsafe {
NonZero::new(*self.ptr) NonZero::new(*self.ptr as *const LeafNode<K, V>)
} }
} }
} }
@ -209,6 +209,11 @@ impl<K, V> Root<K, V> {
} }
} }
// N.B. `NodeRef` is always covariant in `K` and `V`, even when the `BorrowType`
// is `Mut`. This is technically wrong, but cannot result in any unsafety due to
// internal use of `NodeRef` because we stay completely generic over `K` and `V`.
// However, whenever a public type wraps `NodeRef`, make sure that it has the
// correct variance.
/// A reference to a node. /// A reference to a node.
/// ///
/// This type has a number of paramaters that controls how it acts: /// This type has a number of paramaters that controls how it acts:
@ -223,8 +228,8 @@ impl<K, V> Root<K, V> {
/// `NodeRef` could be pointing to either type of node. /// `NodeRef` could be pointing to either type of node.
pub struct NodeRef<BorrowType, K, V, Type> { pub struct NodeRef<BorrowType, K, V, Type> {
height: usize, height: usize,
node: NonZero<*mut LeafNode<K, V>>, node: NonZero<*const LeafNode<K, V>>,
root: *mut Root<K, V>, root: *const Root<K, V>,
_marker: PhantomData<(BorrowType, Type)> _marker: PhantomData<(BorrowType, Type)>
} }
@ -401,7 +406,7 @@ impl<'a, K, V, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
fn as_leaf_mut(&mut self) -> &mut LeafNode<K, V> { fn as_leaf_mut(&mut self) -> &mut LeafNode<K, V> {
unsafe { unsafe {
&mut **self.node &mut *(*self.node as *mut LeafNode<K, V>)
} }
} }
@ -434,7 +439,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Immut<'a>, K, V, Type> {
impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Mut<'a>, K, V, Type> { impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
pub fn into_root_mut(self) -> &'a mut Root<K, V> { pub fn into_root_mut(self) -> &'a mut Root<K, V> {
unsafe { unsafe {
&mut *self.root &mut *(self.root as *mut Root<K, V>)
} }
} }

View File

@ -378,6 +378,22 @@ fn test_clone() {
} }
} }
#[test]
fn test_variance() {
use std::collections::btree_map::{Iter, IntoIter, Range, Keys, Values};
fn map_key<'new>(v: BTreeMap<&'static str, ()>) -> BTreeMap<&'new str, ()> { v }
fn map_val<'new>(v: BTreeMap<(), &'static str>) -> BTreeMap<(), &'new str> { v }
fn iter_key<'a, 'new>(v: Iter<'a, &'static str, ()>) -> Iter<'a, &'new str, ()> { v }
fn iter_val<'a, 'new>(v: Iter<'a, (), &'static str>) -> Iter<'a, (), &'new str> { v }
fn into_iter_key<'new>(v: IntoIter<&'static str, ()>) -> IntoIter<&'new str, ()> { v }
fn into_iter_val<'new>(v: IntoIter<(), &'static str>) -> IntoIter<(), &'new str> { v }
fn range_key<'a, 'new>(v: Range<'a, &'static str, ()>) -> Range<'a, &'new str, ()> { v }
fn range_val<'a, 'new>(v: Range<'a, (), &'static str>) -> Range<'a, (), &'new str> { v }
fn keys<'a, 'new>(v: Keys<'a, &'static str, ()>) -> Keys<'a, &'new str, ()> { v }
fn vals<'a, 'new>(v: Values<'a, (), &'static str>) -> Values<'a, (), &'new str> { v }
}
mod bench { mod bench {
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::__rand::{Rng, thread_rng}; use std::__rand::{Rng, thread_rng};

View File

@ -0,0 +1,56 @@
// Copyright 2016 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(rustc_attrs)]
use std::collections::btree_map::{IterMut, OccupiedEntry, VacantEntry};
macro_rules! test_invariant {
{ $m:ident $t:ident } => {
mod $m {
use std::collections::btree_map::{IterMut, OccupiedEntry, VacantEntry};
fn not_covariant_key<'a, 'min,'max>(v: $t<'a, &'max (), ()>)
-> $t<'a, &'min (), ()>
where 'max : 'min
{
v //~ ERROR mismatched types
}
fn not_contravariant_key<'a, 'min,'max>(v: $t<'a, &'min (), ()>)
-> $t<'a, &'max (), ()>
where 'max : 'min
{
v //~ ERROR mismatched types
}
fn not_covariant_val<'a, 'min,'max>(v: $t<'a, (), &'max ()>)
-> $t<'a, (), &'min ()>
where 'max : 'min
{
v //~ ERROR mismatched types
}
fn not_contravariant_val<'a, 'min,'max>(v: $t<'a, (), &'min ()>)
-> $t<'a, (), &'max ()>
where 'max : 'min
{
v //~ ERROR mismatched types
}
}
}
}
test_invariant! { foo IterMut }
test_invariant! { bar OccupiedEntry }
test_invariant! { baz VacantEntry }
#[rustc_error]
fn main() { }