diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index 37c15f3708c35..006ed92cefb93 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -84,6 +84,7 @@ set(SWIFTLIB_ESSENTIAL Print.swift RandomAccessCollection.swift Range.swift.gyb + RangeExpression.swift.gyb RangeReplaceableCollection.swift.gyb Reflection.swift Repeat.swift diff --git a/stdlib/public/core/CollectionAlgorithms.swift.gyb b/stdlib/public/core/CollectionAlgorithms.swift.gyb index 974fc1341b5c4..969bdd5135fa7 100644 --- a/stdlib/public/core/CollectionAlgorithms.swift.gyb +++ b/stdlib/public/core/CollectionAlgorithms.swift.gyb @@ -469,101 +469,6 @@ ${orderingExplanation} } } -% for Self in 'Indexable', 'MutableIndexable': -%{ - -subscriptCommentPre = """\ - /// Accesses a contiguous subrange of the collection's elements. - /// - /// The accessed slice uses the same indices for the same elements as the - /// original collection. Always use the slice's `startIndex` property - /// instead of assuming that its indices start at a particular value. - /// - /// This example demonstrates getting a slice of an array of strings, finding - /// the index of one of the strings in the slice, and then using that index - /// in the original array. - /// - /// let streets = ["Adams", "Bryant", "Channing", "Douglas", "Evarts"] - /// let streetsSlice = streets[2 ..< streets.endIndex] - /// print(streetsSlice) - /// // Prints "["Channing", "Douglas", "Evarts"]" - /// - /// let index = streetsSlice.index(of: "Evarts") // 4""" - -if 'Mutable' in Self: - subscriptCommentMid = """\ - /// streets[index!] = "Eustace" - /// print(streets[index!]) - /// // Prints "Eustace\"""" -else: - subscriptCommentMid = """\ - /// print(streets[index!]) - /// // Prints "Evarts\"""" - -subscriptCommentPost = """\ - /// - /// - Parameter bounds: A range of the collection's indices. The bounds of - /// the range must be valid indices of the collection.""" -}% -// WORKAROUND rdar://25214066 - should be on Collection -extension ${Self} { -${subscriptCommentPre} -${subscriptCommentMid} -${subscriptCommentPost} - public subscript(bounds: ClosedRange) -> SubSequence { - get { - return self[ - Range( - uncheckedBounds: ( - lower: bounds.lowerBound, - upper: index(after: bounds.upperBound))) - ] - } -% if 'Mutable' in Self: - set { - self[ - Range( - uncheckedBounds: ( - lower: bounds.lowerBound, - upper: index(after: bounds.upperBound))) - ] = newValue - } -% end - } -} - -// WORKAROUND rdar://25214066 - should be on Collection -extension ${Self} where Index : Strideable, Index.Stride : SignedInteger { -${subscriptCommentPre} -${subscriptCommentMid} -${subscriptCommentPost} - public subscript(bounds: CountableRange) -> SubSequence { - get { - return self[Range(bounds)] - } -% if 'Mutable' in Self: - set { - self[Range(bounds)] = newValue - } -% end - } - -${subscriptCommentPre} -${subscriptCommentMid} -${subscriptCommentPost} - public subscript(bounds: CountableClosedRange) -> SubSequence { - get { - return self[ClosedRange(bounds)] - } -% if 'Mutable' in Self: - set { - self[ClosedRange(bounds)] = newValue - } -% end - } -} -% end - //===--- Unavailable stuff ------------------------------------------------===// extension MutableCollection where Self : RandomAccessCollection { diff --git a/stdlib/public/core/GroupInfo.json b/stdlib/public/core/GroupInfo.json index c572705d2f47c..e314e81aad1ea 100644 --- a/stdlib/public/core/GroupInfo.json +++ b/stdlib/public/core/GroupInfo.json @@ -39,6 +39,7 @@ "Sort.swift", "Range.swift", "ClosedRange.swift", + "RangeExpression.swift", "CollectionOfOne.swift", "HeapBuffer.swift", "Sequence.swift", diff --git a/stdlib/public/core/Policy.swift b/stdlib/public/core/Policy.swift index eccd9c868f268..ab9fab3aff3a4 100644 --- a/stdlib/public/core/Policy.swift +++ b/stdlib/public/core/Policy.swift @@ -557,6 +557,8 @@ public func ~= (a: T, b: T) -> Bool { // Standard postfix operators. postfix operator ++ {} postfix operator -- {} +postfix operator ..< {} +postfix operator ... {} // Optional unwrapping operator is built into the compiler as a part of // postfix expression grammar. @@ -570,6 +572,8 @@ prefix operator ! {} prefix operator ~ {} prefix operator + {} prefix operator - {} +prefix operator ..< {} +prefix operator ... {} // Standard infix operators. diff --git a/stdlib/public/core/Range.swift.gyb b/stdlib/public/core/Range.swift.gyb index 3fd47fb3a2d54..32a0103c50f99 100644 --- a/stdlib/public/core/Range.swift.gyb +++ b/stdlib/public/core/Range.swift.gyb @@ -587,6 +587,93 @@ public func ..< ( return CountableRange(uncheckedBounds: (lower: minimum, upper: maximum)) } +% for (Closed, Op) in [('', '..<'), ('Closed', '...')]: +/// A `${Closed}Range` which may not have all of its bounds specified. +/// The `Incomplete${Closed}Range` can be completed by providing a +/// `${Closed}Range` or `Countable${Closed}Range` from which it can retrieve +/// default upper and lower bounds. +@_fixed_layout +public struct Incomplete${Closed}Range< + Bound : Comparable +> { + /// The lowest value within the range. + /// If `nil`, completing the range will adopt the default value's + /// `lowerBound`. + public let lowerBound: Bound? +% if Closed == 'Closed': + /// The highest value within the range. +% else: + /// The value just above the highest value within the range. +% end + /// If `nil`, completing the range will adopt the default value's + /// `upperBound`. + public let upperBound: Bound? + + internal init(_bounds bounds: (lower: Bound?, upper: Bound?)) { + lowerBound = bounds.lower + upperBound = bounds.upper + } +} + +% for Countable in ['', 'Countable']: +extension Incomplete${Closed}Range +% if Countable == 'Countable': + where + // WORKAROUND rdar://25214598 - should be just Bound : Strideable + Bound : _Strideable & Comparable, + Bound.Stride : SignedInteger { +% else: + { +% end +% for Unchecked in ['', 'Unchecked']: + /// Returns a `${Countable}${Closed}Range` with the same `upperBound` + /// and `lowerBound` as the current instance. `nil` bounds are + /// filled in from `defaultBounds`. + /// + /// This method does not check whether `lowerBound` and `upperBound` + /// lie within `defaultBounds`. +% if Unchecked == 'Unchecked': + /// Nor does it check whether the resulting `lowerBound` is below + /// its `upperBound`. +% end + @_transparent + public func completed(by${Unchecked} defaultBounds: ${Countable}${Closed}Range) -> ${Countable}${Closed}Range { + let lower = lowerBound ?? defaultBounds.lowerBound + let upper = upperBound ?? defaultBounds.upperBound +% if Unchecked == 'Unchecked': + return .init(uncheckedBounds: (lower: lower, upper: upper)) +% else: + return lower ${Op} upper +% end + } +% end +} +% end + +/// Constructs an `Incomplete${Closed}Range` with the provided upper +/// bound and an unknown lower bound. +@_transparent +public prefix func ${Op} (upperBound: Bound) -> Incomplete${Closed}Range { + return .init(_bounds: (lower: nil, upper: upperBound)) +} + +/// Constructs an `Incomplete${Closed}Range` with the provided lower +/// bound and an unknown upper bound. +@_transparent +public postfix func ${Op} (lowerBound: Bound) -> Incomplete${Closed}Range { + return .init(_bounds: (lower: lowerBound, upper: nil)) +} + +/// Constructs an `Incomplete${Closed}Range` with the provided upper +/// and lower bounds. Either or both may be `nil`, in which case the +/// bound will be provided when the `Incomplete${Closed}Range` is +/// completed. +@_transparent +public func ${Op} (lowerBound: Bound?, upperBound: Bound?) -> Incomplete${Closed}Range { + return .init(_bounds: (lower: lowerBound, upper: upperBound)) +} +% end + // swift-3-indexing-model: this is not really a proper rename @available(*, unavailable, renamed: "IndexingIterator") public struct RangeGenerator {} diff --git a/stdlib/public/core/RangeExpression.swift.gyb b/stdlib/public/core/RangeExpression.swift.gyb new file mode 100644 index 0000000000000..4fd3a1429883c --- /dev/null +++ b/stdlib/public/core/RangeExpression.swift.gyb @@ -0,0 +1,250 @@ +//===--- RangeExpression.swift.gyb ----------------------------*- swift -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +/// A type which can be used to slice a collection. A `RangeExpression` can +/// convert itself to a `Range` of indices within a given collection; +/// the collection can then slice itself with that `Range`. +/// +/// -Warning: The requirements of `RangeExpression` are likely to change +/// in a future version of Swift. If you conform your own +/// types to `RangeExpression`, be prepared to migrate them. +public protocol RangeExpression { + /// The type of the bounds of the `Range` produced by this + /// type when used as a `RangeExpression`. + associatedtype Bound : Comparable + + /// Returns `self` expressed as a `Range` suitable for + /// slicing a collection with the indicated properties. + /// + /// -Parameter bounds: The range of indices in the collection. + /// Equivalent to `startIndex ..< endIndex` + /// in `Collection`. + /// + /// -Parameter offset: A function which can be used to add to or + /// subtract from a bound. Equivalent to + /// `index(_:offsetBy:)` in `Collection`. + /// + /// -Returns: A `Range` suitable for slicing a collection. + /// The return value is *not* guaranteed to be inside + /// `bounds`. Callers should apply the same preconditions + /// to the return value as they would to a range provided + /// directly by the user. + /// + /// -Warning: This method is likely to be replaced in a future version of Swift. + /// If you are calling this method, we recommend using the + /// `relative(to:)` extension method instead. If you are implementing + /// it, be prepared to migrate your code. + /// + /// -Recommended: `relative(to:)` + // + // WORKAROUND SR-2158 - We want to have this requirement, but it triggers a generics bug + // func relative(to collection: C) -> Range where C.Index == Bound + func relative(to bounds: Range, offsettingBy offset: (Bound, BoundDistance) -> Bound) -> Range +} + +extension RangeExpression { + /// Returns `self` expressed as a range of indices within `collection`. + /// + /// -Parameter collection: The collection `self` should be + /// relative to. + /// + /// -Returns: A `Range` suitable for slicing `collection`. + /// The return value is *not* guaranteed to be inside + /// its bounds. Callers should apply the same preconditions + /// to the return value as they would to a range provided + /// directly by the user. + /// + /// -RecommendedOver: `relative(to:offsettingBy:)` + public func relative(to collection: C) -> Range where C.Index == Bound { + let bounds = Range(uncheckedBounds: (lower: collection.startIndex, upper: collection.endIndex)) + return relative(to: bounds, offsettingBy: collection.index(_:offsetBy:)) + } +} + +extension Range: RangeExpression { + public func relative(to bounds: Range, offsettingBy offset: (Bound, BoundDistance) -> Bound) -> Range { + return self + } +} + +extension CountableRange: RangeExpression { + public func relative(to bounds: Range, offsettingBy offset: (Bound, BoundDistance) -> Bound) -> Range { + return Range(self) + } +} + +extension ClosedRange: RangeExpression { + public func relative(to bounds: Range, offsettingBy offset: (Bound, BoundDistance) -> Bound) -> Range { + return Range(uncheckedBounds: (lower: lowerBound, upper: offset(upperBound, 1))) + } +} + +extension CountableClosedRange: RangeExpression { + public func relative(to bounds: Range, offsettingBy offset: (Bound, BoundDistance) -> Bound) -> Range { + return CountableRange(self).relative(to: bounds, offsettingBy: offset) + } +} + +extension IncompleteRange: RangeExpression { + public func relative(to bounds: Range, offsettingBy offset: (Bound, BoundDistance) -> Bound) -> Range { + return completed(by: bounds) + } +} + +extension IncompleteClosedRange: RangeExpression { + public func relative(to bounds: Range, offsettingBy offset: (Bound, BoundDistance) -> Bound) -> Range { + let closedBounds = ClosedRange(uncheckedBounds: (bounds.lowerBound, bounds.upperBound)) + return completed(by: closedBounds).relative(to: bounds, offsettingBy: offset) + } +} + +// WORKAROUND SR-115 - Should be generic subscripts, not generated code for each conforming type +% for R in ['CountableRange', 'ClosedRange', 'CountableClosedRange', 'IncompleteRange', 'IncompleteClosedRange']: +// WORKAROUND rdar://25214066 - should be on Collection +extension Indexable +% if 'Countable' in R: + where Index : Strideable, Index.Stride : SignedInteger +% end +{ + /// Accesses a contiguous subrange of the collection's elements. + /// + /// The accessed slice uses the same indices for the same elements as the + /// original collection. Always use the slice's `startIndex` property + /// instead of assuming that its indices start at a particular value. + /// + /// This example demonstrates getting a slice of an array of strings, finding + /// the index of one of the strings in the slice, and then using that index + /// in the original array. + /// + /// let streets = ["Adams", "Bryant", "Channing", "Douglas", "Evarts"] + /// let streetsSlice = streets[2 ..< streets.endIndex] + /// print(streetsSlice) + /// // Prints "["Channing", "Douglas", "Evarts"]" + /// + /// let index = streetsSlice.index(of: "Evarts") // 4 + /// print(streets[index!]) + /// // Prints "Evarts" + /// + /// - Parameter bounds: A range of the collection's indices. The bounds of + /// the range must be valid indices of the collection. + public subscript(bounds: ${R}) -> SubSequence { + get { + return self[bounds.relative(to: self)] + } + } +} + +// WORKAROUND rdar://25214066 - should be on Collection +extension MutableIndexable +% if 'Countable' in R: + where Index : Strideable, Index.Stride : SignedInteger +% end +{ + /// Accesses a contiguous subrange of the collection's elements. + /// + /// The accessed slice uses the same indices for the same elements as the + /// original collection. Always use the slice's `startIndex` property + /// instead of assuming that its indices start at a particular value. + /// + /// This example demonstrates getting a slice of an array of strings, finding + /// the index of one of the strings in the slice, and then using that index + /// in the original array. + /// + /// let streets = ["Adams", "Bryant", "Channing", "Douglas", "Evarts"] + /// let streetsSlice = streets[2 ..< streets.endIndex] + /// print(streetsSlice) + /// // Prints "["Channing", "Douglas", "Evarts"]" + /// + /// let index = streetsSlice.index(of: "Evarts") // 4 + /// streets[index!] = "Eustace" + /// print(streets[index!]) + /// // Prints "Eustace" + /// + /// - Parameter bounds: A range of the collection's indices. The bounds of + /// the range must be valid indices of the collection. + public subscript(bounds: ${R}) -> SubSequence { + get { + return self[bounds.relative(to: self)] + } + set { + self[bounds.relative(to: self)] = newValue + } + } +} + +% end +extension RangeReplaceableCollection { + /// Replaces the specified subrange of elements with the given collection. + /// + /// This method has the effect of removing the specified range of elements + /// from the collection and inserting the new elements at the same location. + /// The number of new elements need not match the number of elements being + /// removed. + /// + /// In this example, three elements in the middle of an array of integers are + /// replaced by the five elements of a `Repeated` instance. + /// + /// var nums = [10, 20, 30, 40, 50] + /// nums.replaceSubrange(1...3, with: repeatElement(1, count: 5)) + /// print(nums) + /// // Prints "[10, 1, 1, 1, 1, 1, 50]" + /// + /// If you pass a zero-length range as the `subrange` parameter, this method + /// inserts the elements of `newElements` at `subrange.startIndex`. Calling + /// the `insert(contentsOf:at:)` method instead is preferred. + /// + /// Likewise, if you pass a zero-length collection as the `newElements` + /// parameter, this method removes the elements in the given subrange + /// without replacement. Calling the `removeSubrange(_:)` method instead is + /// preferred. + /// + /// Calling this method may invalidate any existing indices for use with this + /// collection. + /// + /// - Parameters: + /// - subrange: The subrange of the collection to replace. The bounds of + /// the range must be valid indices of the collection. + /// - newElements: The new elements to add to the collection. + /// + /// - Complexity: O(*m*), where *m* is the combined length of the collection + /// and `newElements`. If the call to `replaceSubrange` simply appends the + /// contents of `newElements` to the collection, the complexity is O(*n*), + /// where *n* is the length of `newElements`. + public mutating func replaceSubrange( + _ subrange: R, + with newElements: C + ) where R : RangeExpression, R.Bound == Index, C : Collection, C.Iterator.Element == Iterator.Element { + replaceSubrange(subrange.relative(to: self), with: newElements) + } + + /// Removes the elements in the specified subrange from the collection. + /// + /// All the elements following the specified position are moved to close the + /// gap. This example removes two elements from the middle of an array of + /// measurements. + /// + /// var measurements = [1.2, 1.5, 2.9, 1.2, 1.5] + /// measurements.removeSubrange(1..<3) + /// print(measurements) + /// // Prints "[1.2, 1.5]" + /// + /// Calling this method may invalidate any existing indices for use with this + /// collection. + /// + /// - Parameter bounds: The range of the collection to be removed. The + /// bounds of the range must be valid indices of the collection. + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. + public mutating func removeSubrange(_ bounds: R) where R : RangeExpression, R.Bound == Index { + removeSubrange(bounds.relative(to: self)) + } +} \ No newline at end of file diff --git a/stdlib/public/core/RangeReplaceableCollection.swift.gyb b/stdlib/public/core/RangeReplaceableCollection.swift.gyb index e94a7cbeff93a..d928c03ba529d 100644 --- a/stdlib/public/core/RangeReplaceableCollection.swift.gyb +++ b/stdlib/public/core/RangeReplaceableCollection.swift.gyb @@ -843,93 +843,6 @@ extension RangeReplaceableCollection where SubSequence == Self { } } -% for Range in ['CountableRange', 'ClosedRange', 'CountableClosedRange']: -extension RangeReplaceableCollection -% if 'Countable' in Range: - where - Index : Strideable, Index.Stride : SignedInteger -% end -{ - /// Returns a half-open range denoting the same positions as `r`. - internal func _makeHalfOpen(_ r: ${Range}) -> Range { - // The upperBound of the result depends on whether `r` is a closed - // range. -% if 'Closed' in Range: - return Range(uncheckedBounds: ( - lower: r.lowerBound, - upper: index(after: r.upperBound))) -% else: - return Range(r) -% end - } - - /// Replaces the specified subrange of elements with the given collection. - /// - /// This method has the effect of removing the specified range of elements - /// from the collection and inserting the new elements at the same location. - /// The number of new elements need not match the number of elements being - /// removed. - /// - /// In this example, three elements in the middle of an array of integers are - /// replaced by the five elements of a `Repeated` instance. - /// - /// var nums = [10, 20, 30, 40, 50] - /// nums.replaceSubrange(1...3, with: repeatElement(1, count: 5)) - /// print(nums) - /// // Prints "[10, 1, 1, 1, 1, 1, 50]" - /// - /// If you pass a zero-length range as the `subrange` parameter, this method - /// inserts the elements of `newElements` at `subrange.startIndex`. Calling - /// the `insert(contentsOf:at:)` method instead is preferred. - /// - /// Likewise, if you pass a zero-length collection as the `newElements` - /// parameter, this method removes the elements in the given subrange - /// without replacement. Calling the `removeSubrange(_:)` method instead is - /// preferred. - /// - /// Calling this method may invalidate any existing indices for use with this - /// collection. - /// - /// - Parameters: - /// - subrange: The subrange of the collection to replace. The bounds of - /// the range must be valid indices of the collection. - /// - newElements: The new elements to add to the collection. - /// - /// - Complexity: O(*m*), where *m* is the combined length of the collection - /// and `newElements`. If the call to `replaceSubrange` simply appends the - /// contents of `newElements` to the collection, the complexity is O(*n*), - /// where *n* is the length of `newElements`. - public mutating func replaceSubrange( - _ subrange: ${Range}, - with newElements: C - ) where C : Collection, C.Iterator.Element == Iterator.Element { - self.replaceSubrange(_makeHalfOpen(subrange), with: newElements) - } - - /// Removes the elements in the specified subrange from the collection. - /// - /// All the elements following the specified position are moved to close the - /// gap. This example removes two elements from the middle of an array of - /// measurements. - /// - /// var measurements = [1.2, 1.5, 2.9, 1.2, 1.5] - /// measurements.removeSubrange(1..<3) - /// print(measurements) - /// // Prints "[1.2, 1.5]" - /// - /// Calling this method may invalidate any existing indices for use with this - /// collection. - /// - /// - Parameter bounds: The range of the collection to be removed. The - /// bounds of the range must be valid indices of the collection. - /// - /// - Complexity: O(*n*), where *n* is the length of the collection. - public mutating func removeSubrange(_ bounds: ${Range}) { - removeSubrange(_makeHalfOpen(bounds)) - } -} -% end - extension RangeReplaceableCollection { public mutating func _customRemoveLast() -> Iterator.Element? { return nil diff --git a/stdlib/public/core/StringRangeReplaceableCollection.swift.gyb b/stdlib/public/core/StringRangeReplaceableCollection.swift.gyb index c62fb0b2c7aa2..8bb4068c0eed2 100644 --- a/stdlib/public/core/StringRangeReplaceableCollection.swift.gyb +++ b/stdlib/public/core/StringRangeReplaceableCollection.swift.gyb @@ -77,21 +77,16 @@ extension String { /// string's end index. public subscript(i: Index) -> Character { return characters[i] } + // WORKAROUND SR-115 - Should be generic subscripts, not generated code for each RangeExpression-conforming type +% for Range in ['Range', 'ClosedRange', 'IncompleteRange', 'IncompleteClosedRange']: /// Accesses the text in the given range. /// /// - Complexity: O(*n*) if the underlying string is bridged from /// Objective-C, where *n* is the length of the string; otherwise, O(1). - public subscript(bounds: Range) -> String { - return String(characters[bounds]) - } - - /// Accesses the text in the given range. - /// - /// - Complexity: O(*n*) if the underlying string is bridged from - /// Objective-C, where *n* is the length of the string; otherwise, O(1). - public subscript(bounds: ClosedRange) -> String { + public subscript(bounds: ${Range}) -> String { return String(characters[bounds]) } +% end } extension String.Index { @@ -168,7 +163,6 @@ extension String { } } -% for Range in ['Range', 'ClosedRange']: /// Replaces the text within the specified bounds with the given characters. /// /// Calling this method invalidates any existing indices for use with this @@ -183,10 +177,8 @@ extension String { /// `newElements`. If the call to `replaceSubrange(_:with:)` simply /// removes text at the end of the string, the complexity is O(*n*), where /// *n* is equal to `bounds.count`. - public mutating func replaceSubrange( - _ bounds: ${Range}, - with newElements: C - ) where C : Collection, C.Iterator.Element == Character { + public mutating func replaceSubrange(_ bounds: R, with newElements: C) + where R : RangeExpression, R.Bound == Index, C : Collection, C.Iterator.Element == Character { withMutableCharacters { (v: inout CharacterView) in v.replaceSubrange(bounds, with: newElements) @@ -207,13 +199,11 @@ extension String { /// `newElements`. If the call to `replaceSubrange(_:with:)` simply /// removes text at the end of the string, the complexity is O(*n*), where /// *n* is equal to `bounds.count`. - public mutating func replaceSubrange( - _ bounds: ${Range}, with newElements: String - ) { + public mutating func replaceSubrange(_ bounds: R, with newElements: String) + where R : RangeExpression, R.Bound == Index { replaceSubrange(bounds, with: newElements.characters) } -% end - + /// Inserts a new character at the specified position. /// /// Calling this method invalidates any existing indices for use with this @@ -278,26 +268,19 @@ extension String { } } -% for Range in ['Range', 'ClosedRange']: /// Removes the characters in the given range. /// /// Calling this method invalidates any existing indices for use with this /// string. /// -% if Range == 'ClosedRange': - /// - Parameter bounds: The range of the elements to remove. The upper and - /// lower bounds of `bounds` must be valid indices of the string and not - /// equal to the string's end index. -% else: /// - Parameter bounds: The range of the elements to remove. The upper and /// lower bounds of `bounds` must be valid indices of the string. -% end - public mutating func removeSubrange(_ bounds: ${Range}) { + public mutating func removeSubrange(_ bounds: R) + where R : RangeExpression, R.Bound == Index { withMutableCharacters { (v: inout CharacterView) in v.removeSubrange(bounds) } } -% end /// Replaces this string with the empty string. /// diff --git a/test/1_stdlib/IntervalTraps.swift b/test/1_stdlib/IntervalTraps.swift index cab06a7ac86b5..b2bbf30551cb0 100644 --- a/test/1_stdlib/IntervalTraps.swift +++ b/test/1_stdlib/IntervalTraps.swift @@ -50,5 +50,34 @@ IntervalTraps.test("Closed") _ = 1.0...0.0 } +IntervalTraps.test("IncompleteHalfOpen") + .skip(.custom( + { _isFastAssertConfiguration() }, + reason: "this trap is not guaranteed to happen in -Ounchecked")) + .code { + var incompleteInterval = 1.0..< + var completeInterval = -1.0..<0.0 + // FIXME: the plan is for floating point numbers to no longer be + // strideable; then this will drop the "OfStrideable" + expectType(IncompleteRange.self, &incompleteInterval) + expectCrashLater() + _ = incompleteInterval.completed(by: completeInterval) +} + +IntervalTraps.test("IncompleteClosed") + .skip(.custom( + { _isFastAssertConfiguration() }, + reason: "this trap is not guaranteed to happen in -Ounchecked")) + .code { + var incompleteInterval = 1.0... + var completeInterval = -1.0...0.0 + // FIXME: the plan is for floating point numbers to no longer be + // strideable; then this will drop the "OfStrideable" + expectType(IncompleteClosedRange.self, &incompleteInterval) + + expectCrashLater() + _ = incompleteInterval.completed(by: completeInterval) +} + runAllTests() diff --git a/test/1_stdlib/RangeTraps.swift b/test/1_stdlib/RangeTraps.swift index c1c54cef9be8d..cf6b630e28a64 100644 --- a/test/1_stdlib/RangeTraps.swift +++ b/test/1_stdlib/RangeTraps.swift @@ -47,6 +47,36 @@ RangeTraps.test("Closed") _ = 1...0 } +RangeTraps.test("IncompleteHalfOpen") + .skip(.custom( + { _isFastAssertConfiguration() }, + reason: "this trap is not guaranteed to happen in -Ounchecked")) + .code { + var range = 1..< + expectType(IncompleteRange.self, &range) + + var badRange = range.completed(byUnchecked: -1..<0) + expectType(CountableRange.self, &badRange) + + expectCrashLater() + _ = range.completed(by: -1..<0) +} + +RangeTraps.test("IncompleteClosed") + .skip(.custom( + { _isFastAssertConfiguration() }, + reason: "this trap is not guaranteed to happen in -Ounchecked")) + .code { + var range = 1... + expectType(IncompleteClosedRange.self, &range) + + var badRange = range.completed(byUnchecked: -1...0) + expectType(CountableClosedRange.self, &badRange) + + expectCrashLater() + _ = range.completed(by: -1...0) +} + RangeTraps.test("OutOfRange") .skip(.custom( { _isFastAssertConfiguration() }, diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 61952c925bc4f..44e8f80a3e3d8 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -722,7 +722,7 @@ func r23272739(_ contentType: String) { // QoI: Strings in Swift cannot be indexed directly with integer offsets func r23641896() { var g = "Hello World" - g.replaceSubrange(0...2, with: "ce") // expected-error {{cannot invoke 'replaceSubrange' with an argument list of type '(CountableClosedRange, with: String)'}} expected-note {{overloads for 'replaceSubrange' exist}} + g.replaceSubrange(0...2, with: "ce") // expected-error {{no '...' candidates produce the expected contextual result type 'Range' (aka 'Range')}} expected-note {{overloads for '...' exist with these result types: ClosedRange, CountableClosedRange, IncompleteClosedRange}} _ = g[12] // expected-error {{'subscript' is unavailable: cannot subscript String with an Int, see the documentation comment for discussion}} diff --git a/test/IDE/print_type_interface.swift b/test/IDE/print_type_interface.swift index 07ef6617b73b5..de19a3aeba76a 100644 --- a/test/IDE/print_type_interface.swift +++ b/test/IDE/print_type_interface.swift @@ -73,7 +73,7 @@ extension D { // TYPE4-DAG: public func min() -> Int? // TYPE4-DAG: public mutating func insert(contentsOf newElements: C, at i: Int) // TYPE4-DAG: public mutating func removeFirst(_ n: Int) -// TYPE4-DAG: public mutating func replaceSubrange(_ subrange: CountableRange, with newElements: C) +// TYPE4-DAG: public mutating func replaceSubrange(_ subrange: Range, with newElements: C) // TYPE4-DAG: public func makeIterator() -> IndexingIterator> // TYPE4-NOT: public func joined diff --git a/test/Interpreter/arrays.swift b/test/Interpreter/arrays.swift index ff94adbc535bf..d3a2390a529f0 100644 --- a/test/Interpreter/arrays.swift +++ b/test/Interpreter/arrays.swift @@ -120,6 +120,25 @@ print("contig_arr2 != contig_arr3: \(contig_arr2 != contig_arr3)") print("contig_arr1 != contig_arr4: \(contig_arr1 != contig_arr4)") print("contig_arr1 == contig_arr4: \(contig_arr1 == contig_arr4)") +// Check if slicing with incomplete ranges is equivalent to slicing +// with complete ranges. + +let slice_nil_2_open = arr1[..<2] +let slice_0_2_open = arr1[0..<2] + +let slice_nil_2_closed = arr1[...2] +let slice_0_2_closed = arr1[0...2] + +let slice_2_nil_open = arr1[2..<] +let slice_2_4_open = arr1[2..<4] + +// CHECK: slice_nil_2_open == slice_0_2_open: true +// CHECK-NEXT: slice_nil_2_closed == slice_0_2_closed: true +// CHECK-NEXT: slice_2_nil_open == slice_2_4_open: true +print("slice_nil_2_open == slice_0_2_open: \(slice_nil_2_open == slice_0_2_open)") +print("slice_nil_2_closed == slice_0_2_closed: \(slice_nil_2_closed == slice_0_2_closed)") +print("slice_2_nil_open == slice_2_4_open: \(slice_2_nil_open == slice_2_4_open)") + // protocol Runcible {} class Spoon: Runcible { diff --git a/test/decl/func/operator.swift b/test/decl/func/operator.swift index bf19888954444..84374bac2f2d4 100644 --- a/test/decl/func/operator.swift +++ b/test/decl/func/operator.swift @@ -193,7 +193,7 @@ _ = -+n // expected-error {{unary operators may not be juxtaposed; parenthesize _ = -++n // expected-error {{unary operators may not be juxtaposed; parenthesize inner expression}} // Cannot use a negative constant as the second operator of ... operator -_ = 3...-5 // expected-error {{missing whitespace between '...' and '-' operators}} +_ = 3...-5 // expected-error {{ambiguous missing whitespace between unary and binary operators}} expected-note {{could be binary '...' and prefix '-'}} expected-note {{could be postfix '...' and binary '-'}} protocol P0 {