diff --git a/src/librustc_data_structures/array_vec.rs b/src/librustc_data_structures/array_vec.rs
deleted file mode 100644
index 45fb565706180..0000000000000
--- a/src/librustc_data_structures/array_vec.rs
+++ /dev/null
@@ -1,305 +0,0 @@
-// 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.
-
-//! A stack-allocated vector, allowing storage of N elements on the stack.
-
-use std::marker::Unsize;
-use std::iter::Extend;
-use std::ptr::{self, drop_in_place, NonNull};
-use std::ops::{Deref, DerefMut, Range};
-use std::hash::{Hash, Hasher};
-use std::slice;
-use std::fmt;
-use std::mem;
-use std::mem::ManuallyDrop;
-use std::ops::Bound::{Excluded, Included, Unbounded};
-use std::ops::RangeBounds;
-
-pub unsafe trait Array {
-    type Element;
-    type PartialStorage: Unsize<[ManuallyDrop<Self::Element>]>;
-    const LEN: usize;
-}
-
-unsafe impl<T> Array for [T; 1] {
-    type Element = T;
-    type PartialStorage = [ManuallyDrop<T>; 1];
-    const LEN: usize = 1;
-}
-
-unsafe impl<T> Array for [T; 8] {
-    type Element = T;
-    type PartialStorage = [ManuallyDrop<T>; 8];
-    const LEN: usize = 8;
-}
-
-unsafe impl<T> Array for [T; 32] {
-    type Element = T;
-    type PartialStorage = [ManuallyDrop<T>; 32];
-    const LEN: usize = 32;
-}
-
-pub struct ArrayVec<A: Array> {
-    count: usize,
-    values: A::PartialStorage
-}
-
-impl<A> Hash for ArrayVec<A>
-    where A: Array,
-          A::Element: Hash {
-    fn hash<H>(&self, state: &mut H) where H: Hasher {
-        (&self[..]).hash(state);
-    }
-}
-
-impl<A> Clone for ArrayVec<A>
-    where A: Array,
-          A::Element: Clone {
-    fn clone(&self) -> Self {
-        let mut v = ArrayVec::new();
-        v.extend(self.iter().cloned());
-        v
-    }
-}
-
-impl<A: Array> ArrayVec<A> {
-    pub fn new() -> Self {
-        ArrayVec {
-            count: 0,
-            values: unsafe { ::std::mem::uninitialized() },
-        }
-    }
-
-    pub fn len(&self) -> usize {
-        self.count
-    }
-
-    pub unsafe fn set_len(&mut self, len: usize) {
-        self.count = len;
-    }
-
-    /// Panics when the stack vector is full.
-    pub fn push(&mut self, el: A::Element) {
-        let arr = &mut self.values as &mut [ManuallyDrop<_>];
-        arr[self.count] = ManuallyDrop::new(el);
-        self.count += 1;
-    }
-
-    pub fn pop(&mut self) -> Option<A::Element> {
-        if self.count > 0 {
-            let arr = &mut self.values as &mut [ManuallyDrop<_>];
-            self.count -= 1;
-            unsafe {
-                let value = ptr::read(&*arr[self.count]);
-                Some(value)
-            }
-        } else {
-            None
-        }
-    }
-
-    pub fn drain<R>(&mut self, range: R) -> Drain<A>
-        where R: RangeBounds<usize>
-    {
-        // Memory safety
-        //
-        // When the Drain is first created, it shortens the length of
-        // the source vector to make sure no uninitialized or moved-from elements
-        // are accessible at all if the Drain's destructor never gets to run.
-        //
-        // Drain will ptr::read out the values to remove.
-        // When finished, remaining tail of the vec is copied back to cover
-        // the hole, and the vector length is restored to the new length.
-        //
-        let len = self.len();
-        let start = match range.start_bound() {
-            Included(&n) => n,
-            Excluded(&n) => n + 1,
-            Unbounded    => 0,
-        };
-        let end = match range.end_bound() {
-            Included(&n) => n + 1,
-            Excluded(&n) => n,
-            Unbounded    => len,
-        };
-        assert!(start <= end);
-        assert!(end <= len);
-
-        unsafe {
-            // set self.vec length's to start, to be safe in case Drain is leaked
-            self.set_len(start);
-            // Use the borrow in the IterMut to indicate borrowing behavior of the
-            // whole Drain iterator (like &mut T).
-            let range_slice = {
-                let arr = &mut self.values as &mut [ManuallyDrop<<A as Array>::Element>];
-                slice::from_raw_parts_mut(arr.as_mut_ptr().add(start),
-                                          end - start)
-            };
-            Drain {
-                tail_start: end,
-                tail_len: len - end,
-                iter: range_slice.iter(),
-                array_vec: NonNull::from(self),
-            }
-        }
-    }
-}
-
-impl<A> Default for ArrayVec<A>
-    where A: Array {
-    fn default() -> Self {
-        ArrayVec::new()
-    }
-}
-
-impl<A> fmt::Debug for ArrayVec<A>
-    where A: Array,
-          A::Element: fmt::Debug {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        self[..].fmt(f)
-    }
-}
-
-impl<A: Array> Deref for ArrayVec<A> {
-    type Target = [A::Element];
-    fn deref(&self) -> &Self::Target {
-        unsafe {
-            slice::from_raw_parts(&self.values as *const _ as *const A::Element, self.count)
-        }
-    }
-}
-
-impl<A: Array> DerefMut for ArrayVec<A> {
-    fn deref_mut(&mut self) -> &mut [A::Element] {
-        unsafe {
-            slice::from_raw_parts_mut(&mut self.values as *mut _ as *mut A::Element, self.count)
-        }
-    }
-}
-
-impl<A: Array> Drop for ArrayVec<A> {
-    fn drop(&mut self) {
-        unsafe {
-            drop_in_place(&mut self[..])
-        }
-    }
-}
-
-impl<A: Array> Extend<A::Element> for ArrayVec<A> {
-    fn extend<I>(&mut self, iter: I) where I: IntoIterator<Item=A::Element> {
-        for el in iter {
-            self.push(el);
-        }
-    }
-}
-
-pub struct Iter<A: Array> {
-    indices: Range<usize>,
-    store: A::PartialStorage,
-}
-
-impl<A: Array> Drop for Iter<A> {
-    fn drop(&mut self) {
-        self.for_each(drop);
-    }
-}
-
-impl<A: Array> Iterator for Iter<A> {
-    type Item = A::Element;
-
-    fn next(&mut self) -> Option<A::Element> {
-        let arr = &self.store as &[ManuallyDrop<_>];
-        unsafe {
-            self.indices.next().map(|i| ptr::read(&*arr[i]))
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.indices.size_hint()
-    }
-}
-
-pub struct Drain<'a, A: Array>
-        where A::Element: 'a
-{
-    tail_start: usize,
-    tail_len: usize,
-    iter: slice::Iter<'a, ManuallyDrop<A::Element>>,
-    array_vec: NonNull<ArrayVec<A>>,
-}
-
-impl<'a, A: Array> Iterator for Drain<'a, A> {
-    type Item = A::Element;
-
-    #[inline]
-    fn next(&mut self) -> Option<A::Element> {
-        self.iter.next().map(|elt| unsafe { ptr::read(&**elt) })
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-}
-
-impl<'a, A: Array> Drop for Drain<'a, A> {
-    fn drop(&mut self) {
-        // exhaust self first
-        self.for_each(drop);
-
-        if self.tail_len > 0 {
-            unsafe {
-                let source_array_vec: &mut ArrayVec<A> = self.array_vec.as_mut();
-                // memmove back untouched tail, update to new length
-                let start = source_array_vec.len();
-                let tail = self.tail_start;
-                {
-                    let arr =
-                        &mut source_array_vec.values as &mut [ManuallyDrop<<A as Array>::Element>];
-                    let src = arr.as_ptr().add(tail);
-                    let dst = arr.as_mut_ptr().add(start);
-                    ptr::copy(src, dst, self.tail_len);
-                };
-                source_array_vec.set_len(start + self.tail_len);
-            }
-        }
-    }
-}
-
-impl<A: Array> IntoIterator for ArrayVec<A> {
-    type Item = A::Element;
-    type IntoIter = Iter<A>;
-    fn into_iter(self) -> Self::IntoIter {
-        let store = unsafe {
-            ptr::read(&self.values)
-        };
-        let indices = 0..self.count;
-        mem::forget(self);
-        Iter {
-            indices,
-            store,
-        }
-    }
-}
-
-impl<'a, A: Array> IntoIterator for &'a ArrayVec<A> {
-    type Item = &'a A::Element;
-    type IntoIter = slice::Iter<'a, A::Element>;
-    fn into_iter(self) -> Self::IntoIter {
-        self.iter()
-    }
-}
-
-impl<'a, A: Array> IntoIterator for &'a mut ArrayVec<A> {
-    type Item = &'a mut A::Element;
-    type IntoIter = slice::IterMut<'a, A::Element>;
-    fn into_iter(self) -> Self::IntoIter {
-        self.iter_mut()
-    }
-}
diff --git a/src/librustc_data_structures/bit_set.rs b/src/librustc_data_structures/bit_set.rs
index b1fb475bce902..9eb8d0afd46ec 100644
--- a/src/librustc_data_structures/bit_set.rs
+++ b/src/librustc_data_structures/bit_set.rs
@@ -8,9 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use array_vec::ArrayVec;
 use indexed_vec::{Idx, IndexVec};
 use rustc_serialize;
+use smallvec::SmallVec;
 use std::fmt;
 use std::iter;
 use std::marker::PhantomData;
@@ -61,21 +61,21 @@ impl<T: Idx> BitSet<T> {
     }
 
     /// Sets all elements up to and including `size`.
-    pub fn set_up_to(&mut self, bit: usize) {
+    pub fn set_up_to(&mut self, elem: usize) {
         for word in &mut self.words {
             *word = !0;
         }
-        self.clear_above(bit);
+        self.clear_above(elem);
     }
 
-    /// Clear all elements above `bit`.
-    fn clear_above(&mut self, bit: usize) {
-        let first_clear_block = bit / WORD_BITS;
+    /// Clear all elements above `elem`.
+    fn clear_above(&mut self, elem: usize) {
+        let first_clear_block = elem / WORD_BITS;
 
         if first_clear_block < self.words.len() {
-            // Within `first_clear_block`, the `bit % WORD_BITS` LSBs should
+            // Within `first_clear_block`, the `elem % WORD_BITS` LSBs should
             // remain.
-            let mask = (1 << (bit % WORD_BITS)) - 1;
+            let mask = (1 << (elem % WORD_BITS)) - 1;
             self.words[first_clear_block] &= mask;
 
             // All the blocks above `first_clear_block` are fully cleared.
@@ -96,10 +96,10 @@ impl<T: Idx> BitSet<T> {
         self.words.iter().map(|e| e.count_ones() as usize).sum()
     }
 
-    /// True if `self` contains the bit `bit`.
+    /// True if `self` contains `elem`.
     #[inline]
-    pub fn contains(&self, bit: T) -> bool {
-        let (word_index, mask) = word_index_and_mask(bit);
+    pub fn contains(&self, elem: T) -> bool {
+        let (word_index, mask) = word_index_and_mask(elem);
         (self.words[word_index] & mask) != 0
     }
 
@@ -118,10 +118,10 @@ impl<T: Idx> BitSet<T> {
         self.words.iter().all(|a| *a == 0)
     }
 
-    /// Insert a bit. Returns true if the bit has changed.
+    /// Insert `elem`. Returns true if the set has changed.
     #[inline]
-    pub fn insert(&mut self, bit: T) -> bool {
-        let (word_index, mask) = word_index_and_mask(bit);
+    pub fn insert(&mut self, elem: T) -> bool {
+        let (word_index, mask) = word_index_and_mask(elem);
         let word_ref = &mut self.words[word_index];
         let word = *word_ref;
         let new_word = word | mask;
@@ -136,10 +136,10 @@ impl<T: Idx> BitSet<T> {
         }
     }
 
-    /// Returns true if the bit has changed.
+    /// Returns true if the set has changed.
     #[inline]
-    pub fn remove(&mut self, bit: T) -> bool {
-        let (word_index, mask) = word_index_and_mask(bit);
+    pub fn remove(&mut self, elem: T) -> bool {
+        let (word_index, mask) = word_index_and_mask(elem);
         let word_ref = &mut self.words[word_index];
         let word = *word_ref;
         let new_word = word & !mask;
@@ -320,31 +320,44 @@ fn bitwise<Op>(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool
 const SPARSE_MAX: usize = 8;
 
 /// A fixed-size bitset type with a sparse representation and a maximum of
-/// SPARSE_MAX elements. The elements are stored as an unsorted vector with no
-/// duplicates.
+/// `SPARSE_MAX` elements. The elements are stored as a sorted `SmallVec` with
+/// no duplicates; although `SmallVec` can spill its elements to the heap, that
+/// never happens within this type because of the `SPARSE_MAX` limit.
 ///
-/// This type is used by HybridBitSet; do not use directly.
+/// This type is used by `HybridBitSet`; do not use directly.
 #[derive(Clone, Debug)]
-pub struct SparseBitSet<T: Idx>(ArrayVec<[T; SPARSE_MAX]>);
+pub struct SparseBitSet<T: Idx>(SmallVec<[T; SPARSE_MAX]>);
 
 impl<T: Idx> SparseBitSet<T> {
     fn new_empty() -> Self {
-        SparseBitSet(ArrayVec::new())
+        SparseBitSet(SmallVec::new())
     }
 
     fn len(&self) -> usize {
         self.0.len()
     }
 
+    fn is_empty(&self) -> bool {
+        self.0.len() == 0
+    }
+
     fn contains(&self, elem: T) -> bool {
         self.0.contains(&elem)
     }
 
     fn insert(&mut self, elem: T) -> bool {
-        // Ensure there are no duplicates.
-        if self.0.contains(&elem) {
-            false
+        assert!(self.len() < SPARSE_MAX);
+        if let Some(i) = self.0.iter().position(|&e| e >= elem) {
+            if self.0[i] == elem {
+                // `elem` is already in the set.
+                false
+            } else {
+                // `elem` is smaller than one or more existing elements.
+                self.0.insert(i, elem);
+                true
+            }
         } else {
+            // `elem` is larger than all existing elements.
             self.0.push(elem);
             true
         }
@@ -352,10 +365,7 @@ impl<T: Idx> SparseBitSet<T> {
 
     fn remove(&mut self, elem: T) -> bool {
         if let Some(i) = self.0.iter().position(|&e| e == elem) {
-            // Swap the found element to the end, then pop it.
-            let len = self.0.len();
-            self.0.swap(i, len - 1);
-            self.0.pop();
+            self.0.remove(i);
             true
         } else {
             false
@@ -396,8 +406,8 @@ impl<T: Idx> SubtractFromBitSet<T> for SparseBitSet<T> {
 }
 
 /// A fixed-size bitset type with a hybrid representation: sparse when there
-/// are up to a SPARSE_MAX elements in the set, but dense when there are more
-/// than SPARSE_MAX.
+/// are up to a `SPARSE_MAX` elements in the set, but dense when there are more
+/// than `SPARSE_MAX`.
 ///
 /// This type is especially efficient for sets that typically have a small
 /// number of elements, but a large `domain_size`, and are cleared frequently.
@@ -411,15 +421,28 @@ pub enum HybridBitSet<T: Idx> {
 }
 
 impl<T: Idx> HybridBitSet<T> {
+    // FIXME: This function is used in conjunction with `mem::replace()` in
+    // several pieces of awful code below. I can't work out how else to appease
+    // the borrow checker.
+    fn dummy() -> Self {
+        // The cheapest HybridBitSet to construct, which is only used to get
+        // around the borrow checker.
+        HybridBitSet::Sparse(SparseBitSet::new_empty(), 0)
+    }
+
     pub fn new_empty(domain_size: usize) -> Self {
         HybridBitSet::Sparse(SparseBitSet::new_empty(), domain_size)
     }
 
-    pub fn clear(&mut self) {
-        let domain_size = match *self {
+    pub fn domain_size(&self) -> usize {
+        match *self {
             HybridBitSet::Sparse(_, size) => size,
             HybridBitSet::Dense(_, size) => size,
-        };
+        }
+    }
+
+    pub fn clear(&mut self) {
+        let domain_size = self.domain_size();
         *self = HybridBitSet::new_empty(domain_size);
     }
 
@@ -430,6 +453,22 @@ impl<T: Idx> HybridBitSet<T> {
         }
     }
 
+    pub fn superset(&self, other: &HybridBitSet<T>) -> bool {
+        match (self, other) {
+            (HybridBitSet::Dense(self_dense, _), HybridBitSet::Dense(other_dense, _)) => {
+                self_dense.superset(other_dense)
+            }
+            _ => other.iter().all(|elem| self.contains(elem)),
+        }
+    }
+
+    pub fn is_empty(&self) -> bool {
+        match self {
+            HybridBitSet::Sparse(sparse, _) => sparse.is_empty(),
+            HybridBitSet::Dense(dense, _) => dense.is_empty(),
+        }
+    }
+
     pub fn insert(&mut self, elem: T) -> bool {
         match self {
             HybridBitSet::Sparse(sparse, _) if sparse.len() < SPARSE_MAX => {
@@ -443,19 +482,15 @@ impl<T: Idx> HybridBitSet<T> {
             }
             HybridBitSet::Sparse(_, _) => {
                 // The set is sparse and full. Convert to a dense set.
-                //
-                // FIXME: This code is awful, but I can't work out how else to
-                //        appease the borrow checker.
-                let dummy = HybridBitSet::Sparse(SparseBitSet::new_empty(), 0);
-                match mem::replace(self, dummy) {
+                match mem::replace(self, HybridBitSet::dummy()) {
                     HybridBitSet::Sparse(sparse, domain_size) => {
                         let mut dense = sparse.to_dense(domain_size);
                         let changed = dense.insert(elem);
                         assert!(changed);
-                        mem::replace(self, HybridBitSet::Dense(dense, domain_size));
+                        *self = HybridBitSet::Dense(dense, domain_size);
                         changed
                     }
-                    _ => panic!("impossible"),
+                    _ => unreachable!()
                 }
             }
 
@@ -463,6 +498,17 @@ impl<T: Idx> HybridBitSet<T> {
         }
     }
 
+    pub fn insert_all(&mut self) {
+        let domain_size = self.domain_size();
+        match self {
+            HybridBitSet::Sparse(_, _) => {
+                let dense = BitSet::new_filled(domain_size);
+                *self = HybridBitSet::Dense(dense, domain_size);
+            }
+            HybridBitSet::Dense(dense, _) => dense.insert_all(),
+        }
+    }
+
     pub fn remove(&mut self, elem: T) -> bool {
         // Note: we currently don't bother going from Dense back to Sparse.
         match self {
@@ -471,6 +517,42 @@ impl<T: Idx> HybridBitSet<T> {
         }
     }
 
+    pub fn union(&mut self, other: &HybridBitSet<T>) -> bool {
+        match self {
+            HybridBitSet::Sparse(_, _) => {
+                match other {
+                    HybridBitSet::Sparse(other_sparse, _) => {
+                        // Both sets are sparse. Add the elements in
+                        // `other_sparse` to `self_hybrid` one at a time. This
+                        // may or may not cause `self_hybrid` to be densified.
+                        let mut self_hybrid = mem::replace(self, HybridBitSet::dummy());
+                        let mut changed = false;
+                        for elem in other_sparse.iter() {
+                            changed |= self_hybrid.insert(*elem);
+                        }
+                        *self = self_hybrid;
+                        changed
+                    }
+                    HybridBitSet::Dense(other_dense, _) => {
+                        // `self` is sparse and `other` is dense. Densify
+                        // `self` and then do the bitwise union.
+                        match mem::replace(self, HybridBitSet::dummy()) {
+                            HybridBitSet::Sparse(self_sparse, self_domain_size) => {
+                                let mut new_dense = self_sparse.to_dense(self_domain_size);
+                                let changed = new_dense.union(other_dense);
+                                *self = HybridBitSet::Dense(new_dense, self_domain_size);
+                                changed
+                            }
+                            _ => unreachable!()
+                        }
+                    }
+                }
+            }
+
+            HybridBitSet::Dense(self_dense, _) => self_dense.union(other),
+        }
+    }
+
     /// Converts to a dense set, consuming itself in the process.
     pub fn to_dense(self) -> BitSet<T> {
         match self {
@@ -479,7 +561,6 @@ impl<T: Idx> HybridBitSet<T> {
         }
     }
 
-    /// Iteration order is unspecified.
     pub fn iter(&self) -> HybridIter<T> {
         match self {
             HybridBitSet::Sparse(sparse, _) => HybridIter::Sparse(sparse.iter()),
@@ -547,16 +628,16 @@ impl<T: Idx> GrowableBitSet<T> {
         GrowableBitSet { bit_set: BitSet::new_empty(bits) }
     }
 
-    /// Returns true if the bit has changed.
+    /// Returns true if the set has changed.
     #[inline]
-    pub fn insert(&mut self, bit: T) -> bool {
-        self.grow(bit);
-        self.bit_set.insert(bit)
+    pub fn insert(&mut self, elem: T) -> bool {
+        self.grow(elem);
+        self.bit_set.insert(elem)
     }
 
     #[inline]
-    pub fn contains(&self, bit: T) -> bool {
-        let (word_index, mask) = word_index_and_mask(bit);
+    pub fn contains(&self, elem: T) -> bool {
+        let (word_index, mask) = word_index_and_mask(elem);
         if let Some(word) = self.bit_set.words.get(word_index) {
             (word & mask) != 0
         } else {
@@ -682,11 +763,10 @@ impl<R: Idx, C: Idx> BitMatrix<R, C> {
 /// sparse representation.
 ///
 /// Initially, every row has no explicit representation. If any bit within a
-/// row is set, the entire row is instantiated as
-/// `Some(<full-column-width-BitSet>)`. Furthermore, any previously
-/// uninstantiated rows prior to it will be instantiated as `None`. Those prior
-/// rows may themselves become fully instantiated later on if any of their bits
-/// are set.
+/// row is set, the entire row is instantiated as `Some(<HybridBitSet>)`.
+/// Furthermore, any previously uninstantiated rows prior to it will be
+/// instantiated as `None`. Those prior rows may themselves become fully
+/// instantiated later on if any of their bits are set.
 ///
 /// `R` and `C` are index types used to identify rows and columns respectively;
 /// typically newtyped `usize` wrappers, but they can also just be `usize`.
@@ -697,7 +777,7 @@ where
     C: Idx,
 {
     num_columns: usize,
-    rows: IndexVec<R, Option<BitSet<C>>>,
+    rows: IndexVec<R, Option<HybridBitSet<C>>>,
 }
 
 impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
@@ -709,14 +789,14 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
         }
     }
 
-    fn ensure_row(&mut self, row: R) -> &mut BitSet<C> {
+    fn ensure_row(&mut self, row: R) -> &mut HybridBitSet<C> {
         // Instantiate any missing rows up to and including row `row` with an
-        // empty BitSet.
+        // empty HybridBitSet.
         self.rows.ensure_contains_elem(row, || None);
 
-        // Then replace row `row` with a full BitSet if necessary.
+        // Then replace row `row` with a full HybridBitSet if necessary.
         let num_columns = self.num_columns;
-        self.rows[row].get_or_insert_with(|| BitSet::new_empty(num_columns))
+        self.rows[row].get_or_insert_with(|| HybridBitSet::new_empty(num_columns))
     }
 
     /// Sets the cell at `(row, column)` to true. Put another way, insert
@@ -755,8 +835,8 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
         }
     }
 
-    /// Merge a row, `from`, into the `into` row.
-    pub fn union_into_row(&mut self, into: R, from: &BitSet<C>) -> bool {
+    /// Union a row, `from`, into the `into` row.
+    pub fn union_into_row(&mut self, into: R, from: &HybridBitSet<C>) -> bool {
         self.ensure_row(into).union(from)
     }
 
@@ -775,7 +855,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
         self.row(row).into_iter().flat_map(|r| r.iter())
     }
 
-    pub fn row(&self, row: R) -> Option<&BitSet<C>> {
+    pub fn row(&self, row: R) -> Option<&HybridBitSet<C>> {
         if let Some(Some(row)) = self.rows.get(row) {
             Some(row)
         } else {
@@ -866,7 +946,7 @@ fn bitset_iter_works_2() {
 }
 
 #[test]
-fn union_two_vecs() {
+fn union_two_sets() {
     let mut set1: BitSet<usize> = BitSet::new_empty(65);
     let mut set2: BitSet<usize> = BitSet::new_empty(65);
     assert!(set1.insert(3));
@@ -882,6 +962,74 @@ fn union_two_vecs() {
     assert!(set1.contains(64));
 }
 
+#[test]
+fn hybrid_bitset() {
+    let mut sparse038: HybridBitSet<usize> = HybridBitSet::new_empty(256);
+    assert!(sparse038.is_empty());
+    assert!(sparse038.insert(0));
+    assert!(sparse038.insert(1));
+    assert!(sparse038.insert(8));
+    assert!(sparse038.insert(3));
+    assert!(!sparse038.insert(3));
+    assert!(sparse038.remove(1));
+    assert!(!sparse038.is_empty());
+    assert_eq!(sparse038.iter().collect::<Vec<_>>(), [0, 3, 8]);
+
+    for i in 0..256 {
+        if i == 0 || i == 3 || i == 8 {
+            assert!(sparse038.contains(i));
+        } else {
+            assert!(!sparse038.contains(i));
+        }
+    }
+
+    let mut sparse01358 = sparse038.clone();
+    assert!(sparse01358.insert(1));
+    assert!(sparse01358.insert(5));
+    assert_eq!(sparse01358.iter().collect::<Vec<_>>(), [0, 1, 3, 5, 8]);
+
+    let mut dense10 = HybridBitSet::new_empty(256);
+    for i in 0..10 {
+        assert!(dense10.insert(i));
+    }
+    assert!(!dense10.is_empty());
+    assert_eq!(dense10.iter().collect::<Vec<_>>(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+
+    let mut dense256 = HybridBitSet::new_empty(256);
+    assert!(dense256.is_empty());
+    dense256.insert_all();
+    assert!(!dense256.is_empty());
+    for i in 0..256 {
+        assert!(dense256.contains(i));
+    }
+
+    assert!(sparse038.superset(&sparse038));    // sparse + sparse (self)
+    assert!(sparse01358.superset(&sparse038));  // sparse + sparse
+    assert!(dense10.superset(&sparse038));      // dense + sparse
+    assert!(dense10.superset(&dense10));        // dense + dense (self)
+    assert!(dense256.superset(&dense10));       // dense + dense
+
+    let mut hybrid = sparse038;
+    assert!(!sparse01358.union(&hybrid));       // no change
+    assert!(hybrid.union(&sparse01358));
+    assert!(hybrid.superset(&sparse01358) && sparse01358.superset(&hybrid));
+    assert!(!dense10.union(&sparse01358));
+    assert!(!dense256.union(&dense10));
+    let mut dense = dense10;
+    assert!(dense.union(&dense256));
+    assert!(dense.superset(&dense256) && dense256.superset(&dense));
+    assert!(hybrid.union(&dense256));
+    assert!(hybrid.superset(&dense256) && dense256.superset(&hybrid));
+
+    assert_eq!(dense256.iter().count(), 256);
+    let mut dense0 = dense256;
+    for i in 0..256 {
+        assert!(dense0.remove(i));
+    }
+    assert!(!dense0.remove(0));
+    assert!(dense0.is_empty());
+}
+
 #[test]
 fn grow() {
     let mut set: GrowableBitSet<usize> = GrowableBitSet::with_capacity(65);
diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs
index 1d7557953e976..70b960ac351c0 100644
--- a/src/librustc_data_structures/lib.rs
+++ b/src/librustc_data_structures/lib.rs
@@ -60,7 +60,6 @@ extern crate rustc_cratesio_shim;
 pub use rustc_serialize::hex::ToHex;
 
 pub mod svh;
-pub mod array_vec;
 pub mod base_n;
 pub mod bit_set;
 pub mod const_cstr;
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs
index 618bf99c7c0a9..8dc41a9b2d32d 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs
@@ -10,7 +10,7 @@
 
 use rustc::mir::{BasicBlock, Location, Mir};
 use rustc::ty::{self, RegionVid};
-use rustc_data_structures::bit_set::{BitSet, SparseBitMatrix};
+use rustc_data_structures::bit_set::{HybridBitSet, SparseBitMatrix};
 use rustc_data_structures::indexed_vec::Idx;
 use rustc_data_structures::indexed_vec::IndexVec;
 use std::fmt::Debug;
@@ -184,7 +184,7 @@ impl<N: Idx> LivenessValues<N> {
 
     /// Adds all the elements in the given bit array into the given
     /// region. Returns true if any of them are newly added.
-    crate fn add_elements(&mut self, row: N, locations: &BitSet<PointIndex>) -> bool {
+    crate fn add_elements(&mut self, row: N, locations: &HybridBitSet<PointIndex>) -> bool {
         debug!("LivenessValues::add_elements(row={:?}, locations={:?})", row, locations);
         self.points.union_into_row(row, locations)
     }
diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs
index b6410f7de3d5d..47e6ce05cec1f 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs
@@ -22,7 +22,7 @@ use rustc::traits::query::dropck_outlives::DropckOutlivesResult;
 use rustc::traits::query::type_op::outlives::DropckOutlives;
 use rustc::traits::query::type_op::TypeOp;
 use rustc::ty::{Ty, TypeFoldable};
-use rustc_data_structures::bit_set::BitSet;
+use rustc_data_structures::bit_set::HybridBitSet;
 use rustc_data_structures::fx::FxHashMap;
 use std::rc::Rc;
 use util::liveness::LiveVariableMap;
@@ -121,16 +121,16 @@ where
     cx: LivenessContext<'me, 'typeck, 'flow, 'gcx, 'tcx>,
 
     /// Set of points that define the current local.
-    defs: BitSet<PointIndex>,
+    defs: HybridBitSet<PointIndex>,
 
     /// Points where the current variable is "use live" -- meaning
     /// that there is a future "full use" that may use its value.
-    use_live_at: BitSet<PointIndex>,
+    use_live_at: HybridBitSet<PointIndex>,
 
     /// Points where the current variable is "drop live" -- meaning
     /// that there is no future "full use" that may use its value, but
     /// there is a future drop.
-    drop_live_at: BitSet<PointIndex>,
+    drop_live_at: HybridBitSet<PointIndex>,
 
     /// Locations where drops may occur.
     drop_locations: Vec<Location>,
@@ -144,9 +144,9 @@ impl LivenessResults<'me, 'typeck, 'flow, 'gcx, 'tcx> {
         let num_points = cx.elements.num_points();
         LivenessResults {
             cx,
-            defs: BitSet::new_empty(num_points),
-            use_live_at: BitSet::new_empty(num_points),
-            drop_live_at: BitSet::new_empty(num_points),
+            defs: HybridBitSet::new_empty(num_points),
+            use_live_at: HybridBitSet::new_empty(num_points),
+            drop_live_at: HybridBitSet::new_empty(num_points),
             drop_locations: vec![],
             stack: vec![],
         }
@@ -448,7 +448,7 @@ impl LivenessContext<'_, '_, '_, '_, 'tcx> {
     fn add_use_live_facts_for(
         &mut self,
         value: impl TypeFoldable<'tcx>,
-        live_at: &BitSet<PointIndex>,
+        live_at: &HybridBitSet<PointIndex>,
     ) {
         debug!("add_use_live_facts_for(value={:?})", value);
 
@@ -465,7 +465,7 @@ impl LivenessContext<'_, '_, '_, '_, 'tcx> {
         dropped_local: Local,
         dropped_ty: Ty<'tcx>,
         drop_locations: &[Location],
-        live_at: &BitSet<PointIndex>,
+        live_at: &HybridBitSet<PointIndex>,
     ) {
         debug!(
             "add_drop_live_constraint(\
@@ -508,7 +508,7 @@ impl LivenessContext<'_, '_, '_, '_, 'tcx> {
         elements: &RegionValueElements,
         typeck: &mut TypeChecker<'_, '_, 'tcx>,
         value: impl TypeFoldable<'tcx>,
-        live_at: &BitSet<PointIndex>,
+        live_at: &HybridBitSet<PointIndex>,
     ) {
         debug!("make_all_regions_live(value={:?})", value);
         debug!(