Skip to content

Commit 0d99a30

Browse files
authored
Merge pull request swiftlang#35012 from kavon/concurrency-lib-upgrades
[concurrency] partially implement @mainactor + convenience method for UnsafeThrowingContinuation
2 parents 171ceeb + d531835 commit 0d99a30

File tree

10 files changed

+180
-1
lines changed

10 files changed

+180
-1
lines changed

lib/Frontend/ModuleInterfaceSupport.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,11 @@ class InheritedProtocolCollector {
414414
if (!printOptions.shouldPrint(nominal))
415415
return;
416416

417+
/// is this nominal specifically an 'actor class'?
418+
bool actorClass = false;
419+
if (auto klass = dyn_cast<ClassDecl>(nominal))
420+
actorClass = klass->isActor();
421+
417422
SmallPtrSet<ProtocolDecl *, 16> handledProtocols;
418423

419424
// First record all protocols that have already been handled.
@@ -438,6 +443,15 @@ class InheritedProtocolCollector {
438443
if (!handledProtocols.insert(inherited).second)
439444
return TypeWalker::Action::SkipChildren;
440445

446+
// If 'nominal' is an 'actor class', we do not synthesize its
447+
// conformance to the Actor protocol through a dummy extension.
448+
// There is a special restriction on the Actor protocol in that
449+
// it is only valid to conform to Actor on an 'actor class' decl,
450+
// not extensions of that 'actor class'.
451+
if (actorClass &&
452+
inherited->isSpecificProtocol(KnownProtocolKind::Actor))
453+
return TypeWalker::Action::Continue;
454+
441455
if (isPublicOrUsableFromInline(inherited) &&
442456
conformanceDeclaredInModule(M, nominal, inherited)) {
443457
protocolsToPrint.push_back({inherited, protoAndAvailability.second});

lib/SILGen/SILGenProlog.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,10 +514,15 @@ SILValue SILGenFunction::emitLoadGlobalActorExecutor(Type globalActor) {
514514
Type instanceType =
515515
actorType->getTypeOfMember(SGM.SwiftModule, sharedInstanceDecl);
516516

517+
auto metaRepr =
518+
nominal->isResilient(SGM.SwiftModule, ResilienceExpansion::Maximal)
519+
? MetatypeRepresentation::Thick
520+
: MetatypeRepresentation::Thin;
521+
517522
ManagedValue actorMetaType =
518523
ManagedValue::forUnmanaged(B.createMetatype(loc,
519524
SILType::getPrimitiveObjectType(
520-
CanMetatypeType::get(actorType, MetatypeRepresentation::Thin))));
525+
CanMetatypeType::get(actorType, metaRepr))));
521526

522527
RValue actorInstanceRV = emitRValueForStorageLoad(loc, actorMetaType,
523528
actorType, /*isSuper*/ false, sharedInstanceDecl, PreparedArguments(),

stdlib/public/Concurrency/Actor.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,17 @@ public func _defaultActorDestroy(_ actor: AnyObject)
3838
@_silgen_name("swift_defaultActor_enqueue")
3939
public func _defaultActorEnqueue(partialTask: PartialAsyncTask,
4040
actor: AnyObject)
41+
42+
/// A singleton actor whose executor is equivalent to
43+
/// \c DispatchQueue.main, which is the main dispatch queue.
44+
@globalActor public final class MainActor {
45+
public static let shared = _Impl()
46+
47+
public actor class _Impl {
48+
@actorIndependent
49+
public func enqueue(partialTask: PartialAsyncTask) {
50+
// TODO: implement this.
51+
_ = (nil as String?)! + "MainActor is not implemented yet."
52+
}
53+
}
54+
}

stdlib/public/Concurrency/PartialAsyncTask.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ public struct UnsafeThrowingContinuation<T> {
4747
public func resume(returning: __owned T)
4848
@_silgen_name("swift_continuation_throwingResumeWithError")
4949
public func resume(throwing: __owned Error)
50+
51+
public func resume<E: Error>(with result: Result<T, E>) {
52+
switch result {
53+
case .success(let val):
54+
self.resume(returning: val)
55+
case .failure(let err):
56+
self.resume(throwing: err)
57+
}
58+
}
5059
}
5160

5261
#if _runtime(_ObjC)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch) | %FileCheck %s
2+
3+
// REQUIRES: executable_test
4+
// REQUIRES: concurrency
5+
// REQUIRES: libdispatch
6+
7+
// XFAIL: *
8+
9+
#if canImport(Darwin)
10+
import Darwin
11+
#elseif canImport(Glibc)
12+
import Glibc
13+
#endif
14+
15+
import Foundation
16+
17+
// CHECK: starting
18+
// CHECK-NOT: ERROR
19+
// CHECK: hello from main actor!
20+
// CHECK-NOT: ERROR
21+
// CHECK: ending
22+
23+
@MainActor func helloMainActor() {
24+
if Thread.isMainThread {
25+
print("hello from main actor!")
26+
} else {
27+
print("ERROR: not on correct thread!")
28+
}
29+
}
30+
31+
func someFunc() async {
32+
await helloMainActor()
33+
}
34+
35+
runAsyncAndBlock {
36+
print("starting")
37+
let handle = Task.runDetached(operation: someFunc)
38+
do {
39+
try await handle.get()
40+
} catch {
41+
print("ERROR: exception was thrown while waiting for task to complete")
42+
}
43+
print("ending")
44+
}

test/Concurrency/async_tasks.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@ func test_unsafeThrowingContinuations() async {
6262
continuation.resume(throwing: MyError())
6363
}
6464

65+
// using resume(with:)
66+
let _: String = try await withUnsafeThrowingContinuation { continuation in
67+
let result : Result<String, MyError> = .success("")
68+
continuation.resume(with: result)
69+
}
70+
71+
let _: String = try await withUnsafeThrowingContinuation { continuation in
72+
continuation.resume(with: .failure(MyError()))
73+
}
74+
6575
// TODO: Potentially could offer some warnings if we know that a continuation was resumed or escaped at all in a closure?
6676
}
6777

test/Concurrency/global_actor_inference.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ struct GenericGlobalActor<T> {
1818
static var shared: SomeActor { SomeActor() }
1919
}
2020

21+
// ----------------------------------------------------------------------
22+
// Check that MainActor exists
23+
// ----------------------------------------------------------------------
24+
25+
@MainActor protocol Aluminium {
26+
func method()
27+
}
28+
@MainActor class Copper {}
29+
@MainActor func iron() {}
30+
2131
// ----------------------------------------------------------------------
2232
// Global actor inference for protocols
2333
// ----------------------------------------------------------------------
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@globalActor public final class MeowActor {
2+
public static let shared = _Impl()
3+
4+
public actor class _Impl {
5+
@actorIndependent
6+
public func enqueue(partialTask: PartialAsyncTask) {
7+
// DOES NOTHING! :)
8+
}
9+
}
10+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -typecheck -enable-library-evolution -enable-experimental-concurrency -emit-module-interface-path %t/Library.swiftinterface -module-name Library %s
3+
// RUN: %FileCheck --check-prefix CHECK-EXTENSION %s <%t/Library.swiftinterface
4+
// RUN: %FileCheck --check-prefix CHECK %s <%t/Library.swiftinterface
5+
6+
/// This test ensures that, when generating a swiftinterface file,
7+
/// the actor class decl itself is what may conform to the Actor protocol,
8+
/// and not via some extension. The requirement is due to the unique
9+
/// optimizations applied to the implementation of actors.
10+
11+
// CHECK-EXTENSION-NOT: extension {{.+}} : _Concurrency.Actor
12+
13+
// CHECK: actor public class PlainActorClass {
14+
public actor class PlainActorClass {
15+
@actorIndependent public func enqueue(partialTask: PartialAsyncTask) { }
16+
}
17+
18+
// CHECK: actor public class ExplicitActorClass : _Concurrency.Actor {
19+
public actor class ExplicitActorClass : Actor {
20+
@actorIndependent public func enqueue(partialTask: PartialAsyncTask) { }
21+
}
22+
23+
// CHECK: actor public class EmptyActorClass {
24+
public actor class EmptyActorClass {}
25+
26+
// CHECK: public protocol Cat : _Concurrency.Actor {
27+
public protocol Cat : Actor {
28+
func mew()
29+
}
30+
31+
// CHECK: actor public class HouseCat : Library.Cat {
32+
public actor class HouseCat : Cat {
33+
@asyncHandler public func mew() {}
34+
@actorIndependent public func enqueue(partialTask: PartialAsyncTask) { }
35+
}
36+
37+
// CHECK: public protocol ToothyMouth {
38+
public protocol ToothyMouth {
39+
func chew()
40+
}
41+
42+
// CHECK: actor public class Lion : Library.ToothyMouth, _Concurrency.Actor {
43+
public actor class Lion : ToothyMouth, Actor {
44+
@asyncHandler public func chew() {}
45+
@actorIndependent public func enqueue(partialTask: PartialAsyncTask) { }
46+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %empty-directory(%t)
2+
3+
import MeowActor
4+
5+
@MeowActor func doMeow() {}
6+
7+
// RUN: %target-swift-frontend -enable-experimental-concurrency -enable-library-evolution -emit-module -o %t/MeowActor.swiftmodule %S/Inputs/MeowActor.swift
8+
// RUN: %target-swift-frontend -enable-experimental-concurrency -emit-silgen %s -I %t | %FileCheck --check-prefix CHECK-RESILIENT %s
9+
// CHECK-RESILIENT: metatype $@thick MeowActor.Type
10+
11+
// RUN: %target-swift-frontend -enable-experimental-concurrency -emit-module -o %t/MeowActor.swiftmodule %S/Inputs/MeowActor.swift
12+
// RUN: %target-swift-frontend -enable-experimental-concurrency -emit-silgen %s -I %t | %FileCheck --check-prefix CHECK-FRAGILE %s
13+
// CHECK-FRAGILE: metatype $@thin MeowActor.Type
14+
15+
func someFunc() async {
16+
await doMeow()
17+
}

0 commit comments

Comments
 (0)