Merge pull request #22695 from milseman/5_0_en_gadus_offset

[5.0][String.Index] Deprecate encodedOffset var/init
diff --git a/stdlib/public/SDK/Foundation/URLComponents.swift b/stdlib/public/SDK/Foundation/URLComponents.swift
index 2d6eabf..2ba4914 100644
--- a/stdlib/public/SDK/Foundation/URLComponents.swift
+++ b/stdlib/public/SDK/Foundation/URLComponents.swift
@@ -192,16 +192,8 @@
     
     @available(macOS 10.11, iOS 9.0, *)
     private func _toStringRange(_ r : NSRange) -> Range<String.Index>? {
-        guard r.location != NSNotFound else { return nil }
-        
-        let utf16Start = String.UTF16View.Index(encodedOffset: r.location)
-        let utf16End = String.UTF16View.Index(encodedOffset: r.location + r.length)
-
         guard let s = self.string else { return nil }
-        guard let start = String.Index(utf16Start, within: s) else { return nil }
-        guard let end = String.Index(utf16End, within: s) else { return nil }
-        
-        return start..<end
+        return Range(r, in: s)
     }
     
     /// Returns the character range of the scheme in the string returned by `var string`.
diff --git a/stdlib/public/SDK/NaturalLanguage/NLTagger.swift b/stdlib/public/SDK/NaturalLanguage/NLTagger.swift
index a4e39a3..138d448 100644
--- a/stdlib/public/SDK/NaturalLanguage/NLTagger.swift
+++ b/stdlib/public/SDK/NaturalLanguage/NLTagger.swift
@@ -18,7 +18,7 @@
   @nonobjc
   public func tokenRange(at index: String.Index, unit: NLTokenUnit) -> Range<String.Index> {
     let str = self.string ?? ""
-    let characterIndex = index.encodedOffset
+    let characterIndex = index.utf16Offset(in: str)
     let nsrange = self.__tokenRange(at: characterIndex, unit: unit)
     return Range(nsrange, in: str)!
   }
@@ -26,7 +26,7 @@
   @nonobjc
   public func tag(at index: String.Index, unit: NLTokenUnit, scheme: NLTagScheme) -> (NLTag?, Range<String.Index>) {
     let str = self.string ?? ""
-    let characterIndex = index.encodedOffset
+    let characterIndex = index.utf16Offset(in: str)
     let rangePointer = NSRangePointer.allocate(capacity: 1)
     rangePointer.initialize(to: NSMakeRange(0, 0))
     let tag = self.__tag(at: characterIndex, unit: unit, scheme: scheme, tokenRange: rangePointer)
diff --git a/stdlib/public/SDK/NaturalLanguage/NLTokenizer.swift b/stdlib/public/SDK/NaturalLanguage/NLTokenizer.swift
index 0cfc2e6..ca8b337 100644
--- a/stdlib/public/SDK/NaturalLanguage/NLTokenizer.swift
+++ b/stdlib/public/SDK/NaturalLanguage/NLTokenizer.swift
@@ -18,7 +18,7 @@
   @nonobjc
   public func tokenRange(at index: String.Index) -> Range<String.Index> {
     let str = self.string ?? ""
-    let characterIndex = index.encodedOffset
+    let characterIndex = index.utf16Offset(in: str)
     let nsrange = self.__tokenRange(at:characterIndex)
     return Range(nsrange, in: str)!
   }
diff --git a/stdlib/public/core/String.swift b/stdlib/public/core/String.swift
index 0bc6342..c3d93ed 100644
--- a/stdlib/public/core/String.swift
+++ b/stdlib/public/core/String.swift
@@ -923,8 +923,8 @@
   var icuInputBuffer = icuInputBuffer
   var icuOutputBuffer = icuOutputBuffer
 
-  var index = String.Index(encodedOffset: 0)
-  let cachedEndIndex = String.Index(encodedOffset: sourceBuffer.count)
+  var index = String.Index(_encodedOffset: 0)
+  let cachedEndIndex = String.Index(_encodedOffset: sourceBuffer.count)
   
   var hasBufferOwnership = false
   
diff --git a/stdlib/public/core/StringBreadcrumbs.swift b/stdlib/public/core/StringBreadcrumbs.swift
index 5e8625d..1ecae71 100644
--- a/stdlib/public/core/StringBreadcrumbs.swift
+++ b/stdlib/public/core/StringBreadcrumbs.swift
@@ -79,8 +79,8 @@
   internal func getBreadcrumb(
     forIndex idx: String.Index
   ) -> (lowerBound: String.Index, offset: Int) {
-    var lowerBound = idx.encodedOffset / 3 / stride
-    var upperBound = Swift.min(1 + (idx.encodedOffset / stride), crumbs.count)
+    var lowerBound = idx._encodedOffset / 3 / stride
+    var upperBound = Swift.min(1 + (idx._encodedOffset / stride), crumbs.count)
     _internalInvariant(crumbs[lowerBound] <= idx)
     _internalInvariant(upperBound == crumbs.count || crumbs[upperBound] >= idx)
 
diff --git a/stdlib/public/core/StringCharacterView.swift b/stdlib/public/core/StringCharacterView.swift
index 154dc4f..d25e984 100644
--- a/stdlib/public/core/StringCharacterView.swift
+++ b/stdlib/public/core/StringCharacterView.swift
@@ -66,9 +66,9 @@
 
     // TODO: known-ASCII fast path, single-scalar-grapheme fast path, etc.
     let stride = _characterStride(startingAt: i)
-    let nextOffset = i.encodedOffset &+ stride
+    let nextOffset = i._encodedOffset &+ stride
     let nextStride = _characterStride(
-      startingAt: Index(encodedOffset: nextOffset))
+      startingAt: Index(_encodedOffset: nextOffset))
 
     return Index(
       encodedOffset: nextOffset, characterStride: nextStride)
@@ -84,7 +84,7 @@
 
     // TODO: known-ASCII fast path, single-scalar-grapheme fast path, etc.
     let stride = _characterStride(endingAt: i)
-    let priorOffset = i.encodedOffset &- stride
+    let priorOffset = i._encodedOffset &- stride
     return Index(encodedOffset: priorOffset, characterStride: stride)
   }
   /// Returns an index that is the specified distance from the given index.
