diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index 608efba52cb97..b3c3aafd2d432 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -1020,16 +1020,11 @@ SILGenFunction::emitBlockToFunc(SILLocation loc, loc, thunkedFn, SILType::getPrimitiveObjectType(loweredFuncTy)); } -static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, - SILLocation loc, - ManagedValue v, - CanType bridgedType, - CanType nativeType, - SILType loweredNativeTy, - bool isCallResult, - SGFContext C) { +static ManagedValue emitCBridgedToNativeValue( + SILGenFunction &SGF, SILLocation loc, ManagedValue v, CanType bridgedType, + SILType loweredBridgedTy, CanType nativeType, SILType loweredNativeTy, + int bridgedOptionalsToUnwrap, bool isCallResult, SGFContext C) { assert(loweredNativeTy.isObject()); - SILType loweredBridgedTy = v.getType(); if (loweredNativeTy == loweredBridgedTy.getObjectType()) return v; @@ -1040,37 +1035,50 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, if (!bridgedObjectType) { auto helper = [&](SILGenFunction &SGF, SILLocation loc, SGFContext C) { auto loweredNativeObjectTy = loweredNativeTy.getOptionalObjectType(); - return emitCBridgedToNativeValue(SGF, loc, v, bridgedType, - nativeObjectType, - loweredNativeObjectTy, - isCallResult, C); + return emitCBridgedToNativeValue( + SGF, loc, v, bridgedType, loweredBridgedTy, nativeObjectType, + loweredNativeObjectTy, bridgedOptionalsToUnwrap, isCallResult, C); }; return SGF.emitOptionalSome(loc, loweredNativeTy, helper, C); } // Optional-to-optional. - auto helper = - [=](SILGenFunction &SGF, SILLocation loc, ManagedValue v, - SILType loweredNativeObjectTy, SGFContext C) { - return emitCBridgedToNativeValue(SGF, loc, v, bridgedObjectType, - nativeObjectType, loweredNativeObjectTy, - isCallResult, C); + auto helper = [=](SILGenFunction &SGF, SILLocation loc, ManagedValue v, + SILType loweredNativeObjectTy, SGFContext C) { + return emitCBridgedToNativeValue( + SGF, loc, v, bridgedObjectType, + loweredBridgedTy.getOptionalObjectType(), nativeObjectType, + loweredNativeObjectTy, bridgedOptionalsToUnwrap, isCallResult, C); }; return SGF.emitOptionalToOptional(loc, v, loweredNativeTy, helper, C); } + if (auto bridgedObjectType = bridgedType.getOptionalObjectType()) { + return emitCBridgedToNativeValue( + SGF, loc, v, bridgedObjectType, + loweredBridgedTy.getOptionalObjectType(), nativeType, loweredNativeTy, + bridgedOptionalsToUnwrap + 1, isCallResult, C); + } + + auto unwrapBridgedOptionals = [&](ManagedValue v) { + for (int i = 0; i < bridgedOptionalsToUnwrap; ++i) { + v = SGF.emitPreconditionOptionalHasValue(loc, v, + /*implicit*/ true); + }; + return v; + }; // Bridge ObjCBool, DarwinBoolean, WindowsBool to Bool when requested. if (nativeType == SGF.SGM.Types.getBoolType()) { if (bridgedType == SGF.SGM.Types.getObjCBoolType()) { - return emitBridgeForeignBoolToBool(SGF, loc, v, + return emitBridgeForeignBoolToBool(SGF, loc, unwrapBridgedOptionals(v), SGF.SGM.getObjCBoolToBoolFn()); } if (bridgedType == SGF.SGM.Types.getDarwinBooleanType()) { - return emitBridgeForeignBoolToBool(SGF, loc, v, + return emitBridgeForeignBoolToBool(SGF, loc, unwrapBridgedOptionals(v), SGF.SGM.getDarwinBooleanToBoolFn()); } if (bridgedType == SGF.SGM.Types.getWindowsBoolType()) { - return emitBridgeForeignBoolToBool(SGF, loc, v, + return emitBridgeForeignBoolToBool(SGF, loc, unwrapBridgedOptionals(v), SGF.SGM.getWindowsBoolToBoolFn()); } } @@ -1080,8 +1088,8 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, auto bridgedMetaTy = cast(bridgedType); if (bridgedMetaTy->hasRepresentation() && bridgedMetaTy->getRepresentation() == MetatypeRepresentation::ObjC) { - SILValue native = - SGF.B.emitObjCToThickMetatype(loc, v.getValue(), loweredNativeTy); + SILValue native = SGF.B.emitObjCToThickMetatype( + loc, unwrapBridgedOptionals(v).getValue(), loweredNativeTy); // *NOTE*: ObjCMetatypes are trivial types. They only gain ARC semantics // when they are converted to an object via objc_metatype_to_object. assert(!v.hasCleanup() && "Metatypes are trivial and should not have " @@ -1097,7 +1105,8 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, == AnyFunctionType::Representation::Block && nativeFTy->getRepresentation() != AnyFunctionType::Representation::Block) { - return SGF.emitBlockToFunc(loc, v, bridgedFTy, nativeFTy, + return SGF.emitBlockToFunc(loc, unwrapBridgedOptionals(v), bridgedFTy, + nativeFTy, loweredNativeTy.castTo()); } } @@ -1106,8 +1115,10 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, if (auto conformance = SGF.SGM.getConformanceToObjectiveCBridgeable(loc, nativeType)) { if (auto result = emitBridgeObjectiveCToNative(SGF, loc, v, bridgedType, - conformance)) - return *result; + conformance)) { + --bridgedOptionalsToUnwrap; + return unwrapBridgedOptionals(*result); + } assert(SGF.SGM.getASTContext().Diags.hadAnyError() && "Bridging code should have complained"); @@ -1118,7 +1129,8 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, if (nativeType->isAny()) { // If this is not a call result, use the normal erasure logic. if (!isCallResult) { - return SGF.emitTransformedValue(loc, v, bridgedType, nativeType, C); + return SGF.emitTransformedValue(loc, unwrapBridgedOptionals(v), + bridgedType, nativeType, C); } // Otherwise, we use more complicated logic that handles results that @@ -1130,7 +1142,8 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, CanType anyObjectTy = SGF.getASTContext().getAnyObjectType()->getCanonicalType(); if (bridgedType != anyObjectTy) { - v = SGF.emitTransformedValue(loc, v, bridgedType, anyObjectTy); + v = SGF.emitTransformedValue(loc, unwrapBridgedOptionals(v), bridgedType, + anyObjectTy); } // TODO: Ever need to handle +0 values here? @@ -1143,8 +1156,8 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, // Bitcast to Optional. This provides a barrier to the optimizer to prevent // it from attempting to eliminate null checks. auto optionalBridgedTy = SILType::getOptionalType(loweredBridgedTy); - auto optionalMV = - SGF.B.createUncheckedBitCast(loc, v, optionalBridgedTy); + auto optionalMV = SGF.B.createUncheckedBitCast( + loc, unwrapBridgedOptionals(v), optionalBridgedTy); return SGF.emitApplyOfLibraryIntrinsic(loc, SGF.getASTContext().getBridgeAnyObjectToAny(), SubstitutionMap(), optionalMV, C) @@ -1153,9 +1166,9 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &SGF, // Bridge NSError to Error. if (bridgedType == SGF.SGM.Types.getNSErrorType()) - return SGF.emitBridgedToNativeError(loc, v); + return SGF.emitBridgedToNativeError(loc, unwrapBridgedOptionals(v)); - return v; + return unwrapBridgedOptionals(v); } ManagedValue SILGenFunction::emitBridgedToNativeValue(SILLocation loc, @@ -1166,8 +1179,10 @@ ManagedValue SILGenFunction::emitBridgedToNativeValue(SILLocation loc, SGFContext C, bool isCallResult) { loweredNativeTy = loweredNativeTy.getObjectType(); - return emitCBridgedToNativeValue(*this, loc, v, bridgedType, nativeType, - loweredNativeTy, isCallResult, C); + SILType loweredBridgedTy = v.getType(); + return emitCBridgedToNativeValue( + *this, loc, v, bridgedType, loweredBridgedTy, nativeType, loweredNativeTy, + /*bridgedOptionalsToUnwrap=*/0, isCallResult, C); } /// Bridge a possibly-optional foreign error type to Error. diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h index e8450d0ff7969..531888322f293 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -84,6 +84,10 @@ typedef void (^CompletionHandler)(NSString * _Nullable, NSString * _Nullable_res -(void)asyncImportSame:(NSString *)operation replyTo:(void (^)(NSInteger))handler __attribute__((swift_async(none))); -(void)overridableButRunsOnMainThreadWithCompletionHandler:(MAIN_ACTOR_UNSAFE void (^ _Nullable)(NSString *))completion; +- (void)obtainClosureWithCompletionHandler:(void (^)(void (^_Nullable)(void), + NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); @end @protocol RefrigeratorDelegate diff --git a/test/SILGen/objc_async.swift b/test/SILGen/objc_async.swift index 52b813405827e..b95345db32b6d 100644 --- a/test/SILGen/objc_async.swift +++ b/test/SILGen/objc_async.swift @@ -85,6 +85,8 @@ func testSlowServer(slowServer: SlowServer) async throws { let _: ((Any) -> Void, (Any) -> Void) = await slowServer.performId2VoidId2Void() let _: String = try await slowServer.findAnswerFailingly() + + let _: () -> Void = try await slowServer.obtainClosure() } func testGeneric(x: GenericObject) async throws { diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807.h b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807.h new file mode 100644 index 0000000000000..781130727fb21 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807.h @@ -0,0 +1,29 @@ +#include + +#pragma clang assume_nonnull begin + +@interface PFXObject : NSObject { +} +- (void)continuePassSyncWithCompletionHandler:(void (^)(void (^_Nullable)(void), + NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +- (void)continuePassAsyncWithCompletionHandler: + (void (^)(void (^_Nullable)(void), NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +- (void)continueFailSyncWithCompletionHandler:(void (^)(void (^_Nullable)(void), + NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +- (void)continueFailAsyncWithCompletionHandler: + (void (^)(void (^_Nullable)(void), NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +- (void)continueIncorrectWithCompletionHandler: + (void (^)(void (^_Nullable)(void), NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))); +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807.m b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807.m new file mode 100644 index 0000000000000..298f83bdbae9d --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807.m @@ -0,0 +1,54 @@ +#include "rdar81590807.h" + +#pragma clang assume_nonnull begin + +@implementation PFXObject +- (void)continuePassSyncWithCompletionHandler:(void (^)(void (^_Nullable)(void), + NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + completionHandler( + ^{ + NSLog(@"passSync"); + }, + NULL, YES); +} +- (void)continuePassAsyncWithCompletionHandler: + (void (^)(void (^_Nullable)(void), NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + completionHandler( + ^{ + NSLog(@"passAsync"); + }, + NULL, YES); + }); +} +- (void)continueFailSyncWithCompletionHandler:(void (^)(void (^_Nullable)(void), + NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + completionHandler( + NULL, [NSError errorWithDomain:@"failSync" code:1 userInfo:nil], NO); +} +- (void)continueFailAsyncWithCompletionHandler: + (void (^)(void (^_Nullable)(void), NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + completionHandler( + NULL, [NSError errorWithDomain:@"failAsync" code:2 userInfo:nil], NO); + }); +} +- (void)continueIncorrectWithCompletionHandler: + (void (^)(void (^_Nullable)(void), NSError *_Nullable, + BOOL))completionHandler + __attribute__((swift_async_error(zero_argument, 3))) { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + completionHandler(NULL, NULL, NO); + }); +} +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807_2.h b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807_2.h new file mode 100644 index 0000000000000..deae0ddbd09eb --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807_2.h @@ -0,0 +1,24 @@ +#include + +#pragma clang assume_nonnull begin + +@interface PFXObject : NSObject { +} +- (void)findAnswerSyncSuccessAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerSyncSuccess(completionHandler:)"))); +- (void)findAnswerAsyncSuccessAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerAsyncSuccess(completionHandler:)"))); +- (void)findAnswerSyncFailAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerSyncFail(completionHandler:)"))); +- (void)findAnswerAsyncFailAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerAsyncFail(completionHandler:)"))); +- (void)findAnswerIncorrectAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerIncorrect(completionHandler:)"))); +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807_2.m b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807_2.m new file mode 100644 index 0000000000000..4d8bf897c6dc0 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/rdar81590807_2.m @@ -0,0 +1,37 @@ +#include "rdar81590807_2.h" + +#pragma clang assume_nonnull begin + +@implementation PFXObject +- (void)findAnswerSyncSuccessAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerSyncSuccess(completionHandler:)"))) { + handler(@"syncSuccess", NULL); +} +- (void)findAnswerAsyncSuccessAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerAsyncSuccess(completionHandler:)"))) { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + handler(@"asyncSuccess", NULL); + }); +} +- (void)findAnswerSyncFailAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerSyncFail(completionHandler:)"))) { + handler(NULL, [NSError errorWithDomain:@"syncFail" code:1 userInfo:nil]); +} +- (void)findAnswerAsyncFailAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerAsyncFail(completionHandler:)"))) { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + handler(NULL, [NSError errorWithDomain:@"asyncFail" code:2 userInfo:nil]); + }); +} +- (void)findAnswerIncorrectAsynchronously: + (void (^)(NSString *_Nullable, NSError *_Nullable))handler + __attribute__((swift_name("findAnswerIncorrect(completionHandler:)"))) { + handler(NULL, NULL); +} +@end + +#pragma clang assume_nonnull end diff --git a/validation-test/compiler_crashers_2_fixed/rdar81590807.swift b/validation-test/compiler_crashers_2_fixed/rdar81590807.swift new file mode 100644 index 0000000000000..b34845fd2b561 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar81590807.swift @@ -0,0 +1,45 @@ +// RUN: %empty-directory(%t) +// RUN: %target-clang %S/Inputs/rdar81590807.m -I %S/Inputs -c -o %t/rdar81590807.o +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -import-objc-header %S/Inputs/rdar81590807.h -Xlinker %t/rdar81590807.o -parse-as-library %s -o %t/main +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main > %t/log 2>&1 || true +// RUN: %FileCheck %s < %t/log + +// REQUIRES: executable_test +// REQUIRES: OS=macosx || OS=ios + +func run(on object: PFXObject) async throws { + // CHECK: passSync + let cl1 = try await object.continuePassSync() + cl1() + // CHECK: passAsync + let cl2 = try await object.continuePassAsync() + cl2() + do { + let cl = try await object.continueFailSync() + // CHECK-NOT: oh no failSync + fputs("oh no failSync\n", stderr) + } + catch let error { + // CHECK: Error Domain=failSync Code=1 "(null)" + fputs("\(error)\n", stderr) + } + do { + let cl = try await object.continueFailAsync() + // CHECK-NOT: oh no failAsync + fputs("oh no failAsync\n", stderr) + } + catch let error { + // CHECK: Error Domain=failAsync Code=2 "(null)" + fputs("\(error)\n", stderr) + } + // CHECK: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value + print(try await object.continueIncorrect()) +} + +@main struct Main { + static func main() async throws { + let object = PFXObject() + try await run(on: object) + } +} diff --git a/validation-test/compiler_crashers_2_fixed/rdar81590807_2.swift b/validation-test/compiler_crashers_2_fixed/rdar81590807_2.swift new file mode 100644 index 0000000000000..23b52f2ba13a3 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar81590807_2.swift @@ -0,0 +1,42 @@ +// RUN: %empty-directory(%t) +// RUN: %target-clang %S/Inputs/rdar81590807_2.m -I %S/Inputs -c -o %t/rdar81590807_2.o +// RUN: %target-build-swift -Xfrontend -disable-availability-checking -import-objc-header %S/Inputs/rdar81590807_2.h -Xlinker %t/rdar81590807_2.o -parse-as-library %s -o %t/main +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: OS=macosx || OS=ios + +func run(on object: PFXObject) async throws { + // CHECK: syncSuccess + print("\(try await object.findAnswerSyncSuccess())\n") + // CHECK: asyncSuccess + print("\(try await object.findAnswerAsyncSuccess())\n") + do { + try await object.findAnswerSyncFail() + // CHECK-NOT: oh no syncFail + print("\("oh no syncFail")\n") + } + catch let error { + // CHECK: Error Domain=syncFail Code=1 "(null)" + print("\(error)\n") + } + do { + try await object.findAnswerAsyncFail() + // CHECK-NOT: oh no asyncFail + print("\("oh no asyncFail")\n") + } + catch let error { + // CHECK: Error Domain=asyncFail Code=2 "(null)" + print("\(error)\n") + } + // CHECK: <<>> + print("<<\(try await object.findAnswerIncorrect())>>\n") +} + +@main struct Main { + static func main() async throws { + let object = PFXObject() + try await run(on: object) + } +}