Skip to content

Commit 273a550

Browse files
authored
Merge pull request #58515 from kavon/proper-fix-for-jump-to-null
Proper fix for ObjC async jump to null
2 parents f526c42 + 814fb42 commit 273a550

File tree

7 files changed

+81
-35
lines changed

7 files changed

+81
-35
lines changed

lib/SILGen/ExecutorBreadcrumb.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#ifndef SWIFT_SILGEN_EXECUTORBREADCRUMB_H
14+
#define SWIFT_SILGEN_EXECUTORBREADCRUMB_H
15+
1316
#include "swift/SIL/SILValue.h"
1417

1518
namespace swift {
@@ -46,3 +49,5 @@ class ExecutorBreadcrumb {
4649

4750
} // namespace Lowering
4851
} // namespace swift
52+
53+
#endif

lib/SILGen/ResultPlan.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,7 @@ class ForeignAsyncInitializationPlan final : public ResultPlan {
499499
SILType opaqueResumeType;
500500
SILValue resumeBuf;
501501
SILValue continuation;
502+
ExecutorBreadcrumb breadcrumb;
502503

503504
public:
504505
ForeignAsyncInitializationPlan(SILGenFunction &SGF, SILLocation loc,
@@ -597,6 +598,10 @@ class ForeignAsyncInitializationPlan final : public ResultPlan {
597598
return ManagedValue::forUnmanaged(block);
598599
}
599600

601+
void deferExecutorBreadcrumb(ExecutorBreadcrumb &&breadcrumb) override {
602+
this->breadcrumb = breadcrumb;
603+
}
604+
600605
RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
601606
ArrayRef<ManagedValue> &directResults,
602607
SILValue bridgedForeignError) override {
@@ -692,6 +697,7 @@ class ForeignAsyncInitializationPlan final : public ResultPlan {
692697
// Propagate an error if we have one.
693698
if (errorBlock) {
694699
SGF.B.emitBlock(errorBlock);
700+
breadcrumb.emit(SGF, loc);
695701

696702
Scope errorScope(SGF, loc);
697703

@@ -703,6 +709,7 @@ class ForeignAsyncInitializationPlan final : public ResultPlan {
703709
}
704710

705711
SGF.B.emitBlock(resumeBlock);
712+
breadcrumb.emit(SGF, loc);
706713

707714
// The incoming value is the maximally-abstracted result type of the
708715
// continuation. Move it out of the resume buffer and reabstract it if
@@ -772,6 +779,10 @@ class ForeignErrorInitializationPlan final : public ResultPlan {
772779
AbstractionPattern(errorType), errorType);
773780
}
774781

782+
void deferExecutorBreadcrumb(ExecutorBreadcrumb &&breadcrumb) override {
783+
subPlan->deferExecutorBreadcrumb(std::move(breadcrumb));
784+
}
785+
775786
RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
776787
ArrayRef<ManagedValue> &directResults,
777788
SILValue bridgedForeignError) override {

lib/SILGen/ResultPlan.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define SWIFT_SILGEN_RESULTPLAN_H
1515

1616
#include "Callee.h"
17+
#include "ExecutorBreadcrumb.h"
1718
#include "ManagedValue.h"
1819
#include "swift/AST/Types.h"
1920
#include "swift/Basic/LLVM.h"
@@ -42,6 +43,11 @@ class ResultPlan {
4243
SILValue bridgedForeignError) = 0;
4344
virtual ~ResultPlan() = default;
4445

46+
/// Defers the emission of the given breadcrumb until \p finish is invoked.
47+
virtual void deferExecutorBreadcrumb(ExecutorBreadcrumb &&breadcrumb) {
48+
llvm_unreachable("this ResultPlan does not handle deferred breadcrumbs!");
49+
}
50+
4551
virtual void
4652
gatherIndirectResultAddrs(SILGenFunction &SGF, SILLocation loc,
4753
SmallVectorImpl<SILValue> &outList) const = 0;

lib/SILGen/SILGenApply.cpp

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4430,27 +4430,6 @@ class FixLifetimeDestroyCleanup : public Cleanup {
44304430
#endif
44314431
}
44324432
};
4433-
4434-
class EmitBreadcrumbCleanup : public Cleanup {
4435-
ExecutorBreadcrumb breadcrumb;
4436-
4437-
public:
4438-
EmitBreadcrumbCleanup(ExecutorBreadcrumb &&breadcrumb)
4439-
: breadcrumb(std::move(breadcrumb)) {}
4440-
4441-
void emit(SILGenFunction &SGF, CleanupLocation l,
4442-
ForUnwind_t forUnwind) override {
4443-
breadcrumb.emit(SGF, l);
4444-
}
4445-
4446-
void dump(SILGenFunction &SGF) const override {
4447-
#ifndef NDEBUG
4448-
llvm::errs() << "EmitBreadcrumbCleanup "
4449-
<< "State:" << getState()
4450-
<< "NeedsEmit:" << breadcrumb.needsEmit();
4451-
#endif
4452-
}
4453-
};
44544433
} // end anonymous namespace
44554434

44564435
//===----------------------------------------------------------------------===//
@@ -4601,11 +4580,6 @@ RValue SILGenFunction::emitApply(
46014580
rawDirectResult = rawDirectResults[0];
46024581
}
46034582

4604-
if (!calleeTypeInfo.foreign.async) {
4605-
// hop back to the current executor
4606-
breadcrumb.emit(*this, loc);
4607-
}
4608-
46094583
// For objc async calls, lifetime extend the args until the result plan which
46104584
// generates `await_async_continuation`.
46114585
// Lifetime is extended by creating unmanaged copies here and by pushing the
@@ -4617,6 +4591,14 @@ RValue SILGenFunction::emitApply(
46174591
unmanagedCopies.push_back(arg.unmanagedCopy(*this, loc));
46184592
}
46194593
}
4594+
// similarly, we defer the emission of the breadcrumb until the result
4595+
// plan's finish method is called, because it must happen in the
4596+
// successors of the `await_async_continuation` terminator.
4597+
resultPlan->deferExecutorBreadcrumb(std::move(breadcrumb));
4598+
4599+
} else {
4600+
// In the ordinary case, we hop back to the current executor
4601+
breadcrumb.emit(*this, loc);
46204602
}
46214603

