Skip to content

Commit 31f7dfe

Browse files
moiseevairspeedswift
authored andcommitted
[stdlib] Backward compatibility fix for a flatMap on [String] (#9466)
* [stdlib] Backward compatibility fix for a flatMap on [String] Since String started to conform to Collection, the flatMap with a sequence returning closure is now a better match that the one that relies on the optional promotion in this code: [""].flatMap { $0 } which results in the default type of this expression changing from [String] to [Character]. Restoring the old behavior in Swift 3 mode by adding a very explicit overload. Fixes: <rdar://problem/32024978> * [stdlib] Fixing another compatibility issue with [String].flatMap
1 parent 606a3cc commit 31f7dfe

File tree

3 files changed

+81
-0
lines changed

3 files changed

+81
-0
lines changed

stdlib/public/core/SequenceAlgorithms.swift.gyb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,16 @@ extension Sequence {
720720
@_inlineable
721721
public func flatMap<ElementOfResult>(
722722
_ transform: (${GElement}) throws -> ElementOfResult?
723+
) rethrows -> [ElementOfResult] {
724+
return try _flatMap(transform)
725+
}
726+
727+
// The implementation of flatMap accepting a closure with an optional result.
728+
// Factored out into a separate functions in order to be used in multiple
729+
// overloads.
730+
@inline(__always)
731+
public func _flatMap<ElementOfResult>(
732+
_ transform: (${GElement}) throws -> ElementOfResult?
723733
) rethrows -> [ElementOfResult] {
724734
var result: [ElementOfResult] = []
725735
for element in self {

stdlib/public/core/StringRangeReplaceableCollection.swift.gyb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,35 @@ extension String {
380380
}
381381
}
382382

383+
//===----------------------------------------------------------------------===//
384+
// The following overloads of flatMap are carefully crafted to allow the code
385+
// like the following:
386+
// ["hello"].flatMap { $0 }
387+
// return an array of strings without any type context in Swift 3 mode, at the
388+
// same time allowing the following code snippet to compile:
389+
// [0, 1].flatMap { x in
390+
// if String(x) == "foo" { return "bar" } else { return nil }
391+
// }
392+
// Note that the second overload is delcared on a more specific protocol.
393+
// See: test/stdlib/StringFlatMap.swift for tests.
394+
extension Sequence {
395+
@available(swift, obsoleted: 4)
396+
public func flatMap(
397+
_ transform: (Iterator.Element) throws -> String
398+
) rethrows -> [String] {
399+
return try map(transform)
400+
}
401+
}
402+
403+
extension Collection {
404+
public func flatMap(
405+
_ transform: (Iterator.Element) throws -> String?
406+
) rethrows -> [String] {
407+
return try _flatMap(transform)
408+
}
409+
}
410+
//===----------------------------------------------------------------------===//
411+
383412
extension String {
384413
@available(*, unavailable, message: "Operator '+' cannot be used to append a String to a sequence of strings")
385414
public static func + <S : Sequence>(lhs: S, rhs: String) -> Never

test/stdlib/StringFlatMap.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: rm -rf %t ; mkdir -p %t
2+
// RUN: %target-build-swift %s -o %t/a.out3 -swift-version 3 && %target-run %t/a.out3
3+
// RUN: %target-build-swift %s -o %t/a.out4 -swift-version 4 && %target-run %t/a.out4
4+
5+
import StdlibUnittest
6+
7+
#if swift(>=4)
8+
9+
public typealias ExpectedResultType = [Character]
10+
11+
#else
12+
13+
public typealias ExpectedResultType = [String]
14+
15+
#endif
16+
17+
var Tests = TestSuite("StringFlatMap")
18+
19+
Tests.test("DefaultReturnType") {
20+
var result = ["hello", "world"].flatMap { $0 }
21+
expectType(ExpectedResultType.self, &result)
22+
}
23+
24+
Tests.test("ExplicitTypeContext") {
25+
expectEqualSequence(["hello", "world"],
26+
["hello", "world"].flatMap { $0 } as [String])
27+
expectEqualSequence("helloworld".characters,
28+
["hello", "world"].flatMap { $0 } as [Character])
29+
}
30+
31+
Tests.test("inference") {
32+
let result = [1, 2].flatMap { x in
33+
if String(x) == "foo" {
34+
return "bar"
35+
} else {
36+
return nil
37+
}
38+
}
39+
expectEqualSequence([], result)
40+
}
41+
42+
runAllTests()

0 commit comments

Comments
 (0)