[TaskToThread] No unstructured tasks.

In this mode, the following are disabled:

- task creation
- global actors
- MainActor
- custom executors
This commit is contained in:
Nate Chandler
2022-06-27 08:20:17 -07:00
parent b03904d704
commit 68eea8adec
11 changed files with 362 additions and 4 deletions

View File

@@ -6375,5 +6375,17 @@ NOTE(candidate_result_requires_explicit_coercion,none,
"loss of generic requirements",
(Type))
ERROR(concurrency_task_to_thread_model_custom_executor,none,
"custom executors are not permitted within %0", (StringRef))
ERROR(concurrency_task_to_thread_model_async_main,none,
"async main functions are not permitted within %0", (StringRef))
ERROR(concurrency_task_to_thread_model_main_actor,none,
"MainActor is not permitted within %0", (StringRef))
ERROR(concurrency_task_to_thread_model_global_actor,none,
"globalActor is not permitted within %0", (StringRef))
ERROR(concurrency_task_to_thread_model_global_actor_annotation,none,
"annotating a type with a global actor %0 is not permitted within %1",
(TypeRepr*, StringRef))
#define UNDEFINE_DIAGNOSTIC_MACROS
#include "DefineDiagnosticMacros.h"

View File

@@ -2335,6 +2335,14 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator,
auto where = ExportContext::forDeclSignature(D);
diagnoseDeclAvailability(mainFunction, attr->getRange(), nullptr, where, None);
if (mainFunction->hasAsync() &&
context.LangOpts.isConcurrencyModelTaskToThread() &&
!AvailableAttr::isUnavailable(mainFunction)) {
mainFunction->diagnose(diag::concurrency_task_to_thread_model_async_main,
"task-to-thread concurrency model");
return nullptr;
}
auto *const func = FuncDecl::createImplicit(
context, StaticSpellingKind::KeywordStatic,
DeclName(context, DeclBaseName(context.Id_MainEntryPoint),
@@ -3307,6 +3315,15 @@ void AttributeChecker::visitFrozenAttr(FrozenAttr *attr) {
}
}
/// Determine whether this is the main actor type.
/// FIXME: the diagnostics engine and TypeCheckConcurrency both have a copy of
/// this
static bool isMainActor(NominalTypeDecl *nominal) {
return nominal->getName().is("MainActor") &&
nominal->getParentModule()->getName() ==
nominal->getASTContext().Id_Concurrency;
}
void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
auto dc = D->getDeclContext();
@@ -3342,6 +3359,14 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
return;
}
if (isMainActor(nominal) && Ctx.LangOpts.isConcurrencyModelTaskToThread() &&
!AvailableAttr::isUnavailable(D)) {
Ctx.Diags.diagnose(attr->getLocation(),
diag::concurrency_task_to_thread_model_main_actor,
"task-to-thread concurrency model");
return;
}
// If the nominal type is a property wrapper type, we can be delegating
// through a property.
if (nominal->getAttrs().hasAttribute<PropertyWrapperAttr>()) {
@@ -6025,6 +6050,15 @@ void AttributeChecker::visitGlobalActorAttr(GlobalActorAttr *attr) {
if (!nominal)
return; // already diagnosed
auto &context = nominal->getASTContext();
if (context.LangOpts.isConcurrencyModelTaskToThread() &&
!AvailableAttr::isUnavailable(nominal)) {
context.Diags.diagnose(attr->getLocation(),
diag::concurrency_task_to_thread_model_global_actor,
"task-to-thread concurrency model");
return;
}
(void)nominal->isGlobalActor();
}

View File

@@ -188,9 +188,18 @@ bool IsDefaultActorRequest::evaluate(
// If we synthesized the unownedExecutor property, we should've
// added a semantics attribute to it (if it was actually a default
// actor).
if (auto executorProperty = classDecl->getUnownedExecutorProperty())
return executorProperty->getAttrs()
.hasSemanticsAttr(SEMANTICS_DEFAULT_ACTOR);
if (auto executorProperty = classDecl->getUnownedExecutorProperty()) {
bool isDefaultActor =
executorProperty->getAttrs().hasSemanticsAttr(SEMANTICS_DEFAULT_ACTOR);
if (!isDefaultActor &&
classDecl->getASTContext().LangOpts.isConcurrencyModelTaskToThread() &&
!AvailableAttr::isUnavailable(classDecl)) {
classDecl->diagnose(
diag::concurrency_task_to_thread_model_custom_executor,
"task-to-thread concurrency model");
}
return isDefaultActor;
}
return true;
}

View File

@@ -2332,8 +2332,16 @@ TypeResolver::resolveAttributedType(TypeAttributes &attrs, TypeRepr *repr,
// Diagnose custom attributes that haven't been processed yet.
for (auto customAttr : attrs.getCustomAttrs()) {
// If this was the global actor we matched, ignore it.
if (globalActorAttr == customAttr)
if (globalActorAttr == customAttr) {
Decl *decl = nullptr;
if (getASTContext().LangOpts.isConcurrencyModelTaskToThread() &&
(decl = getDeclContext()->getAsDecl()) &&
!AvailableAttr::isUnavailable(decl))
diagnose(customAttr->getLocation(),
diag::concurrency_task_to_thread_model_global_actor_annotation,
customAttr->getTypeRepr(), "task-to-thread concurrency model");
continue;
}
// If this attribute was marked invalid, ignore it.
if (customAttr->isInvalid())

View File

@@ -12,6 +12,36 @@
import Swift
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@available(SwiftStdlib 5.1, *)
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
@globalActor public final actor MainActor: GlobalActor {
public static let shared = MainActor()
@inlinable
public nonisolated var unownedExecutor: UnownedSerialExecutor {
#if compiler(>=5.5) && $BuiltinBuildMainExecutor
return UnownedSerialExecutor(Builtin.buildMainActorExecutorRef())
#else
fatalError("Swift compiler is incompatible with this SDK version")
#endif
}
@inlinable
public static var sharedUnownedExecutor: UnownedSerialExecutor {
#if compiler(>=5.5) && $BuiltinBuildMainExecutor
return UnownedSerialExecutor(Builtin.buildMainActorExecutorRef())
#else
fatalError("Swift compiler is incompatible with this SDK version")
#endif
}
@inlinable
public nonisolated func enqueue(_ job: UnownedJob) {
_enqueueOnMain(job)
}
}
#else
/// A singleton actor whose executor is equivalent to the main
/// dispatch queue.
@available(SwiftStdlib 5.1, *)
@@ -41,7 +71,9 @@ import Swift
_enqueueOnMain(job)
}
}
#endif
#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@available(SwiftStdlib 5.1, *)
extension MainActor {
/// Execute the given body closure on the main actor.
@@ -66,3 +98,4 @@ extension MainActor {
return try await body()
}
}
#endif

View File

@@ -75,6 +75,17 @@ extension Task where Success == Never, Failure == Never {
@available(SwiftStdlib 5.1, *)
extension Task where Failure == Error {
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@discardableResult
@_alwaysEmitIntoClient
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
public static func runDetached(
priority: TaskPriority? = nil,
operation: __owned @Sendable @escaping () async throws -> Success
) -> Task<Success, Failure> {
fatalError("Unavailable in task-to-thread concurrency model")
}
#else
@discardableResult
@_alwaysEmitIntoClient
@available(*, deprecated, message: "`Task.runDetached` was replaced by `Task.detached` and will be removed shortly.")
@@ -84,8 +95,21 @@ extension Task where Failure == Error {
) -> Task<Success, Failure> {
detached(priority: priority, operation: operation)
}
#endif
}
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@discardableResult
@available(SwiftStdlib 5.1, *)
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
@_alwaysEmitIntoClient
public func detach<T>(
priority: TaskPriority? = nil,
operation: __owned @Sendable @escaping () async -> T
) -> Task<T, Never> {
fatalError("Unavailable in task-to-thread concurrency model")
}
#else
@discardableResult
@available(SwiftStdlib 5.1, *)
@available(*, deprecated, message: "`detach` was replaced by `Task.detached` and will be removed shortly.")
@@ -96,7 +120,20 @@ public func detach<T>(
) -> Task<T, Never> {
Task.detached(priority: priority, operation: operation)
}
#endif
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@discardableResult
@available(SwiftStdlib 5.1, *)
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
@_alwaysEmitIntoClient
public func detach<T>(
priority: TaskPriority? = nil,
operation: __owned @Sendable @escaping () async throws -> T
) -> Task<T, Error> {
fatalError("Unavailable in task-to-thread concurrency model")
}
#else
@discardableResult
@available(SwiftStdlib 5.1, *)
@available(*, deprecated, message: "`detach` was replaced by `Task.detached` and will be removed shortly.")
@@ -107,7 +144,20 @@ public func detach<T>(
) -> Task<T, Error> {
Task.detached(priority: priority, operation: operation)
}
#endif
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@discardableResult
@available(SwiftStdlib 5.1, *)
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
@_alwaysEmitIntoClient
public func asyncDetached<T>(
priority: TaskPriority? = nil,
@_implicitSelfCapture operation: __owned @Sendable @escaping () async -> T
) -> Task<T, Never> {
fatalError("Unavailable in task-to-thread concurrency model")
}
#else
@discardableResult
@available(SwiftStdlib 5.1, *)
@available(*, deprecated, message: "`asyncDetached` was replaced by `Task.detached` and will be removed shortly.")
@@ -118,7 +168,20 @@ public func asyncDetached<T>(
) -> Task<T, Never> {
return Task.detached(priority: priority, operation: operation)
}
#endif
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@discardableResult
@available(SwiftStdlib 5.1, *)
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
@_alwaysEmitIntoClient
public func asyncDetached<T>(
priority: TaskPriority? = nil,
@_implicitSelfCapture operation: __owned @Sendable @escaping () async throws -> T
) -> Task<T, Error> {
fatalError("Unavailable in task-to-thread concurrency model")
}
#else
@discardableResult
@available(SwiftStdlib 5.1, *)
@available(*, deprecated, message: "`asyncDetached` was replaced by `Task.detached` and will be removed shortly.")
@@ -129,7 +192,20 @@ public func asyncDetached<T>(
) -> Task<T, Error> {
return Task.detached(priority: priority, operation: operation)
}
#endif
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@available(SwiftStdlib 5.1, *)
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
@discardableResult
@_alwaysEmitIntoClient
public func async<T>(
priority: TaskPriority? = nil,
@_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async -> T
) -> Task<T, Never> {
fatalError("Unavailable in task-to-thread concurrency model")
}
#else
@available(SwiftStdlib 5.1, *)
@available(*, deprecated, message: "`async` was replaced by `Task.init` and will be removed shortly.")
@discardableResult
@@ -140,7 +216,20 @@ public func async<T>(
) -> Task<T, Never> {
.init(priority: priority, operation: operation)
}
#endif
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@available(SwiftStdlib 5.1, *)
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
@discardableResult
@_alwaysEmitIntoClient
public func async<T>(
priority: TaskPriority? = nil,
@_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async throws -> T
) -> Task<T, Error> {
fatalError("Unavailable in task-to-thread concurrency model")
}
#else
@available(SwiftStdlib 5.1, *)
@available(*, deprecated, message: "`async` was replaced by `Task.init` and will be removed shortly.")
@discardableResult
@@ -151,6 +240,7 @@ public func async<T>(
) -> Task<T, Error> {
.init(priority: priority, operation: operation)
}
#endif
@available(SwiftStdlib 5.1, *)
extension Task where Success == Never, Failure == Never {

View File

@@ -463,6 +463,17 @@ func taskCreateFlags(
// ==== Task Creation ----------------------------------------------------------
@available(SwiftStdlib 5.1, *)
extension Task where Failure == Never {
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@discardableResult
@_alwaysEmitIntoClient
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
public init(
priority: TaskPriority? = nil,
@_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async -> Success
) {
fatalError("Unavailable in task-to-thread concurrency model.")
}
#else
/// Runs the given nonthrowing operation asynchronously
/// as part of a new top-level task on behalf of the current actor.
///
@@ -507,10 +518,22 @@ extension Task where Failure == Never {
fatalError("Unsupported Swift compiler")
#endif
}
#endif
}
@available(SwiftStdlib 5.1, *)
extension Task where Failure == Error {
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@discardableResult
@_alwaysEmitIntoClient
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
public init(
priority: TaskPriority? = nil,
@_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async throws -> Success
) {
fatalError("Unavailable in task-to-thread concurrency model")
}
#else
/// Runs the given throwing operation asynchronously
/// as part of a new top-level task on behalf of the current actor.
///
@@ -556,11 +579,23 @@ extension Task where Failure == Error {
fatalError("Unsupported Swift compiler")
#endif
}
#endif
}
// ==== Detached Tasks ---------------------------------------------------------
@available(SwiftStdlib 5.1, *)
extension Task where Failure == Never {
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@discardableResult
@_alwaysEmitIntoClient
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
public static func detached(
priority: TaskPriority? = nil,
operation: __owned @Sendable @escaping () async -> Success
) -> Task<Success, Failure> {
fatalError("Unavailable in task-to-thread concurrency model")
}
#else
/// Runs the given nonthrowing operation asynchronously
/// as part of a new top-level task.
///
@@ -602,10 +637,22 @@ extension Task where Failure == Never {
fatalError("Unsupported Swift compiler")
#endif
}
#endif
}
@available(SwiftStdlib 5.1, *)
extension Task where Failure == Error {
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@discardableResult
@_alwaysEmitIntoClient
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
public static func detached(
priority: TaskPriority? = nil,
operation: __owned @Sendable @escaping () async throws -> Success
) -> Task<Success, Failure> {
fatalError("Unavailable in task-to-thread concurrency model")
}
#else
/// Runs the given throwing operation asynchronously
/// as part of a new top-level task.
///
@@ -650,6 +697,7 @@ extension Task where Failure == Error {
fatalError("Unsupported Swift compiler")
#endif
}
#endif
}
// ==== Voluntary Suspension -----------------------------------------------------
@@ -833,6 +881,15 @@ internal func _asyncMainDrainQueue() -> Never
@_silgen_name("swift_task_getMainExecutor")
internal func _getMainExecutor() -> Builtin.Executor
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@available(SwiftStdlib 5.1, *)
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
@usableFromInline
@preconcurrency
internal func _runAsyncMain(_ asyncFun: @Sendable @escaping () async throws -> ()) {
fatalError("Unavailable in task-to-thread concurrency model")
}
#else
@available(SwiftStdlib 5.1, *)
@usableFromInline
@preconcurrency
@@ -854,6 +911,7 @@ internal func _runAsyncMain(_ asyncFun: @Sendable @escaping () async throws -> (
}
_asyncMainDrainQueue()
}
#endif
// FIXME: both of these ought to take their arguments _owned so that
// we can do a move out of the future in the common case where it's

View File

@@ -0,0 +1,22 @@
// RUN: %target-swift-frontend \
// RUN: -concurrency-model=task-to-thread \
// RUN: -typecheck \
// RUN: -verify %s
// REQUIRES: freestanding
import _Concurrency
actor Simple {}
@globalActor // expected-error{{not permitted within task-to-thread concurrency model}}
class Goo {
typealias ActorType = Simple
static var shared : Simple { fatalError() }
}
@main struct Main {
static func main() async throws {} // expected-error{{not permitted within task-to-thread concurrency model}}
}

View File

@@ -0,0 +1,31 @@
// RUN: %target-typecheck-verify-swift -concurrency-model=task-to-thread
// REQUIRES: freestanding
import _Concurrency
@MainActor(unsafe) // expected-error{{not permitted within task-to-thread concurrency model}}
func chowMein() async {
}
@MainActor // expected-error{{not permitted within task-to-thread concurrency model}}
class ChowMein {}
func foo() async {
Task<Void, Never> {} // expected-error{{Unavailable in task-to-thread concurrency model}}
Task<Void, Error> {} // expected-error{{Unavailable in task-to-thread concurrency model}}
Task<Void, Never>.detached {} // expected-error{{Unavailable in task-to-thread concurrency model}}
Task<Void, Error>.detached {} // expected-error{{Unavailable in task-to-thread concurrency model}}
Task<Void, Error>.runDetached {} // expected-error{{Unavailable in task-to-thread concurrency model}}
detach { () async -> () in } // expected-error{{Unavailable in task-to-thread concurrency model}}
detach { () async throws -> () in } // expected-error{{Unavailable in task-to-thread concurrency model}}
async { () async -> () in } // expected-error{{Unavailable in task-to-thread concurrency model}}
async { () async throws -> () in } // expected-error{{Unavailable in task-to-thread concurrency model}}
asyncDetached { () async -> () in } // expected-error{{Unavailable in task-to-thread concurrency model}}
asyncDetached { () async throws -> () in } // expected-error{{Unavailable in task-to-thread concurrency model}}
_ = MainActor.self // expected-error{{Unavailable in task-to-thread concurrency model}}
}
func foo2(
body: @MainActor @Sendable () throws -> () // expected-error{{annotating a type with a global actor 'MainActor' is not permitted within task-to-thread concurrency model}}
) {}

View File

@@ -0,0 +1,18 @@
// RUN: %target-swift-frontend \
// RUN: -concurrency-model=task-to-thread \
// RUN: -emit-sil \
// RUN: -verify %s
// REQUIRES: freestanding
import _Concurrency
actor Simple {}
actor Custom { // expected-error{{not permitted within task-to-thread concurrency model}}
let simple = Simple()
nonisolated var unownedExecutor: UnownedSerialExecutor {
return simple.unownedExecutor
}
}

View File

@@ -0,0 +1,43 @@
// RUN: %target-swift-frontend \
// RUN: -concurrency-model=task-to-thread \
// RUN: -parse-as-library \
// RUN: -parse-stdlib \
// RUN: -typecheck \
// RUN: -verify %s
// REQUIRES: freestanding
import _Concurrency
@available(*, unavailable, message: "")
@globalActor // fine because unavailable
final class Hoo {
actor Impl {}
typealias ActorType = Impl
static var shared: Impl { Impl() }
}
@main struct Main {
@available(*, unavailable, message: "")
static func main() async throws {} // fine because unavailable
static func main() throws {} // fine because unavailable
}
@available(*, unavailable, message: "")
@MainActor(unsafe) // fine because unavailable
func chowMein() async {}
@available(*, unavailable, message: "")
@MainActor // fine because unavailable
class ChowMein {}
@available(*, unavailable, message: "")
func foo2(
body: @MainActor @Sendable () throws -> () // fine because unavailable
) {}
@available(*, unavailable, message: "")
func foo3(
body: @Hoo @Sendable () throws -> () // fine because unavailable
) {}