Skip to content

Commit

Permalink
Merge pull request #70677 from DougGregor/se-0413-typed-throws
Browse files Browse the repository at this point in the history
Enable SE-0413 "Typed Throws" by default
  • Loading branch information
DougGregor authored Jan 5, 2024
2 parents fea6ff6 + cde18d1 commit baaa8f3
Show file tree
Hide file tree
Showing 33 changed files with 111 additions and 77 deletions.
44 changes: 44 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,49 @@
> **Note**\
> This is in reverse chronological order, so newer entries are added to the top.

## Swift 5.11

* [SE-0413][]:

Functions can now specify the type of error that they throw as part of the
function signature. For example:

```swift
func parseRecord(from string: String) throws(ParseError) -> Record { ... }
```

A call to `parseRecord(from:)` will either return a `Record` instance or throw
an error of type `ParseError`. For example, a `do..catch` block will infer
the `error` variable as being of type `ParseError`:

```swift
do {
let record = try parseRecord(from: myString)
} catch {
// error has type ParseError
}
```

Typed throws generalizes over throwing and non-throwing functions. A function
that is specified as `throws` (without an explicitly-specified error type) is
equivalent to one that specifies `throws(any Error)`, whereas a non-throwing
is equivalent to one that specifies `throws(Never)`. Calls to functions that
are `throws(Never)` are non-throwing.

Typed throws can also be used in generic functions to propagate error types
from parameters, in a manner that is more precise than `rethrows`. For
example, the `Sequence.map` operation can propagate the thrown error type from
its closure parameter, indicating that it only throws errors of the same type
as that closure does:

```swift
extension Sequence {
func map<T, E>(_ body: (Element) throws(E) -> T) throws(E) -> [T] { ... }
}
```

When given a non-throwing closure as a parameter, `map` will not throw.

