diff --git a/src/libcollections/btree/map.rs b/src/libcollections/btree/map.rs index 2375a63fbd7..492263da2bc 100644 --- a/src/libcollections/btree/map.rs +++ b/src/libcollections/btree/map.rs @@ -12,6 +12,7 @@ use core::cmp::Ordering; use core::fmt::Debug; use core::hash::{Hash, Hasher}; use core::iter::FromIterator; +use core::marker::PhantomData; use core::ops::Index; use core::{fmt, intrinsics, mem, ptr}; @@ -158,7 +159,8 @@ impl super::Recover for BTreeMap Found(handle) => { Some(OccupiedEntry { handle: handle, - length: &mut self.length + length: &mut self.length, + _marker: PhantomData, }.remove_kv().0) }, GoDown(_) => None @@ -172,7 +174,8 @@ impl super::Recover for BTreeMap VacantEntry { key: key, handle: handle, - length: &mut self.length + length: &mut self.length, + _marker: PhantomData, }.insert(()); None } @@ -223,7 +226,10 @@ pub struct Range<'a, K: 'a, V: 'a> { /// A mutable iterator over a sub-range of BTreeMap's entries. pub struct RangeMut<'a, K: 'a, V: 'a> { front: Handle, K, V, marker::Leaf>, marker::Edge>, - back: Handle, K, V, marker::Leaf>, marker::Edge> + back: Handle, 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. @@ -247,7 +253,10 @@ pub enum Entry<'a, K: 'a, V: 'a> { pub struct VacantEntry<'a, K: 'a, V: 'a> { key: K, handle: Handle, 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. @@ -259,7 +268,10 @@ pub struct OccupiedEntry<'a, K: 'a, V: 'a> { marker::LeafOrInternal >, marker::KV>, - length: &'a mut usize + length: &'a mut usize, + + // Be invariant in `K` and `V` + _marker: PhantomData<&'a mut (K, V)>, } impl BTreeMap { @@ -415,7 +427,8 @@ impl BTreeMap { Found(handle) => { Some(OccupiedEntry { handle: handle, - length: &mut self.length + length: &mut self.length, + _marker: PhantomData, }.remove()) }, GoDown(_) => None @@ -568,7 +581,8 @@ impl BTreeMap { RangeMut { front: front, - back: back + back: back, + _marker: PhantomData } } @@ -593,12 +607,14 @@ impl BTreeMap { match search::search_tree(self.root.as_mut(), &key) { Found(handle) => Occupied(OccupiedEntry { handle: handle, - length: &mut self.length + length: &mut self.length, + _marker: PhantomData, }), GoDown(handle) => Vacant(VacantEntry { key: key, handle: handle, - length: &mut self.length + length: &mut self.length, + _marker: PhantomData, }) } } @@ -1235,6 +1251,7 @@ impl BTreeMap { range: RangeMut { front: first_leaf_edge(root1), back: last_leaf_edge(root2), + _marker: PhantomData, }, length: self.length } diff --git a/src/libcollections/btree/node.rs b/src/libcollections/btree/node.rs index 2e2a39df347..c8a0f60587e 100644 --- a/src/libcollections/btree/node.rs +++ b/src/libcollections/btree/node.rs @@ -97,13 +97,13 @@ impl BoxedNode { } } - unsafe fn from_ptr(ptr: NonZero<*mut LeafNode>) -> Self { - BoxedNode { ptr: Unique::new(*ptr) } + unsafe fn from_ptr(ptr: NonZero<*const LeafNode>) -> Self { + BoxedNode { ptr: Unique::new(*ptr as *mut LeafNode) } } - fn as_ptr(&self) -> NonZero<*mut LeafNode> { + fn as_ptr(&self) -> NonZero<*const LeafNode> { unsafe { - NonZero::new(*self.ptr) + NonZero::new(*self.ptr as *const LeafNode) } } } @@ -209,6 +209,11 @@ impl Root { } } +// 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. /// /// This type has a number of paramaters that controls how it acts: @@ -223,8 +228,8 @@ impl Root { /// `NodeRef` could be pointing to either type of node. pub struct NodeRef { height: usize, - node: NonZero<*mut LeafNode>, - root: *mut Root, + node: NonZero<*const LeafNode>, + root: *const Root, _marker: PhantomData<(BorrowType, Type)> } @@ -401,7 +406,7 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { fn as_leaf_mut(&mut self) -> &mut LeafNode { unsafe { - &mut **self.node + &mut *(*self.node as *mut LeafNode) } } @@ -434,7 +439,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { pub fn into_root_mut(self) -> &'a mut Root { unsafe { - &mut *self.root + &mut *(self.root as *mut Root) } } diff --git a/src/libcollectionstest/btree/map.rs b/src/libcollectionstest/btree/map.rs index c4473b64066..05d4aff108a 100644 --- a/src/libcollectionstest/btree/map.rs +++ b/src/libcollectionstest/btree/map.rs @@ -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 { use std::collections::BTreeMap; use std::__rand::{Rng, thread_rng}; diff --git a/src/test/compile-fail/variance-btree-invariant-types.rs b/src/test/compile-fail/variance-btree-invariant-types.rs new file mode 100644 index 00000000000..e9607de00a3 --- /dev/null +++ b/src/test/compile-fail/variance-btree-invariant-types.rs @@ -0,0 +1,63 @@ +// 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 or the MIT license +// , 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}; + +fn iter_cov_key<'a, 'new>(v: IterMut<'a, &'static (), ()>) -> IterMut<'a, &'new (), ()> { + v //~ ERROR mismatched types +} +fn iter_cov_val<'a, 'new>(v: IterMut<'a, (), &'static ()>) -> IterMut<'a, (), &'new ()> { + v //~ ERROR mismatched types +} +fn iter_contra_key<'a, 'new>(v: IterMut<'a, &'new (), ()>) -> IterMut<'a, &'static (), ()> { + v //~ ERROR mismatched types +} +fn iter_contra_val<'a, 'new>(v: IterMut<'a, (), &'new ()>) -> IterMut<'a, (), &'static ()> { + v //~ ERROR mismatched types +} + +fn occ_cov_key<'a, 'new>(v: OccupiedEntry<'a, &'static (), ()>) + -> OccupiedEntry<'a, &'new (), ()> { + v //~ ERROR mismatched types +} +fn occ_cov_val<'a, 'new>(v: OccupiedEntry<'a, (), &'static ()>) + -> OccupiedEntry<'a, (), &'new ()> { + v //~ ERROR mismatched types +} +fn occ_contra_key<'a, 'new>(v: OccupiedEntry<'a, &'new (), ()>) + -> OccupiedEntry<'a, &'static (), ()> { + v //~ ERROR mismatched types +} +fn occ_contra_val<'a, 'new>(v: OccupiedEntry<'a, (), &'new ()>) + -> OccupiedEntry<'a, (), &'static ()> { + v //~ ERROR mismatched types +} + +fn vac_cov_key<'a, 'new>(v: VacantEntry<'a, &'static (), ()>) + -> VacantEntry<'a, &'new (), ()> { + v //~ ERROR mismatched types +} +fn vac_cov_val<'a, 'new>(v: VacantEntry<'a, (), &'static ()>) + -> VacantEntry<'a, (), &'new ()> { + v //~ ERROR mismatched types +} +fn vac_contra_key<'a, 'new>(v: VacantEntry<'a, &'new (), ()>) + -> VacantEntry<'a, &'static (), ()> { + v //~ ERROR mismatched types +} +fn vac_contra_val<'a, 'new>(v: VacantEntry<'a, (), &'new ()>) + -> VacantEntry<'a, (), &'static ()> { + v //~ ERROR mismatched types +} + +#[rustc_error] +fn main() { }