@@ -198,7 +198,7 @@
       let i = _guts.scalarAlign(i)
       let distance = _characterStride(startingAt: i)
       return _guts.errorCorrectedCharacter(
-        startingAt: i.encodedOffset, endingAt: i.encodedOffset &+ distance)
+        startingAt: i._encodedOffset, endingAt: i._encodedOffset &+ distance)
     }
   }
 
@@ -209,14 +209,14 @@
 
     if i == endIndex { return 0 }
 
-    return _guts._opaqueCharacterStride(startingAt: i.encodedOffset)
+    return _guts._opaqueCharacterStride(startingAt: i._encodedOffset)
   }
 
   @inlinable @inline(__always)
   internal func _characterStride(endingAt i: Index) -> Int {
     if i == startIndex { return 0 }
 
-    return _guts._opaqueCharacterStride(endingAt: i.encodedOffset)
+    return _guts._opaqueCharacterStride(endingAt: i._encodedOffset)
   }
 }
 
diff --git a/stdlib/public/core/StringComparison.swift b/stdlib/public/core/StringComparison.swift
index 42b749d..3f776bf 100644
--- a/stdlib/public/core/StringComparison.swift
+++ b/stdlib/public/core/StringComparison.swift
@@ -328,10 +328,10 @@
     if _fastPath(self.isFastUTF8 && other.isFastUTF8) {
       return self.withFastUTF8 { leftUTF8 in
         other.withFastUTF8 { rightUTF8 in
-          let leftStartIndex = String.Index(encodedOffset: 0)
-          let rightStartIndex = String.Index(encodedOffset: 0)
-          let leftEndIndex = String.Index(encodedOffset: leftUTF8.count)
-          let rightEndIndex = String.Index(encodedOffset: rightUTF8.count)
+          let leftStartIndex = String.Index(_encodedOffset: 0)
+          let rightStartIndex = String.Index(_encodedOffset: 0)
+          let leftEndIndex = String.Index(_encodedOffset: leftUTF8.count)
+          let rightEndIndex = String.Index(_encodedOffset: rightUTF8.count)
           return _normalizedCompareImpl(
             left_outputBuffer: _castOutputBuffer(&left_output),
             left_icuInputBuffer: _castOutputBuffer(&left_icuInput),
diff --git a/stdlib/public/core/StringGraphemeBreaking.swift b/stdlib/public/core/StringGraphemeBreaking.swift
index 047185b..21222e8 100644
--- a/stdlib/public/core/StringGraphemeBreaking.swift
+++ b/stdlib/public/core/StringGraphemeBreaking.swift
@@ -156,7 +156,7 @@
   internal func isOnGraphemeClusterBoundary(_ i: String.Index) -> Bool {
     guard i.transcodedOffset == 0 else { return false }
 
-    let offset = i.encodedOffset
+    let offset = i._encodedOffset
     if offset == 0 || offset == self.count { return true }
 
     guard isOnUnicodeScalarBoundary(i) else { return false }
@@ -197,7 +197,7 @@
     let count = _object.largeCount
     let cocoa = _object.cocoaObject
 
-    let startIdx = String.Index(encodedOffset: i)
+    let startIdx = String.Index(_encodedOffset: i)
     let (sc1, len) = foreignErrorCorrectedScalar(startingAt: startIdx)
     if i &+ len == count {
       // Last scalar is last grapheme
@@ -263,7 +263,7 @@
     let count = _object.largeCount
     let cocoa = _object.cocoaObject
 
-    let endIdx = String.Index(encodedOffset: i)
+    let endIdx = String.Index(_encodedOffset: i)
     let (sc2, len) = foreignErrorCorrectedScalar(endingAt: endIdx)
     if i &- len == 0 {
       // First scalar is first grapheme
diff --git a/stdlib/public/core/StringGuts.swift b/stdlib/public/core/StringGuts.swift
index f90a886..0b4b580 100644
--- a/stdlib/public/core/StringGuts.swift
+++ b/stdlib/public/core/StringGuts.swift
@@ -274,11 +274,11 @@
 
   @inlinable
   internal var startIndex: String.Index {
-    @inline(__always) get { return Index(encodedOffset: 0) }
+    @inline(__always) get { return Index(_encodedOffset: 0) }
   }
   @inlinable
   internal var endIndex: String.Index {
-    @inline(__always) get { return Index(encodedOffset: self.count) }
+    @inline(__always) get { return Index(_encodedOffset: self.count) }
   }
 }
 
diff --git a/stdlib/public/core/StringGutsRangeReplaceable.swift b/stdlib/public/core/StringGutsRangeReplaceable.swift
index fc27b12..67f524a 100644
--- a/stdlib/public/core/StringGutsRangeReplaceable.swift
+++ b/stdlib/public/core/StringGutsRangeReplaceable.swift
@@ -218,8 +218,8 @@
   }
 
   internal mutating func remove(from lower: Index, to upper: Index) {
-    let lowerOffset = lower.encodedOffset
-    let upperOffset = upper.encodedOffset
+    let lowerOffset = lower._encodedOffset
+    let upperOffset = upper._encodedOffset
     _internalInvariant(lower.transcodedOffset == 0 && upper.transcodedOffset == 0)
     _internalInvariant(lowerOffset <= upperOffset && upperOffset <= self.count)
 
@@ -279,16 +279,16 @@
     isASCII: Bool
   ) {
     let neededCapacity =
-      bounds.lowerBound.encodedOffset
-      + codeUnits.count + (self.count - bounds.upperBound.encodedOffset)
+      bounds.lowerBound._encodedOffset
+      + codeUnits.count + (self.count - bounds.upperBound._encodedOffset)
     reserveCapacity(neededCapacity)
 
     _internalInvariant(bounds.lowerBound.transcodedOffset == 0)
     _internalInvariant(bounds.upperBound.transcodedOffset == 0)
 
     _object.nativeStorage.replace(
-      from: bounds.lowerBound.encodedOffset,
-      to: bounds.upperBound.encodedOffset,
+      from: bounds.lowerBound._encodedOffset,
+      to: bounds.upperBound._encodedOffset,
       with: codeUnits)
     self = _StringGuts(_object.nativeStorage)
   }
@@ -300,16 +300,16 @@
     let replCount = codeUnits.count
 
     let neededCapacity =
-      bounds.lowerBound.encodedOffset
-      + replCount + (self.count - bounds.upperBound.encodedOffset)
+      bounds.lowerBound._encodedOffset
+      + replCount + (self.count - bounds.upperBound._encodedOffset)
     reserveCapacity(neededCapacity)
 
     _internalInvariant(bounds.lowerBound.transcodedOffset == 0)
     _internalInvariant(bounds.upperBound.transcodedOffset == 0)
 
     _object.nativeStorage.replace(
-      from: bounds.lowerBound.encodedOffset,
-      to: bounds.upperBound.encodedOffset,
+      from: bounds.lowerBound._encodedOffset,
+      to: bounds.upperBound._encodedOffset,
       with: codeUnits,
       replacementCount: replCount)
     self = _StringGuts(_object.nativeStorage)
diff --git a/stdlib/public/core/StringGutsSlice.swift b/stdlib/public/core/StringGutsSlice.swift
index 3c6d25d4..614c294 100644
--- a/stdlib/public/core/StringGutsSlice.swift
+++ b/stdlib/public/core/StringGutsSlice.swift
@@ -74,8 +74,8 @@
   @inlinable
   internal var range: Range<String.Index> {
     @inline(__always) get {
-      return String.Index(encodedOffset: _offsetRange.lowerBound)
-         ..< String.Index(encodedOffset: _offsetRange.upperBound)
+      return String.Index(_encodedOffset: _offsetRange.lowerBound)
+         ..< String.Index(_encodedOffset: _offsetRange.upperBound)
     }
   }
 
diff --git a/stdlib/public/core/StringIndex.swift b/stdlib/public/core/StringIndex.swift
index c1e8d0f..7974004 100644
--- a/stdlib/public/core/StringIndex.swift
+++ b/stdlib/public/core/StringIndex.swift
@@ -62,10 +62,22 @@
     @inline(__always) get { return orderingValue == 0 }
   }
 
+  /// The UTF-16 code unit offset corresponding to this Index
+  public func utf16Offset<S: StringProtocol>(in s: S) -> Int {
+    return s.utf16.distance(from: s.utf16.startIndex, to: self)
+  }
+
   /// The offset into a string's code units for this index.
+  @available(swift, deprecated: 4.2, message: """
+    encodedOffset has been deprecated as most common usage is incorrect. \
+    Use utf16Offset(in:) to achieve the same behavior.
+    """)
   @inlinable
-  public var encodedOffset: Int {
-    @inline(__always) get { return Int(truncatingIfNeeded: _rawBits &>> 16) }
+  public var encodedOffset: Int { return _encodedOffset }
+
+  @inlinable @inline(__always)
+  internal var _encodedOffset: Int {
+    return Int(truncatingIfNeeded: _rawBits &>> 16)
   }
 
   @inlinable
@@ -91,12 +103,35 @@
     self.init((pos &<< 16) | (trans &<< 14))
   }
 
+  /// Creates a new index at the specified UTF-16 code unit offset
+  ///
+  /// - Parameter offset: An offset in UTF-16 code units.
+  public init<S: StringProtocol>(utf16Offset offset: Int, in s: S) {
+    let (start, end) = (s.utf16.startIndex, s.utf16.endIndex)
+    guard offset >= 0,
+          let idx = s.utf16.index(start, offsetBy: offset, limitedBy: end)
+    else {
+      self = end.nextEncoded
+      return
+    }
+    self = idx
+  }
+
   /// Creates a new index at the specified code unit offset.
   ///
   /// - Parameter offset: An offset in code units.
+  @available(swift, deprecated: 4.2, message: """
+    encodedOffset has been deprecated as most common usage is incorrect. \
+    Use String.Index(utf16Offset:in:) to achieve the same behavior.
+    """)
+  @inlinable
+  public init(encodedOffset offset: Int) {
+    self.init(_encodedOffset: offset)
+  }
+
   @inlinable @inline(__always)
-  public init(encodedOffset: Int) {
-    self.init(encodedOffset: encodedOffset, transcodedOffset: 0)
+  internal init(_encodedOffset offset: Int) {
+    self.init(encodedOffset: offset, transcodedOffset: 0)
   }
 
   @usableFromInline
@@ -121,7 +156,7 @@
   #else
   @usableFromInline @inline(never) @_effects(releasenone)
   internal func _invariantCheck() {
-    _internalInvariant(encodedOffset >= 0)
+    _internalInvariant(_encodedOffset >= 0)
   }
   #endif // INTERNAL_CHECKS_ENABLED
 }
@@ -132,7 +167,7 @@
   @inlinable
   internal var strippingTranscoding: String.Index {
     @inline(__always) get {
-      return String.Index(encodedOffset: self.encodedOffset)
+      return String.Index(_encodedOffset: self._encodedOffset)
     }
   }
 
@@ -140,7 +175,7 @@
   internal var nextEncoded: String.Index {
     @inline(__always) get {
       _internalInvariant(self.transcodedOffset == 0)
-      return String.Index(encodedOffset: self.encodedOffset &+ 1)
+      return String.Index(_encodedOffset: self._encodedOffset &+ 1)
     }
   }
 
@@ -148,7 +183,7 @@
   internal var priorEncoded: String.Index {
     @inline(__always) get {
       _internalInvariant(self.transcodedOffset == 0)
-      return String.Index(encodedOffset: self.encodedOffset &- 1)
+      return String.Index(_encodedOffset: self._encodedOffset &- 1)
     }
   }
 
@@ -156,7 +191,7 @@
   internal var nextTranscoded: String.Index {
     @inline(__always) get {
       return String.Index(
-        encodedOffset: self.encodedOffset,
+        encodedOffset: self._encodedOffset,
         transcodedOffset: self.transcodedOffset &+ 1)
     }
   }
@@ -165,7 +200,7 @@
   internal var priorTranscoded: String.Index {
     @inline(__always) get {
       return String.Index(
-        encodedOffset: self.encodedOffset,
+        encodedOffset: self._encodedOffset,
         transcodedOffset: self.transcodedOffset &- 1)
     }
   }
@@ -174,13 +209,13 @@
   // Note: strips any transcoded offset.
   @inlinable @inline(__always)
   internal func encoded(offsetBy n: Int) -> String.Index {
-    return String.Index(encodedOffset: self.encodedOffset &+ n)
+    return String.Index(_encodedOffset: self._encodedOffset &+ n)
   }
 
   @inlinable @inline(__always)
   internal func transcoded(withOffset n: Int) -> String.Index {
     _internalInvariant(self.transcodedOffset == 0)
-    return String.Index(encodedOffset: self.encodedOffset, transcodedOffset: n)
+    return String.Index(encodedOffset: self._encodedOffset, transcodedOffset: n)
   }
 
 }
diff --git a/stdlib/public/core/StringNormalization.swift b/stdlib/public/core/StringNormalization.swift
index 199225c..c4330b4 100644
--- a/stdlib/public/core/StringNormalization.swift
+++ b/stdlib/public/core/StringNormalization.swift
@@ -94,7 +94,7 @@
   internal func foreignHasNormalizationBoundary(
     before index: String.Index
   ) -> Bool {
-    let offset = index.encodedOffset
+    let offset = index._encodedOffset
     if offset == 0 || offset == count {
       return true
     }
@@ -350,11 +350,11 @@
   icuInputBuffer: inout UnsafeMutableBufferPointer<UInt16>,
   icuOutputBuffer: inout UnsafeMutableBufferPointer<UInt16>
 ) -> NormalizationResult {
-  let start = readIndex.encodedOffset
+  let start = readIndex._encodedOffset
   let rebasedSourceBuffer = UnsafeBufferPointer(rebasing: sourceBuffer[start...])
   if let (read, filled) = fastFill(rebasedSourceBuffer, outputBuffer) {
     let nextIndex = readIndex.encoded(offsetBy: read)
-    _internalInvariant(sourceBuffer.isOnUnicodeScalarBoundary(nextIndex.encodedOffset))
+    _internalInvariant(sourceBuffer.isOnUnicodeScalarBoundary(nextIndex._encodedOffset))
     
     return NormalizationResult(
       amountFilled: filled, nextReadPosition: nextIndex, allocatedBuffers: false)
@@ -384,7 +384,7 @@
   }
   
   let nextIndex = readIndex.encoded(offsetBy: read)
-  _internalInvariant(sourceBuffer.isOnUnicodeScalarBoundary(nextIndex.encodedOffset))
+  _internalInvariant(sourceBuffer.isOnUnicodeScalarBoundary(nextIndex._encodedOffset))
   
   let normalized = performWithAllocationIfNecessary(preserving: .icuInput) { () -> Int? in
     return _tryNormalize(
@@ -426,8 +426,8 @@
     return f()!
   }
   let (read, filled) = performWithAllocationIfNecessary(preserving: .none) { () -> (Int, Int)? in
-    let start = readIndex.encodedOffset
-    let end = endIndex.encodedOffset
+    let start = readIndex._encodedOffset
+    let end = endIndex._encodedOffset
     return copyUTF16Segment(boundedBy: start..<end, into: icuInputBuffer) { gutsOffset in
       return guts.errorCorrectedScalar(startingAt: gutsOffset)
     }
diff --git a/stdlib/public/core/StringProtocol.swift b/stdlib/public/core/StringProtocol.swift
index 433f104..4e31396 100644
--- a/stdlib/public/core/StringProtocol.swift
+++ b/stdlib/public/core/StringProtocol.swift
@@ -153,7 +153,7 @@
       let end = endIndex
       _internalInvariant(
         start.transcodedOffset == 0 && end.transcodedOffset == 0)
-      return Range(uncheckedBounds: (start.encodedOffset, end.encodedOffset))
+      return Range(uncheckedBounds: (start._encodedOffset, end._encodedOffset))
     }
   }
 
diff --git a/stdlib/public/core/StringRangeReplaceableCollection.swift b/stdlib/public/core/StringRangeReplaceableCollection.swift
index 6981d17..0edbd51 100644
--- a/stdlib/public/core/StringRangeReplaceableCollection.swift
+++ b/stdlib/public/core/StringRangeReplaceableCollection.swift
@@ -298,23 +298,23 @@
 extension String {
   @inlinable @inline(__always)
   internal func _boundsCheck(_ index: Index) {
-    _precondition(index.encodedOffset >= 0 && index.encodedOffset < _guts.count,
+    _precondition(index._encodedOffset >= 0 && index._encodedOffset < _guts.count,
       "String index is out of bounds")
   }
 
   @inlinable @inline(__always)
   internal func _boundsCheck(_ range: Range<Index>) {
     _precondition(
-      range.lowerBound.encodedOffset >= 0 &&
-      range.upperBound.encodedOffset <= _guts.count,
+      range.lowerBound._encodedOffset >= 0 &&
+      range.upperBound._encodedOffset <= _guts.count,
       "String index range is out of bounds")
   }
 
   @inlinable @inline(__always)
   internal func _boundsCheck(_ range: ClosedRange<Index>) {
     _precondition(
-      range.lowerBound.encodedOffset >= 0 &&
-      range.upperBound.encodedOffset < _guts.count,
+      range.lowerBound._encodedOffset >= 0 &&
+      range.upperBound._encodedOffset < _guts.count,
       "String index range is out of bounds")
   }
 }
diff --git a/stdlib/public/core/StringUTF16View.swift b/stdlib/public/core/StringUTF16View.swift
index 0cbd168..da372d3 100644
--- a/stdlib/public/core/StringUTF16View.swift
+++ b/stdlib/public/core/StringUTF16View.swift
@@ -149,7 +149,7 @@
 
     // For a BMP scalar (1-3 UTF-8 code units), advance past it. For a non-BMP
     // scalar, use a transcoded offset first.
-    let len = _guts.fastUTF8ScalarLength(startingAt: i.encodedOffset)
+    let len = _guts.fastUTF8ScalarLength(startingAt: i._encodedOffset)
     if len == 4 && i.transcodedOffset == 0 {
       return i.nextTranscoded
     }
@@ -167,7 +167,7 @@
       return i.strippingTranscoding
     }
 
-    let len = _guts.fastUTF8ScalarLength(endingAt: i.encodedOffset)
+    let len = _guts.fastUTF8ScalarLength(endingAt: i._encodedOffset)
     if len == 4 {
       // 2 UTF-16 code units comprise this scalar; advance to the beginning and
       // start mid-scalar transcoding
@@ -249,7 +249,7 @@
 
       if _fastPath(_guts.isFastUTF8) {
         let scalar = _guts.fastUTF8Scalar(
-          startingAt: _guts.scalarAlign(i).encodedOffset)
+          startingAt: _guts.scalarAlign(i)._encodedOffset)
         if scalar.value <= 0xFFFF {
           return UInt16(truncatingIfNeeded: scalar.value)
         }
@@ -455,7 +455,7 @@
   @_effects(releasenone)
   internal func _foreignDistance(from start: Index, to end: Index) -> Int {
     _internalInvariant(_guts.isForeign)
-    return end.encodedOffset - start.encodedOffset
+    return end._encodedOffset - start._encodedOffset
   }
 
   @usableFromInline @inline(never)
@@ -464,7 +464,7 @@
     _ i: Index, offsetBy n: Int, limitedBy limit: Index
   ) -> Index? {
     _internalInvariant(_guts.isForeign)
-    let l = limit.encodedOffset - i.encodedOffset
+    let l = limit._encodedOffset - i._encodedOffset
     if n > 0 ? l >= 0 && l < n : l <= 0 && n < l {
       return nil
     }
@@ -482,7 +482,7 @@
   @_effects(releasenone)
   internal func _foreignCount() -> Int {
     _internalInvariant(_guts.isForeign)
-    return endIndex.encodedOffset - startIndex.encodedOffset
+    return endIndex._encodedOffset - startIndex._encodedOffset
   }
 }
 
@@ -511,10 +511,10 @@
 
     if _guts.isASCII {
       _internalInvariant(idx.transcodedOffset == 0)
-      return idx.encodedOffset
+      return idx._encodedOffset
     }
 
-    if idx.encodedOffset < _shortHeuristic || !_guts.hasBreadcrumbs {
+    if idx._encodedOffset < _shortHeuristic || !_guts.hasBreadcrumbs {
       return _distance(from: startIndex, to: idx)
     }
     
@@ -534,7 +534,7 @@
     // Trivial and common: start
     if offset == 0 { return startIndex }
 
-    if _guts.isASCII { return Index(encodedOffset: offset) }
+    if _guts.isASCII { return Index(_encodedOffset: offset) }
 
     if offset < _shortHeuristic || !_guts.hasBreadcrumbs {
       return _index(startIndex, offsetBy: offset)
@@ -550,7 +550,7 @@
     if remaining == 0 { return crumb }
 
     return _guts.withFastUTF8 { utf8 in
-      var readIdx = crumb.encodedOffset
+      var readIdx = crumb._encodedOffset
       let readEnd = utf8.count
       _internalInvariant(readIdx < readEnd)
 
@@ -575,7 +575,7 @@
             _internalInvariant(utf16Len == 2)
             return Index(encodedOffset: readIdx, transcodedOffset: 1)
           }
-          return Index(encodedOffset: readIdx &+ len)
+          return Index(_encodedOffset: readIdx &+ len)
         }
 
         readIdx &+= len
@@ -598,8 +598,8 @@
     return _guts.withFastUTF8 { utf8 in
       var writeIdx = 0
       let writeEnd = buffer.count
-      var readIdx = range.lowerBound.encodedOffset
-      let readEnd = range.upperBound.encodedOffset
+      var readIdx = range.lowerBound._encodedOffset
+      let readEnd = range.upperBound._encodedOffset
 
       if isASCII {
         _internalInvariant(range.lowerBound.transcodedOffset == 0)
diff --git a/stdlib/public/core/StringUTF8View.swift b/stdlib/public/core/StringUTF8View.swift
index c76ed18..6addee3 100644
--- a/stdlib/public/core/StringUTF8View.swift
+++ b/stdlib/public/core/StringUTF8View.swift
@@ -160,7 +160,7 @@
   @inlinable @inline(__always)
   public func index(_ i: Index, offsetBy n: Int) -> Index {
     if _fastPath(_guts.isFastUTF8) {
-      _precondition(n + i.encodedOffset <= _guts.count)
+      _precondition(n + i._encodedOffset <= _guts.count)
       return i.encoded(offsetBy: n)
     }
 
@@ -175,15 +175,15 @@
       // Check the limit: ignore limit if it precedes `i` (in the correct
       // direction), otherwise must not be beyond limit (in the correct
       // direction).
-      let iOffset = i.encodedOffset
+      let iOffset = i._encodedOffset
       let result = iOffset + n
-      let limitOffset = limit.encodedOffset
+      let limitOffset = limit._encodedOffset
       if n >= 0 {
         guard limitOffset < iOffset || result <= limitOffset else { return nil }
       } else {
         guard limitOffset > iOffset || result >= limitOffset else { return nil }
       }
-      return Index(encodedOffset: result)
+      return Index(_encodedOffset: result)
     }
 
     return _foreignIndex(i, offsetBy: n, limitedBy: limit)
@@ -192,7 +192,7 @@
   @inlinable @inline(__always)
   public func distance(from i: Index, to j: Index) -> Int {
     if _fastPath(_guts.isFastUTF8) {
-      return j.encodedOffset &- i.encodedOffset
+      return j._encodedOffset &- i._encodedOffset
     }
     return _foreignDistance(from: i, to: j)
   }
@@ -214,7 +214,7 @@
     @inline(__always) get {
       String(_guts)._boundsCheck(i)
       if _fastPath(_guts.isFastUTF8) {
-        return _guts.withFastUTF8 { utf8 in utf8[_unchecked: i.encodedOffset] }
+        return _guts.withFastUTF8 { utf8 in utf8[_unchecked: i._encodedOffset] }
       }
 
       return _foreignSubscript(position: i)
diff --git a/stdlib/public/core/StringUnicodeScalarView.swift b/stdlib/public/core/StringUnicodeScalarView.swift
index ee3d68e..48465ea 100644
--- a/stdlib/public/core/StringUnicodeScalarView.swift
+++ b/stdlib/public/core/StringUnicodeScalarView.swift
@@ -114,7 +114,7 @@
     // TODO(String performance): isASCII fast-path
 
     if _fastPath(_guts.isFastUTF8) {
-      let len = _guts.fastUTF8ScalarLength(startingAt: i.encodedOffset)
+      let len = _guts.fastUTF8ScalarLength(startingAt: i._encodedOffset)
       return i.encoded(offsetBy: len)
     }
 
@@ -126,12 +126,12 @@
   /// - Precondition: The previous location exists.
   @inlinable @inline(__always)
   public func index(before i: Index) -> Index {
-    precondition(i.encodedOffset > 0)
+    precondition(i._encodedOffset > 0)
     // TODO(String performance): isASCII fast-path
 
     if _fastPath(_guts.isFastUTF8) {
       let len = _guts.withFastUTF8 { utf8 -> Int in
-        return _utf8ScalarLength(utf8, endingAt: i.encodedOffset)
+        return _utf8ScalarLength(utf8, endingAt: i._encodedOffset)
       }
       _internalInvariant(len <= 4, "invalid UTF8")
       return i.encoded(offsetBy: -len)
@@ -161,7 +161,7 @@
     @inline(__always) get {
       String(_guts)._boundsCheck(position)
       let i = _guts.scalarAlign(position)
-      return _guts.errorCorrectedScalar(startingAt: i.encodedOffset).0
+      return _guts.errorCorrectedScalar(startingAt: i._encodedOffset).0
     }
   }
 }
diff --git a/stdlib/public/core/Substring.swift b/stdlib/public/core/Substring.swift
index 33e3615..91b124d 100644
--- a/stdlib/public/core/Substring.swift
+++ b/stdlib/public/core/Substring.swift
@@ -132,7 +132,7 @@
       let end = _slice.endIndex
       _internalInvariant(start.transcodedOffset == 0 && end.transcodedOffset == 0)
 
-      return Range(uncheckedBounds: (start.encodedOffset, end.encodedOffset))
+      return Range(uncheckedBounds: (start._encodedOffset, end._encodedOffset))
     }
   }
 
diff --git a/stdlib/public/core/UnicodeHelpers.swift b/stdlib/public/core/UnicodeHelpers.swift
index f57b52c..668c1ee 100644
--- a/stdlib/public/core/UnicodeHelpers.swift
+++ b/stdlib/public/core/UnicodeHelpers.swift
@@ -217,23 +217,23 @@
   internal func scalarAlign(_ idx: Index) -> Index {
     // TODO(String performance): isASCII check
 
-    if _slowPath(idx.transcodedOffset != 0 || idx.encodedOffset == 0) {
+    if _slowPath(idx.transcodedOffset != 0 || idx._encodedOffset == 0) {
       // Transcoded indices are already scalar aligned
-      return String.Index(encodedOffset: idx.encodedOffset)
+      return String.Index(_encodedOffset: idx._encodedOffset)
     }
     if _slowPath(self.isForeign) {
       return foreignScalarAlign(idx)
     }
 
     return self.withFastUTF8 { utf8 in
-      let i = _scalarAlign(utf8, idx.encodedOffset)
+      let i = _scalarAlign(utf8, idx._encodedOffset)
 
       // If no alignment is performed, keep grapheme cache
-      if i == idx.encodedOffset {
+      if i == idx._encodedOffset {
         return idx
       }
 
-      return Index(encodedOffset: i)
+      return Index(_encodedOffset: i)
     }
   }
 
@@ -277,7 +277,7 @@
     if i == self.startIndex || i == self.endIndex { return true }
 
     if _fastPath(isFastUTF8) {
-      return self.withFastUTF8 { return !_isContinuation($0[i.encodedOffset]) }
+      return self.withFastUTF8 { return !_isContinuation($0[i._encodedOffset]) }
     }
 
     return i == foreignScalarAlign(i)
@@ -305,9 +305,9 @@
     startingAt idx: String.Index
   ) -> (Unicode.Scalar, scalarLength: Int) {
     _internalInvariant(idx.transcodedOffset == 0)
-    _internalInvariant(idx.encodedOffset < self.count)
+    _internalInvariant(idx._encodedOffset < self.count)
 
-    let start = idx.encodedOffset
+    let start = idx._encodedOffset
     let leading = _getForeignCodeUnit(at: start)
 
     if _fastPath(!_isSurrogate(leading)) {
@@ -338,10 +338,10 @@
     endingAt idx: String.Index
   ) -> (Unicode.Scalar, scalarLength: Int) {
     _internalInvariant(idx.transcodedOffset == 0)
-    _internalInvariant(idx.encodedOffset <= self.count)
-    _internalInvariant(idx.encodedOffset > 0)
+    _internalInvariant(idx._encodedOffset <= self.count)
+    _internalInvariant(idx._encodedOffset > 0)
 
-    let end = idx.encodedOffset
+    let end = idx._encodedOffset
     let trailing = _getForeignCodeUnit(at: end &- 1)
     if _fastPath(!_isSurrogate(trailing)) {
       return (Unicode.Scalar(_unchecked: UInt32(trailing)), 1)
@@ -371,9 +371,9 @@
     at idx: String.Index
   ) -> UInt16 {
     _internalInvariant(idx.transcodedOffset == 0)
-    _internalInvariant(idx.encodedOffset < self.count)
+    _internalInvariant(idx._encodedOffset < self.count)
 
-    let start = idx.encodedOffset
+    let start = idx._encodedOffset
     let cu = _getForeignCodeUnit(at: start)
     if _fastPath(!_isSurrogate(cu)) {
       return cu
@@ -402,15 +402,15 @@
   @usableFromInline @inline(never) // slow-path
   @_effects(releasenone)
   internal func foreignScalarAlign(_ idx: Index) -> Index {
-    _internalInvariant(idx.encodedOffset < self.count)
+    _internalInvariant(idx._encodedOffset < self.count)
 
     let ecCU = foreignErrorCorrectedUTF16CodeUnit(at: idx)
     if _fastPath(!_isTrailingSurrogate(ecCU)) {
       return idx
     }
-    _internalInvariant(idx.encodedOffset > 0,
+    _internalInvariant(idx._encodedOffset > 0,
       "Error-correction shouldn't give trailing surrogate at position zero")
-    return String.Index(encodedOffset: idx.encodedOffset &- 1)
+    return String.Index(_encodedOffset: idx._encodedOffset &- 1)
   }
 
   @usableFromInline @inline(never)
@@ -426,7 +426,7 @@
     let count = end &- start
     if start &- end == 1 {
       return Character(String(self.foreignErrorCorrectedScalar(
-        startingAt: String.Index(encodedOffset: start)
+        startingAt: String.Index(_encodedOffset: start)
       ).0))
     }
 
@@ -459,7 +459,7 @@
       return withFastUTF8 { _decodeScalar($0, startingAt: i) }
     }
     return foreignErrorCorrectedScalar(
-      startingAt: String.Index(encodedOffset: i))
+      startingAt: String.Index(_encodedOffset: i))
   }
   @inlinable @inline(__always)
   internal func errorCorrectedCharacter(
diff --git a/test/stdlib/StringIndex.swift b/test/stdlib/StringIndex.swift
index 925ef65..5332968 100644
--- a/test/stdlib/StringIndex.swift
+++ b/test/stdlib/StringIndex.swift
@@ -19,6 +19,7 @@
     SimpleString.largeASCII.rawValue,
     SimpleString.largeUnicode.rawValue,
     SimpleString.emoji.rawValue,
+    "",
 ]
 
 StringIndexTests.test("basic sanity checks") {
@@ -131,4 +132,76 @@
   }
 }
 
+StringIndexTests.test("UTF-16 Offsets") {
+  func validateOffsets(_ s: String) {
+    let end = s.endIndex
+    let utf16Count = s.utf16.count
+
+    expectEqual(end, String.Index(utf16Offset: utf16Count, in: s))
+    expectEqual(end, String.Index(utf16Offset: utf16Count, in: s[...]))
+
+    let pastEnd = String.Index(utf16Offset: utf16Count+1, in: s)
+
+    expectNotEqual(end, pastEnd)
+    expectEqual(pastEnd, String.Index(utf16Offset: utf16Count+1, in: s[...]))
+    expectEqual(pastEnd, String.Index(utf16Offset: utf16Count+2, in: s))
+    expectEqual(pastEnd, String.Index(utf16Offset: -1, in: s))
+    expectEqual(
+      pastEnd, String.Index(utf16Offset: Swift.max(1, utf16Count), in: s.dropFirst()))
+
+    let utf16Indices = Array(s.utf16.indices)
+    expectEqual(utf16Count, utf16Indices.count)
+    for i in 0..<utf16Indices.count {
+      let idx = String.Index(utf16Offset: i, in: s)
+      expectEqual(utf16Indices[i], idx)
+      expectEqual(i, idx.utf16Offset(in: s))
+      expectEqual(i, idx.utf16Offset(in: s[...]))
+
+      if i < s.dropLast().utf16.count {
+        expectEqual(
+          utf16Indices[i], String.Index(utf16Offset: i, in: s.dropLast()))
+        expectEqual(i, idx.utf16Offset(in: s.dropLast()))
+      } else if i == s.dropLast().utf16.count {
+        expectEqual(
+          utf16Indices[i], String.Index(utf16Offset: i, in: s.dropLast()))
+      } else {
+        expectNotEqual(
+          utf16Indices[i], String.Index(utf16Offset: i, in: s.dropLast()))
+      }
+    }
+  }
+
+  for s in simpleStrings {
+    validateOffsets(s)
+  }
+}
+
+func swift5ScalarAlign(_ idx: String.Index, in str: String) -> String.Index {
+  var idx = idx
+  while str.utf8[idx] & 0xC0 == 0x80 { str.utf8.formIndex(before: &idx) }
+  return idx
+}
+
+StringIndexTests.test("Scalar Align UTF-8 indices") {
+  // TODO: Test a new aligning API when we add it. For now, we
+  // test scalar-aligning UTF-8 indices
+
+  let str = "a😇"
+  let subScalarIdx = str.utf8.index(str.utf8.startIndex, offsetBy: 2)
+
+  let roundedIdx = swift5ScalarAlign(subScalarIdx, in: str)
+  expectEqual(1, roundedIdx.utf16Offset(in: str))
+
+  let roundedIdx2 = str.utf8[...subScalarIdx].lastIndex { $0 & 0xC0 != 0x80 }
+  expectEqual(roundedIdx, roundedIdx)
+
+  var roundedIdx3 = subScalarIdx
+  while roundedIdx3.samePosition(in: str.unicodeScalars) == nil {
+    str.utf8.formIndex(before: &roundedIdx3)
+  }
+  expectEqual(roundedIdx, roundedIdx3)
+}
+
+
+
 runAllTests()
\ No newline at end of file
diff --git a/test/stdlib/StringTraps.swift b/test/stdlib/StringTraps.swift
index 729eea0..5d06ee7 100644
--- a/test/stdlib/StringTraps.swift
+++ b/test/stdlib/StringTraps.swift
@@ -168,5 +168,17 @@
   _ = s8.utf8[i]
 }
 
+StringTraps.test("String.Index.utf16Offset(in:)/subscalarUTF8")
+  .skip(.custom(
+    { _isFastAssertConfiguration() },
+    reason: "this trap is not guaranteed to happen in -Ounchecked"))
+  .code {
+  let s = "😇"
+  let u8 = s.utf8
+  let i = u8.index(after: u8.startIndex)
+  expectCrashLater()
+  _ = i.utf16Offset(in: s)
+}
+
 runAllTests()
 
diff --git a/validation-test/stdlib/String.swift b/validation-test/stdlib/String.swift
index 8288959..3135d11 100644
--- a/validation-test/stdlib/String.swift
+++ b/validation-test/stdlib/String.swift
@@ -1365,8 +1365,8 @@
     result, flags, stop
   in
     let r = result!.range(at: 1)
-    let start = String.Index(encodedOffset: r.location)
-    let end = String.Index(encodedOffset: r.location + r.length)
+    let start = String.Index(_encodedOffset: r.location)
+    let end = String.Index(_encodedOffset: r.location + r.length)
     matches.append(String(s.utf16[start..<end])!)
   }