diff options
| author | Fenrir <[email protected]> | 2017-12-01 21:57:34 -0700 |
|---|---|---|
| committer | FenrirWolf <[email protected]> | 2017-12-01 22:33:03 -0700 |
| commit | 8ba058552b61484248fc295dfbbe2e18a9d49e48 (patch) | |
| tree | 0c1bdd596147abee8dda56ac98ca009fa699a707 /ctr-std/src/collections | |
| parent | Update bindings for libctru v1.4.0 (diff) | |
| download | ctru-rs-8ba058552b61484248fc295dfbbe2e18a9d49e48.tar.xz ctru-rs-8ba058552b61484248fc295dfbbe2e18a9d49e48.zip | |
Patch `std` to be compatible with Rust nightly-2017-12-01
This only fixes things enough so that the project compiles again. More standard library changes from upstream Rust will be pulled in later.
Diffstat (limited to 'ctr-std/src/collections')
| -rw-r--r-- | ctr-std/src/collections/hash/map.rs | 175 | ||||
| -rw-r--r-- | ctr-std/src/collections/hash/set.rs | 64 | ||||
| -rw-r--r-- | ctr-std/src/collections/hash/table.rs | 52 |
3 files changed, 231 insertions, 60 deletions
diff --git a/ctr-std/src/collections/hash/map.rs b/ctr-std/src/collections/hash/map.rs index 746e180..7a79a47 100644 --- a/ctr-std/src/collections/hash/map.rs +++ b/ctr-std/src/collections/hash/map.rs @@ -20,8 +20,8 @@ use hash::{Hash, Hasher, BuildHasher, SipHasher13}; use iter::{FromIterator, FusedIterator}; use mem::{self, replace}; use ops::{Deref, Index, InPlace, Place, Placer}; -use rand::{self, Rng}; use ptr; +use sys; use super::table::{self, Bucket, EmptyBucket, FullBucket, FullBucketMut, RawTable, SafeHash}; use super::table::BucketState::{Empty, Full}; @@ -203,7 +203,7 @@ const DISPLACEMENT_THRESHOLD: usize = 128; // so we round that up to 128. // // At a load factor of α, the odds of finding the target bucket after exactly n -// unsuccesful probes[1] are +// unsuccessful probes[1] are // // Pr_α{displacement = n} = // (1 - α) / α * ∑_{k≥1} e^(-kα) * (kα)^(k+n) / (k + n)! * (1 - kα / (k + n + 1)) @@ -419,7 +419,7 @@ fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> Inter Empty(bucket) => { // Found a hole! return InternalEntry::Vacant { - hash: hash, + hash, elem: NoElem(bucket, displacement), }; } @@ -433,7 +433,7 @@ fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> Inter // We can finish the search early if we hit any bucket // with a lower distance to initial bucket than we've probed. return InternalEntry::Vacant { - hash: hash, + hash, elem: NeqElem(full, probe_displacement), }; } @@ -588,6 +588,9 @@ impl<K, V, S> HashMap<K, V, S> impl<K: Hash + Eq, V> HashMap<K, V, RandomState> { /// Creates an empty `HashMap`. /// + /// The hash map is initially created with a capacity of 0, so it will not allocate until it + /// is first inserted into. + /// /// # Examples /// /// ``` @@ -646,7 +649,7 @@ impl<K, V, S> HashMap<K, V, S> #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub fn with_hasher(hash_builder: S) -> HashMap<K, V, S> { HashMap { - hash_builder: hash_builder, + hash_builder, resize_policy: DefaultResizePolicy::new(), table: RawTable::new(0), } @@ -679,8 +682,8 @@ impl<K, V, S> HashMap<K, V, S> let resize_policy = DefaultResizePolicy::new(); let raw_cap = resize_policy.raw_capacity(capacity); HashMap { - hash_builder: hash_builder, - resize_policy: resize_policy, + hash_builder, + resize_policy, table: RawTable::new(raw_cap), } } @@ -688,6 +691,17 @@ impl<K, V, S> HashMap<K, V, S> /// Returns a reference to the map's [`BuildHasher`]. /// /// [`BuildHasher`]: ../../std/hash/trait.BuildHasher.html + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::RandomState; + /// + /// let hasher = RandomState::new(); + /// let map: HashMap<isize, isize> = HashMap::with_hasher(hasher); + /// let hasher: &RandomState = map.hasher(); + /// ``` #[stable(feature = "hashmap_public_hasher", since = "1.9.0")] pub fn hasher(&self) -> &S { &self.hash_builder @@ -1099,6 +1113,7 @@ impl<K, V, S> HashMap<K, V, S> /// assert_eq!(map.get(&2), None); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> where K: Borrow<Q>, Q: Hash + Eq @@ -1347,7 +1362,7 @@ pub struct Iter<'a, K: 'a, V: 'a> { inner: table::Iter<'a, K, V>, } -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V> Clone for Iter<'a, K, V> { fn clone(&self) -> Iter<'a, K, V> { @@ -1400,7 +1415,7 @@ pub struct Keys<'a, K: 'a, V: 'a> { inner: Iter<'a, K, V>, } -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V> Clone for Keys<'a, K, V> { fn clone(&self) -> Keys<'a, K, V> { @@ -1429,7 +1444,7 @@ pub struct Values<'a, K: 'a, V: 'a> { inner: Iter<'a, K, V>, } -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V> Clone for Values<'a, K, V> { fn clone(&self) -> Values<'a, K, V> { @@ -1496,14 +1511,14 @@ impl<'a, K, V> InternalEntry<K, V, &'a mut RawTable<K, V>> { InternalEntry::Occupied { elem } => { Some(Occupied(OccupiedEntry { key: Some(key), - elem: elem, + elem, })) } InternalEntry::Vacant { hash, elem } => { Some(Vacant(VacantEntry { - hash: hash, - key: key, - elem: elem, + hash, + key, + elem, })) } InternalEntry::TableIsEmpty => None, @@ -1618,7 +1633,7 @@ impl<'a, K, V, S> IntoIterator for &'a mut HashMap<K, V, S> type Item = (&'a K, &'a mut V); type IntoIter = IterMut<'a, K, V>; - fn into_iter(mut self) -> IterMut<'a, K, V> { + fn into_iter(self) -> IterMut<'a, K, V> { self.iter_mut() } } @@ -1999,6 +2014,68 @@ impl<'a, K, V> Entry<'a, K, V> { Vacant(ref entry) => entry.key(), } } + + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. + /// + /// # Examples + /// + /// ``` + /// #![feature(entry_and_modify)] + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 42); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 43); + /// ``` + #[unstable(feature = "entry_and_modify", issue = "44733")] + pub fn and_modify<F>(self, mut f: F) -> Self + where F: FnMut(&mut V) + { + match self { + Occupied(mut entry) => { + f(entry.get_mut()); + Occupied(entry) + }, + Vacant(entry) => Vacant(entry), + } + } + +} + +impl<'a, K, V: Default> Entry<'a, K, V> { + #[unstable(feature = "entry_or_default", issue = "44324")] + /// Ensures a value is in the entry by inserting the default value if empty, + /// and returns a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(entry_or_default)] + /// # fn main() { + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, Option<u32>> = HashMap::new(); + /// map.entry("poneyland").or_default(); + /// + /// assert_eq!(map["poneyland"], None); + /// # } + /// ``` + pub fn or_default(self) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(Default::default()), + } + } + } impl<'a, K, V> OccupiedEntry<'a, K, V> { @@ -2161,6 +2238,68 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { fn take_key(&mut self) -> Option<K> { self.key.take() } + + /// Replaces the entry, returning the old key and value. The new key in the hash map will be + /// the key used to create this entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_entry_replace)] + /// use std::collections::hash_map::{Entry, HashMap}; + /// use std::rc::Rc; + /// + /// let mut map: HashMap<Rc<String>, u32> = HashMap::new(); + /// map.insert(Rc::new("Stringthing".to_string()), 15); + /// + /// let my_key = Rc::new("Stringthing".to_string()); + /// + /// if let Entry::Occupied(entry) = map.entry(my_key) { + /// // Also replace the key with a handle to our other key. + /// let (old_key, old_value): (Rc<String>, u32) = entry.replace_entry(16); + /// } + /// + /// ``` + #[unstable(feature = "map_entry_replace", issue = "44286")] + pub fn replace_entry(mut self, value: V) -> (K, V) { + let (old_key, old_value) = self.elem.read_mut(); + + let old_key = mem::replace(old_key, self.key.unwrap()); + let old_value = mem::replace(old_value, value); + + (old_key, old_value) + } + + /// Replaces the key in the hash map with the key used to create this entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_entry_replace)] + /// use std::collections::hash_map::{Entry, HashMap}; + /// use std::rc::Rc; + /// + /// let mut map: HashMap<Rc<String>, u32> = HashMap::new(); + /// let mut known_strings: Vec<Rc<String>> = Vec::new(); + /// + /// // Initialise known strings, run program, etc. + /// + /// reclaim_memory(&mut map, &known_strings); + /// + /// fn reclaim_memory(map: &mut HashMap<Rc<String>, u32>, known_strings: &[Rc<String>] ) { + /// for s in known_strings { + /// if let Entry::Occupied(entry) = map.entry(s.clone()) { + /// // Replaces the entry's key with our version of it in `known_strings`. + /// entry.replace_key(); + /// } + /// } + /// } + /// ``` + #[unstable(feature = "map_entry_replace", issue = "44286")] + pub fn replace_key(mut self) -> K { + let (old_key, _) = self.elem.read_mut(); + mem::replace(old_key, self.key.unwrap()) + } } impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { @@ -2354,9 +2493,7 @@ impl RandomState { // increment one of the seeds on every RandomState creation, giving // every corresponding HashMap a different iteration order. thread_local!(static KEYS: Cell<(u64, u64)> = { - let r = rand::OsRng::new(); - let mut r = r.expect("failed to create an OS RNG"); - Cell::new((r.gen(), r.gen())) + Cell::new(sys::hashmap_random_keys()) }); KEYS.with(|keys| { @@ -2448,6 +2585,7 @@ impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S> { type Key = K; + #[inline] fn get(&self, key: &Q) -> Option<&K> { self.search(key).into_occupied_bucket().map(|bucket| bucket.into_refs().0) } @@ -2460,6 +2598,7 @@ impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S> self.search_mut(key).into_occupied_bucket().map(|bucket| pop_internal(bucket).0) } + #[inline] fn replace(&mut self, key: K) -> Option<K> { self.reserve(1); diff --git a/ctr-std/src/collections/hash/set.rs b/ctr-std/src/collections/hash/set.rs index d80df5f..51698ce 100644 --- a/ctr-std/src/collections/hash/set.rs +++ b/ctr-std/src/collections/hash/set.rs @@ -123,13 +123,16 @@ pub struct HashSet<T, S = RandomState> { } impl<T: Hash + Eq> HashSet<T, RandomState> { - /// Creates an empty HashSet. + /// Creates an empty `HashSet`. + /// + /// The hash set is initially created with a capacity of 0, so it will not allocate until it + /// is first inserted into. /// /// # Examples /// /// ``` /// use std::collections::HashSet; - /// let mut set: HashSet<i32> = HashSet::new(); + /// let set: HashSet<i32> = HashSet::new(); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -146,7 +149,8 @@ impl<T: Hash + Eq> HashSet<T, RandomState> { /// /// ``` /// use std::collections::HashSet; - /// let mut set: HashSet<i32> = HashSet::with_capacity(10); + /// let set: HashSet<i32> = HashSet::with_capacity(10); + /// assert!(set.capacity() >= 10); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -215,6 +219,17 @@ impl<T, S> HashSet<T, S> /// Returns a reference to the set's [`BuildHasher`]. /// /// [`BuildHasher`]: ../../std/hash/trait.BuildHasher.html + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// use std::collections::hash_map::RandomState; + /// + /// let hasher = RandomState::new(); + /// let set: HashSet<i32> = HashSet::with_hasher(hasher); + /// let hasher: &RandomState = set.hasher(); + /// ``` #[stable(feature = "hashmap_public_hasher", since = "1.9.0")] pub fn hasher(&self) -> &S { self.map.hasher() @@ -249,6 +264,7 @@ impl<T, S> HashSet<T, S> /// use std::collections::HashSet; /// let mut set: HashSet<i32> = HashSet::new(); /// set.reserve(10); + /// assert!(set.capacity() >= 10); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn reserve(&mut self, additional: usize) { @@ -312,19 +328,19 @@ impl<T, S> HashSet<T, S> /// println!("{}", x); // Print 1 /// } /// - /// let diff: HashSet<_> = a.difference(&b).cloned().collect(); - /// assert_eq!(diff, [1].iter().cloned().collect()); + /// let diff: HashSet<_> = a.difference(&b).collect(); + /// assert_eq!(diff, [1].iter().collect()); /// /// // Note that difference is not symmetric, /// // and `b - a` means something else: - /// let diff: HashSet<_> = b.difference(&a).cloned().collect(); - /// assert_eq!(diff, [4].iter().cloned().collect()); + /// let diff: HashSet<_> = b.difference(&a).collect(); + /// assert_eq!(diff, [4].iter().collect()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn difference<'a>(&'a self, other: &'a HashSet<T, S>) -> Difference<'a, T, S> { Difference { iter: self.iter(), - other: other, + other, } } @@ -343,11 +359,11 @@ impl<T, S> HashSet<T, S> /// println!("{}", x); /// } /// - /// let diff1: HashSet<_> = a.symmetric_difference(&b).cloned().collect(); - /// let diff2: HashSet<_> = b.symmetric_difference(&a).cloned().collect(); + /// let diff1: HashSet<_> = a.symmetric_difference(&b).collect(); + /// let diff2: HashSet<_> = b.symmetric_difference(&a).collect(); /// /// assert_eq!(diff1, diff2); - /// assert_eq!(diff1, [1, 4].iter().cloned().collect()); + /// assert_eq!(diff1, [1, 4].iter().collect()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn symmetric_difference<'a>(&'a self, @@ -371,14 +387,14 @@ impl<T, S> HashSet<T, S> /// println!("{}", x); /// } /// - /// let intersection: HashSet<_> = a.intersection(&b).cloned().collect(); - /// assert_eq!(intersection, [2, 3].iter().cloned().collect()); + /// let intersection: HashSet<_> = a.intersection(&b).collect(); + /// assert_eq!(intersection, [2, 3].iter().collect()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn intersection<'a>(&'a self, other: &'a HashSet<T, S>) -> Intersection<'a, T, S> { Intersection { iter: self.iter(), - other: other, + other, } } @@ -397,8 +413,8 @@ impl<T, S> HashSet<T, S> /// println!("{}", x); /// } /// - /// let union: HashSet<_> = a.union(&b).cloned().collect(); - /// assert_eq!(union, [1, 2, 3, 4].iter().cloned().collect()); + /// let union: HashSet<_> = a.union(&b).collect(); + /// assert_eq!(union, [1, 2, 3, 4].iter().collect()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn union<'a>(&'a self, other: &'a HashSet<T, S>) -> Union<'a, T, S> { @@ -440,6 +456,22 @@ impl<T, S> HashSet<T, S> } /// Clears the set, returning all elements in an iterator. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let mut set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// assert!(!set.is_empty()); + /// + /// // print 1, 2, 3 in an arbitrary order + /// for i in set.drain() { + /// println!("{}", i); + /// } + /// + /// assert!(set.is_empty()); + /// ``` #[inline] #[stable(feature = "drain", since = "1.6.0")] pub fn drain(&mut self) -> Drain<T> { diff --git a/ctr-std/src/collections/hash/table.rs b/ctr-std/src/collections/hash/table.rs index 3844690..7e623a0 100644 --- a/ctr-std/src/collections/hash/table.rs +++ b/ctr-std/src/collections/hash/table.rs @@ -353,14 +353,14 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> Bucket<K, V, M> { let ib_index = ib_index & table.capacity_mask; Bucket { raw: table.raw_bucket_at(ib_index), - table: table, + table, } } pub fn first(table: M) -> Bucket<K, V, M> { Bucket { raw: table.raw_bucket_at(0), - table: table, + table, } } @@ -455,7 +455,7 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> EmptyBucket<K, V, M> { match self.next().peek() { Full(bucket) => { Ok(GapThenFull { - gap: gap, + gap, full: bucket, }) } @@ -563,7 +563,7 @@ impl<'t, K, V> FullBucket<K, V, &'t mut RawTable<K, V>> { /// /// This works similarly to `put`, building an `EmptyBucket` out of the /// taken bucket. - pub fn take(mut self) -> (EmptyBucket<K, V, &'t mut RawTable<K, V>>, K, V) { + pub fn take(self) -> (EmptyBucket<K, V, &'t mut RawTable<K, V>>, K, V) { self.table.size -= 1; unsafe { @@ -717,26 +717,25 @@ fn calculate_offsets(hashes_size: usize, (pairs_offset, end_of_pairs, oflo) } -// Returns a tuple of (minimum required malloc alignment, hash_offset, +// Returns a tuple of (minimum required malloc alignment, // array_size), from the start of a mallocated array. fn calculate_allocation(hash_size: usize, hash_align: usize, pairs_size: usize, pairs_align: usize) - -> (usize, usize, usize, bool) { - let hash_offset = 0; + -> (usize, usize, bool) { let (_, end_of_pairs, oflo) = calculate_offsets(hash_size, pairs_size, pairs_align); let align = cmp::max(hash_align, pairs_align); - (align, hash_offset, end_of_pairs, oflo) + (align, end_of_pairs, oflo) } #[test] fn test_offset_calculation() { - assert_eq!(calculate_allocation(128, 8, 16, 8), (8, 0, 144, false)); - assert_eq!(calculate_allocation(3, 1, 2, 1), (1, 0, 5, false)); - assert_eq!(calculate_allocation(6, 2, 12, 4), (4, 0, 20, false)); + assert_eq!(calculate_allocation(128, 8, 16, 8), (8, 144, false)); + assert_eq!(calculate_allocation(3, 1, 2, 1), (1, 5, false)); + assert_eq!(calculate_allocation(6, 2, 12, 4), (4, 20, false)); assert_eq!(calculate_offsets(128, 15, 4), (128, 143, false)); assert_eq!(calculate_offsets(3, 2, 4), (4, 6, false)); assert_eq!(calculate_offsets(6, 12, 4), (8, 20, false)); @@ -768,10 +767,10 @@ impl<K, V> RawTable<K, V> { // This is great in theory, but in practice getting the alignment // right is a little subtle. Therefore, calculating offsets has been // factored out into a different function. - let (alignment, hash_offset, size, oflo) = calculate_allocation(hashes_size, - align_of::<HashUint>(), - pairs_size, - align_of::<(K, V)>()); + let (alignment, size, oflo) = calculate_allocation(hashes_size, + align_of::<HashUint>(), + pairs_size, + align_of::<(K, V)>()); assert!(!oflo, "capacity overflow"); // One check for overflow that covers calculation and rounding of size. @@ -784,7 +783,7 @@ impl<K, V> RawTable<K, V> { let buffer = Heap.alloc(Layout::from_size_align(size, alignment).unwrap()) .unwrap_or_else(|e| Heap.oom(e)); - let hashes = buffer.offset(hash_offset as isize) as *mut HashUint; + let hashes = buffer as *mut HashUint; RawTable { capacity_mask: capacity.wrapping_sub(1), @@ -860,8 +859,8 @@ impl<K, V> RawTable<K, V> { // Replace the marker regardless of lifetime bounds on parameters. IntoIter { iter: RawBuckets { - raw: raw, - elems_left: elems_left, + raw, + elems_left, marker: marker::PhantomData, }, table: self, @@ -873,8 +872,8 @@ impl<K, V> RawTable<K, V> { // Replace the marker regardless of lifetime bounds on parameters. Drain { iter: RawBuckets { - raw: raw, - elems_left: elems_left, + raw, + elems_left, marker: marker::PhantomData, }, table: Shared::from(self), @@ -925,7 +924,7 @@ struct RawBuckets<'a, K, V> { marker: marker::PhantomData<&'a ()>, } -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` impl<'a, K, V> Clone for RawBuckets<'a, K, V> { fn clone(&self) -> RawBuckets<'a, K, V> { RawBuckets { @@ -976,7 +975,7 @@ pub struct Iter<'a, K: 'a, V: 'a> { unsafe impl<'a, K: Sync, V: Sync> Sync for Iter<'a, K, V> {} unsafe impl<'a, K: Sync, V: Sync> Send for Iter<'a, K, V> {} -// FIXME(#19839) Remove in favor of `#[derive(Clone)]` +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` impl<'a, K, V> Clone for Iter<'a, K, V> { fn clone(&self) -> Iter<'a, K, V> { Iter { @@ -1157,6 +1156,7 @@ impl<K: Clone, V: Clone> Clone for RawTable<K, V> { } new_ht.size = self.size(); + new_ht.set_tag(self.tag()); new_ht } @@ -1183,10 +1183,10 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable<K, V> { let hashes_size = self.capacity() * size_of::<HashUint>(); let pairs_size = self.capacity() * size_of::<(K, V)>(); - let (align, _, size, oflo) = calculate_allocation(hashes_size, - align_of::<HashUint>(), - pairs_size, - align_of::<(K, V)>()); + let (align, size, oflo) = calculate_allocation(hashes_size, + align_of::<HashUint>(), + pairs_size, + align_of::<(K, V)>()); debug_assert!(!oflo, "should be impossible"); |