Skip to content

Commit baaa8f3

Browse files
authored
Merge pull request #70677 from DougGregor/se-0413-typed-throws
Enable SE-0413 "Typed Throws" by default
2 parents fea6ff6 + cde18d1 commit baaa8f3

33 files changed

+111
-77
lines changed

CHANGELOG.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,49 @@
33
> **Note**\
44
> This is in reverse chronological order, so newer entries are added to the top.
55
6+
## Swift 5.11
7+
8+
* [SE-0413][]:
9+
10+
Functions can now specify the type of error that they throw as part of the
11+
function signature. For example:
12+
13+
```swift
14+
func parseRecord(from string: String) throws(ParseError) -> Record { ... }
15+
```
16+
17+
A call to `parseRecord(from:)` will either return a `Record` instance or throw
18+
an error of type `ParseError`. For example, a `do..catch` block will infer
19+
the `error` variable as being of type `ParseError`:
20+
21+
```swift
22+
do {
23+
let record = try parseRecord(from: myString)
24+
} catch {
25+
// error has type ParseError
26+
}
27+
```
28+
29+
Typed throws generalizes over throwing and non-throwing functions. A function
30+
that is specified as `throws` (without an explicitly-specified error type) is
31+
equivalent to one that specifies `throws(any Error)`, whereas a non-throwing
32+
is equivalent to one that specifies `throws(Never)`. Calls to functions that
33+
are `throws(Never)` are non-throwing.
34+
35+
Typed throws can also be used in generic functions to propagate error types
36+
from parameters, in a manner that is more precise than `rethrows`. For
37+
example, the `Sequence.map` operation can propagate the thrown error type from
38+
its closure parameter, indicating that it only throws errors of the same type
39+
as that closure does:
40+
41+
```swift
42+
extension Sequence {
43+
func map<T, E>(_ body: (Element) throws(E) -> T) throws(E) -> [T] { ... }
44+
}
45+
```
46+
47+
When given a non-throwing closure as a parameter, `map` will not throw.
48+
649
* [#70065][]:
750

851
With the implementation of [SE-0110][], a closure parameter syntax consisting
@@ -9861,6 +9904,7 @@ using the `.dynamicType` member to retrieve the type of an expression should mig
98619904
[SE-0394]: https://github.com/apple/swift-evolution/blob/main/proposals/0394-swiftpm-expression-macros.md
98629905
[SE-0397]: https://github.com/apple/swift-evolution/blob/main/proposals/0397-freestanding-declaration-macros.md
98639906
[SE-0407]: https://github.com/apple/swift-evolution/blob/main/proposals/0407-member-macro-conformances.md
9907+
[SE-0413]: https://github.com/apple/swift-evolution/blob/main/proposals/0413-typed-throws.md
98649908
[#64927]: <https://github.com/apple/swift/issues/64927>
98659909
[#42697]: <https://github.com/apple/swift/issues/42697>
98669910
[#42728]: <https://github.com/apple/swift/issues/42728>

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5238,9 +5238,6 @@ WARNING(no_throw_in_try,none,
52385238
WARNING(no_throw_in_do_with_catch,none,
52395239
"'catch' block is unreachable because no errors are thrown in 'do' block", ())
52405240

5241-
ERROR(experimental_typed_throws,none,
5242-
"typed throws is an experimental feature", ())
5243-
52445241
ERROR(thrown_type_not_error,none,
52455242
"thrown type %0 does not conform to the 'Error' protocol", (Type))
52465243

include/swift/Basic/Features.def

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ LANGUAGE_FEATURE(ParameterPacks, 393, "Value and type parameter packs", true)
116116
SUPPRESSIBLE_LANGUAGE_FEATURE(LexicalLifetimes, 0, "@_eagerMove/@_noEagerMove/@_lexicalLifetimes annotations", true)
117117
LANGUAGE_FEATURE(FreestandingMacros, 397, "freestanding declaration macros", true)
118118
SUPPRESSIBLE_LANGUAGE_FEATURE(RetroactiveAttribute, 364, "@retroactive", true)
119+
LANGUAGE_FEATURE(TypedThrows, 413, "Typed throws", true)
119120

120121
UPCOMING_FEATURE(ConciseMagicFile, 274, 6)
121122
UPCOMING_FEATURE(ForwardTrailingClosures, 286, 6)
@@ -249,9 +250,6 @@ EXPERIMENTAL_FEATURE(Embedded, true)
249250
/// Enables noncopyable generics
250251
EXPERIMENTAL_FEATURE(NoncopyableGenerics, false)
251252

252-
/// Enables typed throws.
253-
EXPERIMENTAL_FEATURE(TypedThrows, true)
254-
255253
/// Allow destructuring stored `let` bindings in structs.
256254
EXPERIMENTAL_FEATURE(StructLetDestructuring, true)
257255

lib/ASTGen/Sources/ASTGen/SourceFile.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ extension Parser.ExperimentalFeatures {
5252
}
5353
}
5454
mapFeature(.ThenStatements, to: .thenStatements)
55-
mapFeature(.TypedThrows, to: .typedThrows)
5655
mapFeature(.DoExpressions, to: .doExpressions)
5756
mapFeature(.NonescapableTypes, to: .nonescapableTypes)
5857
mapFeature(.TransferringArgsAndResults, to: .transferringArgsAndResults)

lib/Sema/ConstraintSystem.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ void ConstraintSystem::recordPotentialThrowSite(
374374
ASTContext &ctx = getASTContext();
375375

376376
// Only record potential throw sites when typed throws is enabled.
377-
if (!ctx.LangOpts.hasFeature(Feature::TypedThrows))
377+
if (!ctx.LangOpts.hasFeature(Feature::FullTypedThrows))
378378
return;
379379

380380
// Catch node location is determined by the source location.
@@ -432,6 +432,9 @@ Type ConstraintSystem::getCaughtErrorType(CatchNode catchNode) {
432432
return getClosureType(closure)->getEffectiveThrownErrorTypeOrNever();
433433
}
434434

435+
if (!ctx.LangOpts.hasFeature(Feature::FullTypedThrows))
436+
return ctx.getErrorExistentialType();
437+
435438
// Handle inference of caught error types.
436439

437440
// Collect all of the potential throw sites for this catch node.

lib/Sema/TypeCheckEffects.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -663,8 +663,7 @@ static Expr *removeErasureToExistentialError(Expr *expr) {
663663
return expr;
664664

665665
ASTContext &ctx = type->getASTContext();
666-
if (!ctx.LangOpts.hasFeature(Feature::FullTypedThrows) ||
667-
!ctx.LangOpts.hasFeature(Feature::TypedThrows))
666+
if (!ctx.LangOpts.hasFeature(Feature::FullTypedThrows))
668667
return expr;
669668

670669
// Look for an outer erasure expression.

lib/Sema/TypeCheckPattern.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,7 +1239,9 @@ Pattern *TypeChecker::coercePatternToType(
12391239
} else if (auto MTT = diagTy->getAs<AnyMetatypeType>()) {
12401240
if (MTT->getInstanceType()->isAnyObject())
12411241
shouldRequireType = true;
1242-
} else if (diagTy->isStructurallyUninhabited()) {
1242+
} else if (diagTy->isStructurallyUninhabited() &&
1243+
!(options.contains(TypeResolutionFlags::SilenceNeverWarnings) &&
1244+
type->isNever())) {
12431245
shouldRequireType = true;
12441246
diag = isOptional ? diag::type_inferred_to_undesirable_type
12451247
: diag::type_inferred_to_uninhabited_type;
@@ -1428,11 +1430,15 @@ Pattern *TypeChecker::coercePatternToType(
14281430
if (type->hasError()) {
14291431
return nullptr;
14301432
}
1431-
diags
1432-
.diagnose(IP->getLoc(), diag::downcast_to_unrelated, type,
1433-
IP->getCastType())
1434-
.highlight(IP->getLoc())
1435-
.highlight(IP->getCastTypeRepr()->getSourceRange());
1433+
1434+
if (!(options.contains(TypeResolutionFlags::SilenceNeverWarnings) &&
1435+
type->isNever())) {
1436+
diags
1437+
.diagnose(IP->getLoc(), diag::downcast_to_unrelated, type,
1438+
IP->getCastType())
1439+
.highlight(IP->getLoc())
1440+
.highlight(IP->getCastTypeRepr()->getSourceRange());
1441+
}
14361442

14371443
IP->setCastKind(CheckedCastKind::ValueCast);
14381444
break;

lib/Sema/TypeCheckStmt.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,7 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
14681468
}
14691469

14701470
void checkCaseLabelItemPattern(CaseStmt *caseBlock, CaseLabelItem &labelItem,
1471+
CaseParentKind parentKind,
14711472
bool &limitExhaustivityChecks,
14721473
Type subjectType) {
14731474
Pattern *pattern = labelItem.getPattern();
@@ -1484,6 +1485,9 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
14841485
if (subjectType) {
14851486
auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC);
14861487
TypeResolutionOptions patternOptions(TypeResolverContext::InExpression);
1488+
if (parentKind == CaseParentKind::DoCatch)
1489+
patternOptions |= TypeResolutionFlags::SilenceNeverWarnings;
1490+
14871491
auto coercedPattern = TypeChecker::coercePatternToType(
14881492
contextualPattern, subjectType, patternOptions);
14891493
if (coercedPattern)
@@ -1584,8 +1588,8 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
15841588
for (auto &labelItem : caseLabelItemArray) {
15851589
// Resolve the pattern in our case label if it has not been resolved and
15861590
// check that our var decls follow invariants.
1587-
checkCaseLabelItemPattern(caseBlock, labelItem, limitExhaustivityChecks,
1588-
subjectType);
1591+
checkCaseLabelItemPattern(caseBlock, labelItem, parentKind,
1592+
limitExhaustivityChecks, subjectType);
15891593

15901594
// Check the guard expression, if present.
15911595
if (auto *guard = labelItem.getGuardExpr()) {

lib/Sema/TypeCheckType.cpp

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3720,11 +3720,6 @@ NeverNullType TypeResolver::resolveASTFunctionType(
37203720
Type thrownTy;
37213721
if (auto thrownTypeRepr = repr->getThrownTypeRepr()) {
37223722
ASTContext &ctx = getASTContext();
3723-
if (!ctx.LangOpts.hasFeature(Feature::TypedThrows)) {
3724-
diagnoseInvalid(
3725-
thrownTypeRepr, thrownTypeRepr->getLoc(), diag::experimental_typed_throws);
3726-
}
3727-
37283723
auto thrownTypeOptions = options.withoutContext();
37293724
thrownTy = resolveType(thrownTypeRepr, thrownTypeOptions);
37303725
if (thrownTy->hasError()) {
@@ -5694,10 +5689,6 @@ Type ExplicitCaughtTypeRequest::evaluate(
56945689
}
56955690

56965691
// We have an explicit thrown error type, so resolve it.
5697-
if (!ctx.LangOpts.hasFeature(Feature::TypedThrows)) {
5698-
ctx.Diags.diagnose(thrownTypeRepr->getLoc(), diag::experimental_typed_throws);
5699-
}
5700-
57015692
auto options = TypeResolutionOptions(TypeResolverContext::None);
57025693
if (func->preconcurrency())
57035694
options |= TypeResolutionFlags::Preconcurrency;
@@ -5713,11 +5704,6 @@ Type ExplicitCaughtTypeRequest::evaluate(
57135704
if (auto closure = catchNode.dyn_cast<ClosureExpr *>()) {
57145705
// Explicit thrown error type.
57155706
if (auto thrownTypeRepr = closure->getExplicitThrownTypeRepr()) {
5716-
if (!ctx.LangOpts.hasFeature(Feature::TypedThrows)) {
5717-
ctx.Diags.diagnose(thrownTypeRepr->getLoc(),
5718-
diag::experimental_typed_throws);
5719-
}
5720-
57215707
return TypeResolution::resolveContextualType(
57225708
thrownTypeRepr, closure,
57235709
TypeResolutionOptions(TypeResolverContext::None),
@@ -5739,18 +5725,9 @@ Type ExplicitCaughtTypeRequest::evaluate(
57395725
// A do..catch block with no explicit 'throws' annotation will infer
57405726
// the thrown error type.
57415727
if (doCatch->getThrowsLoc().isInvalid()) {
5742-
// Prior to typed throws, the do..catch always throws 'any Error'.
5743-
if (!ctx.LangOpts.hasFeature(Feature::TypedThrows))
5744-
return ctx.getErrorExistentialType();
5745-
57465728
return Type();
57475729
}
57485730

5749-
if (!ctx.LangOpts.hasFeature(Feature::TypedThrows)) {
5750-
ctx.Diags.diagnose(doCatch->getThrowsLoc(), diag::experimental_typed_throws);
5751-
return ctx.getErrorExistentialType();
5752-
}
5753-
57545731
auto typeRepr = doCatch->getCaughtTypeRepr();
57555732

57565733
// If there is no explicitly-specified thrown error type, it's 'any Error'.

lib/Sema/TypeCheckType.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ enum class TypeResolutionFlags : uint16_t {
8282

8383
/// Whether this is a resolution based on a pack reference.
8484
FromPackReference = 1 << 12,
85+
86+
/// Whether to suppress warnings about conversions from and bindings of type
87+
/// Never
88+
SilenceNeverWarnings = 1 << 13,
8589
};
8690

8791
/// Type resolution contexts that require special handling.

0 commit comments

Comments
 (0)