46224604
// Pop the argument scope.
@@ -4703,8 +4685,6 @@ RValue SILGenFunction::emitApply(
47034685
for (auto unmanagedCopy : unmanagedCopies) {
47044686
Cleanups.pushCleanup<FixLifetimeDestroyCleanup>(unmanagedCopy);
47054687
}
4706-
// save breadcrumb as a clean-up so it is emitted in result / throw cases.
4707-
Cleanups.pushCleanup<EmitBreadcrumbCleanup>(std::move(breadcrumb));
47084688
} else {
47094689
assert(unmanagedCopies.empty());
47104690
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,4 +310,13 @@ struct StructWithSendableContents {
310310
SENDABLE id StructWithSendableContentsGetSendableComputed(struct StructWithSendableContents contents)
311311
__attribute__((swift_name("getter:StructWithSendableContents.sendableComputed(self:)")));
312312

313+
@interface CostcoManager : NSObject
314+
+ (instancetype)sharedManager;
315+
- (void)isCustomerEnrolledInExecutiveProgram:(NSObject *)customer completion:(void(^)(BOOL enrolled))completion;
316+
@end
317+
318+
@interface Person : NSObject
319+
+ (void)getAsCustomer:(void(^_Nonnull)(NSObject *device))completion;
320+
@end
321+
313322
#pragma clang assume_nonnull end

test/SILGen/objc_async.swift

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func testGeneric2<T: AnyObject, U>(x: GenericObject<T>, y: U) async throws {
177177
// CHECK-LABEL: sil {{.*}}@${{.*}}22testSlowServerFromMain
178178
@MainActor
179179
func testSlowServerFromMain(slowServer: SlowServer) async throws {
180-
// CHECK: hop_to_executor %6 : $MainActor
180+
// CHECK: hop_to_executor {{%.*}} : $MainActor
181181
// CHECK: [[RESUME_BUF:%.*]] = alloc_stack $Int
182182
// CHECK: [[STRINGINIT:%.*]] = function_ref @$sSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF :
183183
// CHECK: [[ARG:%.*]] = apply [[STRINGINIT]]
@@ -194,8 +194,8 @@ func testSlowServerFromMain(slowServer: SlowServer) async throws {
194194
// CHECK: destroy_value [[ARG]]
195195
// CHECK: await_async_continuation [[CONT]] {{.*}}, resume [[RESUME:bb[0-9]+]]
196196
// CHECK: [[RESUME]]:
197+
// CHECK: hop_to_executor {{%.*}} : $MainActor
197198
// CHECK: [[RESULT:%.*]] = load [trivial] [[RESUME_BUF]]
198-
// CHECK: hop_to_executor %6 : $MainActor
199199
// CHECK: fix_lifetime [[COPY]]
200200
// CHECK: destroy_value [[COPY]]
201201
// CHECK: dealloc_stack [[RESUME_BUF]]
@@ -223,8 +223,8 @@ func testThrowingMethodFromMain(slowServer: SlowServer) async -> String {
223223
// CHECK: await_async_continuation [[RAW_CONT]] : $Builtin.RawUnsafeContinuation, resume [[RESUME:bb[0-9]+]], error [[ERROR:bb[0-9]+]]
224224

225225
// CHECK: [[RESUME]]
226-
// CHECK: {{.*}} = load [take] [[RESULT_BUF]] : $*String
227226
// CHECK: hop_to_executor {{%.*}} : $MainActor
227+
// CHECK: {{.*}} = load [take] [[RESULT_BUF]] : $*String
228228
// CHECK: fix_lifetime [[STRING_ARG_COPY]] : $NSString
229229
// CHECK: destroy_value [[STRING_ARG_COPY]] : $NSString
230230
// CHECK: dealloc_stack [[RESULT_BUF]] : $*String
@@ -241,3 +241,38 @@ func testThrowingMethodFromMain(slowServer: SlowServer) async -> String {
241241
return "none"
242242
}
243243
}
244+
245+
// rdar://91502776
246+
// CHECK-LABEL: sil hidden [ossa] @$s{{.*}}21checkCostcoMembershipSbyYaF : $@convention(thin) @async () -> Bool {
247+
// CHECK: bb0:
248+
// CHECK: hop_to_executor {{%.*}} : $Optional<Builtin.Executor>
249+
// CHECK: [[FINAL_BUF:%.*]] = alloc_stack $Bool
250+
// CHECK: [[RESULT_BUF:%.*]] = alloc_stack $NSObject
251+
// CHECK: [[METH:%.*]] = objc_method {{%.*}} : $@objc_metatype Person.Type, #Person.asCustomer!foreign
252+
// CHECK: get_async_continuation_addr NSObject, [[RESULT_BUF]] : $*NSObject
253+
// CHECK: = apply [[METH]]
254+
// CHECK: dealloc_stack {{%.*}} : $*@block_storage
255+
// CHECK: await_async_continuation {{%.*}} : $Builtin.RawUnsafeContinuation, resume bb1
256+
// CHECK: bb1:
257+
// CHECK: hop_to_executor {{%.*}} : $Optional<Builtin.Executor>
258+
// CHECK: [[RESULT:%.*]] = load [take] [[RESULT_BUF]] : $*NSObject
259+
// CHECK: objc_method {{%.*}} : $CostcoManager, #CostcoManager.isCustomerEnrolled!foreign
260+
// CHECK: get_async_continuation_addr Bool, [[FINAL_BUF]] : $*Bool
261+
// CHECK: [[BLOCK_ARG:%.*]] = init_block_storage_header [[BLOCK_STORAGE:%.*]] : $*@block_storage
262+
// CHECK: = apply {{%.*}}([[RESULT]], [[BLOCK_ARG]], [[MANAGER:%.*]]) : $@convention(objc_method)
263+
// CHECK: [[EXTEND1:%.*]] = copy_value [[RESULT]] : $NSObject
264+
// CHECK: [[EXTEND2:%.*]] = copy_value [[MANAGER]] : $CostcoManager
265+
// CHECK: dealloc_stack [[BLOCK_STORAGE]] : $*@block_storage
266+
// CHECK: await_async_continuation {{%.*}} : $Builtin.RawUnsafeContinuation, resume bb2
267+
// CHECK: bb2:
268+
// CHECK: hop_to_executor {{%.*}} : $Optional<Builtin.Executor>
269+
// CHECK: [[ANSWER:%.*]] = load [trivial] [[FINAL_BUF]] : $*Bool
270+
// CHECK: fix_lifetime [[EXTEND2]] : $CostcoManager
271+
// CHECK: destroy_value [[EXTEND2]] : $CostcoManager
272+
// CHECK: fix_lifetime [[EXTEND1]] : $NSObject
273+
// CHECK: destroy_value [[EXTEND1]] : $NSObject
274+
// CHECK: return [[ANSWER]] : $Bool
275+
// CHECK: }
276+
func checkCostcoMembership() async -> Bool {
277+
return await CostcoManager.shared().isCustomerEnrolled(inExecutiveProgram: Person.asCustomer())
278+
}

test/SILGen/objc_async_from_swift.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ func testSlowServing(p: SlowServing) async throws {
3131

3232
// CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Int, Optional<NSString>, Optional<NSError>) -> (), τ_0_0) -> ()
3333
// CHECK: hop_to_executor [[GENERIC_EXECUTOR]] :
34-
// CHECK: builtin "willThrow"
35-
// CHECK-NEXT: hop_to_executor [[GENERIC_EXECUTOR]] :
34+
// CHECK: hop_to_executor [[GENERIC_EXECUTOR]] :
35+
// CHECK-NEXT: builtin "willThrow"
3636
let _: (Int, String) = try await p.tryRequestIntAndString()
3737
}
3838

@@ -42,8 +42,8 @@ func testSlowServingAgain(p: SlowServing) async throws {
4242
// CHECK: hop_to_executor [[GENERIC_EXECUTOR]] :
4343
// CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Optional<NSString>, Optional<NSError>) -> (), τ_0_0) -> ()
4444
// CHECK: hop_to_executor [[GENERIC_EXECUTOR]] :
45-
// CHECK: builtin "willThrow"
46-
// CHECK-NEXT: hop_to_executor [[GENERIC_EXECUTOR]] :
45+
// CHECK: hop_to_executor [[GENERIC_EXECUTOR]] :
46+
// CHECK-NEXT: builtin "willThrow"
4747
let _: String = try await p.tryRequestString()
4848
}
4949

0 commit comments

Comments
 (0)