Skip to content

Commit 38cc951

Browse files
committed
[Concurrency] Stage in new Async{Throwing}Stream.init(unfolding:) errors
as warnings. Marking the closure parameter to these inits as `@Sendable` changed the inferred isolation of closure arguments in actor-isolated contexts, which caused new effects checker errors when accessing isolated properties and methods without `await`. Mark these `init`s as `@preconcurrency`, and fix the effects checker to downgrade those errors to warnings when the context of the call is `@preconcurrency`.
1 parent 425b805 commit 38cc951

File tree

4 files changed

+41
-24
lines changed

4 files changed

+41
-24
lines changed

lib/Sema/TypeCheckEffects.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,14 +1183,21 @@ class ApplyClassifier {
11831183
return Classification();
11841184
}
11851185

1186+
bool isContextPreconcurrency() const {
1187+
if (!DC)
1188+
return false;
1189+
1190+
return getActorIsolationOfContext(DC).preconcurrency();
1191+
}
1192+
11861193
/// Whether a missing 'await' error on accessing an async var should be
11871194
/// downgraded to a warning.
11881195
///
11891196
/// Missing 'await' errors are downgraded for synchronous access to isolated
11901197
/// global or static 'let' variables, which was previously accepted in
11911198
/// compiler versions before 5.10, or for declarations marked preconcurrency.
11921199
bool downgradeAsyncAccessToWarning(Decl *decl) {
1193-
if (decl->preconcurrency()) {
1200+
if (decl->preconcurrency() || isContextPreconcurrency()) {
11941201
return true;
11951202
}
11961203

@@ -1339,7 +1346,8 @@ class ApplyClassifier {
13391346

13401347
// Downgrade missing 'await' errors for preconcurrency references.
13411348
result.setDowngradeToWarning(
1342-
result.hasAsync() && fnRef.isPreconcurrency());
1349+
result.hasAsync() &&
1350+
(fnRef.isPreconcurrency() || isContextPreconcurrency()));
13431351

13441352
auto classifyApplyEffect = [&](EffectKind kind) {
13451353
if (!fnType->hasEffect(kind) &&

stdlib/public/Concurrency/AsyncStream.swift

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -330,21 +330,10 @@ public struct AsyncStream<Element> {
330330
/// }
331331
///
332332
///
333-
@_alwaysEmitIntoClient
333+
@preconcurrency
334334
public init(
335335
unfolding produce: @escaping @Sendable () async -> Element?,
336336
onCancel: (@Sendable () -> Void)? = nil
337-
) {
338-
self.init(
339-
unfolding: produce as () async -> Element?,
340-
onCancel: onCancel
341-
)
342-
}
343-
344-
@usableFromInline
345-
internal init(
346-
unfolding produce: @escaping () async -> Element?,
347-
onCancel: (@Sendable () -> Void)? = nil
348337
) {
349338
let storage: _AsyncStreamCriticalStorage<Optional<() async -> Element?>>
350339
= .create(produce)

stdlib/public/Concurrency/AsyncThrowingStream.swift

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -369,18 +369,9 @@ public struct AsyncThrowingStream<Element, Failure: Error> {
369369
/// print(error)
370370
/// }
371371
///
372-
@_alwaysEmitIntoClient
372+
@preconcurrency
373373
public init(
374374
unfolding produce: @escaping @Sendable () async throws -> Element?
375-
) where Failure == Error {
376-
self.init(
377-
unfolding: produce as () async throws -> Element?
378-
)
379-
}
380-
381-
@usableFromInline
382-
internal init(
383-
unfolding produce: @escaping () async throws -> Element?
384375
) where Failure == Error {
385376
let storage: _AsyncStreamCriticalStorage<Optional<() async throws -> Element?>>
386377
= .create(produce)

test/Concurrency/sendable_checking.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,32 @@ final class UseNonisolatedUnsafe: Sendable {
377377
}
378378
}
379379
}
380+
381+
@available(SwiftStdlib 5.1, *)
382+
@preconcurrency
383+
func preconcurrencyContext(_: @escaping @Sendable () -> Void) {}
384+
385+
@available(SwiftStdlib 5.1, *)
386+
@MainActor
387+
struct DowngradeForPreconcurrency {
388+
func capture(completion: @escaping @MainActor () -> Void) {
389+
preconcurrencyContext {
390+
Task {
391+
completion()
392+
// expected-warning@-1 2 {{capture of 'completion' with non-sendable type '@MainActor () -> Void' in a `@Sendable` closure; this is an error in the Swift 6 language mode}}
393+
// expected-note@-2 2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
394+
// expected-warning@-3 {{expression is 'async' but is not marked with 'await'; this is an error in the Swift 6 language mode}}
395+
// expected-note@-4 {{calls to parameter 'completion' from outside of its actor context are implicitly asynchronous}}
396+
}
397+
}
398+
}
399+
400+
var x: Int
401+
func createStream() -> AsyncStream<Int> {
402+
AsyncStream<Int> {
403+
self.x
404+
// expected-warning@-1 {{expression is 'async' but is not marked with 'await'; this is an error in the Swift 6 language mode}}
405+
// expected-note@-2 {{property access is 'async'}}
406+
}
407+
}
408+
}

0 commit comments

Comments
 (0)