Skip to content

[stdlib] Adopt typed throws in some Dictionary APIs and some Sequence APIs #77957

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ void ArrayInfo::getLastDestroys(
/// not clean up any resulting dead instructions.
static void removeForEachCall(TryApplyInst *forEachCall,
InstructionDeleter &deleter) {
auto *sbi = cast<StoreBorrowInst>(forEachCall->getArgument(1));
auto *sbi = cast<StoreBorrowInst>(forEachCall->getArgument(2));
auto *asi = cast<AllocStackInst>(sbi->getDest());
// The allocStack will be used in the forEach call and also in a store
// instruction and a dealloc_stack instruction. Force delete all of them.
Expand Down Expand Up @@ -440,7 +440,8 @@ static void unrollForEach(ArrayInfo &arrayInfo, TryApplyInst *forEachCall,

SILFunction *fun = forEachCall->getFunction();
SILLocation forEachLoc = forEachCall->getLoc();
SILValue forEachBodyClosure = forEachCall->getArgument(0);
SILValue typedThrowValue = forEachCall->getArgument(0);
SILValue forEachBodyClosure = forEachCall->getArgument(1);
SILType arrayElementType = arrayInfo.getElementSILType();

SILFunctionType *bodyClosureType =
Expand Down Expand Up @@ -582,7 +583,7 @@ static void unrollForEach(ArrayInfo &arrayInfo, TryApplyInst *forEachCall,
// elements of address-only type. All elements in the array are guaranteed
// to be loadable. TODO: generalize this to address-only types.
unrollBuilder.createTryApply(forEachLoc, forEachBodyClosure,
SubstitutionMap(), addr, nextNormalBB,
SubstitutionMap(), {typedThrowValue, addr}, nextNormalBB,
errorTarget);

if (nextNormalBB == normalBB) {
Expand Down
21 changes: 11 additions & 10 deletions stdlib/public/core/Dictionary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -926,10 +926,10 @@ extension Dictionary {
/// this dictionary.
///
/// - Complexity: O(*n*), where *n* is the length of the dictionary.
@inlinable
public func mapValues<T>(
_ transform: (Value) throws -> T
) rethrows -> Dictionary<Key, T> {
@_alwaysEmitIntoClient
public func mapValues<T, E: Error>(
_ transform: (Value) throws(E) -> T
) throws(E) -> Dictionary<Key, T> {
return try Dictionary<Key, T>(_native: _variant.mapValues(transform))
}

Expand Down Expand Up @@ -959,12 +959,13 @@ extension Dictionary {
///
/// - Complexity: O(*m* + *n*), where *n* is the length of the original
/// dictionary and *m* is the length of the resulting dictionary.
@inlinable
public func compactMapValues<T>(
_ transform: (Value) throws -> T?
) rethrows -> Dictionary<Key, T> {
let result: _NativeDictionary<Key, T> =
try self.reduce(into: _NativeDictionary<Key, T>()) { (result, element) in
@_alwaysEmitIntoClient
public func compactMapValues<T, E: Error>(
_ transform: (Value) throws(E) -> T?
) throws(E) -> Dictionary<Key, T> {
let result: _NativeDictionary<Key, T> = try self.reduce(
into: _NativeDictionary<Key, T>()
) { (result, element) throws(E) in
if let value = try transform(element.value) {
result.insertNew(key: element.key, value: value)
}
Expand Down
8 changes: 4 additions & 4 deletions stdlib/public/core/DictionaryBridging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -559,10 +559,10 @@ extension __CocoaDictionary: _DictionaryBuffer {
}

extension __CocoaDictionary {
@inlinable
internal func mapValues<Key: Hashable, Value, T>(
_ transform: (Value) throws -> T
) rethrows -> _NativeDictionary<Key, T> {
@_alwaysEmitIntoClient
internal func mapValues<Key: Hashable, Value, T, E: Error>(
_ transform: (Value) throws(E) -> T
) throws(E) -> _NativeDictionary<Key, T> {
var result = _NativeDictionary<Key, T>(capacity: self.count)
for (cocoaKey, cocoaValue) in self {
let key = _forceBridgeFromObjectiveC(cocoaKey, Key.self)
Expand Down
8 changes: 4 additions & 4 deletions stdlib/public/core/DictionaryVariant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -452,10 +452,10 @@ extension Dictionary._Variant {
}

extension Dictionary._Variant {
@inlinable
internal func mapValues<T>(
_ transform: (Value) throws -> T
) rethrows -> _NativeDictionary<Key, T> {
@_alwaysEmitIntoClient
internal func mapValues<T, E: Error>(
_ transform: (Value) throws(E) -> T
) throws(E) -> _NativeDictionary<Key, T> {
#if _runtime(_ObjC)
guard isNative else {
return try asCocoa.mapValues(transform)
Expand Down
158 changes: 158 additions & 0 deletions stdlib/public/core/LegacyABI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,161 @@ internal func _unsafeMinus(_ lhs: Int, _ rhs: Int) -> Int {
return lhs &- rhs
#endif
}

extension Dictionary {
@usableFromInline
@_silgen_name("$sSD9mapValuesySDyxqd__Gqd__q_KXEKlF")
internal func __abi_mapValues<T>(
_ transform: (Value) throws -> T
) rethrows -> Dictionary<Key, T> {
return try Dictionary<Key, T>(_native: _variant.mapValues(transform))
}

@usableFromInline
@_silgen_name("$sSD16compactMapValuesySDyxqd__Gqd__Sgq_KXEKlF")
internal func __abi_compactMapValues<T>(
_ transform: (Value) throws -> T?
) rethrows -> Dictionary<Key, T> {
let result: _NativeDictionary<Key, T> =
try self.reduce(into: _NativeDictionary<Key, T>()) { (result, element) in
if let value = try transform(element.value) {
result.insertNew(key: element.key, value: value)
}
}
return Dictionary<Key, T>(_native: result)
}
}

extension Dictionary._Variant {
@usableFromInline
@_silgen_name("$sSD8_VariantV9mapValuesys17_NativeDictionaryVyxqd__Gqd__q_KXEKlF")
internal func __abi_mapValues<T>(
_ transform: (Value) throws -> T
) rethrows -> _NativeDictionary<Key, T> {
#if _runtime(_ObjC)
guard isNative else {
return try asCocoa.mapValues(transform)
}
#endif
return try asNative.mapValues(transform)
}
}

#if _runtime(_ObjC)
extension __CocoaDictionary {
@usableFromInline
@_silgen_name("$ss17__CocoaDictionaryV9mapValuesys07_NativeB0Vyxq0_Gq0_q_KXEKSHRzr1_lF")
internal func __abi_mapValues<Key: Hashable, Value, T>(
_ transform: (Value) throws -> T
) rethrows -> _NativeDictionary<Key, T> {
var result = _NativeDictionary<Key, T>(capacity: self.count)
for (cocoaKey, cocoaValue) in self {
let key = _forceBridgeFromObjectiveC(cocoaKey, Key.self)
let value = _forceBridgeFromObjectiveC(cocoaValue, Value.self)
try result.insertNew(key: key, value: transform(value))
}
return result
}
}
#endif

extension _NativeDictionary {
@usableFromInline
@_silgen_name("$ss17_NativeDictionaryV9mapValuesyAByxqd__Gqd__q_KXEKlF")
internal func __abi_mapValues<T>(
_ transform: (Value) throws -> T
) rethrows -> _NativeDictionary<Key, T> {
let resultStorage = unsafe _DictionaryStorage<Key, T>.copy(original: _storage)
unsafe _internalInvariant(resultStorage._seed == _storage._seed)
let result = unsafe _NativeDictionary<Key, T>(resultStorage)
// Because the current and new buffer have the same scale and seed, we can
// initialize to the same locations in the new buffer, skipping hash value
// recalculations.
for unsafe bucket in unsafe hashTable {
let key = unsafe self.uncheckedKey(at: bucket)
let value = unsafe self.uncheckedValue(at: bucket)
try result._insert(at: bucket, key: key, value: transform(value))
}
return result
}
}

extension Sequence {
// ABI-only entrypoint for the rethrows version of map, which has been
// superseded by the typed-throws version. Expressed as "throws", which is
// ABI-compatible with "rethrows".
@_spi(SwiftStdlibLegacyABI) @available(swift, obsoleted: 1)
@usableFromInline
@_silgen_name("$sSTsE3mapySayqd__Gqd__7ElementQzKXEKlF")
func __rethrows_map<T>(
_ transform: (Element) throws -> T
) throws -> [T] {
try map(transform)
}

@usableFromInline
@_silgen_name("$sSTsE6filterySay7ElementQzGSbACKXEKF")
internal __consuming func __abi_filter(
_ isIncluded: (Element) throws -> Bool
) rethrows -> [Element] {
return try __abi__filter(isIncluded)
}

@usableFromInline
@_silgen_name("$sSTsE7_filterySay7ElementQzGSbACKXEKF")
internal func __abi__filter(
_ isIncluded: (Element) throws -> Bool
) rethrows -> [Element] {

var result = ContiguousArray<Element>()

var iterator = self.makeIterator()

while let element = iterator.next() {
if try isIncluded(element) {
result.append(element)
}
}

return Array(result)
}

@usableFromInline
@_semantics("sequence.forEach")
@_silgen_name("$sSTsE7forEachyyy7ElementQzKXEKF")
internal func __abi_forEach(
_ body: (Element) throws -> Void
) rethrows {
for element in self {
try body(element)
}
}

@usableFromInline
@_silgen_name("$sSTsE6reduceyqd__qd___qd__qd___7ElementQztKXEtKlF")
internal func __abi_reduce<Result>(
_ initialResult: Result,
_ nextPartialResult:
(_ partialResult: Result, Element) throws -> Result
) rethrows -> Result {
var accumulator = initialResult
for element in self {
accumulator = try nextPartialResult(accumulator, element)
}
return accumulator
}

@usableFromInline
@_silgen_name("$sSTsE6reduce4into_qd__qd__n_yqd__z_7ElementQztKXEtKlF")
internal func __abi_reduce<Result>(
into initialResult: __owned Result,
_ updateAccumulatingResult:
(_ partialResult: inout Result, Element) throws -> ()
) rethrows -> Result {
var accumulator = initialResult
for element in self {
try updateAccumulatingResult(&accumulator, element)
}
return accumulator
}
}
8 changes: 4 additions & 4 deletions stdlib/public/core/NativeDictionary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -755,10 +755,10 @@ extension _NativeDictionary { // Deletion
}

extension _NativeDictionary { // High-level operations
@inlinable
internal func mapValues<T>(
_ transform: (Value) throws -> T
) rethrows -> _NativeDictionary<Key, T> {
@_alwaysEmitIntoClient
internal func mapValues<T, E: Error>(
_ transform: (Value) throws(E) -> T
) throws(E) -> _NativeDictionary<Key, T> {
let resultStorage = unsafe _DictionaryStorage<Key, T>.copy(original: _storage)
unsafe _internalInvariant(resultStorage._seed == _storage._seed)
let result = unsafe _NativeDictionary<Key, T>(resultStorage)
Expand Down
40 changes: 13 additions & 27 deletions stdlib/public/core/Sequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -705,18 +705,6 @@ extension Sequence {
return Array(result)
}

// ABI-only entrypoint for the rethrows version of map, which has been
// superseded by the typed-throws version. Expressed as "throws", which is
// ABI-compatible with "rethrows".
@_spi(SwiftStdlibLegacyABI) @available(swift, obsoleted: 1)
@usableFromInline
@_silgen_name("$sSTsE3mapySayqd__Gqd__7ElementQzKXEKlF")
func __rethrows_map<T>(
_ transform: (Element) throws -> T
) throws -> [T] {
try map(transform)
}

/// Returns an array containing, in order, the elements of the sequence
/// that satisfy the given predicate.
///
Expand All @@ -734,20 +722,18 @@ extension Sequence {
/// - Returns: An array of the elements that `isIncluded` allowed.
///
/// - Complexity: O(*n*), where *n* is the length of the sequence.
@inlinable
public __consuming func filter(
_ isIncluded: (Element) throws -> Bool
) rethrows -> [Element] {
return try _filter(isIncluded)
@_alwaysEmitIntoClient
public __consuming func filter<E: Error>(
_ isIncluded: (Element) throws(E) -> Bool
) throws(E) -> [Element] {
try _filter(isIncluded)
}

@_transparent
public func _filter(
_ isIncluded: (Element) throws -> Bool
) rethrows -> [Element] {

@_alwaysEmitIntoClient
public func _filter<E: Error>(
_ isIncluded: (Element) throws(E) -> Bool
) throws(E) -> [Element] {
var result = ContiguousArray<Element>()

var iterator = self.makeIterator()

while let element = iterator.next() {
Expand Down Expand Up @@ -809,11 +795,11 @@ extension Sequence {
///
/// - Parameter body: A closure that takes an element of the sequence as a
/// parameter.
@_alwaysEmitIntoClient
@_semantics("sequence.forEach")
@inlinable
public func forEach(
_ body: (Element) throws -> Void
) rethrows {
public func forEach<E: Error>(
_ body: (Element) throws(E) -> Void
) throws(E) {
for element in self {
try body(element)
}
Expand Down
18 changes: 8 additions & 10 deletions stdlib/public/core/SequenceAlgorithms.swift
Original file line number Diff line number Diff line change
Expand Up @@ -664,12 +664,11 @@ extension Sequence {
/// the result is `initialResult`.
///
/// - Complexity: O(*n*), where *n* is the length of the sequence.
@inlinable
public func reduce<Result>(
@_alwaysEmitIntoClient
public func reduce<Result, E: Error>(
_ initialResult: Result,
_ nextPartialResult:
(_ partialResult: Result, Element) throws -> Result
) rethrows -> Result {
_ nextPartialResult: (Result, Element) throws(E) -> Result
) throws(E) -> Result {
var accumulator = initialResult
for element in self {
accumulator = try nextPartialResult(accumulator, element)
Expand Down Expand Up @@ -721,12 +720,11 @@ extension Sequence {
/// the result is `initialResult`.
///
/// - Complexity: O(*n*), where *n* is the length of the sequence.
@inlinable
public func reduce<Result>(
@_alwaysEmitIntoClient
public func reduce<Result, E: Error>(
into initialResult: __owned Result,
_ updateAccumulatingResult:
(_ partialResult: inout Result, Element) throws -> ()
) rethrows -> Result {
_ updateAccumulatingResult: (inout Result, Element) throws(E) -> ()
) throws(E) -> Result {
var accumulator = initialResult
for element in self {
try updateAccumulatingResult(&accumulator, element)
Expand Down
3 changes: 2 additions & 1 deletion test/Constraints/diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1127,7 +1127,8 @@ func rdar17170728() {
}

let _ = [i, j, k].reduce(0 as Int?) { // expected-error {{missing argument label 'into:' in call}}
// expected-error@-1 {{cannot convert value of type 'Int?' to expected argument type '(inout (Bool, Bool) -> Bool?, Int?) throws -> ()'}}
// expected-error@-1 {{cannot convert value of type 'Int?' to expected argument type '(inout (Bool, Bool) -> Bool?, Int?) throws(E) -> ()'}}
// expected-error@-2 {{generic parameter 'E' could not be inferred}}
$0 && $1 ? $0 + $1 : ($0 ? $0 : ($1 ? $1 : nil))
// expected-error@-1 {{binary operator '+' cannot be applied to two 'Bool' operands}}
}
Expand Down
4 changes: 3 additions & 1 deletion test/Constraints/issue-55410.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@
protocol P {}
typealias T = (P) -> Void
let x: T! = [1, 2, 3].reversed().reduce()
// expected-error@-1 {{missing arguments for parameters #1, #2 in call}}
// expected-error@-1 {{no exact matches in call to instance method 'reduce'}}
// expected-note@-2 {{found candidate with type '(Optional<T>, (Optional<T>, Int) throws(_) -> Optional<T>) throws(_) -> Optional<T>' (aka '(Optional<(any P) -> ()>, (Optional<(any P) -> ()>, Int) throws(_) -> Optional<(any P) -> ()>) throws(_) -> Optional<(any P) -> ()>')}}
// expected-note@-3 {{found candidate with type '(__owned Optional<T>, (inout Optional<T>, Int) throws(_) -> ()) throws(_) -> Optional<T>' (aka '(__owned Optional<(any P) -> ()>, (inout Optional<(any P) -> ()>, Int) throws(_) -> ()) throws(_) -> Optional<(any P) -> ()>')}}
2 changes: 1 addition & 1 deletion test/Constraints/pack-expansion-expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func forEachEach<each C, U>(c: repeat each C, function: (U) -> Void)
where repeat each C: Collection, repeat (each C).Element == U {
// expected-error@-1{{same-element requirements are not yet supported}}
_ = (repeat (each c).forEach(function))
// expected-error@-1 {{cannot convert value of type '(U) -> Void' to expected argument type '((each C).Element) throws -> Void'}}
// expected-error@-1 {{cannot convert value of type '(U) -> Void' to expected argument type '((each C).Element) -> Void'}}
}

func typeReprPacks<each T: ExpressibleByIntegerLiteral>(_ t: repeat each T) {
Expand Down
Loading