[swift-4.1-branch][stdlib] Utilize conditional conformances for lazy collections (#13349)

* [stdlib] Conditional conformances for LazyFilterCollection

(cherry picked from commit 833721a1728dcc8321f6a6824da49880c5a37d22)

* [stdlib] Conditional conformances for LazyMapCollection

(cherry picked from commit b927254564ff35154acb76f7835b665000dfee7a)

* Fix tests

(cherry picked from commit a1b1778c53256016079b58905004c3156a1eaf94)

* Conditional conformances for LazyCollection

(cherry picked from commit 6e7b36a66cdbc33ad062009541b06b38d7602286)

* Rearrange code in LazyFilterCollection and provide delegating overrides

(cherry picked from commit 1388064e37828f6b44fed8c8a29efaea0860c0cd)

* Enforce forward collection preconditions in LazyFilterCollection

(cherry picked from commit 229dcd6275a699dea6c533f4c7ca1c3d001d5958)

* [stdlib] Improve default implementation of Collection.distance(from:to:)

(cherry picked from commit 72948cae9af6d4772f9be3f8be30bca63f078c68)
(cherry picked from commit e4f94a318f78957b05354fc1e2f3c0e34eb55eb1)
diff --git a/benchmark/single-source/LazyFilter.swift b/benchmark/single-source/LazyFilter.swift
index 652fe00..2cfdfc8 100644
--- a/benchmark/single-source/LazyFilter.swift
+++ b/benchmark/single-source/LazyFilter.swift
@@ -47,7 +47,7 @@
   CheckResults(res == 123)
 }
 