* [#70065][]:

With the implementation of [SE-0110][], a closure parameter syntax consisting
Expand Down Expand Up @@ -9861,6 +9904,7 @@ using the `.dynamicType` member to retrieve the type of an expression should mig
[SE-0394]: https://github.com/apple/swift-evolution/blob/main/proposals/0394-swiftpm-expression-macros.md
[SE-0397]: https://github.com/apple/swift-evolution/blob/main/proposals/0397-freestanding-declaration-macros.md
[SE-0407]: https://github.com/apple/swift-evolution/blob/main/proposals/0407-member-macro-conformances.md
[SE-0413]: https://github.com/apple/swift-evolution/blob/main/proposals/0413-typed-throws.md
[#64927]: <https://github.com/apple/swift/issues/64927>
[#42697]: <https://github.com/apple/swift/issues/42697>
[#42728]: <https://github.com/apple/swift/issues/42728>
Expand Down
3 changes: 0 additions & 3 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5238,9 +5238,6 @@ WARNING(no_throw_in_try,none,
WARNING(no_throw_in_do_with_catch,none,
"'catch' block is unreachable because no errors are thrown in 'do' block", ())

ERROR(experimental_typed_throws,none,
"typed throws is an experimental feature", ())

ERROR(thrown_type_not_error,none,
"thrown type %0 does not conform to the 'Error' protocol", (Type))

Expand Down
4 changes: 1 addition & 3 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ LANGUAGE_FEATURE(ParameterPacks, 393, "Value and type parameter packs", true)
SUPPRESSIBLE_LANGUAGE_FEATURE(LexicalLifetimes, 0, "@_eagerMove/@_noEagerMove/@_lexicalLifetimes annotations", true)
LANGUAGE_FEATURE(FreestandingMacros, 397, "freestanding declaration macros", true)
SUPPRESSIBLE_LANGUAGE_FEATURE(RetroactiveAttribute, 364, "@retroactive", true)
LANGUAGE_FEATURE(TypedThrows, 413, "Typed throws", true)

UPCOMING_FEATURE(ConciseMagicFile, 274, 6)
UPCOMING_FEATURE(ForwardTrailingClosures, 286, 6)
Expand Down Expand Up @@ -249,9 +250,6 @@ EXPERIMENTAL_FEATURE(Embedded, true)
/// Enables noncopyable generics
EXPERIMENTAL_FEATURE(NoncopyableGenerics, false)

/// Enables typed throws.
EXPERIMENTAL_FEATURE(TypedThrows, true)

/// Allow destructuring stored `let` bindings in structs.
EXPERIMENTAL_FEATURE(StructLetDestructuring, true)

Expand Down
1 change: 0 additions & 1 deletion lib/ASTGen/Sources/ASTGen/SourceFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ extension Parser.ExperimentalFeatures {
}
}
mapFeature(.ThenStatements, to: .thenStatements)
mapFeature(.TypedThrows, to: .typedThrows)
mapFeature(.DoExpressions, to: .doExpressions)
mapFeature(.NonescapableTypes, to: .nonescapableTypes)
mapFeature(.TransferringArgsAndResults, to: .transferringArgsAndResults)
Expand Down
5 changes: 4 additions & 1 deletion lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ void ConstraintSystem::recordPotentialThrowSite(
ASTContext &ctx = getASTContext();

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

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

if (!ctx.LangOpts.hasFeature(Feature::FullTypedThrows))
return ctx.getErrorExistentialType();

// Handle inference of caught error types.

// Collect all of the potential throw sites for this catch node.
Expand Down
3 changes: 1 addition & 2 deletions lib/Sema/TypeCheckEffects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -663,8 +663,7 @@ static Expr *removeErasureToExistentialError(Expr *expr) {
return expr;

ASTContext &ctx = type->getASTContext();
if (!ctx.LangOpts.hasFeature(Feature::FullTypedThrows) ||
!ctx.LangOpts.hasFeature(Feature::TypedThrows))
if (!ctx.LangOpts.hasFeature(Feature::FullTypedThrows))
return expr;

// Look for an outer erasure expression.
Expand Down
18 changes: 12 additions & 6 deletions lib/Sema/TypeCheckPattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1239,7 +1239,9 @@ Pattern *TypeChecker::coercePatternToType(
} else if (auto MTT = diagTy->getAs<AnyMetatypeType>()) {
if (MTT->getInstanceType()->isAnyObject())
shouldRequireType = true;
} else if (diagTy->isStructurallyUninhabited()) {
} else if (diagTy->isStructurallyUninhabited() &&
!(options.contains(TypeResolutionFlags::SilenceNeverWarnings) &&
type->isNever())) {
shouldRequireType = true;
diag = isOptional ? diag::type_inferred_to_undesirable_type
: diag::type_inferred_to_uninhabited_type;
Expand Down Expand Up @@ -1428,11 +1430,15 @@ Pattern *TypeChecker::coercePatternToType(
if (type->hasError()) {
return nullptr;
}
diags
.diagnose(IP->getLoc(), diag::downcast_to_unrelated, type,
IP->getCastType())
.highlight(IP->getLoc())
.highlight(IP->getCastTypeRepr()->getSourceRange());

if (!(options.contains(TypeResolutionFlags::SilenceNeverWarnings) &&
type->isNever())) {
diags
.diagnose(IP->getLoc(), diag::downcast_to_unrelated, type,
IP->getCastType())
.highlight(IP->getLoc())
.highlight(IP->getCastTypeRepr()->getSourceRange());
}

IP->setCastKind(CheckedCastKind::ValueCast);
break;
Expand Down
8 changes: 6 additions & 2 deletions lib/Sema/TypeCheckStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,7 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
}

void checkCaseLabelItemPattern(CaseStmt *caseBlock, CaseLabelItem &labelItem,
CaseParentKind parentKind,
bool &limitExhaustivityChecks,
Type subjectType) {
Pattern *pattern = labelItem.getPattern();
Expand All @@ -1484,6 +1485,9 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
if (subjectType) {
auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC);
TypeResolutionOptions patternOptions(TypeResolverContext::InExpression);
if (parentKind == CaseParentKind::DoCatch)
patternOptions |= TypeResolutionFlags::SilenceNeverWarnings;

auto coercedPattern = TypeChecker::coercePatternToType(
contextualPattern, subjectType, patternOptions);
if (coercedPattern)
Expand Down Expand Up @@ -1584,8 +1588,8 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
for (auto &labelItem : caseLabelItemArray) {
// Resolve the pattern in our case label if it has not been resolved and
// check that our var decls follow invariants.
checkCaseLabelItemPattern(caseBlock, labelItem, limitExhaustivityChecks,
subjectType);
checkCaseLabelItemPattern(caseBlock, labelItem, parentKind,
limitExhaustivityChecks, subjectType);

// Check the guard expression, if present.
if (auto *guard = labelItem.getGuardExpr()) {
Expand Down
23 changes: 0 additions & 23 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3720,11 +3720,6 @@ NeverNullType TypeResolver::resolveASTFunctionType(
Type thrownTy;
if (auto thrownTypeRepr = repr->getThrownTypeRepr()) {
ASTContext &ctx = getASTContext();
if (!ctx.LangOpts.hasFeature(Feature::TypedThrows)) {
diagnoseInvalid(
thrownTypeRepr, thrownTypeRepr->getLoc(), diag::experimental_typed_throws);
}

auto thrownTypeOptions = options.withoutContext();
thrownTy = resolveType(thrownTypeRepr, thrownTypeOptions);
if (thrownTy->hasError()) {
Expand Down Expand Up @@ -5694,10 +5689,6 @@ Type ExplicitCaughtTypeRequest::evaluate(
}

// We have an explicit thrown error type, so resolve it.
if (!ctx.LangOpts.hasFeature(Feature::TypedThrows)) {
ctx.Diags.diagnose(thrownTypeRepr->getLoc(), diag::experimental_typed_throws);
}

auto options = TypeResolutionOptions(TypeResolverContext::None);
if (func->preconcurrency())
options |= TypeResolutionFlags::Preconcurrency;
Expand All @@ -5713,11 +5704,6 @@ Type ExplicitCaughtTypeRequest::evaluate(
if (auto closure = catchNode.dyn_cast<ClosureExpr *>()) {
// Explicit thrown error type.
if (auto thrownTypeRepr = closure->getExplicitThrownTypeRepr()) {
if (!ctx.LangOpts.hasFeature(Feature::TypedThrows)) {
ctx.Diags.diagnose(thrownTypeRepr->getLoc(),
diag::experimental_typed_throws);
}

return TypeResolution::resolveContextualType(
thrownTypeRepr, closure,
TypeResolutionOptions(TypeResolverContext::None),
Expand All @@ -5739,18 +5725,9 @@ Type ExplicitCaughtTypeRequest::evaluate(
// A do..catch block with no explicit 'throws' annotation will infer
// the thrown error type.
if (doCatch->getThrowsLoc().isInvalid()) {
// Prior to typed throws, the do..catch always throws 'any Error'.
if (!ctx.LangOpts.hasFeature(Feature::TypedThrows))
return ctx.getErrorExistentialType();

return Type();
}

if (!ctx.LangOpts.hasFeature(Feature::TypedThrows)) {
ctx.Diags.diagnose(doCatch->getThrowsLoc(), diag::experimental_typed_throws);
return ctx.getErrorExistentialType();
}

auto typeRepr = doCatch->getCaughtTypeRepr();

// If there is no explicitly-specified thrown error type, it's 'any Error'.
Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/TypeCheckType.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ enum class TypeResolutionFlags : uint16_t {

/// Whether this is a resolution based on a pack reference.
FromPackReference = 1 << 12,

/// Whether to suppress warnings about conversions from and bindings of type
/// Never
SilenceNeverWarnings = 1 << 13,
};

/// Type resolution contexts that require special handling.
Expand Down
2 changes: 1 addition & 1 deletion test/Concurrency/typed_throws.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-typecheck-verify-swift -enable-experimental-feature TypedThrows
// RUN: %target-typecheck-verify-swift

// REQUIRES: concurrency

Expand Down
2 changes: 1 addition & 1 deletion test/Generics/typed_throws.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -enable-experimental-feature TypedThrows 2>&1 | %FileCheck %s
// RUN: %target-typecheck-verify-swift -debug-generic-signatures 2>&1 | %FileCheck %s

protocol P1 {
associatedtype A
Expand Down
18 changes: 9 additions & 9 deletions test/IDE/complete_exception.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH_ERR_DOT1 | %FileCheck %s -check-prefix=ERROR_DOT
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH_ERR_DOT2 | %FileCheck %s -check-prefix=ERROR_DOT
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH_ERR_DOT3 | %FileCheck %s -check-prefix=NSERROR_DOT
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH_ERR_DOT4 | %FileCheck %s -check-prefix=INT_DOT
// RUNFIXME: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH_ERR_DOT4 | %FileCheck %s -check-prefix=INT_DOT

// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_INSIDE_CATCH1 > %t.top_level_inside_catch1
// RUN: %FileCheck %s -check-prefix=STMT < %t.top_level_inside_catch1
Expand Down Expand Up @@ -71,10 +71,10 @@ func getNSError() -> NSError { return NSError(domain: "", code: 1, userInfo: [:]
func test001() {
do {} catch #^CATCH1^#

// CATCH1-DAG: Decl[Enum]/CurrModule/TypeRelation[Convertible]: Error4[#Error4#]; name=Error4{{$}}
// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error3[#Error3#]; name=Error3{{$}}
// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error2[#Error2#]; name=Error2{{$}}
// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error1[#Error1#]; name=Error1{{$}}
// CATCH1-DAG: Decl[Enum]/CurrModule: Error4[#Error4#]; name=Error4{{$}}
// CATCH1-DAG: Decl[Class]/CurrModule: Error3[#Error3#]; name=Error3{{$}}
// CATCH1-DAG: Decl[Class]/CurrModule: Error2[#Error2#]; name=Error2{{$}}
// CATCH1-DAG: Decl[Class]/CurrModule: Error1[#Error1#]; name=Error1{{$}}
// CATCH1-DAG: Keyword[let]/None: let{{; name=.+$}}
// CATCH1-DAG: Decl[Class]/CurrModule: NoneError1[#NoneError1#]; name=NoneError1{{$}}
// CATCH1-DAG: Decl[Class]/OtherModule[Foundation]/IsSystem: NSError[#NSError#]{{; name=.+$}}
Expand Down Expand Up @@ -147,14 +147,14 @@ func test006() {
} catch {
#^INSIDE_CATCH1^#
}
// IMPLICIT_ERROR: Decl[LocalVar]/Local: error[#any Error#]; name=error
// IMPLICIT_ERROR: Decl[LocalVar]/Local: error[#Never#]; name=error
}
func test007() {
do {
} catch let e {
#^INSIDE_CATCH2^#
}
// EXPLICIT_ERROR_E: Decl[LocalVar]/Local: e[#any Error#]; name=e
// EXPLICIT_ERROR_E: Decl[LocalVar]/Local: e[#Never#]; name=e
}
func test008() {
do {
Expand All @@ -170,7 +170,7 @@ func test009() {
}

// FIXME: we're getting parentheses around the type when it's unnamed...
// EXPLICIT_ERROR_PAYLOAD_I: Decl[LocalVar]/Local: i[#(Int32)#]; name=i
// EXPLICIT_ERROR_PAYLOAD_I: Decl[LocalVar]/Local: i[#<<error type>>#]; name=i
}
func test010() {
do {
Expand All @@ -195,7 +195,7 @@ func test012() {
error.#^INSIDE_CATCH_ERR_DOT1^#
}
}
// ERROR_DOT: Keyword[self]/CurrNominal: self[#any Error#]; name=self
// ERROR_DOT: Keyword[self]/CurrNominal: self[#Never#]; name=self
func test013() {
do {
} catch let e {
Expand Down
6 changes: 3 additions & 3 deletions test/IRGen/typed_throws.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// RUN: %target-swift-frontend -primary-file %s -emit-ir -enable-experimental-feature TypedThrows -disable-availability-checking -runtime-compatibility-version none -target %module-target-future | %FileCheck %s --check-prefix=CHECK-MANGLE
// RUN: %target-swift-frontend -primary-file %s -emit-ir -disable-availability-checking -runtime-compatibility-version none -target %module-target-future | %FileCheck %s --check-prefix=CHECK-MANGLE

// RUN: %target-swift-frontend -primary-file %s -emit-ir -enable-experimental-feature TypedThrows -disable-availability-checking -runtime-compatibility-version 5.8 -disable-concrete-type-metadata-mangled-name-accessors | %FileCheck %s --check-prefix=CHECK-NOMANGLE
// RUN: %target-swift-frontend -primary-file %s -emit-ir -disable-availability-checking -runtime-compatibility-version 5.8 -disable-concrete-type-metadata-mangled-name-accessors | %FileCheck %s --check-prefix=CHECK-NOMANGLE

// RUN: %target-swift-frontend -primary-file %s -emit-ir -enable-experimental-feature TypedThrows | %FileCheck %s --check-prefix=CHECK
// RUN: %target-swift-frontend -primary-file %s -emit-ir | %FileCheck %s --check-prefix=CHECK

// XFAIL: CPU=arm64e
// REQUIRES: PTRSIZE=64
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/typed_throws_exec.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -module-name=test -enable-experimental-feature TypedThrows %s -o %t/a.out
// RUN: %target-build-swift -module-name=test %s -o %t/a.out
// RUN: %target-codesign %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s
// REQUIRES: executable_test
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/typed_throws_thunks.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend -primary-file %s -emit-irgen -enable-experimental-feature TypedThrows -disable-availability-checking -runtime-compatibility-version none -target %module-target-future -enable-library-evolution | %FileCheck %s --check-prefix=CHECK
// RUN: %target-swift-frontend -primary-file %s -emit-irgen -disable-availability-checking -runtime-compatibility-version none -target %module-target-future -enable-library-evolution | %FileCheck %s --check-prefix=CHECK

// REQUIRES: PTRSIZE=64
// UNSUPPORTED: CPU=arm64e
Expand Down
4 changes: 2 additions & 2 deletions test/ModuleInterface/typed_throws.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name Test -enable-experimental-feature TypedThrows
// RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name Test -enable-experimental-feature TypedThrows
// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name Test
// RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name Test
// RUN: %FileCheck %s < %t.swiftinterface

public enum MyError: Error {
Expand Down
6 changes: 4 additions & 2 deletions test/Parse/errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ func one() {
do {

} catch { // expected-warning {{'catch' block is unreachable because no errors are thrown in 'do' block}}
let error2 = error
let error2 = error // expected-warning{{constant 'error2' inferred to have type 'Never', which is an enum with no cases}}
// expected-note@-1{{add an explicit type annotation to silence this warning}}
}

do {
} catch where true { // expected-warning {{'catch' block is unreachable because no errors are thrown in 'do' block}}
let error2 = error
let error2 = error // expected-warning{{constant 'error2' inferred to have type 'Never', which is an enum with no cases}}
// expected-note@-1{{add an explicit type annotation to silence this warning}}
} catch {
}

Expand Down
2 changes: 1 addition & 1 deletion test/Parse/typed_throws.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-typecheck-verify-swift -swift-version 5 -module-name test -enable-experimental-feature TypedThrows
// RUN: %target-typecheck-verify-swift -swift-version 5 -module-name test

// Parsing support for typed throws.

Expand Down
2 changes: 1 addition & 1 deletion test/Runtime/demangleToMetadata.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -Xfrontend -disable-availability-checking -parse-stdlib -enable-experimental-feature TypedThrows %s -module-name main -o %t/a.out
// RUN: %target-build-swift -Xfrontend -disable-availability-checking -parse-stdlib %s -module-name main -o %t/a.out
// RUN: %target-codesign %t/a.out
// RUN: %target-run %t/a.out
// REQUIRES: executable_test
Expand Down
2 changes: 1 addition & 1 deletion test/SILGen/typed_throws.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-emit-silgen %s -enable-experimental-feature TypedThrows | %FileCheck %s
// RUN: %target-swift-emit-silgen %s | %FileCheck %s

enum MyError: Error {
case fail
Expand Down
2 changes: 1 addition & 1 deletion test/SILGen/typed_throws_generic.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-emit-silgen %s -enable-experimental-feature TypedThrows -enable-experimental-feature FullTypedThrows | %FileCheck %s
// RUN: %target-swift-emit-silgen %s -enable-experimental-feature FullTypedThrows | %FileCheck %s

public func genericThrow<E>(e: E) throws(E) {
throw e
Expand Down
Loading

0 comments on commit baaa8f3

Please sign in to comment.