From c433740c080016f20048d08ce996cd14fd44fbf7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 9 Nov 2023 22:22:35 -0800 Subject: [PATCH 01/12] Enable TypedThrows in the standard library build --- stdlib/public/core/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt index b069d1b06fe18..79310e935f230 100644 --- a/stdlib/public/core/CMakeLists.txt +++ b/stdlib/public/core/CMakeLists.txt @@ -298,7 +298,7 @@ endif() # STAGING: Temporarily avoids having to write #fileID in Swift.swiftinterface. list(APPEND swift_stdlib_compile_flags "-Xfrontend" "-enable-experimental-concise-pound-file") -list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "Macros") +list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "TypedThrows") list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "FreestandingMacros") list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "Extern") From 0d15d4edbeb410c1b396908afbd6e75793272da9 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 9 Nov 2023 22:22:52 -0800 Subject: [PATCH 02/12] Replace rethrowing `map` with generic typed throws Adopt typed throws for the `map` operation to propagate thrown error types through the `map` API. This is done in a manner that is backward compatible for almost all cases: * The new typed-throws entrypoint is `@_alwaysEmitIntoClient` so it can back-deploy all the way back. * The old `rethrows` entrypoint is left in place, using `@usableFromInline` with `internal` so it is part of the ABI but not the public interface, and `@_disfavoredOverload` so the type checker avoids it while building the standard library itself. The old entrypoint is implemented in terms of the new one. Note that the implementation details for the existential collection "box" classes rely on method overriding of `_map` operations, and the types are frozen, so we don't get to change their signatures. However, these are only implementations, not API: the actual API `map` functions can be upgraded to typed throws. Note that this code makes use of a known hole in `rethrows` checking to allow calling between `rethrows` and typed throws. We'll need to do something about this for source-compatibility reasons, but I'll follow up with that separately. --- stdlib/public/core/Collection.swift | 21 ++- .../public/core/ExistentialCollection.swift | 120 ++++++++++++++++-- stdlib/public/core/Misc.swift | 11 ++ stdlib/public/core/Sequence.swift | 21 ++- 4 files changed, 155 insertions(+), 18 deletions(-) diff --git a/stdlib/public/core/Collection.swift b/stdlib/public/core/Collection.swift index b16d4cdcef077..e44e89b5a632b 100644 --- a/stdlib/public/core/Collection.swift +++ b/stdlib/public/core/Collection.swift @@ -1190,9 +1190,10 @@ extension Collection { /// - Returns: An array containing the transformed elements of this /// sequence. @inlinable - public func map( - _ transform: (Element) throws -> T - ) rethrows -> [T] { + @_alwaysEmitIntoClient + public func map( + _ transform: (Element) throws(E) -> T + ) throws(E) -> [T] { // TODO: swift-3-indexing-model - review the following let n = self.count if n == 0 { @@ -1213,6 +1214,20 @@ extension Collection { return Array(result) } + // ABI-only entrypoint + @usableFromInline + @_disfavoredOverload + func map( + _ transform: (Element) throws -> T + ) rethrows -> [T] { + do { + return try map(transform) + } catch { + try _rethrowsViaClosure { throw error } + Builtin.unreachable() + } + } + /// Returns a subsequence containing all but the given number of initial /// elements. /// diff --git a/stdlib/public/core/ExistentialCollection.swift b/stdlib/public/core/ExistentialCollection.swift index be20e2fdc8ebe..30d7a7a7d6ca9 100644 --- a/stdlib/public/core/ExistentialCollection.swift +++ b/stdlib/public/core/ExistentialCollection.swift @@ -526,7 +526,12 @@ internal final class _SequenceBox: _AnySequenceBox { internal override func _map( _ transform: (Element) throws -> T ) rethrows -> [T] { - return try _base.map(transform) + do { + return try _base.map(transform) + } catch { + try _rethrowsViaClosure { throw error } + Builtin.unreachable() + } } @inlinable internal override func _filter( @@ -619,7 +624,12 @@ internal final class _CollectionBox: _AnyCollectionBox internal override func _map( _ transform: (Element) throws -> T ) rethrows -> [T] { - return try _base.map(transform) + do { + return try _base.map(transform) + } catch { + try _rethrowsViaClosure { throw error } + Builtin.unreachable() + } } @inlinable internal override func _filter( @@ -814,7 +824,12 @@ internal final class _BidirectionalCollectionBox internal override func _map( _ transform: (Element) throws -> T ) rethrows -> [T] { - return try _base.map(transform) + do { + return try _base.map(transform) + } catch { + try _rethrowsViaClosure { throw error } + Builtin.unreachable() + } } @inlinable internal override func _filter( @@ -1027,7 +1042,12 @@ internal final class _RandomAccessCollectionBox internal override func _map( _ transform: (Element) throws -> T ) rethrows -> [T] { - return try _base.map(transform) + do { + return try _base.map(transform) + } catch { + try _rethrowsViaClosure { throw error } + Builtin.unreachable() + } } @inlinable internal override func _filter( @@ -1303,10 +1323,29 @@ extension AnySequence { } @inlinable - public func map( + @_alwaysEmitIntoClient + public func map( + _ transform: (Element) throws(E) -> T + ) throws(E) -> [T] { + do { + return try _box._map(transform) + } catch { + throw error as! E + } + } + + // ABI-only entrypoint + @usableFromInline + @_disfavoredOverload + func map( _ transform: (Element) throws -> T ) rethrows -> [T] { - return try _box._map(transform) + do { + return try map(transform) + } catch { + try _rethrowsViaClosure { throw error } + Builtin.unreachable() + } } @inlinable @@ -1390,10 +1429,29 @@ extension AnyCollection { } @inlinable - public func map( + @_alwaysEmitIntoClient + public func map( + _ transform: (Element) throws(E) -> T + ) throws(E) -> [T] { + do { + return try _box._map(transform) + } catch { + throw error as! E + } + } + + // ABI-only entrypoint + @usableFromInline + @_disfavoredOverload + func map( _ transform: (Element) throws -> T ) rethrows -> [T] { - return try _box._map(transform) + do { + return try map(transform) + } catch { + try _rethrowsViaClosure { throw error } + Builtin.unreachable() + } } @inlinable @@ -1483,10 +1541,29 @@ extension AnyBidirectionalCollection { } @inlinable - public func map( + @_alwaysEmitIntoClient + public func map( + _ transform: (Element) throws(E) -> T + ) throws(E) -> [T] { + do { + return try _box._map(transform) + } catch { + throw error as! E + } + } + + // ABI-only entrypoint + @usableFromInline + @_disfavoredOverload + func map( _ transform: (Element) throws -> T ) rethrows -> [T] { - return try _box._map(transform) + do { + return try map(transform) + } catch { + try _rethrowsViaClosure { throw error } + Builtin.unreachable() + } } @inlinable @@ -1578,10 +1655,29 @@ extension AnyRandomAccessCollection { } @inlinable - public func map( + @_alwaysEmitIntoClient + public func map( + _ transform: (Element) throws(E) -> T + ) throws(E) -> [T] { + do { + return try _box._map(transform) + } catch { + throw error as! E + } + } + + // ABI-only entrypoint + @usableFromInline + @_disfavoredOverload + func map( _ transform: (Element) throws -> T ) rethrows -> [T] { - return try _box._map(transform) + do { + return try map(transform) + } catch { + try _rethrowsViaClosure { throw error } + Builtin.unreachable() + } } @inlinable diff --git a/stdlib/public/core/Misc.swift b/stdlib/public/core/Misc.swift index 7bdc22e3ed9d9..efb92d9e8c3f1 100644 --- a/stdlib/public/core/Misc.swift +++ b/stdlib/public/core/Misc.swift @@ -159,6 +159,17 @@ public func _unsafePerformance(_ c: () -> T) -> T { return c() } +// Helper function that exploits a bug in rethrows checking to +// allow us to call rethrows functions from generic typed-throws functions +// and vice-versa. +@usableFromInline +@_alwaysEmitIntoClient +@inline(__always) +func _rethrowsViaClosure(_ fn: () throws -> ()) rethrows { + try fn() +} + + /// This marker protocol represents types that support copying. /// This type is not yet available for use to express explicit /// constraints on generics in your programs. It is currently diff --git a/stdlib/public/core/Sequence.swift b/stdlib/public/core/Sequence.swift index a325dac5aafe5..53af012726bf0 100644 --- a/stdlib/public/core/Sequence.swift +++ b/stdlib/public/core/Sequence.swift @@ -670,9 +670,10 @@ extension Sequence { /// /// - Complexity: O(*n*), where *n* is the length of the sequence. @inlinable - public func map( - _ transform: (Element) throws -> T - ) rethrows -> [T] { + @_alwaysEmitIntoClient + public func map( + _ transform: (Element) throws(E) -> T + ) throws(E) -> [T] { let initialCapacity = underestimatedCount var result = ContiguousArray() result.reserveCapacity(initialCapacity) @@ -690,6 +691,20 @@ extension Sequence { return Array(result) } + // ABI-only entrypoint + @usableFromInline + @_disfavoredOverload + func map( + _ transform: (Element) throws -> T + ) rethrows -> [T] { + do { + return try map(transform) + } catch { + try _rethrowsViaClosure { throw error } + Builtin.unreachable() + } + } + /// Returns an array containing, in order, the elements of the sequence /// that satisfy the given predicate. /// From 1892396e14ce304abd9eebc25d7ed75fc1a7ea80 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 10 Nov 2023 08:08:55 -0800 Subject: [PATCH 03/12] Improve ABI-only rethrowing `map` shims based on code review Replace the hackish use of `@_disfavoredOverload` with the more principled use of `@_silgen_name` for the entrypoint we are maintaining, then rename these functions in source to `__rethrows_map` to indicate what they're for. While here, make them `throws` instead of `rethrows`. The ABI is the same, and `throws` allows us do avoid to do/catch tricks with rethrows functions. --- stdlib/public/core/Collection.swift | 17 ++--- .../public/core/ExistentialCollection.swift | 68 ++++++++----------- stdlib/public/core/Sequence.swift | 17 ++--- 3 files changed, 42 insertions(+), 60 deletions(-) diff --git a/stdlib/public/core/Collection.swift b/stdlib/public/core/Collection.swift index e44e89b5a632b..3a090d10f0a22 100644 --- a/stdlib/public/core/Collection.swift +++ b/stdlib/public/core/Collection.swift @@ -1214,18 +1214,15 @@ extension Collection { return Array(result) } - // ABI-only entrypoint + // 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". @usableFromInline - @_disfavoredOverload - func map( + @_silgen_name("$sSlsE3mapySayqd__Gqd__7ElementQzKXEKlF") + func __rethrows_map( _ transform: (Element) throws -> T - ) rethrows -> [T] { - do { - return try map(transform) - } catch { - try _rethrowsViaClosure { throw error } - Builtin.unreachable() - } + ) throws -> [T] { + try map(transform) } /// Returns a subsequence containing all but the given number of initial diff --git a/stdlib/public/core/ExistentialCollection.swift b/stdlib/public/core/ExistentialCollection.swift index 30d7a7a7d6ca9..686d125fec818 100644 --- a/stdlib/public/core/ExistentialCollection.swift +++ b/stdlib/public/core/ExistentialCollection.swift @@ -1334,18 +1334,15 @@ extension AnySequence { } } - // ABI-only entrypoint + // 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". @usableFromInline - @_disfavoredOverload - func map( + @_silgen_name("$ss11AnySequenceV3mapySayqd__Gqd__xKXEKlF") + func __rethrows_map( _ transform: (Element) throws -> T - ) rethrows -> [T] { - do { - return try map(transform) - } catch { - try _rethrowsViaClosure { throw error } - Builtin.unreachable() - } + ) throws -> [T] { + try map(transform) } @inlinable @@ -1440,18 +1437,15 @@ extension AnyCollection { } } - // ABI-only entrypoint + // 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". @usableFromInline - @_disfavoredOverload - func map( + @_silgen_name("$ss13AnyCollectionV3mapySayqd__Gqd__xKXEKlF") + func __rethrows_map( _ transform: (Element) throws -> T - ) rethrows -> [T] { - do { - return try map(transform) - } catch { - try _rethrowsViaClosure { throw error } - Builtin.unreachable() - } + ) throws -> [T] { + try map(transform) } @inlinable @@ -1552,18 +1546,15 @@ extension AnyBidirectionalCollection { } } - // ABI-only entrypoint + // 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". @usableFromInline - @_disfavoredOverload - func map( + @_silgen_name("$ss26AnyBidirectionalCollectionV3mapySayqd__Gqd__xKXEKlF") + func __rethrows_map( _ transform: (Element) throws -> T - ) rethrows -> [T] { - do { - return try map(transform) - } catch { - try _rethrowsViaClosure { throw error } - Builtin.unreachable() - } + ) throws -> [T] { + try map(transform) } @inlinable @@ -1666,18 +1657,15 @@ extension AnyRandomAccessCollection { } } - // ABI-only entrypoint + // 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". @usableFromInline - @_disfavoredOverload - func map( + @_silgen_name("$ss25AnyRandomAccessCollectionV3mapySayqd__Gqd__xKXEKlF") + func __rethrows_map( _ transform: (Element) throws -> T - ) rethrows -> [T] { - do { - return try map(transform) - } catch { - try _rethrowsViaClosure { throw error } - Builtin.unreachable() - } + ) throws -> [T] { + try map(transform) } @inlinable diff --git a/stdlib/public/core/Sequence.swift b/stdlib/public/core/Sequence.swift index 53af012726bf0..d1afc0e1c2805 100644 --- a/stdlib/public/core/Sequence.swift +++ b/stdlib/public/core/Sequence.swift @@ -691,18 +691,15 @@ extension Sequence { return Array(result) } - // ABI-only entrypoint + // 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". @usableFromInline - @_disfavoredOverload - func map( + @_silgen_name("$sSTsE3mapySayqd__Gqd__7ElementQzKXEKlF") + func __rethrows_map( _ transform: (Element) throws -> T - ) rethrows -> [T] { - do { - return try map(transform) - } catch { - try _rethrowsViaClosure { throw error } - Builtin.unreachable() - } + ) throws -> [T] { + try map(transform) } /// Returns an array containing, in order, the elements of the sequence From 5db3685e1cab242dad643edbeaaa53f85a973d39 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 10 Nov 2023 08:20:10 -0800 Subject: [PATCH 04/12] [Existential collections] Switch internal _map rethrows methods to throws These class methods are internal, but because they are overridden and are part of a `@usableFromInline`, `@_fixed_layout` class, we they can't be moved over to typed throws without breaking ABI. However, they are only ever called from typed-throws functions, which already need a do...catch dance to downcast the error itself. Make them `throws` instead, which is ABI-compatible, but eliminates the need for do...catch hackery in the function itself. --- .../public/core/ExistentialCollection.swift | 38 +++++-------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/stdlib/public/core/ExistentialCollection.swift b/stdlib/public/core/ExistentialCollection.swift index 686d125fec818..ac2b2e04626bc 100644 --- a/stdlib/public/core/ExistentialCollection.swift +++ b/stdlib/public/core/ExistentialCollection.swift @@ -175,7 +175,7 @@ internal class _AnySequenceBox { @inlinable internal func _map( _ transform: (Element) throws -> T - ) rethrows -> [T] { + ) throws -> [T] { _abstract() } @@ -525,13 +525,8 @@ internal final class _SequenceBox: _AnySequenceBox { @inlinable internal override func _map( _ transform: (Element) throws -> T - ) rethrows -> [T] { - do { - return try _base.map(transform) - } catch { - try _rethrowsViaClosure { throw error } - Builtin.unreachable() - } + ) throws -> [T] { + try _base.map(transform) } @inlinable internal override func _filter( @@ -623,13 +618,8 @@ internal final class _CollectionBox: _AnyCollectionBox @inlinable internal override func _map( _ transform: (Element) throws -> T - ) rethrows -> [T] { - do { - return try _base.map(transform) - } catch { - try _rethrowsViaClosure { throw error } - Builtin.unreachable() - } + ) throws -> [T] { + try _base.map(transform) } @inlinable internal override func _filter( @@ -823,13 +813,8 @@ internal final class _BidirectionalCollectionBox @inlinable internal override func _map( _ transform: (Element) throws -> T - ) rethrows -> [T] { - do { - return try _base.map(transform) - } catch { - try _rethrowsViaClosure { throw error } - Builtin.unreachable() - } + ) throws -> [T] { + try _base.map(transform) } @inlinable internal override func _filter( @@ -1041,13 +1026,8 @@ internal final class _RandomAccessCollectionBox @inlinable internal override func _map( _ transform: (Element) throws -> T - ) rethrows -> [T] { - do { - return try _base.map(transform) - } catch { - try _rethrowsViaClosure { throw error } - Builtin.unreachable() - } + ) throws -> [T] { + try _base.map(transform) } @inlinable internal override func _filter( From 0e3b10a2e1e94aad31505d04305aa2b9d5996172 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 13 Nov 2023 13:43:46 -0800 Subject: [PATCH 05/12] Drop `throws(any Error)` and `throws(Never)` sugar after substitution When substitution into a function type with `throws(E)` produces either `throws(any Error)` or `throws(Never)`, adjust the resulting function type to the equivalent `throws` or non-throwing. --- lib/AST/Type.cpp | 37 ++++++++++++++++++++----------- test/decl/func/typed_throws.swift | 11 +++++++++ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index d7693f396d075..f033ca00b7238 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -5065,6 +5065,28 @@ case TypeKind::Id: isUnchanged = false; } + llvm::Optional extInfo; + if (function->hasExtInfo()) { + extInfo = function->getExtInfo() + .withGlobalActor(globalActorType) + .withThrows(function->isThrowing(), thrownError); + + // If there was a generic thrown error and it substituted with + // 'any Error' or 'Never', map to 'throws' or non-throwing rather than + // maintaining the sugar. + if (auto origThrownError = function->getThrownError()) { + if (origThrownError->isTypeParameter() || + origThrownError->isTypeVariableOrMember()) { + // 'any Error' + if (thrownError->isEqual( + thrownError->getASTContext().getErrorExistentialType())) + extInfo = extInfo->withThrows(true, Type()); + else if (thrownError->isNever()) + extInfo = extInfo->withThrows(false, Type()); + } + } + } + if (auto genericFnType = dyn_cast(base)) { #ifndef NDEBUG // Check that generic parameters won't be transformed. @@ -5080,24 +5102,13 @@ case TypeKind::Id: if (isUnchanged) return *this; auto genericSig = genericFnType->getGenericSignature(); - if (!function->hasExtInfo()) - return GenericFunctionType::get(genericSig, substParams, resultTy); return GenericFunctionType::get( - genericSig, substParams, resultTy, - function->getExtInfo() - .withGlobalActor(globalActorType) - .withThrows(function->isThrowing(), thrownError)); + genericSig, substParams, resultTy, extInfo); } if (isUnchanged) return *this; - if (!function->hasExtInfo()) - return FunctionType::get(substParams, resultTy); - return FunctionType::get( - substParams, resultTy, - function->getExtInfo() - .withGlobalActor(globalActorType) - .withThrows(function->isThrowing(), thrownError)); + return FunctionType::get(substParams, resultTy, extInfo); } case TypeKind::ArraySlice: { diff --git a/test/decl/func/typed_throws.swift b/test/decl/func/typed_throws.swift index d293d7fb85dc4..df45a6eda6357 100644 --- a/test/decl/func/typed_throws.swift +++ b/test/decl/func/typed_throws.swift @@ -179,3 +179,14 @@ func fromRethrows(body: () throws -> Void) rethrows { try notRethrowsLike2(body) // expected-error{{call can throw, but the error is not handled; a function declared 'rethrows' may only throw if its parameter does}} try notRethrowsLike3(body) // expected-error{{call can throw, but the error is not handled; a function declared 'rethrows' may only throw if its parameter does}} } + +// Substitution involving 'any Error' or 'Never' thrown error types should +// use untyped throws or be non-throwing. +enum G_E { + case tuple((x: T, y: T)) +} + +func testArrMap(arr: [String]) { + _ = mapArray(arr, body: G_E.tuple) + // expected-error@-1{{cannot convert value of type '((x: Int, y: Int)) -> G_E' to expected argument type '(String) -> G_E'}} +} From b93e228f9ddfa6be01f85966889413a799e8e012 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 13 Nov 2023 14:08:44 -0800 Subject: [PATCH 06/12] Update tests for map switching to typed throws --- test/Constraints/closures.swift | 7 +++--- test/Constraints/enum_cases.swift | 8 +++---- test/Constraints/tuple_arguments.swift | 2 +- test/IDE/complete_from_stdlib.swift | 10 ++++---- test/SILGen/function_conversion.swift | 4 ++-- test/SILGen/partial_apply_override.swift | 6 ++--- ...bility-stdlib-source-x86_64.swift.expected | 12 ++++++++++ .../stability-stdlib-abi-without-asserts.test | 23 +++++++++++++++++++ 8 files changed, 53 insertions(+), 19 deletions(-) diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 594291a3239db..3cca710ba9565 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -1143,11 +1143,11 @@ struct R_76250381 { // https://github.com/apple/swift/issues/55926 (0..<10).map { x, y in } -// expected-error@-1 {{contextual closure type '(Range.Element) throws -> ()' (aka '(Int) throws -> ()') expects 1 argument, but 2 were used in closure body}} +// expected-error@-1 {{contextual closure type '(Range.Element) -> ()' (aka '(Int) -> ()') expects 1 argument, but 2 were used in closure body}} (0..<10).map { x, y, z in } -// expected-error@-1 {{contextual closure type '(Range.Element) throws -> ()' (aka '(Int) throws -> ()') expects 1 argument, but 3 were used in closure body}} +// expected-error@-1 {{contextual closure type '(Range.Element) -> ()' (aka '(Int) -> ()') expects 1 argument, but 3 were used in closure body}} (0..<10).map { x, y, z, w in } -// expected-error@-1 {{contextual closure type '(Range.Element) throws -> ()' (aka '(Int) throws -> ()') expects 1 argument, but 4 were used in closure body}} +// expected-error@-1 {{contextual closure type '(Range.Element) -> ()' (aka '(Int) -> ()') expects 1 argument, but 4 were used in closure body}} // rdar://77022842 - crash due to a missing argument to a ternary operator func rdar77022842(argA: Bool? = nil, argB: Bool? = nil) { @@ -1171,6 +1171,7 @@ func rdar76058892() { test { // expected-error {{contextual closure type '() -> String' expects 0 arguments, but 1 was used in closure body}} if let arr = arr { arr.map($0.test) // expected-note {{anonymous closure parameter '$0' is used here}} // expected-error {{generic parameter 'T' could not be inferred}} + // expected-error@-1 {{generic parameter 'E' could not be inferred}} } } } diff --git a/test/Constraints/enum_cases.swift b/test/Constraints/enum_cases.swift index 6d925378351a4..6408ed1b65700 100644 --- a/test/Constraints/enum_cases.swift +++ b/test/Constraints/enum_cases.swift @@ -19,14 +19,14 @@ enum G_E { let arr: [String] = [] let _ = arr.map(E.foo) // Ok let _ = arr.map(E.bar) // Ok -let _ = arr.map(E.two) // expected-error {{cannot convert value of type '(Int, Int) -> E' to expected argument type '(String) throws -> E'}} +let _ = arr.map(E.two) // expected-error {{cannot convert value of type '(Int, Int) -> E' to expected argument type '(String) -> E'}} -let _ = arr.map(E.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> E' to expected argument type '(String) throws -> E'}} +let _ = arr.map(E.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> E' to expected argument type '(String) -> E'}} let _ = arr.map(G_E.foo) // Ok let _ = arr.map(G_E.bar) // Ok -let _ = arr.map(G_E.two) // expected-error {{cannot convert value of type '(String, String) -> G_E' to expected argument type '(String) throws -> G_E'}} -let _ = arr.map(G_E.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> G_E' to expected argument type '(String) throws -> G_E'}} +let _ = arr.map(G_E.two) // expected-error {{cannot convert value of type '(String, String) -> G_E' to expected argument type '(String) -> G_E'}} +let _ = arr.map(G_E.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> G_E' to expected argument type '(String) -> G_E'}} let _ = E.foo("hello") // expected-error {{missing argument label 'bar:' in call}} let _ = E.bar("hello") // Ok diff --git a/test/Constraints/tuple_arguments.swift b/test/Constraints/tuple_arguments.swift index d36e6f95bed15..5b34df8a9579f 100644 --- a/test/Constraints/tuple_arguments.swift +++ b/test/Constraints/tuple_arguments.swift @@ -1726,7 +1726,7 @@ do { do { func f(_: Int...) {} let _ = [(1, 2, 3)].map(f) // expected-error {{no exact matches in call to instance method 'map'}} - // expected-note@-1 {{found candidate with type '(((Int, Int, Int)) throws -> _) throws -> Array<_>'}} + // expected-note@-1 {{found candidate with type '(((Int, Int, Int)) -> _) -> Array<_>'}} } // rdar://problem/48443263 - cannot convert value of type '() -> Void' to expected argument type '(_) -> Void' diff --git a/test/IDE/complete_from_stdlib.swift b/test/IDE/complete_from_stdlib.swift index de35aa28046d3..bb7f11eea3b36 100644 --- a/test/IDE/complete_from_stdlib.swift +++ b/test/IDE/complete_from_stdlib.swift @@ -20,7 +20,7 @@ func protocolExtCollection1a(_ a: C) { a.#^PRIVATE_NOMINAL_MEMBERS_2A?check=PRIVATE_NOMINAL_MEMBERS_2A;check=NEGATIVE_PRIVATE_NOMINAL_MEMBERS_2A;check=NO_STDLIB_PRIVATE^# } -// PRIVATE_NOMINAL_MEMBERS_2A-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(transform): (C.Element) throws -> T##(C.Element) throws -> T#})[' rethrows'][#[T]#]; +// PRIVATE_NOMINAL_MEMBERS_2A-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(transform): (C.Element) throws(Error) -> T##(C.Element) throws(Error) -> T#})[' throws'][#[T]#]; // NEGATIVE_PRIVATE_NOMINAL_MEMBERS_2A-NOT: Decl{{.*}}: index({#before: any Comparable#}) func protocolExtCollection1b(_ a: Collection) { @@ -28,14 +28,14 @@ func protocolExtCollection1b(_ a: Collection) { } // FIXME(https://github.com/apple/swift/issues/65696): We should not be showing this because (1) it cannot be accessed on the existential (2) we don't have the syntax and features to represent the projected type sig anyway. -// PRIVATE_NOMINAL_MEMBERS_2B-DAG: map({#(transform): (any Collection.Element) throws -> T##(any Collection.Element) throws -> T#})[' rethrows'][#[T]#]{{; name=.+}} +// PRIVATE_NOMINAL_MEMBERS_2B-DAG: map({#(transform): (any Collection.Element) throws(Error) -> T##(any Collection.Element) throws(Error) -> T#})[' throws'][#[T]#]{{; name=.+}} // NEGATIVE_PRIVATE_NOMINAL_MEMBERS_2B-NOT: Decl{{.*}}: index({#before: any Comparable#}) func protocolExtCollection2(_ a: C) { a.#^PRIVATE_NOMINAL_MEMBERS_3?check=PRIVATE_NOMINAL_MEMBERS_3;check=NEGATIVE_PRIVATE_NOMINAL_MEMBERS_3;check=NO_STDLIB_PRIVATE^# } -// PRIVATE_NOMINAL_MEMBERS_3-DAG: Decl[InstanceMethod]/Super/IsSystem: map({#(transform): (C.Element) throws -> T##(C.Element) throws -> T#})[' rethrows'][#[T]#]{{; name=.+}} +// PRIVATE_NOMINAL_MEMBERS_3-DAG: Decl[InstanceMethod]/Super/IsSystem: map({#(transform): (C.Element) throws(Error) -> T##(C.Element) throws(Error) -> T#})[' throws'][#[T]#]{{; name=.+}} // PRIVATE_NOMINAL_MEMBERS_3-DAG: Decl[InstanceVar]/Super/IsSystem: lazy[#LazySequence#]{{; name=.+}} // PRIVATE_NOMINAL_MEMBERS_3-DAG: firstIndex({#where: (C.Element) throws -> Bool##(C.Element) throws -> Bool#})[' rethrows'][#Comparable?#]{{; name=.+}} // NEGATIVE_PRIVATE_NOMINAL_MEMBERS_3-NOT: Decl{{.*}}: firstIndex({#({{.*}}): Self.Iterator.Element @@ -43,7 +43,7 @@ func protocolExtCollection2(_ func protocolExtArray(_ a: [T]) { a.#^PRIVATE_NOMINAL_MEMBERS_4?check=PRIVATE_NOMINAL_MEMBERS_4;check=NO_STDLIB_PRIVATE^# } -// PRIVATE_NOMINAL_MEMBERS_4-DAG: Decl[InstanceMethod]/Super/IsSystem: map({#(transform): (Equatable) throws -> T##(Equatable) throws -> T#})[' rethrows'][#[T]#]{{; name=.+}} +// PRIVATE_NOMINAL_MEMBERS_4-DAG: Decl[InstanceMethod]/Super/IsSystem: map({#(transform): (Equatable) throws(Error) -> T##(Equatable) throws(Error) -> T#})[' throws'][#[T]#]{{; name=.+}} // PRIVATE_NOMINAL_MEMBERS_4-DAG: Decl[InstanceVar]/Super/IsSystem: last[#Equatable?#]{{; name=.+}} // PRIVATE_NOMINAL_MEMBERS_4-DAG: Decl[InstanceMethod]/Super/IsSystem: firstIndex({#of: Equatable#})[#Int?#]{{; name=.+}} // PRIVATE_NOMINAL_MEMBERS_4-DAG: Decl[InstanceMethod]/Super/IsSystem: firstIndex({#where: (Equatable) throws -> Bool##(Equatable) throws -> Bool#})[' rethrows'][#Int?#]{{; name=.+}} @@ -85,7 +85,7 @@ func testArchetypeReplacement3 (_ a : [Int]) { // PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: append({#(newElement): Int#})[#Void#] // PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceMethod]/Super/IsSystem: removeLast()[#Int#] // PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceVar]/Super/IsSystem: first[#Int?#] -// PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceMethod]/Super/IsSystem: map({#(transform): (Int) throws -> T##(Int) throws -> T#})[' rethrows'][#[T]#] +// PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceMethod]/Super/IsSystem: map({#(transform): (Int) throws(Error) -> T##(Int) throws(Error) -> T#})[' throws'][#[T]#] // PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceMethod]/Super/IsSystem: dropLast({#(k): Int#})[#ArraySlice#] // PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceMethod]/Super/IsSystem: elementsEqual({#(other): Sequence#}, {#by: (Int, Sequence.Element) throws -> Bool##(Int, Sequence.Element) throws -> Bool#})[' rethrows'][#Bool#]; name=elementsEqual(:by:) // PRIVATE_NOMINAL_MEMBERS_7-DAG: Decl[InstanceMethod]/Super/IsSystem: elementsEqual({#(other): Sequence#})[#Bool#]; name=elementsEqual(:) diff --git a/test/SILGen/function_conversion.swift b/test/SILGen/function_conversion.swift index 0a1ccae288b51..64321fa5dd375 100644 --- a/test/SILGen/function_conversion.swift +++ b/test/SILGen/function_conversion.swift @@ -663,14 +663,14 @@ struct FunctionConversionParameterSubstToOrigReabstractionTest { } } -// CHECK: sil {{.*}} [ossa] @$sS4SIgggoo_S2Ss11AnyHashableVyps5Error_pIegggrrzo_TR +// CHECK: sil {{.*}} [ossa] @$sS4SIgggoo_S2Ss11AnyHashableVypIegggrr_TR // CHECK: [[TUPLE:%.*]] = apply %4(%2, %3) : $@noescape @callee_guaranteed (@guaranteed String, @guaranteed String) -> (@owned String, @owned String) // CHECK: ([[LHS:%.*]], [[RHS:%.*]]) = destructure_tuple [[TUPLE]] // CHECK: [[ADDR:%.*]] = alloc_stack $String // CHECK: store [[LHS]] to [init] [[ADDR]] : $*String // CHECK: [[CVT:%.*]] = function_ref @$ss21_convertToAnyHashableys0cD0VxSHRzlF : $@convention(thin) <τ_0_0 where τ_0_0 : Hashable> (@in_guaranteed τ_0_0) -> @out AnyHashable // CHECK: apply [[CVT]](%0, [[ADDR]]) -// CHECK: } // end sil function '$sS4SIgggoo_S2Ss11AnyHashableVyps5Error_pIegggrrzo_TR' +// CHECK: } // end sil function '$sS4SIgggoo_S2Ss11AnyHashableVypIegggrr_TR' func dontCrash() { let userInfo = ["hello": "world"] diff --git a/test/SILGen/partial_apply_override.swift b/test/SILGen/partial_apply_override.swift index c84540bb2bb99..915c1b84ede3a 100644 --- a/test/SILGen/partial_apply_override.swift +++ b/test/SILGen/partial_apply_override.swift @@ -26,11 +26,9 @@ public func convert(strings: [String]) -> [Int] { // CHECK-NEXT: // function_ref // CHECK-NEXT: [[CURRY_THUNK:%.*]] = function_ref @$s22partial_apply_override7convert7stringsSaySiGSaySSG_tFSiSScAA18StringIntConverterCcfu_ : $@convention(thin) (@guaranteed StringIntConverter) -> @owned @callee_guaranteed (@guaranteed String) -> Int // CHECK-NEXT: [[CURRY_RESULT:%.*]] = apply [[CURRY_THUNK]]([[CONVERTER]]) -// CHECK: [[CONVERTED:%.*]] = convert_function [[CURRY_RESULT]] -// CHECK: [[NOESCAPE:%.*]] = convert_escape_to_noescape [not_guaranteed] [[CONVERTED]] +// CHECK: [[NOESCAPE:%.*]] = convert_escape_to_noescape [not_guaranteed] [[CURRY_RESULT]] // CHECK: // function_ref -// CHECK-NEXT: [[THUNK:%.*]] = function_ref @$sSSSis5Error_pIggdzo_SSSisAA_pIegnrzo_TR : $@convention(thin) (@in_guaranteed String, @guaranteed @noescape @callee_guaranteed (@guaranteed String) -> (Int, @error any Error)) -> (@out Int, @error any Error) -// CHECK-NEXT: [[REABSTRACTED:%.*]] = partial_apply [callee_guaranteed] [[THUNK]]([[NOESCAPE]]) +// CHECK-NEXT: [[THUNK:%.*]] = function_ref @$sSSSiIggd_SSSis5NeverOIegnrzr_TR : $@convention(thin) (@in_guaranteed String, @guaranteed @noescape @callee_guaranteed (@guaranteed String) -> Int) -> (@out Int, @error_indirect Never) // CHECK-LABEL: sil private [ossa] @$s22partial_apply_override7convert7stringsSaySiGSaySSG_tFSiSScAA18StringIntConverterCcfu_SiSScfu0_ : $@convention(thin) (@guaranteed String, @guaranteed StringIntConverter) -> Int // CHECK: [[METHOD:%.*]] = class_method %1 : $StringIntConverter, #StringIntConverter.convert : (StringIntConverter) -> (String) -> Int, $@convention(method) (@in_guaranteed String, @guaranteed StringIntConverter) -> @out Int diff --git a/test/api-digester/Outputs/stability-stdlib-source-x86_64.swift.expected b/test/api-digester/Outputs/stability-stdlib-source-x86_64.swift.expected index 3a93963485761..6402a5ea5b752 100644 --- a/test/api-digester/Outputs/stability-stdlib-source-x86_64.swift.expected +++ b/test/api-digester/Outputs/stability-stdlib-source-x86_64.swift.expected @@ -26,5 +26,17 @@ Func Unicode.UTF32.Parser.parseScalar(from:) has generic signature change from < Func Unicode.UTF32.decode(_:) has generic signature change from to Func Unicode.UTF8.decode(_:) has generic signature change from to Constructor Mirror.init(_:children:displayStyle:ancestorRepresentation:) has generic signature change from to +Func AnyBidirectionalCollection.map(_:) has generic signature change from to +Func AnyBidirectionalCollection.map(_:) is now without @rethrows +Func AnyCollection.map(_:) has generic signature change from to +Func AnyCollection.map(_:) is now without @rethrows +Func AnyRandomAccessCollection.map(_:) has generic signature change from to +Func AnyRandomAccessCollection.map(_:) is now without @rethrows +Func AnySequence.map(_:) has generic signature change from to +Func AnySequence.map(_:) is now without @rethrows +Func Collection.map(_:) has generic signature change from to +Func Collection.map(_:) is now without @rethrows +Func Sequence.map(_:) has generic signature change from to +Func Sequence.map(_:) is now without @rethrows Protocol SIMDScalar has generic signature change from to diff --git a/test/api-digester/stability-stdlib-abi-without-asserts.test b/test/api-digester/stability-stdlib-abi-without-asserts.test index f487f9ce222f9..735506b7dfe88 100644 --- a/test/api-digester/stability-stdlib-abi-without-asserts.test +++ b/test/api-digester/stability-stdlib-abi-without-asserts.test @@ -60,12 +60,18 @@ Func _swift_retainCount(_:) is a new API without @available attribute Func _swift_unownedRetainCount(_:) is a new API without @available attribute Func _swift_weakRetainCount(_:) is a new API without @available attribute +Func Collection.map(_:) has been renamed to Func __rethrows_map(_:) +Func Collection.map(_:) has mangled name changing from '(extension in Swift):Swift.Collection.map((A.Element) throws -> A1) throws -> Swift.Array' to '(extension in Swift):Swift.Collection.__rethrows_map((A.Element) throws -> A1) throws -> Swift.Array' +Func Collection.map(_:) is now without @rethrows Func Collection.removingSubranges(_:) has been removed Func Collection.subranges(of:) has been removed Func Collection.subranges(where:) has been removed Func MutableCollection.moveSubranges(_:to:) has been removed Func MutableCollection.removeSubranges(_:) has been removed Func RangeReplaceableCollection.removeSubranges(_:) has been removed +Func Sequence.map(_:) has been renamed to Func __rethrows_map(_:) +Func Sequence.map(_:) has mangled name changing from '(extension in Swift):Swift.Sequence.map((A.Element) throws -> A1) throws -> Swift.Array' to '(extension in Swift):Swift.Sequence.__rethrows_map((A.Element) throws -> A1) throws -> Swift.Array' +Func Sequence.map(_:) is now without @rethrows Struct AnyHashable has added a conformance to an existing protocol _HasCustomAnyHashableRepresentation Class AnyKeyPath has added a conformance to an existing protocol CustomDebugStringConvertible Class KeyPath has added a conformance to an existing protocol CustomDebugStringConvertible @@ -85,6 +91,18 @@ Constructor _SmallString.init(taggedCocoa:) has return type change from Swift._S Enum Never has added a conformance to an existing protocol Decodable Enum Never has added a conformance to an existing protocol Encodable Enum Never has added a conformance to an existing protocol Identifiable +Func AnyBidirectionalCollection.map(_:) has been renamed to Func __rethrows_map(_:) +Func AnyBidirectionalCollection.map(_:) has mangled name changing from 'Swift.AnyBidirectionalCollection.map((A) throws -> A1) throws -> Swift.Array' to 'Swift.AnyBidirectionalCollection.__rethrows_map((A) throws -> A1) throws -> Swift.Array' +Func AnyBidirectionalCollection.map(_:) is now without @rethrows +Func AnyCollection.map(_:) has been renamed to Func __rethrows_map(_:) +Func AnyCollection.map(_:) has mangled name changing from 'Swift.AnyCollection.map((A) throws -> A1) throws -> Swift.Array' to 'Swift.AnyCollection.__rethrows_map((A) throws -> A1) throws -> Swift.Array' +Func AnyCollection.map(_:) is now without @rethrows +Func AnyRandomAccessCollection.map(_:) has been renamed to Func __rethrows_map(_:) +Func AnyRandomAccessCollection.map(_:) has mangled name changing from 'Swift.AnyRandomAccessCollection.map((A) throws -> A1) throws -> Swift.Array' to 'Swift.AnyRandomAccessCollection.__rethrows_map((A) throws -> A1) throws -> Swift.Array' +Func AnyRandomAccessCollection.map(_:) is now without @rethrows +Func AnySequence.map(_:) has been renamed to Func __rethrows_map(_:) +Func AnySequence.map(_:) has mangled name changing from 'Swift.AnySequence.map((A) throws -> A1) throws -> Swift.Array' to 'Swift.AnySequence.__rethrows_map((A) throws -> A1) throws -> Swift.Array' +Func AnySequence.map(_:) is now without @rethrows // These haven't actually been removed; they are simply marked unavailable. // This seems to be a false positive in the ABI checker. This is not an ABI @@ -110,6 +128,11 @@ Func UnsafeBufferPointer.withMemoryRebound(to:_:) has been removed Func UnsafeMutableBufferPointer.withMemoryRebound(to:_:) has been removed Func UnsafeMutablePointer.withMemoryRebound(to:capacity:_:) has been removed Func UnsafePointer.withMemoryRebound(to:capacity:_:) has been removed +Func _AnySequenceBox._map(_:) is now without @rethrows +Func _BidirectionalCollectionBox._map(_:) is now without @rethrows +Func _CollectionBox._map(_:) is now without @rethrows +Func _RandomAccessCollectionBox._map(_:) is now without @rethrows +Func _SequenceBox._map(_:) is now without @rethrows Func UnsafeMutableRawBufferPointer.storeBytes(of:toByteOffset:as:) has been removed Func UnsafeMutableRawPointer.storeBytes(of:toByteOffset:as:) has been removed Func UnsafeMutableBufferPointer.assign(repeating:) has been removed From 3173a3d4b3b2653aa3913876f62881c01187e6ff Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 13 Nov 2023 14:16:22 -0800 Subject: [PATCH 07/12] Avoid use of typed throws in SIL opaque values test SIL opaque values do not yet support typed throws, so avoid the API that uses them. --- test/SILGen/opaque_values_silgen.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/SILGen/opaque_values_silgen.swift b/test/SILGen/opaque_values_silgen.swift index 28c21de9324f6..739ddff7d6328 100644 --- a/test/SILGen/opaque_values_silgen.swift +++ b/test/SILGen/opaque_values_silgen.swift @@ -365,8 +365,16 @@ public struct EnumSeq : Seq { } extension Collection { + func myMap(_ body: (Element) -> T) -> [T] { + var result = [T]() + for element in self { + result.append(body(element)) + } + return result + } + func transformEachElement(_ cl: (Element) -> U) -> [U] { - return map(cl) + return myMap(cl) } } From 3bc6497e493106950bfdb0908c312e7e3453877e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 13 Nov 2023 14:21:36 -0800 Subject: [PATCH 08/12] Teach TransferNonSendable that not all Error BB's have arguments --- lib/SILOptimizer/Mandatory/TransferNonSendable.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/SILOptimizer/Mandatory/TransferNonSendable.cpp b/lib/SILOptimizer/Mandatory/TransferNonSendable.cpp index c50a44c724ad2..3dcb8939c2a13 100644 --- a/lib/SILOptimizer/Mandatory/TransferNonSendable.cpp +++ b/lib/SILOptimizer/Mandatory/TransferNonSendable.cpp @@ -647,7 +647,8 @@ class PartitionOpTranslator { if (auto tryApplyInst = dyn_cast(inst)) { foundResults.emplace_back(tryApplyInst->getNormalBB()->getArgument(0)); - foundResults.emplace_back(tryApplyInst->getErrorBB()->getArgument(0)); + if (tryApplyInst->getErrorBB()->getNumArguments() > 0) + foundResults.emplace_back(tryApplyInst->getErrorBB()->getArgument(0)); return; } From 14065bf9a0494852e664351eedbfb06d058200da Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 13 Nov 2023 14:47:30 -0800 Subject: [PATCH 09/12] Teach ForEachLoopUnroll to deal with error basic blocks without arguments --- .../LoopTransforms/ForEachLoopUnroll.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp b/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp index e6f6c6ff052e2..46186d6b55891 100644 --- a/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp +++ b/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp @@ -483,10 +483,11 @@ static void unrollForEach(ArrayInfo &arrayInfo, TryApplyInst *forEachCall, // targets must be taking a phi argument. SILBasicBlock *normalBB = forEachCall->getNormalBB(); SILBasicBlock *errorBB = forEachCall->getErrorBB(); - assert(errorBB->getSILPhiArguments().size() == 1 && - normalBB->getSILPhiArguments().size() == 1); + assert(normalBB->getSILPhiArguments().size() == 1); SILPhiArgument *normalArgument = normalBB->getSILPhiArguments()[0]; - SILPhiArgument *errorArgument = errorBB->getSILPhiArguments()[0]; + SILPhiArgument *errorArgument = nullptr; + if (errorBB->getSILPhiArguments().size() == 1) + errorArgument = errorBB->getSILPhiArguments()[0]; // A generator for creating a basic block for use as the target of the // "normal" branch of a try_apply. @@ -503,8 +504,12 @@ static void unrollForEach(ArrayInfo &arrayInfo, TryApplyInst *forEachCall, auto errorTargetGenerator = [&](SILBasicBlock *insertionBlock, SILValue borrowedElem, SILValue storeBorrow) { SILBasicBlock *newErrorBB = fun->createBasicBlockBefore(insertionBlock); - SILValue argument = newErrorBB->createPhiArgument( + SILValue argument; + if (errorArgument) { + argument = newErrorBB->createPhiArgument( errorArgument->getType(), errorArgument->getOwnershipKind()); + } + // Make the errorBB jump to the error target of the original forEach. SILBuilderWithScope builder(newErrorBB, forEachCall); if (storeBorrow) { @@ -513,7 +518,11 @@ static void unrollForEach(ArrayInfo &arrayInfo, TryApplyInst *forEachCall, if (borrowedElem) { builder.createEndBorrow(forEachLoc, borrowedElem); } - builder.createBranch(forEachLoc, errorBB, argument); + + if (argument) + builder.createBranch(forEachLoc, errorBB, argument); + else + builder.createBranch(forEachLoc, errorBB); return newErrorBB; }; From 3d84493298bb52ab4e197463f00090c5e9e335e8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 13 Nov 2023 14:50:20 -0800 Subject: [PATCH 10/12] Teach the speculative devirtualize to deal with error basic blocks without args --- .../Transforms/SpeculativeDevirtualizer.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp index 06085700e4da5..10dbcc72165ad 100644 --- a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp +++ b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp @@ -239,11 +239,19 @@ static FullApplySite speculateMonomorphicTarget(FullApplySite AI, // Split critical edges resulting from VirtAI. if (auto *TAI = dyn_cast(VirtAI)) { auto *ErrorBB = TAI->getFunction()->createBasicBlock(); - ErrorBB->createPhiArgument(TAI->getErrorBB()->getArgument(0)->getType(), - OwnershipKind::Owned); + SILArgument *ErrorArg = nullptr; + if (TAI->getErrorBB()->getNumArguments() == 1) { + ErrorArg = TAI->getErrorBB()->getArgument(0); + ErrorBB->createPhiArgument(ErrorArg->getType(), OwnershipKind::Owned); + } Builder.setInsertionPoint(ErrorBB); - Builder.createBranch(TAI->getLoc(), TAI->getErrorBB(), - {ErrorBB->getArgument(0)}); + + if (ErrorArg) { + Builder.createBranch(TAI->getLoc(), TAI->getErrorBB(), + {ErrorBB->getArgument(0)}); + } else { + Builder.createBranch(TAI->getLoc(), TAI->getErrorBB()); + } auto *NormalBB = TAI->getFunction()->createBasicBlock(); NormalBB->createPhiArgument(TAI->getNormalBB()->getArgument(0)->getType(), From 33d694468c3151243d470b3f0238fd24b4038828 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 13 Nov 2023 22:30:26 -0800 Subject: [PATCH 11/12] [Typed throws] Support overrides that are contravariant in the thrown error --- include/swift/AST/DiagnosticsSema.def | 3 ++ include/swift/AST/ExtInfo.h | 2 +- lib/AST/Type.cpp | 3 +- lib/Sema/TypeCheckDeclOverride.cpp | 46 ++++++++++++++-- test/SILGen/typed_throws.swift | 20 +++++++ test/decl/class/typed_throws_override.swift | 58 +++++++++++++++++++++ 6 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 test/decl/class/typed_throws_override.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 06723d7a3b931..77a00fc90fb6b 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3362,6 +3362,9 @@ ERROR(override_class_declaration_in_extension,none, ERROR(override_with_more_effects,none, "cannot override non-%1 %0 with %1 %0", (DescriptiveDeclKind, StringRef)) +ERROR(override_typed_throws,none, + "%0 that throws %1 cannot override one that throws %2", + (DescriptiveDeclKind, Type, Type)) ERROR(override_throws_objc,none, "overriding a throwing @objc %select{method|initializer}0 with " "a non-throwing %select{method|initializer}0 is not supported", (bool)) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index 472a9f546fe6a..c896543ea964c 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -544,7 +544,7 @@ class ASTExtInfoBuilder { return bits == other.bits && (useClangTypes ? (clangTypeInfo == other.clangTypeInfo) : true) && globalActor.getPointer() == other.globalActor.getPointer() && - thrownError.getPointer() == thrownError.getPointer(); + thrownError.getPointer() == other.thrownError.getPointer(); } constexpr std::tuple diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index f033ca00b7238..3d84b41f7af7e 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3421,7 +3421,8 @@ static bool matchesFunctionType(CanAnyFunctionType fn1, CanAnyFunctionType fn2, if (matchMode.contains(TypeMatchFlags::AllowOverride)) { // Removing 'throwing' is ABI-compatible for synchronous functions, but // not for async ones. - if (ext2.isThrowing() && + if (ext2.isThrowing() && !ext1.isThrowing() && + ext2.getThrownError().isNull() && !(ext2.isAsync() && matchMode.contains(TypeMatchFlags::AllowABICompatible))) { ext1 = ext1.withThrows(true, ext2.getThrownError()); diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index d5d916ab3494b..c254a556719c9 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -17,6 +17,7 @@ #include "TypeCheckAvailability.h" #include "TypeCheckConcurrency.h" #include "TypeCheckDecl.h" +#include "TypeCheckEffects.h" #include "TypeCheckObjC.h" #include "TypeChecker.h" #include "swift/AST/ASTVisitor.h" @@ -2036,16 +2037,53 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) { diags.diagnose(base, diag::overridden_here); } } - // If the overriding declaration is 'throws' but the base is not, - // complain. Do the same for 'async' + + // Check effects. if (auto overrideFn = dyn_cast(override)) { - if (overrideFn->hasThrows() && - !cast(base)->hasThrows()) { + // Determine the thrown errors in the base and override declarations. + auto baseFn = cast(base); + Type overrideThrownError = + overrideFn->getEffectiveThrownErrorType().value_or(ctx.getNeverType()); + Type baseThrownError = + baseFn->getEffectiveThrownErrorType().value_or(ctx.getNeverType()); + + if (baseThrownError->hasTypeParameter()) { + auto subs = SubstitutionMap::getOverrideSubstitutions(base, override); + baseThrownError = baseThrownError.subst(subs); + baseThrownError = overrideFn->mapTypeIntoContext(baseThrownError); + } + + overrideThrownError = overrideFn->mapTypeIntoContext(overrideThrownError); + + // Check for a subtyping relationship. + switch (compareThrownErrorsForSubtyping( + overrideThrownError, baseThrownError, overrideFn)) { + case ThrownErrorSubtyping::DropsThrows: diags.diagnose(override, diag::override_with_more_effects, override->getDescriptiveKind(), "throwing"); diags.diagnose(base, diag::overridden_here); + break; + + case ThrownErrorSubtyping::Mismatch: + diags.diagnose(override, diag::override_typed_throws, + override->getDescriptiveKind(), overrideThrownError, + baseThrownError); + diags.diagnose(base, diag::overridden_here); + break; + + case ThrownErrorSubtyping::ExactMatch: + case ThrownErrorSubtyping::Subtype: + // Proper subtyping. + break; + + case ThrownErrorSubtyping::Dependent: + // Only in already ill-formed code. + assert(ctx.Diags.hadAnyError()); + break; } + // If the override is 'async' but the base declaration is not, we have a + // problem. if (overrideFn->hasAsync() && !cast(base)->hasAsync()) { diags.diagnose(override, diag::override_with_more_effects, diff --git a/test/SILGen/typed_throws.swift b/test/SILGen/typed_throws.swift index 6170e5112ab5b..78e0f107d0a71 100644 --- a/test/SILGen/typed_throws.swift +++ b/test/SILGen/typed_throws.swift @@ -126,4 +126,24 @@ open class MyClass { // CHECK-NEXT: throw_addr public init(body: () throws(E) -> Void) throws(E) { } + + func f() throws { } +} + +// CHECK-LABEL: sil_vtable MySubclass { +// CHECK-NEXT: #MyClass.init!allocator: (MyClass.Type) -> (() throws(E) -> ()) throws(E) -> MyClass : @$s12typed_throws10MySubclassC4bodyACyyxYKXE_txYKcs5ErrorRzlufC [override] +// CHECK-NEXT: #MyClass.f: (MyClass) -> () throws -> () : @$s12typed_throws10MySubclassC1fyyAA0C5ErrorOYKFAA0C5ClassCADyyKFTV [override] +// CHECK-NEXT: #MySubclass.f: (MySubclass) -> () throws(MyError) -> () : @$s12typed_throws10MySubclassC1fyyAA0C5ErrorOYKF + +class MySubclass: MyClass { + override func f() throws(MyError) { } +} + +// CHECK-LABEL: sil_vtable MySubsubclass +// CHECK-NEXT: #MyClass.init!allocator: (MyClass.Type) -> (() throws(E) -> ()) throws(E) -> MyClass : @$s12typed_throws13MySubsubclassC4bodyACyyxYKXE_txYKcs5ErrorRzlufC [override] +// CHECK-NEXT: #MyClass.f: (MyClass) -> () throws -> () : @$s12typed_throws13MySubsubclassC1fyyF [override] +// CHECK-NEXT: #MySubclass.f: (MySubclass) -> () throws(MyError) -> () : @$s12typed_throws13MySubsubclassC1fyyF [override] +// CHECK-NEXT: #MySubsubclass.f: (MySubsubclass) -> () -> () : @$s12typed_throws13MySubsubclassC1fyyF +class MySubsubclass: MySubclass { + override func f() { } } diff --git a/test/decl/class/typed_throws_override.swift b/test/decl/class/typed_throws_override.swift new file mode 100644 index 0000000000000..68d060c5025d0 --- /dev/null +++ b/test/decl/class/typed_throws_override.swift @@ -0,0 +1,58 @@ +// RUN: %target-typecheck-verify-swift -parse-as-library -enable-experimental-feature TypedThrows + +enum MyError: Error { +case failed +} + +enum HomeworkError: Error { +case dogAteIt +} + +class SuperError: Error { } +class SubError: SuperError { } + +class Super { + func f() throws { } + func g() throws(MyError) { } + func h() throws(HomeworkError) { } // expected-note{{overridden declaration is here}} + func i() throws(SuperError) { } + + var propF: Int { + get throws { 5 } + } + + var propG: Int { + get throws(MyError) { 5 } + } + + var propH: Int { + get throws(HomeworkError) { 5 } // expected-note{{overridden declaration is here}} + } + + var propI: Int { + get throws(SuperError) { 5 } + } +} + +class Sub: Super { + override func f() throws(MyError) { } // okay to make type more specific + override func g() { } // okay to be non-throwing + override func h() throws(MyError) { } // expected-error{{instance method that throws 'MyError' cannot override one that throws 'HomeworkError'}} + override func i() throws(SubError) { } // okay to have a subtype + + override var propF: Int { + get throws(MyError) { 5 } // okay to make type more specific + } + + override var propG: Int { + get { 5 } // okay to be non-throwing + } + + override var propH: Int { + get throws(MyError) { 5 } // expected-error{{getter that throws 'MyError' cannot override one that throws 'HomeworkError'}} + } + + override var propI: Int { + get throws(SubError) { 5 } // okay to make type more specific + } +} From 07b26e2a17cd8c63c555326d9b981aae59049c87 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Nov 2023 22:36:06 -0800 Subject: [PATCH 12/12] Deal with tests that don't import the Swift standard library --- lib/Sema/TypeCheckDeclOverride.cpp | 5 +++-- lib/Sema/TypeCheckEffects.cpp | 7 +++++++ test/IRGen/protocol_synthesized.swift | 3 +++ test/SIL/OwnershipVerifier/definite_init.sil | 3 +++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index c254a556719c9..474bdda2ae2f3 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -2047,13 +2047,14 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) { Type baseThrownError = baseFn->getEffectiveThrownErrorType().value_or(ctx.getNeverType()); - if (baseThrownError->hasTypeParameter()) { + if (baseThrownError && baseThrownError->hasTypeParameter()) { auto subs = SubstitutionMap::getOverrideSubstitutions(base, override); baseThrownError = baseThrownError.subst(subs); baseThrownError = overrideFn->mapTypeIntoContext(baseThrownError); } - overrideThrownError = overrideFn->mapTypeIntoContext(overrideThrownError); + if (overrideThrownError) + overrideThrownError = overrideFn->mapTypeIntoContext(overrideThrownError); // Check for a subtyping relationship. switch (compareThrownErrorsForSubtyping( diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 0ae9263035c83..5217c8955975e 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -3549,6 +3549,13 @@ ThrownErrorSubtyping swift::compareThrownErrorsForSubtyping( Type subThrownError, Type superThrownError, DeclContext *dc ) { + // Deal with NULL errors. This should only occur when there is no standard + // library. + if (!subThrownError || !superThrownError) { + assert(!dc->getASTContext().getStdlibModule() && "NULL thrown error type"); + return ThrownErrorSubtyping::ExactMatch; + } + // Easy case: exact match. if (superThrownError->isEqual(subThrownError)) return ThrownErrorSubtyping::ExactMatch; diff --git a/test/IRGen/protocol_synthesized.swift b/test/IRGen/protocol_synthesized.swift index f21979914709e..6fdb1955a7595 100644 --- a/test/IRGen/protocol_synthesized.swift +++ b/test/IRGen/protocol_synthesized.swift @@ -10,6 +10,9 @@ import SynthesizedProtocol +enum Never { } +protocol Error { } + // CHECK: @"$sSo5Flagsas9OptionSetSCMc" = linkonce_odr hidden constant { i32, i32, i32, i32, i16, i16, i32, i32 } { i32 {{(trunc \(i64 )?}}sub ({{i(32|64)}} ptrtoint (ptr @"$ss9OptionSetMp" to {{i(32|64)}}), {{i(32|64)}} ptrtoint (ptr @"$sSo5Flagsas9OptionSetSCMc" to {{i(32|64)}})){{( to i32\))?}}, i32 {{(trunc \(i64 )?}}sub ({{i(32|64)}} ptrtoint (ptr @"$sSo5FlagsaMn" to {{i(32|64)}}), {{i(32|64)}} ptrtoint (ptr getelementptr inbounds ({ i32, i32, i32, i32, i16, i16, i32, i32 }, ptr @"$sSo5Flagsas9OptionSetSCMc", i32 0, i32 1) to {{i(32|64)}})){{( to i32\))?}}, i32 {{(trunc \(i64 )?}}sub ({{i(32|64)}} ptrtoint (ptr @"$sSo5Flagsas9OptionSetSCWP" to {{i(32|64)}}), {{i(32|64)}} ptrtoint (ptr getelementptr inbounds ({ i32, i32, i32, i32, i16, i16, i32, i32 }, ptr @"$sSo5Flagsas9OptionSetSCMc", i32 0, i32 2) to {{i(32|64)}})){{( to i32\))?}}, i32 131200, i16 3, i16 1, i32 {{(trunc \(i64 )?}}sub ({{i(32|64)}} ptrtoint (ptr @"$sSo5Flagsas9OptionSetSCWI" to {{i(32|64)}}), {{i(32|64)}} ptrtoint (ptr getelementptr inbounds ({ i32, i32, i32, i32, i16, i16, i32, i32 }, ptr @"$sSo5Flagsas9OptionSetSCMc", i32 0, i32 6) to {{i(32|64)}})){{( to i32\))?}}, i32 {{(trunc \(i64 )?}}sub ({{i(32|64)}} ptrtoint (ptr @"$sSo5Flagsas9OptionSetSCMcMK" to {{i(32|64)}}), {{i(32|64)}} ptrtoint (ptr getelementptr inbounds ({ i32, i32, i32, i32, i16, i16, i32, i32 }, ptr @"$sSo5Flagsas9OptionSetSCMc", i32 0, i32 7) to {{i(32|64)}})) {{(to i32\) )?}}}, section "{{[^"]*}}"{{(, comdat)?}},{{.*}} align 4 // Triggers the inclusion of the relevant ProtocolConformanceDescriptor diff --git a/test/SIL/OwnershipVerifier/definite_init.sil b/test/SIL/OwnershipVerifier/definite_init.sil index e2a25d56e1836..f557e5e30b68d 100644 --- a/test/SIL/OwnershipVerifier/definite_init.sil +++ b/test/SIL/OwnershipVerifier/definite_init.sil @@ -5,6 +5,9 @@ sil_stage raw import Builtin +enum Never { } +protocol Error { } + enum Optional { case some(T) case none