Skip to content

Commit b0fb7c5

Browse files
authored
Merge pull request #60399 from kavon/fix-97646309
Fix crash when emitting force-unwrap-and-call expression for optional ObjC methods
2 parents 471142c + 4fcc58a commit b0fb7c5

File tree

4 files changed

+86
-3
lines changed

4 files changed

+86
-3
lines changed

lib/SILGen/SILGenApply.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1595,6 +1595,39 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
15951595
return std::move(*applyCallee);
15961596
}
15971597

1598+
/// \returns true if the conversion is from an async function to
1599+
/// the same type but with a global actor added. For example this:
1600+
/// () async -> () ==> @MainActor () async -> ()
1601+
/// will return true. In all other cases, returns false.
1602+
static bool addsGlobalActorToAsyncFn(FunctionConversionExpr *fce) {
1603+
CanType oldTy = fce->getSubExpr()->getType()->getCanonicalType();
1604+
CanType newTy = fce->getType()->getCanonicalType();
1605+
1606+
if (auto oldFnTy = dyn_cast<AnyFunctionType>(oldTy)) {
1607+
if (auto newFnTy = dyn_cast<AnyFunctionType>(newTy)) {
1608+
// old type MUST be async
1609+
if (!oldFnTy->hasEffect(EffectKind::Async))
1610+
return false;
1611+
1612+
// old type MUST NOT have a global actor
1613+
if (oldFnTy->hasGlobalActor())
1614+
return false;
1615+
1616+
// new type MUST have a global actor
1617+
if (!newFnTy->hasGlobalActor())
1618+
return false;
1619+
1620+
// see if adding the global actor to the old type yields the new type.
1621+
auto globalActor = newFnTy->getGlobalActor();
1622+
auto addedActor = oldFnTy->getExtInfo().withGlobalActor(globalActor);
1623+
1624+
return oldFnTy->withExtInfo(addedActor) == newFnTy;
1625+
}
1626+
}
1627+
1628+
return false;
1629+
}
1630+
15981631
/// Ignore parentheses and implicit conversions.
15991632
static Expr *ignoreParensAndImpConversions(Expr *expr) {
16001633
while (true) {
@@ -1608,7 +1641,16 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
16081641
// works given that we check the result for certain forms.
16091642
if (auto eval = dyn_cast<OptionalEvaluationExpr>(expr)) {
16101643
if (auto inject = dyn_cast<InjectIntoOptionalExpr>(eval->getSubExpr())) {
1611-
if (auto bind = dyn_cast<BindOptionalExpr>(inject->getSubExpr())) {
1644+
1645+
auto nextSubExpr = inject->getSubExpr();
1646+
1647+
// skip over a specific, known no-op function conversion, if it exists
1648+
if (auto funcConv = dyn_cast<FunctionConversionExpr>(nextSubExpr)) {
1649+
if (addsGlobalActorToAsyncFn(funcConv))
1650+
nextSubExpr = funcConv->getSubExpr();
1651+
}
1652+
1653+
if (auto bind = dyn_cast<BindOptionalExpr>(nextSubExpr)) {
16121654
if (bind->getDepth() == 0)
16131655
return bind->getSubExpr();
16141656
}

test/ClangImporter/objc_async.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,3 +397,11 @@ extension SomeWrapper: Sendable where T: Sendable {}
397397
}
398398
}
399399
}
400+
401+
// rdar://97646309 -- lookup and direct call of an optional global-actor constrained method would crash in SILGen
402+
@available(SwiftStdlib 5.5, *)
403+
extension CoffeeDelegate {
404+
@MainActor func test() async -> (NSObject?, NSObject, NSObject) {
405+
return await self.icedMochaServiceGenerateMocha!(NSObject())
406+
}
407+
}

test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,4 +319,12 @@ SENDABLE id StructWithSendableContentsGetSendableComputed(struct StructWithSenda
319319
+ (void)getAsCustomer:(void(^_Nonnull)(NSObject *device))completion;
320320
@end
321321

322+
323+
// rdar://97646309
324+
UI_ACTOR
325+
@protocol CoffeeDelegate <NSObject>
326+
@optional
327+
- (void)icedMochaService:(NSObject *)mochaService generateMochaWithCompletion:(void (^)(NSObject *_Nullable ingredient1, NSObject *ingredient2, NSObject *ingredient3))completionHandler;
328+
@end
329+
322330
#pragma clang assume_nonnull end

test/SILGen/dynamic_lookup.swift

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
// RUN: %target-swift-emit-silgen -module-name dynamic_lookup -enable-objc-interop -parse-as-library -disable-objc-attr-requires-foundation-module %s | %FileCheck %s
2-
// RUN: %target-swift-emit-silgen -module-name dynamic_lookup -enable-objc-interop -parse-as-library -disable-objc-attr-requires-foundation-module %s | %FileCheck %s --check-prefix=GUARANTEED
1+
// RUN: %target-swift-emit-silgen -module-name dynamic_lookup -enable-objc-interop -parse-as-library -disable-objc-attr-requires-foundation-module -disable-availability-checking %s | %FileCheck %s
2+
// RUN: %target-swift-emit-silgen -module-name dynamic_lookup -enable-objc-interop -parse-as-library -disable-objc-attr-requires-foundation-module -disable-availability-checking %s | %FileCheck %s --check-prefix=GUARANTEED
3+
4+
// REQUIRES: objc_interop
5+
// REQUIRES: concurrency
36

47
class X {
58
@objc func f() { }
@@ -405,3 +408,25 @@ func testAnyObjectWithDefault(_ x: AnyObject) {
405408
// CHECK: apply [[METHOD]]([[DEFARG]], [[OPENEDX]])
406409
x.hasDefaultParam()
407410
}
411+
412+
413+
// rdar://97646309 -- lookup and direct call of an optional global-actor constrained method would crash in SILGen
414+
@MainActor
415+
@objc protocol OptionalMemberLookups {
416+
@objc optional func generateMaybe() async
417+
}
418+
419+
extension OptionalMemberLookups {
420+
// CHECK-LABEL: sil hidden [ossa] @$s14dynamic_lookup21OptionalMemberLookupsPAAE19testForceDirectCallyyYaF
421+
// CHECK: [[SELF:%[0-9]+]] = copy_value {{.*}} : $Self
422+
// CHECK: [[METH:%[0-9]+]] = objc_method {{.*}} : $Self, #OptionalMemberLookups.generateMaybe!foreign : <Self where Self : OptionalMemberLookups> (Self) -> () async -> (), $@convention(objc_method) (@convention(block) () -> (), Self) -> ()
423+
// CHECK: = function_ref @$sIeyB_yt14dynamic_lookup21OptionalMemberLookupsRzlTz_ : $@convention(c) @pseudogeneric <τ_0_0 where τ_0_0 : OptionalMemberLookups> (@inout_aliasable @block_storage UnsafeContinuation<(), Never>) -> ()
424+
// CHECK: [[BLOCK:%[0-9]+]] = init_block_storage_header {{.*}} : $*@block_storage UnsafeContinuation<(), Never>
425+
// CHECK: = apply [[METH]]([[BLOCK]], [[SELF]]) : $@convention(objc_method) (@convention(block) () -> (), Self) -> ()
426+
// CHECK: await_async_continuation {{.*}} : $Builtin.RawUnsafeContinuation, resume bb1
427+
// CHECK: hop_to_executor {{.*}} : $MainActor
428+
// CHECK: } // end sil function '$s14dynamic_lookup21OptionalMemberLookupsPAAE19testForceDirectCallyyYaF'
429+
func testForceDirectCall() async -> Void {
430+
await self.generateMaybe!()
431+
}
432+
}

0 commit comments

Comments
 (0)