Skip to content

[Concurrency] TaskLocal.withValue closure should produce no warnings inside Actor #62239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions stdlib/public/BackDeployConcurrency/TaskLocal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
self.defaultValue = defaultValue
}

@_alwaysEmitIntoClient
var key: Builtin.RawPointer {
unsafeBitCast(self, to: Builtin.RawPointer.self)
}
Expand Down Expand Up @@ -135,6 +136,10 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
/// If the value is a reference type, it will be retained for the duration of
/// the operation closure.
@discardableResult
@inlinable
@_unsafeInheritExecutor
@available(SwiftStdlib 5.1, *) // back deploy requires we declare the availability explicitly on this method
@_backDeploy(before: SwiftStdlib 5.8)
public func withValue<R>(_ valueDuringOperation: Value, operation: () async throws -> R,
file: String = #file, line: UInt = #line) async rethrows -> R {
// check if we're not trying to bind a value from an illegal context; this may crash
Expand All @@ -159,6 +164,7 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
///
/// If the value is a reference type, it will be retained for the duration of
/// the operation closure.
@inlinable
@discardableResult
public func withValue<R>(_ valueDuringOperation: Value, operation: () throws -> R,
file: String = #file, line: UInt = #line) rethrows -> R {
Expand Down Expand Up @@ -212,13 +218,15 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
// ==== ------------------------------------------------------------------------

@available(SwiftStdlib 5.1, *)
@usableFromInline
@_silgen_name("swift_task_localValuePush")
func _taskLocalValuePush<Value>(
key: Builtin.RawPointer/*: Key*/,
value: __owned Value
) // where Key: TaskLocal

@available(SwiftStdlib 5.1, *)
@usableFromInline
@_silgen_name("swift_task_localValuePop")
func _taskLocalValuePop()

Expand Down
8 changes: 8 additions & 0 deletions stdlib/public/Concurrency/TaskLocal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
self.defaultValue = defaultValue
}

@_alwaysEmitIntoClient
var key: Builtin.RawPointer {
unsafeBitCast(self, to: Builtin.RawPointer.self)
}
Expand Down Expand Up @@ -134,7 +135,11 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
///
/// If the value is a reference type, it will be retained for the duration of
/// the operation closure.
@inlinable
@discardableResult
@_unsafeInheritExecutor
@available(SwiftStdlib 5.1, *) // back deploy requires we declare the availability explicitly on this method
@_backDeploy(before: SwiftStdlib 5.8)
public func withValue<R>(_ valueDuringOperation: Value, operation: () async throws -> R,
file: String = #fileID, line: UInt = #line) async rethrows -> R {
// check if we're not trying to bind a value from an illegal context; this may crash
Expand All @@ -159,6 +164,7 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
///
/// If the value is a reference type, it will be retained for the duration of
/// the operation closure.
@inlinable
@discardableResult
public func withValue<R>(_ valueDuringOperation: Value, operation: () throws -> R,
file: String = #fileID, line: UInt = #line) rethrows -> R {
Expand Down Expand Up @@ -212,13 +218,15 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
// ==== ------------------------------------------------------------------------

@available(SwiftStdlib 5.1, *)
@usableFromInline
@_silgen_name("swift_task_localValuePush")
func _taskLocalValuePush<Value>(
key: Builtin.RawPointer/*: Key*/,
value: __owned Value
) // where Key: TaskLocal

@available(SwiftStdlib 5.1, *)
@usableFromInline
@_silgen_name("swift_task_localValuePop")
func _taskLocalValuePop()

Expand Down
19 changes: 19 additions & 0 deletions test/Concurrency/Runtime/async_task_locals_basic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,24 @@ func withLocal_body_mustNotEscape() async {
_ = something // silence not used warning
}

@available(SwiftStdlib 5.1, *)
actor Worker {
@TaskLocal
static var declaredInActor: String = "<default-value>"

func setAndRead() async {
print("setAndRead") // CHECK: setAndRead
await Worker.$declaredInActor.withValue("value-1") {
await printTaskLocalAsync(Worker.$declaredInActor) // CHECK-NEXT: TaskLocal<String>(defaultValue: <default-value>) (value-1)
}
}
}

@available(SwiftStdlib 5.1, *)
func inside_actor() async {
await Worker().setAndRead()
}

@available(SwiftStdlib 5.1, *)
@main struct Main {
static func main() async {
Expand All @@ -210,5 +228,6 @@ func withLocal_body_mustNotEscape() async {
await nested_3_onlyTopContributes()
await nested_3_onlyTopContributesAsync()
await nested_3_onlyTopContributesMixed()
await inside_actor()
}
}
23 changes: 23 additions & 0 deletions test/Concurrency/async_task_locals_basic_warnings.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/OtherActors.swiftmodule -module-name OtherActors %S/Inputs/OtherActors.swift -disable-availability-checking
// RUN: %target-typecheck-verify-swift -I %t -disable-availability-checking -warn-concurrency -parse-as-library
// REQUIRES: concurrency

// REQUIRES: concurrency

@available(SwiftStdlib 5.1, *)
actor Test {

@TaskLocal static var local: Int?

func run() async {
// This should NOT produce any warnings, the closure withValue uses is @Sendable:
await Test.$local.withValue(42) {
await work()
}
}

func work() async {
print("Hello \(Test.local ?? 0)")
}
}