Skip to content

Commit 975519b

Browse files
authored
Merge pull request #69839 from DougGregor/typed-throws-override
[Typed throws] Support overrides that are contravariant in the thrown error
2 parents 7b21ed1 + 07b26e2 commit 975519b

23 files changed

+329
-54
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3366,6 +3366,9 @@ ERROR(override_class_declaration_in_extension,none,
33663366
ERROR(override_with_more_effects,none,
33673367
"cannot override non-%1 %0 with %1 %0",
33683368
(DescriptiveDeclKind, StringRef))
3369+
ERROR(override_typed_throws,none,
3370+
"%0 that throws %1 cannot override one that throws %2",
3371+
(DescriptiveDeclKind, Type, Type))
33693372
ERROR(override_throws_objc,none,
33703373
"overriding a throwing @objc %select{method|initializer}0 with "
33713374
"a non-throwing %select{method|initializer}0 is not supported", (bool))

include/swift/AST/ExtInfo.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ class ASTExtInfoBuilder {
544544
return bits == other.bits &&
545545
(useClangTypes ? (clangTypeInfo == other.clangTypeInfo) : true) &&
546546
globalActor.getPointer() == other.globalActor.getPointer() &&
547-
thrownError.getPointer() == thrownError.getPointer();
547+
thrownError.getPointer() == other.thrownError.getPointer();
548548
}
549549

550550
constexpr std::tuple<unsigned, const void *, const void *, const void *>

lib/AST/Type.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3421,7 +3421,8 @@ static bool matchesFunctionType(CanAnyFunctionType fn1, CanAnyFunctionType fn2,
34213421
if (matchMode.contains(TypeMatchFlags::AllowOverride)) {
34223422
// Removing 'throwing' is ABI-compatible for synchronous functions, but
34233423
// not for async ones.
3424-
if (ext2.isThrowing() &&
3424+
if (ext2.isThrowing() && !ext1.isThrowing() &&
3425+
ext2.getThrownError().isNull() &&
34253426
!(ext2.isAsync() &&
34263427
matchMode.contains(TypeMatchFlags::AllowABICompatible))) {
34273428
ext1 = ext1.withThrows(true, ext2.getThrownError());

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "TypeCheckAvailability.h"
1818
#include "TypeCheckConcurrency.h"
1919
#include "TypeCheckDecl.h"
20+
#include "TypeCheckEffects.h"
2021
#include "TypeCheckObjC.h"
2122
#include "TypeChecker.h"
2223
#include "swift/AST/ASTVisitor.h"
@@ -2036,16 +2037,54 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) {
20362037
diags.diagnose(base, diag::overridden_here);
20372038
}
20382039
}
2039-
// If the overriding declaration is 'throws' but the base is not,
2040-
// complain. Do the same for 'async'
2040+
2041+
// Check effects.
20412042
if (auto overrideFn = dyn_cast<AbstractFunctionDecl>(override)) {
2042-
if (overrideFn->hasThrows() &&
2043-
!cast<AbstractFunctionDecl>(base)->hasThrows()) {
2043+
// Determine the thrown errors in the base and override declarations.
2044+
auto baseFn = cast<AbstractFunctionDecl>(base);
2045+
Type overrideThrownError =
2046+
overrideFn->getEffectiveThrownErrorType().value_or(ctx.getNeverType());
2047+
Type baseThrownError =
2048+
baseFn->getEffectiveThrownErrorType().value_or(ctx.getNeverType());
2049+
2050+
if (baseThrownError && baseThrownError->hasTypeParameter()) {
2051+
auto subs = SubstitutionMap::getOverrideSubstitutions(base, override);
2052+
baseThrownError = baseThrownError.subst(subs);
2053+
baseThrownError = overrideFn->mapTypeIntoContext(baseThrownError);
2054+
}
2055+
2056+
if (overrideThrownError)
2057+
overrideThrownError = overrideFn->mapTypeIntoContext(overrideThrownError);
2058+
2059+
// Check for a subtyping relationship.
2060+
switch (compareThrownErrorsForSubtyping(
2061+
overrideThrownError, baseThrownError, overrideFn)) {
2062+
case ThrownErrorSubtyping::DropsThrows:
20442063
diags.diagnose(override, diag::override_with_more_effects,
20452064
override->getDescriptiveKind(), "throwing");
20462065
diags.diagnose(base, diag::overridden_here);
2066+
break;
2067+
2068+
case ThrownErrorSubtyping::Mismatch:
2069+
diags.diagnose(override, diag::override_typed_throws,
2070+
override->getDescriptiveKind(), overrideThrownError,
2071+
baseThrownError);
2072+
diags.diagnose(base, diag::overridden_here);
2073+
break;
2074+
2075+
case ThrownErrorSubtyping::ExactMatch:
2076+
case ThrownErrorSubtyping::Subtype:
2077+
// Proper subtyping.
2078+
break;
2079+
2080+
case ThrownErrorSubtyping::Dependent:
2081+
// Only in already ill-formed code.
2082+
assert(ctx.Diags.hadAnyError());
2083+
break;
20472084
}
20482085

2086+
// If the override is 'async' but the base declaration is not, we have a
2087+
// problem.
20492088
if (overrideFn->hasAsync() &&
20502089
!cast<AbstractFunctionDecl>(base)->hasAsync()) {
20512090
diags.diagnose(override, diag::override_with_more_effects,

lib/Sema/TypeCheckEffects.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3595,6 +3595,13 @@ ThrownErrorSubtyping
35953595
swift::compareThrownErrorsForSubtyping(
35963596
Type subThrownError, Type superThrownError, DeclContext *dc
35973597
) {
3598+
// Deal with NULL errors. This should only occur when there is no standard
3599+
// library.
3600+
if (!subThrownError || !superThrownError) {
3601+
assert(!dc->getASTContext().getStdlibModule() && "NULL thrown error type");
3602+
return ThrownErrorSubtyping::ExactMatch;
3603+
}
3604+
35983605
// Easy case: exact match.
35993606
if (superThrownError->isEqual(subThrownError))
36003607
return ThrownErrorSubtyping::ExactMatch;

stdlib/public/core/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ endif()
298298
# STAGING: Temporarily avoids having to write #fileID in Swift.swiftinterface.
299299
list(APPEND swift_stdlib_compile_flags "-Xfrontend" "-enable-experimental-concise-pound-file")
300300

301-
list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "Macros")
301+
list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "TypedThrows")
302302
list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "FreestandingMacros")
303303
list(APPEND swift_stdlib_compile_flags "-enable-experimental-feature" "Extern")
304304

stdlib/public/core/Collection.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,9 +1190,10 @@ extension Collection {
11901190
/// - Returns: An array containing the transformed elements of this
11911191
/// sequence.
11921192
@inlinable
1193-
public func map<T>(
1194-
_ transform: (Element) throws -> T
1195-
) rethrows -> [T] {
1193+
@_alwaysEmitIntoClient
1194+
public func map<T, E>(
1195+
_ transform: (Element) throws(E) -> T
1196+
) throws(E) -> [T] {
11961197
// TODO: swift-3-indexing-model - review the following
11971198
let n = self.count
11981199
if n == 0 {
@@ -1213,6 +1214,17 @@ extension Collection {
12131214
return Array(result)
12141215
}
12151216

1217+
// ABI-only entrypoint for the rethrows version of map, which has been
1218+
// superseded by the typed-throws version. Expressed as "throws", which is
1219+
// ABI-compatible with "rethrows".
1220+
@usableFromInline
1221+
@_silgen_name("$sSlsE3mapySayqd__Gqd__7ElementQzKXEKlF")
1222+
func __rethrows_map<T>(
1223+
_ transform: (Element) throws -> T
1224+
) throws -> [T] {
1225+
try map(transform)
1226+
}
1227+
12161228
/// Returns a subsequence containing all but the given number of initial
12171229
/// elements.
12181230
///

stdlib/public/core/ExistentialCollection.swift

Lines changed: 85 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ internal class _AnySequenceBox<Element> {
175175
@inlinable
176176
internal func _map<T>(
177177
_ transform: (Element) throws -> T
178-
) rethrows -> [T] {
178+
) throws -> [T] {
179179
_abstract()
180180
}
181181

@@ -525,8 +525,8 @@ internal final class _SequenceBox<S: Sequence>: _AnySequenceBox<S.Element> {
525525
@inlinable
526526
internal override func _map<T>(
527527
_ transform: (Element) throws -> T
528-
) rethrows -> [T] {
529-
return try _base.map(transform)
528+
) throws -> [T] {
529+
try _base.map(transform)
530530
}
531531
@inlinable
532532
internal override func _filter(
@@ -618,8 +618,8 @@ internal final class _CollectionBox<S: Collection>: _AnyCollectionBox<S.Element>
618618
@inlinable
619619
internal override func _map<T>(
620620
_ transform: (Element) throws -> T
621-
) rethrows -> [T] {
622-
return try _base.map(transform)
621+
) throws -> [T] {
622+
try _base.map(transform)
623623
}
624624
@inlinable
625625
internal override func _filter(
@@ -813,8 +813,8 @@ internal final class _BidirectionalCollectionBox<S: BidirectionalCollection>
813813
@inlinable
814814
internal override func _map<T>(
815815
_ transform: (Element) throws -> T
816-
) rethrows -> [T] {
817-
return try _base.map(transform)
816+
) throws -> [T] {
817+
try _base.map(transform)
818818
}
819819
@inlinable
820820
internal override func _filter(
@@ -1026,8 +1026,8 @@ internal final class _RandomAccessCollectionBox<S: RandomAccessCollection>
10261026
@inlinable
10271027
internal override func _map<T>(
10281028
_ transform: (Element) throws -> T
1029-
) rethrows -> [T] {
1030-
return try _base.map(transform)
1029+
) throws -> [T] {
1030+
try _base.map(transform)
10311031
}
10321032
@inlinable
10331033
internal override func _filter(
@@ -1303,10 +1303,26 @@ extension AnySequence {
13031303
}
13041304

13051305
@inlinable
1306-
public func map<T>(
1306+
@_alwaysEmitIntoClient
1307+
public func map<T, E>(
1308+
_ transform: (Element) throws(E) -> T
1309+
) throws(E) -> [T] {
1310+
do {
1311+
return try _box._map(transform)
1312+
} catch {
1313+
throw error as! E
1314+
}
1315+
}
1316+
1317+
// ABI-only entrypoint for the rethrows version of map, which has been
1318+
// superseded by the typed-throws version. Expressed as "throws", which is
1319+
// ABI-compatible with "rethrows".
1320+
@usableFromInline
1321+
@_silgen_name("$ss11AnySequenceV3mapySayqd__Gqd__xKXEKlF")
1322+
func __rethrows_map<T>(
13071323
_ transform: (Element) throws -> T
1308-
) rethrows -> [T] {
1309-
return try _box._map(transform)
1324+
) throws -> [T] {
1325+
try map(transform)
13101326
}
13111327

13121328
@inlinable
@@ -1390,10 +1406,26 @@ extension AnyCollection {
13901406
}
13911407

13921408
@inlinable
1393-
public func map<T>(
1409+
@_alwaysEmitIntoClient
1410+
public func map<T, E>(
1411+
_ transform: (Element) throws(E) -> T
1412+
) throws(E) -> [T] {
1413+
do {
1414+
return try _box._map(transform)
1415+
} catch {
1416+
throw error as! E
1417+
}
1418+
}
1419+
1420+
// ABI-only entrypoint for the rethrows version of map, which has been
1421+
// superseded by the typed-throws version. Expressed as "throws", which is
1422+
// ABI-compatible with "rethrows".
1423+
@usableFromInline
1424+
@_silgen_name("$ss13AnyCollectionV3mapySayqd__Gqd__xKXEKlF")
1425+
func __rethrows_map<T>(
13941426
_ transform: (Element) throws -> T
1395-
) rethrows -> [T] {
1396-
return try _box._map(transform)
1427+
) throws -> [T] {
1428+
try map(transform)
13971429
}
13981430

13991431
@inlinable
@@ -1483,10 +1515,26 @@ extension AnyBidirectionalCollection {
14831515
}
14841516

14851517
@inlinable
1486-
public func map<T>(
1518+
@_alwaysEmitIntoClient
1519+
public func map<T, E>(
1520+
_ transform: (Element) throws(E) -> T
1521+
) throws(E) -> [T] {
1522+
do {
1523+
return try _box._map(transform)
1524+
} catch {
1525+
throw error as! E
1526+
}
1527+
}
1528+
1529+
// ABI-only entrypoint for the rethrows version of map, which has been
1530+
// superseded by the typed-throws version. Expressed as "throws", which is
1531+
// ABI-compatible with "rethrows".
1532+
@usableFromInline
1533+
@_silgen_name("$ss26AnyBidirectionalCollectionV3mapySayqd__Gqd__xKXEKlF")
1534+
func __rethrows_map<T>(
14871535
_ transform: (Element) throws -> T
1488-
) rethrows -> [T] {
1489-
return try _box._map(transform)
1536+
) throws -> [T] {
1537+
try map(transform)
14901538
}
14911539

14921540
@inlinable
@@ -1578,10 +1626,26 @@ extension AnyRandomAccessCollection {
15781626
}
15791627

15801628
@inlinable
1581-
public func map<T>(
1629+
@_alwaysEmitIntoClient
1630+
public func map<T, E>(
1631+
_ transform: (Element) throws(E) -> T
1632+
) throws(E) -> [T] {
1633+
do {
1634+
return try _box._map(transform)
1635+
} catch {
1636+
throw error as! E
1637+
}
1638+
}
1639+
1640+
// ABI-only entrypoint for the rethrows version of map, which has been
1641+
// superseded by the typed-throws version. Expressed as "throws", which is
1642+
// ABI-compatible with "rethrows".
1643+
@usableFromInline
1644+
@_silgen_name("$ss25AnyRandomAccessCollectionV3mapySayqd__Gqd__xKXEKlF")
1645+
func __rethrows_map<T>(
15821646
_ transform: (Element) throws -> T
1583-
) rethrows -> [T] {
1584-
return try _box._map(transform)
1647+
) throws -> [T] {
1648+
try map(transform)
15851649
}
15861650

15871651
@inlinable

stdlib/public/core/Misc.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,17 @@ public func _unsafePerformance<T>(_ c: () -> T) -> T {
159159
return c()
160160
}
161161

162+
// Helper function that exploits a bug in rethrows checking to
163+
// allow us to call rethrows functions from generic typed-throws functions
164+
// and vice-versa.
165+
@usableFromInline
166+
@_alwaysEmitIntoClient
167+
@inline(__always)
168+
func _rethrowsViaClosure(_ fn: () throws -> ()) rethrows {
169+
try fn()
170+
}
171+
172+
162173
/// This marker protocol represents types that support copying.
163174
/// This type is not yet available for use to express explicit
164175
/// constraints on generics in your programs. It is currently

stdlib/public/core/Sequence.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -670,9 +670,10 @@ extension Sequence {
670670
///
671671
/// - Complexity: O(*n*), where *n* is the length of the sequence.
672672
@inlinable
673-
public func map<T>(
674-
_ transform: (Element) throws -> T
675-
) rethrows -> [T] {
673+
@_alwaysEmitIntoClient
674+
public func map<T, E>(
675+
_ transform: (Element) throws(E) -> T
676+
) throws(E) -> [T] {
676677
let initialCapacity = underestimatedCount
677678
var result = ContiguousArray<T>()
678679
result.reserveCapacity(initialCapacity)
@@ -690,6 +691,17 @@ extension Sequence {
690691
return Array(result)
691692
}
692693

694+
// ABI-only entrypoint for the rethrows version of map, which has been
695+
// superseded by the typed-throws version. Expressed as "throws", which is
696+
// ABI-compatible with "rethrows".
697+
@usableFromInline
698+
@_silgen_name("$sSTsE3mapySayqd__Gqd__7ElementQzKXEKlF")
699+
func __rethrows_map<T>(
700+
_ transform: (Element) throws -> T
701+
) throws -> [T] {
702+
try map(transform)
703+
}
704+
693705
/// Returns an array containing, in order, the elements of the sequence
694706
/// that satisfy the given predicate.
695707
///

test/Constraints/closures.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,11 +1143,11 @@ struct R_76250381<Result, Failure: Error> {
11431143

11441144
// https://github.com/apple/swift/issues/55926
11451145
(0..<10).map { x, y in }
1146-
// expected-error@-1 {{contextual closure type '(Range<Int>.Element) throws -> ()' (aka '(Int) throws -> ()') expects 1 argument, but 2 were used in closure body}}
1146+
// expected-error@-1 {{contextual closure type '(Range<Int>.Element) -> ()' (aka '(Int) -> ()') expects 1 argument, but 2 were used in closure body}}
11471147
(0..<10).map { x, y, z in }
1148-
// expected-error@-1 {{contextual closure type '(Range<Int>.Element) throws -> ()' (aka '(Int) throws -> ()') expects 1 argument, but 3 were used in closure body}}
1148+
// expected-error@-1 {{contextual closure type '(Range<Int>.Element) -> ()' (aka '(Int) -> ()') expects 1 argument, but 3 were used in closure body}}
11491149
(0..<10).map { x, y, z, w in }
1150-
// expected-error@-1 {{contextual closure type '(Range<Int>.Element) throws -> ()' (aka '(Int) throws -> ()') expects 1 argument, but 4 were used in closure body}}
1150+
// expected-error@-1 {{contextual closure type '(Range<Int>.Element) -> ()' (aka '(Int) -> ()') expects 1 argument, but 4 were used in closure body}}
11511151

11521152
// rdar://77022842 - crash due to a missing argument to a ternary operator
11531153
func rdar77022842(argA: Bool? = nil, argB: Bool? = nil) {
@@ -1171,6 +1171,7 @@ func rdar76058892() {
11711171
test { // expected-error {{contextual closure type '() -> String' expects 0 arguments, but 1 was used in closure body}}
11721172
if let arr = arr {
11731173
arr.map($0.test) // expected-note {{anonymous closure parameter '$0' is used here}} // expected-error {{generic parameter 'T' could not be inferred}}
1174+
// expected-error@-1 {{generic parameter 'E' could not be inferred}}
11741175
}
11751176
}
11761177
}

0 commit comments

Comments
 (0)