Skip to content

Commit 6ebb0ff

Browse files
committed
Replace AsyncIteratorProtocol.nextElement() with isolated next(_:)
Use an optional isolated parameter to this new `next(_:)` overload to keep it on the same actor as the caller, and pass `#isolation` when desugaring the async for..in loop. This keeps async iteration loops on the same actor, allowing non-Sendable values to be used with many async sequences.
1 parent 3558237 commit 6ebb0ff

29 files changed

+140
-116
lines changed

include/swift/AST/ASTContext.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -650,8 +650,8 @@ class ASTContext final {
650650
/// Get AsyncIteratorProtocol.next().
651651
FuncDecl *getAsyncIteratorNext() const;
652652

653-
/// Get AsyncIteratorProtocol.nextElement().
654-
FuncDecl *getAsyncIteratorNextElement() const;
653+
/// Get AsyncIteratorProtocol.next(actor).
654+
FuncDecl *getAsyncIteratorNextIsolated() const;
655655

656656
/// Check whether the standard library provides all the correct
657657
/// intrinsic support for Optional<T>.

include/swift/AST/KnownIdentifiers.def

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ IDENTIFIER(load)
123123
IDENTIFIER(main)
124124
IDENTIFIER_WITH_NAME(MainEntryPoint, "$main")
125125
IDENTIFIER(next)
126-
IDENTIFIER(nextElement)
127126
IDENTIFIER_(nsErrorDomain)
128127
IDENTIFIER(objectAtIndexedSubscript)
129128
IDENTIFIER(objectForKeyedSubscript)

lib/AST/ASTContext.cpp

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,9 @@ struct ASTContext::Implementation {
287287
/// The declaration of 'AsyncIteratorProtocol.next()'.
288288
FuncDecl *AsyncIteratorNext = nullptr;
289289

290-
/// The declaration of 'AsyncIteratorProtocol.nextElement()'.
291-
FuncDecl *AsyncIteratorNextElement = nullptr;
290+
/// The declaration of 'AsyncIteratorProtocol.next(_:)' that takes
291+
/// an actor isolation.
292+
FuncDecl *AsyncIteratorNextIsolated = nullptr;
292293

293294
/// The declaration of Swift.Optional<T>.Some.
294295
EnumElementDecl *OptionalSomeDecl = nullptr;
@@ -951,38 +952,48 @@ FuncDecl *ASTContext::getIteratorNext() const {
951952
return nullptr;
952953
}
953954

954-
FuncDecl *ASTContext::getAsyncIteratorNext() const {
955-
if (getImpl().AsyncIteratorNext) {
956-
return getImpl().AsyncIteratorNext;
957-
}
958-
959-
auto proto = getProtocol(KnownProtocolKind::AsyncIteratorProtocol);
955+
static std::pair<FuncDecl *, FuncDecl *>
956+
getAsyncIteratorNextRequirements(const ASTContext &ctx) {
957+
auto proto = ctx.getProtocol(KnownProtocolKind::AsyncIteratorProtocol);
960958
if (!proto)
961-
return nullptr;
959+
return { nullptr, nullptr };
962960

963-
if (auto *func = lookupRequirement(proto, Id_next)) {
964-
getImpl().AsyncIteratorNext = func;
965-
return func;
961+
FuncDecl *next = nullptr;
962+
FuncDecl *nextThrowing = nullptr;
963+
for (auto result : proto->lookupDirect(ctx.Id_next)) {
964+
if (result->getDeclContext() != proto)
965+
continue;
966+
967+
if (auto func = dyn_cast<FuncDecl>(result)) {
968+
switch (func->getParameters()->size()) {
969+
case 0: next = func; break;
970+
case 1: nextThrowing = func; break;
971+
default: break;
972+
}
973+
}
966974
}
967975

968-
return nullptr;
976+
return { next, nextThrowing };
969977
}
970978

971-
FuncDecl *ASTContext::getAsyncIteratorNextElement() const {
972-
if (getImpl().AsyncIteratorNextElement) {
973-
return getImpl().AsyncIteratorNextElement;
979+
FuncDecl *ASTContext::getAsyncIteratorNext() const {
980+
if (getImpl().AsyncIteratorNext) {
981+
return getImpl().AsyncIteratorNext;
974982
}
975983

976-
auto proto = getProtocol(KnownProtocolKind::AsyncIteratorProtocol);
977-
if (!proto)
978-
return nullptr;
984+
auto next = getAsyncIteratorNextRequirements(*this).first;
985+
getImpl().AsyncIteratorNext = next;
986+
return next;
987+
}
979988

980-
if (auto *func = lookupRequirement(proto, Id_nextElement)) {
981-
getImpl().AsyncIteratorNextElement = func;
982-
return func;
989+
FuncDecl *ASTContext::getAsyncIteratorNextIsolated() const {
990+
if (getImpl().AsyncIteratorNextIsolated) {
991+
return getImpl().AsyncIteratorNextIsolated;
983992
}
984993

985-
return nullptr;
994+
auto nextThrowing = getAsyncIteratorNextRequirements(*this).second;
995+
getImpl().AsyncIteratorNextIsolated = nextThrowing;
996+
return nextThrowing;
986997
}
987998

988999
#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \

lib/Sema/CSGen.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4603,7 +4603,15 @@ generateForEachStmtConstraints(ConstraintSystem &cs, DeclContext *dc,
46034603
nextId, /*labels=*/ArrayRef<Identifier>());
46044604
nextRef->setFunctionRefKind(FunctionRefKind::SingleApply);
46054605

4606-
Expr *nextCall = CallExpr::createImplicitEmpty(ctx, nextRef);
4606+
ArgumentList *nextArgs;
4607+
if (nextFn && nextFn->getParameters()->size() == 1) {
4608+
auto isolationArg =
4609+
new (ctx) CurrentContextIsolationExpr(stmt->getForLoc(), Type());
4610+
nextArgs = ArgumentList::forImplicitUnlabeled(ctx, { isolationArg });
4611+
} else {
4612+
nextArgs = ArgumentList::createImplicit(ctx, {});
4613+
}
4614+
Expr *nextCall = CallExpr::createImplicit(ctx, nextRef, nextArgs);
46074615

46084616
// `next` is always async but witness might not be throwing
46094617
if (isAsync) {

lib/Sema/CSSimplify.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10705,7 +10705,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
1070510705
// Handle `next` reference.
1070610706
if (getContextualTypePurpose(baseExpr) == CTP_ForEachSequence &&
1070710707
(isRefTo(memberRef, ctx.Id_next, /*labels=*/{}) ||
10708-
isRefTo(memberRef, ctx.Id_nextElement, /*labels=*/{}))) {
10708+
isRefTo(memberRef, ctx.Id_next, /*labels=*/{StringRef()}))) {
1070910709
auto *iteratorProto = cast<ProtocolDecl>(
1071010710
getContextualType(baseExpr, /*forConstraint=*/false)
1071110711
->getAnyNominal());
@@ -10931,8 +10931,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
1093110931
if (auto *base = dyn_cast<DeclRefExpr>(UDE->getBase())) {
1093210932
if (auto var = dyn_cast_or_null<VarDecl>(base->getDecl())) {
1093310933
if (var->getNameStr().contains("$generator") &&
10934-
(UDE->getName().getBaseIdentifier() == Context.Id_next ||
10935-
UDE->getName().getBaseIdentifier() == Context.Id_nextElement))
10934+
(UDE->getName().getBaseIdentifier() == Context.Id_next))
1093610935
return success();
1093710936
}
1093810937
}

lib/Sema/TypeCheckEffects.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1335,7 +1335,7 @@ class ApplyClassifier {
13351335
isRethrowLikeTypedThrows(fnRef.getFunction())) {
13361336
// If we are in a rethrowing context and the function we're referring
13371337
// to is a rethrow-like function using typed throws or we are
1338-
// calling the next() or nextElement() of an async iterator,
1338+
// calling the next() or next(_:) of an async iterator,
13391339
// then look at all of the closure arguments.
13401340
LLVM_FALLTHROUGH;
13411341
} else {

lib/Sema/TypeCheckStmt.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3237,17 +3237,17 @@ FuncDecl *TypeChecker::getForEachIteratorNextFunction(
32373237
if (!isAsync)
32383238
return ctx.getIteratorNext();
32393239

3240-
// If AsyncIteratorProtocol.nextElement() isn't available at all,
3240+
// If AsyncIteratorProtocol.next(_:) isn't available at all,
32413241
// we're stuck using AsyncIteratorProtocol.next().
3242-
auto nextElement = ctx.getAsyncIteratorNextElement();
3242+
auto nextElement = ctx.getAsyncIteratorNextIsolated();
32433243
if (!nextElement)
32443244
return ctx.getAsyncIteratorNext();
32453245

3246-
// If availability checking is disabled, use nextElement().
3246+
// If availability checking is disabled, use next(_:).
32473247
if (ctx.LangOpts.DisableAvailabilityChecking || loc.isInvalid())
32483248
return nextElement;
32493249

3250-
// We can only call nextElement() if we are in an availability context
3250+
// We can only call next(_:) if we are in an availability context
32513251
// that supports typed throws.
32523252
auto availability = overApproximateAvailabilityAtLocation(loc, dc);
32533253
if (availability.isContainedIn(ctx.getTypedThrowsAvailability()))

stdlib/public/Concurrency/AsyncCompactMapSequence.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,9 @@ extension AsyncCompactMapSequence: AsyncSequence {
140140
/// that transforms to a non-`nil` value.
141141
@available(SwiftStdlib 5.11, *)
142142
@inlinable
143-
public mutating func nextElement() async throws(Failure) -> ElementOfResult? {
143+
public mutating func next(_ actor: isolated (any Actor)?) async throws(Failure) -> ElementOfResult? {
144144
while true {
145-
guard let element = try await baseIterator.nextElement() else {
145+
guard let element = try await baseIterator.next(actor) else {
146146
return nil
147147
}
148148

stdlib/public/Concurrency/AsyncDropFirstSequence.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,24 +118,24 @@ extension AsyncDropFirstSequence: AsyncSequence {
118118
/// Produces the next element in the drop-first sequence.
119119
///
120120
/// Until reaching the number of elements to drop, this iterator calls
121-
/// `nextElement()` on its base iterator and discards the result. If the
121+
/// `next(_:)` on its base iterator and discards the result. If the
122122
/// base iterator returns `nil`, indicating the end of the sequence, this
123123
/// iterator returns `nil`. After reaching the number of elements to drop,
124-
/// this iterator passes along the result of calling `nextElement()` on the
124+
/// this iterator passes along the result of calling `next(_:)` on the
125125
/// base iterator.
126126
@available(SwiftStdlib 5.11, *)
127127
@inlinable
128-
public mutating func nextElement() async throws(Failure) -> Base.Element? {
128+
public mutating func next(_ actor: isolated (any Actor)?) async throws(Failure) -> Base.Element? {
129129
var remainingToDrop = count
130130
while remainingToDrop > 0 {
131-
guard try await baseIterator.nextElement() != nil else {
131+
guard try await baseIterator.next(actor) != nil else {
132132
count = 0
133133
return nil
134134
}
135135
remainingToDrop -= 1
136136
}
137137
count = 0
138-
return try await baseIterator.nextElement()
138+
return try await baseIterator.next(actor)
139139
}
140140
}
141141

stdlib/public/Concurrency/AsyncDropWhileSequence.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ extension AsyncDropWhileSequence: AsyncSequence {
129129

130130
/// Produces the next element in the drop-while sequence.
131131
///
132-
/// This iterator calls `nextElement()` on its base iterator and evaluates
132+
/// This iterator calls `next(_:)` on its base iterator and evaluates
133133
/// the result with the `predicate` closure. As long as the predicate
134134
/// returns `true`, this method returns `nil`. After the predicate returns
135135
/// `false`, for a value received from the base iterator, this method
@@ -138,17 +138,17 @@ extension AsyncDropWhileSequence: AsyncSequence {
138138
/// again.
139139
@available(SwiftStdlib 5.11, *)
140140
@inlinable
141-
public mutating func nextElement() async throws(Failure) -> Base.Element? {
141+
public mutating func next(_ actor: isolated (any Actor)?) async throws(Failure) -> Base.Element? {
142142
while let predicate = self.predicate {
143-
guard let element = try await baseIterator.nextElement() else {
143+
guard let element = try await baseIterator.next(actor) else {
144144
return nil
145145
}
146146
if await predicate(element) == false {
147147
self.predicate = nil
148148
return element
149149
}
150150
}
151-
return try await baseIterator.nextElement()
151+
return try await baseIterator.next(actor)
152152
}
153153
}
154154

0 commit comments

Comments
 (0)