-fileprivate var multiplesOfThree: LazyFilterBidirectionalCollection<Array<Int>>?
+fileprivate var multiplesOfThree: LazyFilterCollection<Array<Int>>?
 
 fileprivate func setup_LazilyFilteredArrayContains() {
   multiplesOfThree = Array(1..<5_000).lazy.filter { $0 % 3 == 0 }
diff --git a/stdlib/private/StdlibCollectionUnittest/CheckCollectionType.swift.gyb b/stdlib/private/StdlibCollectionUnittest/CheckCollectionType.swift.gyb
index 7e597d6..00843b0 100644
--- a/stdlib/private/StdlibCollectionUnittest/CheckCollectionType.swift.gyb
+++ b/stdlib/private/StdlibCollectionUnittest/CheckCollectionType.swift.gyb
@@ -1584,11 +1584,6 @@
       .forEach(in: distanceFromToTests) {
       test in
       let c = toCollection(0..<20)
-      let backwards = (test.startOffset > test.endOffset)
-      if backwards && !collectionIsBidirectional {
-        expectCrashLater()
-      }
-
       let d = c.distance(
         from: c.nthIndex(test.startOffset), to: c.nthIndex(test.endOffset))
       expectEqual(
diff --git a/stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift.gyb b/stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift.gyb
index 45d8399..bfc4cdd 100644
--- a/stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift.gyb
+++ b/stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift.gyb
@@ -658,8 +658,6 @@
   public func distance(from start: ${Index}, to end: ${Index})
     -> Int {
 %     if Traversal == 'Forward':
-    _precondition(start <= end,
-      "Only BidirectionalCollections can have end come before start")
 %     end
     // FIXME: swift-3-indexing-model: perform a range check properly.
     if start != endIndex {
diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt
index 9e17e41..ae2d805 100644
--- a/stdlib/public/core/CMakeLists.txt
+++ b/stdlib/public/core/CMakeLists.txt
@@ -55,7 +55,7 @@
   Equatable.swift
   ErrorType.swift
   Existential.swift
-  Filter.swift.gyb
+  Filter.swift
   FixedArray.swift.gyb
   FlatMap.swift
   Flatten.swift.gyb
@@ -83,7 +83,7 @@
   LazySequence.swift
   LifetimeManager.swift
   ManagedBuffer.swift
-  Map.swift.gyb
+  Map.swift
   MemoryLayout.swift
   UnicodeScalar.swift # ORDER DEPENDENCY: Must precede Mirrors.swift
   Mirrors.swift.gyb
diff --git a/stdlib/public/core/Collection.swift b/stdlib/public/core/Collection.swift
index ea210d2..abd0980 100644
--- a/stdlib/public/core/Collection.swift
+++ b/stdlib/public/core/Collection.swift
@@ -701,16 +701,11 @@
 
   /// Returns the distance between two indices.
   ///
-  /// Unless the collection conforms to the `BidirectionalCollection` protocol,
-  /// `start` must be less than or equal to `end`.
-  ///
   /// - Parameters:
   ///   - start: A valid index of the collection.
   ///   - end: Another valid index of the collection. If `end` is equal to
   ///     `start`, the result is zero.
-  /// - Returns: The distance between `start` and `end`. The result can be
-  ///   negative only if the collection conforms to the
-  ///   `BidirectionalCollection` protocol.
+  /// - Returns: The distance between `start` and `end`.
   ///
   /// - Complexity: O(1) if the collection conforms to
   ///   `RandomAccessCollection`; otherwise, O(*n*), where *n* is the
@@ -962,30 +957,34 @@
 
   /// Returns the distance between two indices.
   ///
-  /// Unless the collection conforms to the `BidirectionalCollection` protocol,
-  /// `start` must be less than or equal to `end`.
-  ///
   /// - Parameters:
   ///   - start: A valid index of the collection.
   ///   - end: Another valid index of the collection. If `end` is equal to
   ///     `start`, the result is zero.
-  /// - Returns: The distance between `start` and `end`. The result can be
-  ///   negative only if the collection conforms to the
-  ///   `BidirectionalCollection` protocol.
+  /// - Returns: The distance between `start` and `end`.
   ///
   /// - Complexity: O(1) if the collection conforms to
   ///   `RandomAccessCollection`; otherwise, O(*n*), where *n* is the
   ///   resulting distance.
   @_inlineable
   public func distance(from start: Index, to end: Index) -> Int {
-    _precondition(start <= end,
-      "Only BidirectionalCollections can have end come before start")
-
-    var start = start
+    var _start: Index
+    let _end: Index
+    let step: Int
+    if start > end {
+      _start = end
+      _end = start
+      step = -1
+    }
+    else {
+      _start = start
+      _end = end
+      step = 1
+    }
     var count = 0
-    while start != end {
-      count = count + 1
-      formIndex(after: &start)
+    while _start != _end {
+      count += step
+      formIndex(after: &_start)
     }
     return count
   }
diff --git a/stdlib/public/core/Filter.swift.gyb b/stdlib/public/core/Filter.swift
similarity index 75%
rename from stdlib/public/core/Filter.swift.gyb
rename to stdlib/public/core/Filter.swift
index 86d5d94..668311c 100644
--- a/stdlib/public/core/Filter.swift.gyb
+++ b/stdlib/public/core/Filter.swift
@@ -1,4 +1,4 @@
-//===--- Filter.swift.gyb -------------------------------------*- swift -*-===//
+//===--- Filter.swift -----------------------------------------*- swift -*-===//
 //
 // This source file is part of the Swift.org open source project
 //
@@ -10,12 +10,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-%{
-from gyb_stdlib_support import (
-    collectionForTraversal
-)
-}%
-
 /// An iterator over the elements traversed by some base iterator that also
 /// satisfy a given predicate.
 ///
@@ -122,14 +116,6 @@
 @available(swift, deprecated: 3.1, obsoleted: 4.0, message: "Use Base.Index")
 public typealias LazyFilterIndex<Base : Collection> = Base.Index
 
-// FIXME(ABI)#27 (Conditional Conformance): `LazyFilter*Collection` types should be
-// collapsed into one `LazyFilterCollection` using conditional conformances.
-// Maybe even combined with `LazyFilterSequence`.
-// rdar://problem/17144340
-
-% for Traversal in ['Forward', 'Bidirectional']:
-%   Self = "LazyFilter" + collectionForTraversal(Traversal)
-
 /// A lazy `Collection` wrapper that includes the elements of an
 /// underlying collection that satisfy a predicate.
 ///
@@ -140,17 +126,11 @@
 ///   general operations on `LazyFilterCollection` instances may not have the
 ///   documented complexity.
 @_fixed_layout // FIXME(sil-serialize-all)
-public struct ${Self}<
-  Base : ${collectionForTraversal(Traversal)}
-> : LazyCollectionProtocol, ${collectionForTraversal(Traversal)}
-{
-
-  /// A type that represents a valid position in the collection.
-  ///
-  /// Valid indices consist of the position of every element and a
-  /// "past the end" position that's not valid for use as a subscript.
-  public typealias Index = Base.Index
-
+public struct LazyFilterCollection<Base : Collection> {
+  @_versioned // FIXME(sil-serialize-all)
+  internal var _base: Base
+  @_versioned // FIXME(sil-serialize-all)
+  internal let _predicate: (Base.Element) -> Bool
 
   /// Creates an instance containing the elements of `base` that
   /// satisfy `isIncluded`.
@@ -163,6 +143,38 @@
     self._base = _base
     self._predicate = isIncluded
   }
+}
+
+extension LazyFilterCollection : Sequence {
+  public typealias SubSequence = LazyFilterCollection<Base.SubSequence>
+  public typealias Element = Base.Element
+
+  // Any estimate of the number of elements that pass `_predicate` requires
+  // iterating the collection and evaluating each element, which can be costly,
+  // is unexpected, and usually doesn't pay for itself in saving time through
+  // preventing intermediate reallocations. (SR-4164)
+  @_inlineable // FIXME(sil-serialize-all)
+  public var underestimatedCount: Int { return 0 }
+
+  @_inlineable // FIXME(sil-serialize-all)
+  public func _copyToContiguousArray()
+    -> ContiguousArray<Base.Iterator.Element> {
+
+    // The default implementation of `_copyToContiguousArray` queries the
+    // `count` property, which evaluates `_predicate` for every element --
+    // see the note above `underestimatedCount`. Here we treat `self` as a
+    // sequence and only rely on underestimated count.
+    return _copySequenceToContiguousArray(self)
+  }
+
+  /// Returns an iterator over the elements of this sequence.
+  ///
+  /// - Complexity: O(1).
+  @_inlineable // FIXME(sil-serialize-all)
+  public func makeIterator() -> LazyFilterIterator<Base.Iterator> {
+    return LazyFilterIterator(
+      _base: _base.makeIterator(), _predicate)
+  }
 
   @_inlineable
   public func _customContainsEquatableElement(
@@ -176,6 +188,14 @@
     }
     return nil
   }
+}
+
+extension LazyFilterCollection : LazyCollectionProtocol, Collection {
+  /// A type that represents a valid position in the collection.
+  ///
+  /// Valid indices consist of the position of every element and a
+  /// "past the end" position that's not valid for use as a subscript.
+  public typealias Index = Base.Index
 
   /// The position of the first element in a non-empty collection.
   ///
@@ -221,7 +241,98 @@
     i = index
   }
 
-%   if Traversal == 'Bidirectional':
+  @inline(__always)
+  @_inlineable // FIXME(sil-serialize-all)
+  @_versioned // FIXME(sil-serialize-all)
+  internal func _advanceIndex(_ i: inout Index, step: Int) {
+      repeat {
+        _base.formIndex(&i, offsetBy: step)
+      } while i != _base.endIndex && !_predicate(_base[i])
+  }
+
+  @inline(__always)
+  @_inlineable // FIXME(sil-serialize-all)
+  @_versioned // FIXME(sil-serialize-all)
+  internal func _ensureBidirectional(step: Int) {
+    // FIXME: This seems to be the best way of checking whether _base is
+    // forward only without adding an extra protocol requirement.
+    // index(_:offsetBy:limitedBy:) is chosen becuase it is supposed to return
+    // nil when the resulting index lands outside the collection boundaries,
+    // and therefore likely does not trap in these cases.
+    if step < 0 {
+      _ = _base.index(
+        _base.endIndex, offsetBy: step, limitedBy: _base.startIndex)
+    }
+  }
+
+  @_inlineable // FIXME(sil-serialize-all)
+  public func index(_ i: Index, offsetBy n: Int) -> Index {
+    var i = i
+    let step = n.signum()
+    // The following line makes sure that index(_:offsetBy:) is invoked on the
+    // _base at least once, to trigger a _precondition in forward only
+    // collections.
+    _ensureBidirectional(step: step)
+    for _ in 0 ..< abs(numericCast(n)) {
+      _advanceIndex(&i, step: step)
+    }
+    return i
+  }
+
+  @_inlineable // FIXME(sil-serialize-all)
+  public func formIndex(_ i: inout Index, offsetBy n: Int) {
+    i = index(i, offsetBy: n)
+  }
+
+  @_inlineable // FIXME(sil-serialize-all)
+  public func index(
+    _ i: Index, offsetBy n: Int, limitedBy limit: Index
+  ) -> Index? {
+    var i = i
+    let step = n.signum()
+    // The following line makes sure that index(_:offsetBy:limitedBy:) is
+    // invoked on the _base at least once, to trigger a _precondition in
+    // forward only collections.
+    _ensureBidirectional(step: step)
+    for _ in 0 ..< abs(numericCast(n)) {
+      if i == limit {
+        return nil
+      }
+      _advanceIndex(&i, step: step)
+    }
+    return i
+  }
+
+  @_inlineable // FIXME(sil-serialize-all)
+  public func formIndex(
+    _ i: inout Index, offsetBy n: Int, limitedBy limit: Index
+  ) -> Bool {
+    if let advancedIndex = index(i, offsetBy: n, limitedBy: limit) {
+      i = advancedIndex
+      return true
+    }
+    i = limit
+    return false
+  }
+
+  /// Accesses the element at `position`.
+  ///
+  /// - Precondition: `position` is a valid position in `self` and
+  /// `position != endIndex`.
+  @_inlineable // FIXME(sil-serialize-all)
+  public subscript(position: Index) -> Base.Element {
+    return _base[position]
+  }
+
+  @_inlineable // FIXME(sil-serialize-all)
+  public subscript(bounds: Range<Index>) -> SubSequence {
+    return SubSequence(_base: _base[bounds], _predicate)
+  }
+}
+
+extension LazyFilterCollection : BidirectionalCollection
+  where Base : BidirectionalCollection {
+
   @_inlineable // FIXME(sil-serialize-all)
   public func index(before i: Index) -> Index {
     var i = i
@@ -239,59 +350,8 @@
     } while !_predicate(_base[index])
     i = index
   }
-%   end
-
-  /// Accesses the element at `position`.
-  ///
-  /// - Precondition: `position` is a valid position in `self` and
-  /// `position != endIndex`.
-  @_inlineable // FIXME(sil-serialize-all)
-  public subscript(position: Index) -> Base.Element {
-    return _base[position]
-  }
-
-  public typealias SubSequence = ${Self}<Base.SubSequence>
-
-  @_inlineable // FIXME(sil-serialize-all)
-  public subscript(bounds: Range<Index>) -> SubSequence {
-    return SubSequence(_base: _base[bounds], _predicate)
-  }
-
-  // Any estimate of the number of elements that pass `_predicate` requires
-  // iterating the collection and evaluating each element, which can be costly,
-  // is unexpected, and usually doesn't pay for itself in saving time through
-  // preventing intermediate reallocations. (SR-4164)
-  @_inlineable // FIXME(sil-serialize-all)
-  public var underestimatedCount: Int { return 0 }
-
-  @_inlineable // FIXME(sil-serialize-all)
-  public func _copyToContiguousArray()
-    -> ContiguousArray<Base.Iterator.Element> {
-
-    // The default implementation of `_copyToContiguousArray` queries the
-    // `count` property, which evaluates `_predicate` for every element --
-    // see the note above `underestimatedCount`. Here we treat `self` as a
-    // sequence and only rely on underestimated count.
-    return _copySequenceToContiguousArray(self)
-  }
-
-  /// Returns an iterator over the elements of this sequence.
-  ///
-  /// - Complexity: O(1).
-  @_inlineable // FIXME(sil-serialize-all)
-  public func makeIterator() -> LazyFilterIterator<Base.Iterator> {
-    return LazyFilterIterator(
-      _base: _base.makeIterator(), _predicate)
-  }
-
-  @_versioned // FIXME(sil-serialize-all)
-  internal var _base: Base
-  @_versioned // FIXME(sil-serialize-all)
-  internal let _predicate: (Base.Element) -> Bool
 }
 
-% end
-
 extension LazySequenceProtocol {
   /// Returns the elements of `self` that satisfy `isIncluded`.
   ///
@@ -303,20 +363,11 @@
   public func filter(
     _ isIncluded: @escaping (Elements.Element) -> Bool
   ) -> LazyFilterSequence<Self.Elements> {
-    return LazyFilterSequence(
-      _base: self.elements, isIncluded)
+    return LazyFilterSequence(_base: self.elements, isIncluded)
   }
 }
 
-% for Traversal in ['Forward', 'Bidirectional']:
-
-extension LazyCollectionProtocol
-%   if Traversal != 'Forward':
-  where
-  Self : ${collectionForTraversal(Traversal)},
-  Elements : ${collectionForTraversal(Traversal)}
-%   end
-{
+extension LazyCollectionProtocol {
   /// Returns the elements of `self` that satisfy `predicate`.
   ///
   /// - Note: The elements of the result are computed on-demand, as
@@ -326,14 +377,10 @@
   @_inlineable // FIXME(sil-serialize-all)
   public func filter(
     _ isIncluded: @escaping (Elements.Element) -> Bool
-  ) -> LazyFilter${collectionForTraversal(Traversal)}<Self.Elements> {
-    return LazyFilter${collectionForTraversal(Traversal)}(
-      _base: self.elements, isIncluded)
+  ) -> LazyFilterCollection<Self.Elements> {
+    return LazyFilterCollection(_base: self.elements, isIncluded)
   }
 }
 
-% end
-
-// ${'Local Variables'}:
-// eval: (read-only-mode 1)
-// End:
+@available(*, deprecated, renamed: "LazyFilterCollection")
+public typealias LazyFilterBidirectionalCollection<T> = LazyFilterCollection<T> where T : BidirectionalCollection
diff --git a/stdlib/public/core/FlatMap.swift b/stdlib/public/core/FlatMap.swift
index 61e6483..33b2757 100644
--- a/stdlib/public/core/FlatMap.swift
+++ b/stdlib/public/core/FlatMap.swift
@@ -95,8 +95,7 @@
 extension LazyCollectionProtocol
   where
   Self : BidirectionalCollection,
-  Elements : BidirectionalCollection
-{
+  Elements : BidirectionalCollection {
   /// Returns the concatenated results of mapping the given transformation over
   /// this collection.
   ///
@@ -111,7 +110,7 @@
     _ transform: @escaping (Elements.Element) -> SegmentOfResult
   ) -> LazyCollection<
     FlattenBidirectionalCollection<
-      LazyMapBidirectionalCollection<Elements, SegmentOfResult>>> {
+      LazyMapCollection<Elements, SegmentOfResult>>> {
     return self.map(transform).joined()
   }
   
@@ -128,9 +127,9 @@
   @_inlineable // FIXME(sil-serialize-all)
   public func flatMap<ElementOfResult>(
     _ transform: @escaping (Elements.Element) -> ElementOfResult?
-  ) -> LazyMapBidirectionalCollection<
-    LazyFilterBidirectionalCollection<
-      LazyMapBidirectionalCollection<Elements, ElementOfResult?>>,
+  ) -> LazyMapCollection<
+    LazyFilterCollection<
+      LazyMapCollection<Elements, ElementOfResult?>>,
     ElementOfResult
   > {
     return self.map(transform).filter { $0 != nil }.map { $0! }
diff --git a/stdlib/public/core/LazyCollection.swift.gyb b/stdlib/public/core/LazyCollection.swift.gyb
index 2ea4e0c..c50d741 100644
--- a/stdlib/public/core/LazyCollection.swift.gyb
+++ b/stdlib/public/core/LazyCollection.swift.gyb
@@ -38,18 +38,13 @@
   public var elements: Self { return self }
 }
 
-% for Traversal in TRAVERSALS:
-%   TraversalCollection = collectionForTraversal(Traversal)
-%   Self = 'Lazy' + TraversalCollection
-%   Slice = TraversalCollection.replace('Collection', 'Slice')
-
 /// A collection containing the same elements as a `Base` collection,
 /// but on which some operations such as `map` and `filter` are
 /// implemented lazily.
 ///
 /// - See also: `LazySequenceProtocol`, `LazyCollection`
 @_fixed_layout
-public struct ${Self}<Base : ${TraversalCollection}> : LazyCollectionProtocol {
+public struct LazyCollection<Base : Collection> : LazyCollectionProtocol {
 
   /// The type of the underlying collection.
   public typealias Elements = Base
@@ -78,7 +73,7 @@
 
 /// Forward implementations to the base collection, to pick up any
 /// optimizations it might implement.
-extension ${Self} : Sequence {
+extension LazyCollection : Sequence {
   
   public typealias Iterator = Base.Iterator
 
@@ -118,7 +113,7 @@
   }
 }
 
-extension ${Self} : ${TraversalCollection} {
+extension LazyCollection : Collection {
   /// The position of the first element in a non-empty collection.
   ///
   /// In an empty collection, `startIndex == endIndex`.
@@ -162,7 +157,7 @@
   ///
   /// - Complexity: O(1)
   @_inlineable
-  public subscript(bounds: Range<Index>) -> Slice<${Self}<Base>> {
+  public subscript(bounds: Range<Index>) -> Slice<LazyCollection<Base>> {
     return Slice(base: self, bounds: bounds)
   }
 
@@ -226,8 +221,10 @@
     return _base.distance(from:start, to: end)
   }
 
-%   if Traversal != 'Forward':
+}
 
+extension LazyCollection : BidirectionalCollection
+  where Base : BidirectionalCollection {
   @_inlineable
   public func index(before i: Base.Index) -> Base.Index {
     return _base.index(before: i)
@@ -237,11 +234,13 @@
   public var last: Base.Element? {
     return _base.last
   }
-%   end
 }
 
+extension LazyCollection : RandomAccessCollection
+  where Base : RandomAccessCollection {}
+
 /// Augment `self` with lazy methods such as `map`, `filter`, etc.
-extension ${TraversalCollection} {
+extension Collection {
   /// A view onto this collection that provides lazy implementations of
   /// normally eager operations, such as `map` and `filter`.
   ///
@@ -249,11 +248,13 @@
   /// intermediate operations from allocating storage, or when you only
   /// need a part of the final collection to avoid unnecessary computation.
   @_inlineable
-  public var lazy: ${Self}<Self> {
-    return ${Self}(_base: self)
+  public var lazy: LazyCollection<Self> {
+    return LazyCollection(_base: self)
   }
 }
 
+% for Traversal in TRAVERSALS:
+%   TraversalCollection = collectionForTraversal(Traversal)
 // Without this specific overload the non-re-wrapping extension on
 // LazyCollectionProtocol (below) is not selected for some reason.
 extension ${TraversalCollection} where Self : LazyCollectionProtocol {
@@ -263,12 +264,15 @@
     return self
   }
 }
-
 % end
 
 extension Slice: LazySequenceProtocol where Base: LazySequenceProtocol { }
 extension Slice: LazyCollectionProtocol where Base: LazyCollectionProtocol { }
 
+@available(*, deprecated, renamed: "LazyCollection")
+public typealias LazyBidirectionalCollection<T> = LazyCollection<T> where T : BidirectionalCollection
+@available(*, deprecated, renamed: "LazyCollection")
+public typealias LazyRandomAccessCollection<T> = LazyCollection<T> where T : RandomAccessCollection
 // ${'Local Variables'}:
 // eval: (read-only-mode 1)
 // End:
diff --git a/stdlib/public/core/Map.swift.gyb b/stdlib/public/core/Map.swift
similarity index 85%
rename from stdlib/public/core/Map.swift.gyb
rename to stdlib/public/core/Map.swift
index 1a73442..a8a0d90 100644
--- a/stdlib/public/core/Map.swift.gyb
+++ b/stdlib/public/core/Map.swift
@@ -1,4 +1,4 @@
-//===--- Map.swift.gyb - Lazily map over a Sequence -----------*- swift -*-===//
+//===--- Map.swift - Lazily map over a Sequence ---------------*- swift -*-===//
 //
 // This source file is part of the Swift.org open source project
 //
@@ -10,13 +10,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-%{
-from gyb_stdlib_support import (
-    TRAVERSALS,
-    collectionForTraversal
-)
-}%
-
 /// The `IteratorProtocol` used by `MapSequence` and `MapCollection`.
 /// Produces each element by passing the output of the `Base`
 /// `IteratorProtocol` through a transform function returning `Element`.
@@ -96,22 +89,14 @@
 
 //===--- Collections ------------------------------------------------------===//
 
-// FIXME(ABI)#45 (Conditional Conformance): `LazyMap*Collection` types should be
-// collapsed into one `LazyMapCollection` using conditional conformances.
-// Maybe even combined with `LazyMapSequence`.
-// rdar://problem/17144340
-
-% for Traversal in TRAVERSALS:
-%   Self = "LazyMap" + collectionForTraversal(Traversal)
-
 /// A `Collection` whose elements consist of those in a `Base`
 /// `Collection` passed through a transform function returning `Element`.
 /// These elements are computed lazily, each time they're read, by
 /// calling the transform function on a base element.
 @_fixed_layout
-public struct ${Self}<
-  Base : ${collectionForTraversal(Traversal)}, Element
-> : LazyCollectionProtocol, ${collectionForTraversal(Traversal)} {
+public struct LazyMapCollection<
+  Base : Collection, Element
+> : LazyCollectionProtocol, Collection {
 
   // FIXME(compiler limitation): should be inferable.
   public typealias Index = Base.Index
@@ -129,16 +114,6 @@
     _base.formIndex(after: &i)
   }
 
-%   if Traversal in ['Bidirectional', 'RandomAccess']:
-  @_inlineable
-  public func index(before i: Index) -> Index { return _base.index(before: i) }
-
-  @_inlineable
-  public func formIndex(before i: inout Index) {
-    _base.formIndex(before: &i)
-  }
-%   end
-
   /// Accesses the element at `position`.
   ///
   /// - Precondition: `position` is a valid position in `self` and
@@ -148,7 +123,7 @@
     return _transform(_base[position])
   }
 
-  public typealias SubSequence = ${Self}<Base.SubSequence, Element>
+  public typealias SubSequence = LazyMapCollection<Base.SubSequence, Element>
 
   @_inlineable
   public subscript(bounds: Range<Base.Index>) -> SubSequence {
@@ -183,11 +158,6 @@
   @_inlineable
   public var first: Element? { return _base.first.map(_transform) }
 
-%   if Traversal in ['Bidirectional', 'RandomAccess']:
-  @_inlineable
-  public var last: Element? { return _base.last.map(_transform) }
-%   end
-
   @_inlineable
   public func index(_ i: Index, offsetBy n: Int) -> Index {
     return _base.index(i, offsetBy: n)
@@ -233,7 +203,24 @@
   internal let _transform: (Base.Element) -> Element
 }
 
-% end
+extension LazyMapCollection : BidirectionalCollection
+  where Base : BidirectionalCollection {
+
+  @_inlineable
+  public func index(before i: Index) -> Index { return _base.index(before: i) }
+
+  @_inlineable
+  public func formIndex(before i: inout Index) {
+    _base.formIndex(before: &i)
+  }
+
+  @_inlineable
+  public var last: Element? { return _base.last.map(_transform) }
+}
+
+extension LazyMapCollection : RandomAccessCollection
+  where Base : RandomAccessCollection {}
+
 
 //===--- Support for s.lazy -----------------------------------------------===//
 
@@ -249,30 +236,18 @@
   }
 }
 
-% for Traversal in TRAVERSALS:
-
-extension LazyCollectionProtocol
-%   if Traversal != 'Forward':
-  where
-  Self : ${collectionForTraversal(Traversal)},
-  Elements : ${collectionForTraversal(Traversal)}
-%   end
-{
+extension LazyCollectionProtocol {
   /// Returns a `LazyMapCollection` over this `Collection`.  The elements of
   /// the result are computed lazily, each time they are read, by
   /// calling `transform` function on a base element.
   @_inlineable
   public func map<U>(
     _ transform: @escaping (Elements.Element) -> U
-  ) -> LazyMap${collectionForTraversal(Traversal)}<Self.Elements, U> {
-    return LazyMap${collectionForTraversal(Traversal)}(
-      _base: self.elements,
-      transform: transform)
+  ) -> LazyMapCollection<Self.Elements, U> {
+    return LazyMapCollection(_base: self.elements, transform: transform)
   }
 }
 
-% end
-
 extension LazyMapCollection {
   // This overload is needed to re-enable Swift 3 source compatibility related
   // to a bugfix in ranking behavior of the constraint solver.
@@ -289,6 +264,7 @@
   }
 }
 
-// ${'Local Variables'}:
-// eval: (read-only-mode 1)
-// End:
+@available(*, deprecated, renamed: "LazyMapCollection")
+public typealias LazyMapBidirectionalCollection<T, E> = LazyMapCollection<T, E> where T : BidirectionalCollection
+@available(*, deprecated, renamed: "LazyMapCollection")
+public typealias LazyMapRandomAccessCollection<T, E> = LazyMapCollection<T, E> where T : RandomAccessCollection
diff --git a/stdlib/public/core/Reverse.swift b/stdlib/public/core/Reverse.swift
index f9303ad..da7c0c9 100644
--- a/stdlib/public/core/Reverse.swift
+++ b/stdlib/public/core/Reverse.swift
@@ -312,25 +312,7 @@
   ///
   /// - Complexity: O(1)
   @_inlineable
-  public func reversed() -> LazyBidirectionalCollection<
-    ReversedCollection<Elements>
-  > {
-    return ReversedCollection(_base: elements).lazy
-  }
-}
-
-extension LazyCollectionProtocol
-  where
-  Self : RandomAccessCollection,
-  Elements : RandomAccessCollection {
-
-  /// Returns the elements of the collection in reverse order.
-  ///
-  /// - Complexity: O(1)
-  @_inlineable
-  public func reversed() -> LazyRandomAccessCollection<
-    ReversedCollection<Elements>
-  > {
+  public func reversed() -> LazyCollection<ReversedCollection<Elements>> {
     return ReversedCollection(_base: elements).lazy
   }
 }
diff --git a/validation-test/stdlib/Collection/LazyFilterCollection.swift.gyb b/validation-test/stdlib/Collection/LazyFilterCollection.swift.gyb
index ff3513f..0aa6967 100644
--- a/validation-test/stdlib/Collection/LazyFilterCollection.swift.gyb
+++ b/validation-test/stdlib/Collection/LazyFilterCollection.swift.gyb
@@ -15,12 +15,12 @@
 // Test collections using value types as elements.
 % for (traversal, kind) in variations:
 CollectionTests.add${traversal}${kind}Tests(
-  make${kind}: { (elements: [OpaqueValue<Int>]) -> LazyFilter${traversal}${kind}<Minimal${traversal}${kind}<OpaqueValue<Int>>> in
+  make${kind}: { (elements: [OpaqueValue<Int>]) -> LazyFilter${kind}<Minimal${traversal}${kind}<OpaqueValue<Int>>> in
     Minimal${traversal}${kind}(elements: elements).lazy.filter { _ in return true }
   },
   wrapValue: identity,
   extractValue: identity,
-  make${kind}OfEquatable: { (elements: [MinimalEquatableValue]) -> LazyFilter${traversal}${kind}<Minimal${traversal}${kind}<MinimalEquatableValue>> in
+  make${kind}OfEquatable: { (elements: [MinimalEquatableValue]) -> LazyFilter${kind}<Minimal${traversal}${kind}<MinimalEquatableValue>> in
     Minimal${traversal}${kind}(elements: elements).lazy.filter { _ in return true }
   },
   wrapValueIntoEquatable: identityEq,
@@ -31,7 +31,7 @@
 // Test collections using reference types as elements.
 % for (traversal, kind) in variations:
 CollectionTests.add${traversal}${kind}Tests(
-  make${kind}: { (elements: [LifetimeTracked]) -> LazyFilter${traversal}${kind}<Minimal${traversal}${kind}<LifetimeTracked>> in
+  make${kind}: { (elements: [LifetimeTracked]) -> LazyFilter${kind}<Minimal${traversal}${kind}<LifetimeTracked>> in
     // FIXME: create a better sequence and filter
     Minimal${traversal}${kind}(elements: elements).lazy.filter { _ in return true }
   },
@@ -41,7 +41,7 @@
   extractValue: { (element: LifetimeTracked) in
     OpaqueValue(element.value, identity: element.identity)
   },
-  make${kind}OfEquatable: { (elements: [LifetimeTracked]) -> LazyFilter${traversal}${kind}<Minimal${traversal}${kind}<LifetimeTracked>> in
+  make${kind}OfEquatable: { (elements: [LifetimeTracked]) -> LazyFilter${kind}<Minimal${traversal}${kind}<LifetimeTracked>> in
     // FIXME: create a better sequence and filter
     Minimal${traversal}${kind}(elements: elements).lazy.filter { _ in return true }
   },
diff --git a/validation-test/stdlib/Collection/LazyMapBidirectionalCollection.swift b/validation-test/stdlib/Collection/LazyMapBidirectionalCollection.swift
index de4162b..38acd9b 100644
--- a/validation-test/stdlib/Collection/LazyMapBidirectionalCollection.swift
+++ b/validation-test/stdlib/Collection/LazyMapBidirectionalCollection.swift
@@ -15,12 +15,12 @@
 
 // Test collections using value types as elements.
 CollectionTests.addBidirectionalCollectionTests(
-  makeCollection: { (elements: [OpaqueValue<Int>]) -> LazyMapBidirectionalCollection<MinimalBidirectionalCollection<OpaqueValue<Int>>, OpaqueValue<Int>> in
+  makeCollection: { (elements: [OpaqueValue<Int>]) -> LazyMapCollection<MinimalBidirectionalCollection<OpaqueValue<Int>>, OpaqueValue<Int>> in
     MinimalBidirectionalCollection(elements: elements).lazy.map(identity)
   },
   wrapValue: identity,
   extractValue: identity,
-  makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) -> LazyMapBidirectionalCollection<MinimalBidirectionalCollection<MinimalEquatableValue>, MinimalEquatableValue> in
+  makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) -> LazyMapCollection<MinimalBidirectionalCollection<MinimalEquatableValue>, MinimalEquatableValue> in
     MinimalBidirectionalCollection(elements: elements).lazy.map(identityEq)
   },
   wrapValueIntoEquatable: identityEq,
@@ -29,7 +29,7 @@
 
 // Test collections using reference types as elements.
 CollectionTests.addBidirectionalCollectionTests(
-  makeCollection: { (elements: [LifetimeTracked]) -> LazyMapBidirectionalCollection<MinimalBidirectionalCollection<LifetimeTracked>, LifetimeTracked> in
+  makeCollection: { (elements: [LifetimeTracked]) -> LazyMapCollection<MinimalBidirectionalCollection<LifetimeTracked>, LifetimeTracked> in
     MinimalBidirectionalCollection(elements: elements).lazy.map { $0 }
   },
   wrapValue: { (element: OpaqueValue<Int>) in
@@ -38,7 +38,7 @@
   extractValue: { (element: LifetimeTracked) in
     OpaqueValue(element.value, identity: element.identity)
   },
-  makeCollectionOfEquatable: { (elements: [LifetimeTracked]) -> LazyMapBidirectionalCollection<MinimalBidirectionalCollection<LifetimeTracked>, LifetimeTracked> in
+  makeCollectionOfEquatable: { (elements: [LifetimeTracked]) -> LazyMapCollection<MinimalBidirectionalCollection<LifetimeTracked>, LifetimeTracked> in
     MinimalBidirectionalCollection(elements: elements).lazy.map { $0 }
   },
   wrapValueIntoEquatable: { (element: MinimalEquatableValue) in
diff --git a/validation-test/stdlib/Lazy.swift.gyb b/validation-test/stdlib/Lazy.swift.gyb
index 58b6e57..22e4a79 100644
--- a/validation-test/stdlib/Lazy.swift.gyb
+++ b/validation-test/stdlib/Lazy.swift.gyb
@@ -507,7 +507,7 @@
     OpaqueValue(Int32(input.value))
   }
   expectType(
-    LazyMap${TraversalCollection}<
+    LazyMapCollection<
       Minimal${TraversalCollection}<OpaqueValue<Int>>, OpaqueValue<Int32>
     >.self,
     &lazyMap)
@@ -810,7 +810,7 @@
   expectEqual(0, calls)
 
   expectType(
-    LazyMap${TraversalCollection}<
+    LazyMapCollection<
       Minimal${TraversalCollection}<OpaqueValue<Int>>,
       OpaqueValue<Double>>.self,
     &mapped)
@@ -880,22 +880,22 @@
 
 tests.test("LazyMapBidirectionalCollection/AssociatedTypes") {
   typealias Base = MinimalBidirectionalCollection<OpaqueValue<Int>>
-  typealias Subject = LazyMapBidirectionalCollection<Base, OpaqueValue<Int32>>
+  typealias Subject = LazyMapCollection<Base, OpaqueValue<Int32>>
   expectBidirectionalCollectionAssociatedTypes(
     collectionType: Subject.self,
     iteratorType: LazyMapIterator<Base.Iterator, OpaqueValue<Int32>>.self,
-    subSequenceType: LazyMapBidirectionalCollection<Base.SubSequence, OpaqueValue<Int32>>.self,
+    subSequenceType: LazyMapCollection<Base.SubSequence, OpaqueValue<Int32>>.self,
     indexType: Base.Index.self,
     indicesType: Base.Indices.self)
 }
 
 tests.test("LazyMapRandomAccessCollection/AssociatedTypes") {
   typealias Base = MinimalRandomAccessCollection<OpaqueValue<Int>>
-  typealias Subject = LazyMapRandomAccessCollection<Base, OpaqueValue<Int32>>
+  typealias Subject = LazyMapCollection<Base, OpaqueValue<Int32>>
   expectRandomAccessCollectionAssociatedTypes(
     collectionType: Subject.self,
     iteratorType: LazyMapIterator<Base.Iterator, OpaqueValue<Int32>>.self,
-    subSequenceType: LazyMapRandomAccessCollection<Base.SubSequence, OpaqueValue<Int32>>.self,
+    subSequenceType: LazyMapCollection<Base.SubSequence, OpaqueValue<Int32>>.self,
     indexType: Base.Index.self,
     indicesType: Base.Indices.self)
 }
@@ -926,7 +926,7 @@
     var mapped = MinimalBidirectionalCollection(elements: baseArray)
       .lazy.map { _ in OpaqueValue<Int8>(0) }
     expectType(
-      LazyMapBidirectionalCollection<
+      LazyMapCollection<
         MinimalBidirectionalCollection<OpaqueValue<Int>>,
         OpaqueValue<Int8>
       >.self,
@@ -936,7 +936,7 @@
     var mapped = MinimalRandomAccessCollection(elements: baseArray)
       .lazy.map { _ in OpaqueValue<Int8>(0) }
     expectType(
-      LazyMapRandomAccessCollection<
+      LazyMapCollection<
         MinimalRandomAccessCollection<OpaqueValue<Int>>,
         OpaqueValue<Int8>
       >.self,
@@ -960,14 +960,14 @@
   }
 
   checkBidirectionalCollection(
-    "raboof".characters,
-    "foobar".characters.reversed())
+    "raboof",
+    "foobar".reversed())
 
   // Check that the reverse collection is still eager
   do {
     var calls = 0
-    _ = "foobar".characters.reversed().map { _ in calls += 1 }
-    expectEqual("foobar".characters.count, calls)
+    _ = "foobar".reversed().map { _ in calls += 1 }
+    expectEqual("foobar".count, calls)
   }
 }
 
@@ -985,11 +985,10 @@
 
     let base = Array(stride(from: 11, through: 0, by: -1)).lazy.map { $0 }
 
-    typealias Base = LazyMapRandomAccessCollection<[Int], Int>
+    typealias Base = LazyMapCollection<[Int], Int>
     ExpectType<Base>.test(base)
 
-    typealias LazyReversedBase = LazyRandomAccessCollection<
-      ReversedCollection<Base>>
+    typealias LazyReversedBase = LazyCollection<ReversedCollection<Base>>
 
     let reversed = base.reversed()
     ExpectType<LazyReversedBase>.test(reversed)
@@ -1002,17 +1001,11 @@
   }
 
   do {
-    typealias Expected = LazyBidirectionalCollection<
-      ReversedCollection<String.CharacterView>
-    >
-
-    let base = "foobar".characters.lazy.map { $0 }
-    typealias Base = LazyMapBidirectionalCollection<
-      String.CharacterView, Character>
+    let base = "foobar".lazy.map { $0 }
+    typealias Base = LazyMapCollection<String, Character>
     ExpectType<Base>.test(base)
 
-    typealias LazyReversedBase = LazyBidirectionalCollection<
-      ReversedCollection<Base>>
+    typealias LazyReversedBase = LazyCollection<ReversedCollection<Base>>
 
     let reversed = base.reversed()
     ExpectType<LazyReversedBase>.test(reversed)
@@ -1020,7 +1013,7 @@
     var calls = 0
     let reversedAndMapped = reversed.map { (x) -> Character in calls += 1; return x }
     expectEqual(0, calls)
-    checkBidirectionalCollection("raboof".characters, reversedAndMapped)
+    checkBidirectionalCollection("raboof", reversedAndMapped)
     expectNotEqual(0, calls)
   }
 }
@@ -1161,18 +1154,18 @@
     collectionType: Subject.self,
     iteratorType: LazyFilterIterator<Base.Iterator>.self,
     subSequenceType: LazyFilterCollection<Base.SubSequence>.self,
-    indexType: LazyFilterIndex<Base>.self,
+    indexType: Base.Index.self,
     indicesType: DefaultIndices<Subject>.self)
 }
 
 tests.test("LazyFilterBidirectionalCollection/AssociatedTypes") {
   typealias Base = MinimalBidirectionalCollection<OpaqueValue<Int>>
-  typealias Subject = LazyFilterBidirectionalCollection<Base>
+  typealias Subject = LazyFilterCollection<Base>
   expectBidirectionalCollectionAssociatedTypes(
     collectionType: Subject.self,
     iteratorType: LazyFilterIterator<Base.Iterator>.self,
-    subSequenceType: LazyFilterBidirectionalCollection<Base.SubSequence>.self,
-    indexType: LazyFilterIndex<Base>.self,
+    subSequenceType: LazyFilterCollection<Base.SubSequence>.self,
+    indexType: Base.Index.self,
     indicesType: DefaultBidirectionalIndices<Subject>.self)
 }
 
@@ -1196,7 +1189,7 @@
     var filtered = MinimalBidirectionalCollection(elements: baseArray)
       .lazy.filter { _ in true }
     expectType(
-      LazyFilterBidirectionalCollection<
+      LazyFilterCollection<
         MinimalBidirectionalCollection<OpaqueValue<Int>>
       >.self,
       &filtered)
@@ -1205,7 +1198,7 @@
     var filtered = MinimalRandomAccessCollection(elements: baseArray)
       .lazy.filter { _ in true }
     expectType(
-      LazyFilterBidirectionalCollection<
+      LazyFilterCollection<
         MinimalRandomAccessCollection<OpaqueValue<Int>>
       >.self,
       &filtered)
diff --git a/validation-test/stdlib/Range.swift.gyb b/validation-test/stdlib/Range.swift.gyb
index d94930d..a9675e2 100644
--- a/validation-test/stdlib/Range.swift.gyb
+++ b/validation-test/stdlib/Range.swift.gyb
@@ -784,7 +784,7 @@
 
 MiscTestSuite.test("reversed()") {
   var result = (0..<10).lazy.reversed()
-  typealias Expected = LazyRandomAccessCollection<
+  typealias Expected = LazyCollection<
     ReversedRandomAccessCollection<CountableRange<Int>>>
     expectType(Expected.self, &result)
   expectEqualSequence(