mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[SE-0413] Adopt typed throws in withoutActuallyEscaping(_:do:)
There is a small bug fix here in the identification of the catch node,
where the leading `{` of a closure was considered to be "inside" the
closure for code like
{ ... }()
causing us to assume that the call to the closure would catch the error
within the closure.
Other than that, introduce the thrown error type into the type checker's
modeling of `withoutActuallyEscaping(_:do:)`, and mirror that in the
library declaration.
This commit is contained in:
@@ -704,6 +704,21 @@ getCatchNode(const ASTScopeImpl *scope) {
|
||||
return { CatchNode(), nullptr };
|
||||
}
|
||||
|
||||
/// Check whether the given location precedes the start of the catch location
|
||||
/// despite being technically within the catch node's source range.
|
||||
static bool locationIsPriorToStartOfCatchScope(SourceLoc loc, CatchNode node) {
|
||||
auto closure = node.dyn_cast<ClosureExpr *>();
|
||||
if (!closure)
|
||||
return false;
|
||||
|
||||
SourceManager &sourceMgr = closure->getASTContext().SourceMgr;
|
||||
SourceLoc inLoc = closure->getInLoc();
|
||||
if (inLoc.isValid())
|
||||
return sourceMgr.isBefore(loc, inLoc);
|
||||
|
||||
return sourceMgr.isAtOrBefore(loc, closure->getStartLoc());
|
||||
}
|
||||
|
||||
CatchNode ASTScopeImpl::lookupCatchNode(ModuleDecl *module, SourceLoc loc) {
|
||||
auto sourceFile = module->getSourceFileContainingLocation(loc);
|
||||
if (!sourceFile)
|
||||
@@ -721,7 +736,8 @@ CatchNode ASTScopeImpl::lookupCatchNode(ModuleDecl *module, SourceLoc loc) {
|
||||
// If we are at a catch node and in the body of the region from which that
|
||||
// node catches thrown errors, we have our result.
|
||||
auto caught = getCatchNode(scope);
|
||||
if (caught.first && caught.second == innerBodyScope) {
|
||||
if (caught.first && caught.second == innerBodyScope &&
|
||||
!locationIsPriorToStartOfCatchScope(loc, caught.first)) {
|
||||
return caught.first;
|
||||
}
|
||||
|
||||
|
||||
@@ -3108,12 +3108,15 @@ static DeclReferenceType getTypeOfReferenceWithSpecialTypeCheckingSemantics(
|
||||
auto result = CS.createTypeVariable(
|
||||
CS.getConstraintLocator(locator, ConstraintLocator::FunctionResult),
|
||||
TVO_CanBindToNoEscape);
|
||||
auto thrownError = CS.createTypeVariable(
|
||||
CS.getConstraintLocator(locator, ConstraintLocator::ThrownErrorType),
|
||||
0);
|
||||
FunctionType::Param arg(escapeClosure);
|
||||
auto bodyClosure = FunctionType::get(arg, result,
|
||||
FunctionType::ExtInfoBuilder()
|
||||
.withNoEscape(true)
|
||||
.withAsync(true)
|
||||
.withThrows(true, /*FIXME:*/Type())
|
||||
.withThrows(true, thrownError)
|
||||
.build());
|
||||
FunctionType::Param args[] = {
|
||||
FunctionType::Param(noescapeClosure),
|
||||
@@ -3124,7 +3127,7 @@ static DeclReferenceType getTypeOfReferenceWithSpecialTypeCheckingSemantics(
|
||||
FunctionType::ExtInfoBuilder()
|
||||
.withNoEscape(false)
|
||||
.withAsync(true)
|
||||
.withThrows(true, /*FIXME:*/Type())
|
||||
.withThrows(true, thrownError)
|
||||
.build());
|
||||
return {refType, refType, refType, refType, Type()};
|
||||
}
|
||||
|
||||
@@ -1022,12 +1022,28 @@ public func type<T, Metatype>(of value: T) -> Metatype {
|
||||
/// - body: A closure that is executed immediately with an escapable copy of
|
||||
/// `closure` as its argument.
|
||||
/// - Returns: The return value, if any, of the `body` closure.
|
||||
@_alwaysEmitIntoClient
|
||||
@_transparent
|
||||
@_semantics("typechecker.withoutActuallyEscaping(_:do:)")
|
||||
public func withoutActuallyEscaping<ClosureType, ResultType>(
|
||||
public func withoutActuallyEscaping<ClosureType, ResultType, Failure>(
|
||||
_ closure: ClosureType,
|
||||
do body: (_ escapingClosure: ClosureType) throws(Failure) -> ResultType
|
||||
) throws(Failure) -> ResultType {
|
||||
// This implementation is never used, since calls to
|
||||
// `Swift.withoutActuallyEscaping(_:do:)` are resolved as a special case by
|
||||
// the type checker.
|
||||
Builtin.staticReport(_trueAfterDiagnostics(), true._value,
|
||||
("internal consistency error: 'withoutActuallyEscaping(_:do:)' operation failed to resolve"
|
||||
as StaticString).utf8Start._rawValue)
|
||||
Builtin.unreachable()
|
||||
}
|
||||
|
||||
@_silgen_name("$ss23withoutActuallyEscaping_2doq_x_q_xKXEtKr0_lF")
|
||||
@usableFromInline
|
||||
func __abi_withoutActuallyEscaping<ClosureType, ResultType>(
|
||||
_ closure: ClosureType,
|
||||
do body: (_ escapingClosure: ClosureType) throws -> ResultType
|
||||
) rethrows -> ResultType {
|
||||
) throws -> ResultType {
|
||||
// This implementation is never used, since calls to
|
||||
// `Swift.withoutActuallyEscaping(_:do:)` are resolved as a special case by
|
||||
// the type checker.
|
||||
|
||||
@@ -60,10 +60,8 @@ func rethrowThroughWAE(_ zz: (Int, Int, Int) throws -> Int, _ value: Int) throws
|
||||
}
|
||||
}
|
||||
|
||||
// There should be two errors - here one for async -> sync, and another throws -> non-throws
|
||||
let _: ((Int) -> Int, (@escaping (Int) -> Int) -> ()) -> () = withoutActuallyEscaping(_:do:)
|
||||
// expected-error@-1 {{invalid conversion from throwing function of type '((Int) -> Int, (@escaping (Int) -> Int) async throws -> ()) async throws -> ()' to non-throwing function type '((Int) -> Int, (@escaping (Int) -> Int) -> ()) -> ()'}}
|
||||
// expected-error@-2 {{invalid conversion from 'async' function of type '((Int) -> Int, (@escaping (Int) -> Int) async throws -> ()) async throws -> ()' to synchronous function type '((Int) -> Int, (@escaping (Int) -> Int) -> ()) -> ()'}}
|
||||
// expected-error@-1 {{invalid conversion from 'async' function of type '((Int) -> Int, (@escaping (Int) -> Int) async -> ()) async -> ()' to synchronous function type '((Int) -> Int, (@escaping (Int) -> Int) -> ()) -> ()'}}
|
||||
|
||||
|
||||
// Failing to propagate @noescape into non-single-expression
|
||||
@@ -91,3 +89,24 @@ class Box<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum HomeworkError: Error {
|
||||
case forgot
|
||||
case dogAteIt
|
||||
}
|
||||
|
||||
enum MyError: Error {
|
||||
case fail
|
||||
}
|
||||
|
||||
func letEscapeThrowTyped(f: () throws(HomeworkError) -> () -> ()) throws(HomeworkError) -> () -> () {
|
||||
// Note: thrown error type inference for closures will fix this error below.
|
||||
return try withoutActuallyEscaping(f) { return try $0() }
|
||||
// expected-error@-1{{thrown expression type 'any Error' cannot be converted to error type 'HomeworkError'}}
|
||||
}
|
||||
|
||||
func letEscapeThrowTypedBad(f: () throws(HomeworkError) -> () -> ()) throws(MyError) -> () -> () {
|
||||
// Note: thrown error type inference for closures will change this error below.
|
||||
return try withoutActuallyEscaping(f) { return try $0() }
|
||||
// expected-error@-1{{thrown expression type 'any Error' cannot be converted to error type 'MyError'}}
|
||||
}
|
||||
|
||||
@@ -51,9 +51,31 @@ func letEscapeThrow(f: () throws -> () -> ()) throws -> () -> () {
|
||||
return try withoutActuallyEscaping(f) { return try $0() }
|
||||
}
|
||||
|
||||
// thunk for @callee_guaranteed () -> (@owned @escaping @callee_guaranteed () -> (), @error @owned Error)
|
||||
enum HomeworkError: Error {
|
||||
case forgot
|
||||
case dogAteIt
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden [ossa] @$s25without_actually_escaping19letEscapeThrowTyped1fyycyycyAA13HomeworkErrorOYKXE_tKF : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> (@owned @callee_guaranteed () -> (), @error HomeworkError)) -> (@owned @callee_guaranteed () -> (), @error any Error)
|
||||
// CHECK: bb0([[ARG:%.*]] : @guaranteed $@noescape @callee_guaranteed () -> (@owned @callee_guaranteed () -> (), @error HomeworkError)):
|
||||
// CHECK: function_ref @$sIeg_25without_actually_escaping13HomeworkErrorOIgozo_Ieg_ACIegozo_TR : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> (@owned @callee_guaranteed () -> (), @error HomeworkError)) -> (@owned @callee_guaranteed () -> (), @error HomeworkError)
|
||||
// CHECK: bb2([[ERR:%.*]] : @owned $any Error):
|
||||
// CHECK: end_borrow [[BORROW]]
|
||||
// CHECK: destroy_value [[MD]]
|
||||
// CHECK: throw [[ERR]] : $any Error
|
||||
// CHECK: }
|
||||
|
||||
func letEscapeThrowTyped(f: () throws(HomeworkError) -> () -> ()) throws -> () -> () {
|
||||
return try withoutActuallyEscaping(f) { return try $0() }
|
||||
}
|
||||
|
||||
// The thunk must be [without_actually_escaping].
|
||||
// CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [without_actually_escaping] [ossa] @$sIeg_s5Error_pIgozo_Ieg_sAA_pIegozo_TR : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> (@owned @callee_guaranteed () -> (), @error any Error)) -> (@owned @callee_guaranteed () -> (), @error any Error) {
|
||||
// CHECK-LABEL: sil shared [transparent] [serialized] [reabstraction_thunk] [without_actually_escaping] [ossa] @$sIeg_25without_actually_escaping13HomeworkErrorOIgozo_Ieg_ACIegozo_TR : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> (@owned @callee_guaranteed () -> (), @error HomeworkError)) -> (@owned @callee_guaranteed () -> (), @error HomeworkError)
|
||||
|
||||
func letEscapeThrowTyped2(f: () throws(HomeworkError) -> () -> ()) throws(HomeworkError) -> () -> () {
|
||||
return try withoutActuallyEscaping(f) { (g) throws(HomeworkError) in return try g() }
|
||||
}
|
||||
|
||||
|
||||
// We used to crash on this example because we would use the wrong substitution
|
||||
// map.
|
||||
|
||||
@@ -41,5 +41,7 @@ Func Collection.map(_:) is now without @rethrows
|
||||
Func Sequence.map(_:) has generic signature change from <Self, T where Self : Swift.Sequence> to <Self, T, E where Self : Swift.Sequence, E : Swift.Error>
|
||||
Func Sequence.map(_:) is now without @rethrows
|
||||
Constructor Result.init(catching:) has generic signature change from <Success, Failure where Failure == any Swift.Error> to <Success, Failure where Failure : Swift.Error>
|
||||
Func withoutActuallyEscaping(_:do:) has generic signature change from <ClosureType, ResultType> to <ClosureType, ResultType, Failure where Failure : Swift.Error>
|
||||
Func withoutActuallyEscaping(_:do:) is now without @rethrows
|
||||
|
||||
Protocol SIMDScalar has generic signature change from <Self == Self.SIMD16Storage.Scalar, Self.SIMD16Storage : Swift.SIMDStorage, Self.SIMD2Storage : Swift.SIMDStorage, Self.SIMD32Storage : Swift.SIMDStorage, Self.SIMD4Storage : Swift.SIMDStorage, Self.SIMD64Storage : Swift.SIMDStorage, Self.SIMD8Storage : Swift.SIMDStorage, Self.SIMDMaskScalar : Swift.FixedWidthInteger, Self.SIMDMaskScalar : Swift.SIMDScalar, Self.SIMDMaskScalar : Swift.SignedInteger, Self.SIMD16Storage.Scalar == Self.SIMD2Storage.Scalar, Self.SIMD2Storage.Scalar == Self.SIMD32Storage.Scalar, Self.SIMD32Storage.Scalar == Self.SIMD4Storage.Scalar, Self.SIMD4Storage.Scalar == Self.SIMD64Storage.Scalar, Self.SIMD64Storage.Scalar == Self.SIMD8Storage.Scalar> to <Self == Self.SIMD16Storage.Scalar, Self.SIMD16Storage : Swift.SIMDStorage, Self.SIMD2Storage : Swift.SIMDStorage, Self.SIMD32Storage : Swift.SIMDStorage, Self.SIMD4Storage : Swift.SIMDStorage, Self.SIMD64Storage : Swift.SIMDStorage, Self.SIMD8Storage : Swift.SIMDStorage, Self.SIMDMaskScalar : Swift.FixedWidthInteger, Self.SIMDMaskScalar : Swift.SIMDScalar, Self.SIMDMaskScalar : Swift.SignedInteger, Self.SIMDMaskScalar == Self.SIMDMaskScalar.SIMDMaskScalar, Self.SIMD16Storage.Scalar == Self.SIMD2Storage.Scalar, Self.SIMD2Storage.Scalar == Self.SIMD32Storage.Scalar, Self.SIMD32Storage.Scalar == Self.SIMD4Storage.Scalar, Self.SIMD4Storage.Scalar == Self.SIMD64Storage.Scalar, Self.SIMD64Storage.Scalar == Self.SIMD8Storage.Scalar>
|
||||
|
||||
@@ -99,6 +99,10 @@ Constructor Result.init(__abi_catching:) is a new API without @available attribu
|
||||
Constructor Result.init(catching:) has been removed
|
||||
Func Result.get() has been renamed to Func __abi_get()
|
||||
Func Result.get() has mangled name changing from 'Swift.Result.get() throws -> A' to 'Swift.Result.__abi_get() throws -> A'
|
||||
Func withoutActuallyEscaping(_:do:) has been renamed to Func __abi_withoutActuallyEscaping(_:do:)
|
||||
Func withoutActuallyEscaping(_:do:) has mangled name changing from 'Swift.withoutActuallyEscaping<A, B>(_: A, do: (A) throws -> B) throws -> B' to 'Swift.__abi_withoutActuallyEscaping<A, B>(_: A, do: (A) throws -> B) throws -> B'
|
||||
Func withoutActuallyEscaping(_:do:) 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
|
||||
|
||||
Reference in New Issue
Block a user