Skip to content

Commit dc85ae3

Browse files
committed
Choose between AsyncIteratorProtocol's next() and nextElement() based on availability
This allows us to not break backward deployment
1 parent a8f60a5 commit dc85ae3

File tree

10 files changed

+71
-9
lines changed

10 files changed

+71
-9
lines changed

include/swift/AST/ASTContext.h

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

653+
/// Get AsyncIteratorProtocol.nextElement().
654+
FuncDecl *getAsyncIteratorNextElement() const;
655+
653656
/// Check whether the standard library provides all the correct
654657
/// intrinsic support for Optional<T>.
655658
///

lib/AST/ASTContext.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +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;
292+
290293
/// The declaration of Swift.Optional<T>.Some.
291294
EnumElementDecl *OptionalSomeDecl = nullptr;
292295

@@ -957,13 +960,25 @@ FuncDecl *ASTContext::getAsyncIteratorNext() const {
957960
if (!proto)
958961
return nullptr;
959962

960-
if (auto *func = lookupRequirement(proto, Id_nextElement)) {
963+
if (auto *func = lookupRequirement(proto, Id_next)) {
961964
getImpl().AsyncIteratorNext = func;
962965
return func;
963966
}
964967

965-
if (auto *func = lookupRequirement(proto, Id_next)) {
966-
getImpl().AsyncIteratorNext = func;
968+
return nullptr;
969+
}
970+
971+
FuncDecl *ASTContext::getAsyncIteratorNextElement() const {
972+
if (getImpl().AsyncIteratorNextElement) {
973+
return getImpl().AsyncIteratorNextElement;
974+
}
975+
976+
auto proto = getProtocol(KnownProtocolKind::AsyncIteratorProtocol);
977+
if (!proto)
978+
return nullptr;
979+
980+
if (auto *func = lookupRequirement(proto, Id_nextElement)) {
981+
getImpl().AsyncIteratorNextElement = func;
967982
return func;
968983
}
969984

lib/AST/Availability.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,7 @@ ASTContext::getSwift5PlusAvailability(llvm::VersionTuple swiftVersion) {
819819
case 7: return getSwift57Availability();
820820
case 8: return getSwift58Availability();
821821
case 9: return getSwift59Availability();
822+
case 11: return getSwift511Availability();
822823
default: break;
823824
}
824825
}

lib/Sema/CSGen.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4592,8 +4592,8 @@ generateForEachStmtConstraints(ConstraintSystem &cs, DeclContext *dc,
45924592
// Now, result type of `.makeIterator()` is used to form a call to
45934593
// `.next()`. `next()` is called on each iteration of the loop.
45944594
{
4595-
FuncDecl *nextFn = isAsync ? ctx.getAsyncIteratorNext()
4596-
: ctx.getIteratorNext();
4595+
FuncDecl *nextFn =
4596+
TypeChecker::getForEachIteratorNextFunction(dc, stmt->getForLoc(), isAsync);
45974597
Identifier nextId = nextFn ? nextFn->getName().getBaseIdentifier()
45984598
: ctx.Id_next;
45994599
auto *nextRef = UnresolvedDotExpr::createImplicit(

lib/Sema/CSSimplify.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10712,8 +10712,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
1071210712
bool isAsync = iteratorProto->getKnownProtocolKind() ==
1071310713
KnownProtocolKind::AsyncIteratorProtocol;
1071410714

10715-
auto *next =
10716-
isAsync ? ctx.getAsyncIteratorNext() : ctx.getIteratorNext();
10715+
auto loc = locator->getAnchor().getStartLoc();
10716+
auto *next = TypeChecker::getForEachIteratorNextFunction(DC, loc, isAsync);
1071710717

1071810718
return simplifyValueWitnessConstraint(
1071910719
ConstraintKind::ValueWitness, baseTy, next, memberTy, useDC,

lib/Sema/TypeCheckStmt.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3227,3 +3227,32 @@ void swift::bindSwitchCasePatternVars(DeclContext *dc, CaseStmt *caseStmt) {
32273227
recordVar(nullptr, bodyVar);
32283228
}
32293229
}
3230+
3231+
FuncDecl *TypeChecker::getForEachIteratorNextFunction(
3232+
DeclContext *dc, SourceLoc loc, bool isAsync
3233+
) {
3234+
ASTContext &ctx = dc->getASTContext();
3235+
3236+
// A synchronous for..in loop uses IteratorProtocol.next().
3237+
if (!isAsync)
3238+
return ctx.getIteratorNext();
3239+
3240+
// If AsyncIteratorProtocol.nextElement() isn't available at all,
3241+
// we're stuck using AsyncIteratorProtocol.next().
3242+
auto nextElement = ctx.getAsyncIteratorNextElement();
3243+
if (!nextElement)
3244+
return ctx.getAsyncIteratorNext();
3245+
3246+
// If availability checking is disabled, use nextElement().
3247+
if (ctx.LangOpts.DisableAvailabilityChecking || loc.isInvalid())
3248+
return nextElement;
3249+
3250+
// We can only call nextElement() if we are in an availability context
3251+
// that supports typed throws.
3252+
auto availability = overApproximateAvailabilityAtLocation(loc, dc);
3253+
if (availability.isContainedIn(ctx.getTypedThrowsAvailability()))
3254+
return nextElement;
3255+
3256+
// Fall back to AsyncIteratorProtocol.next().
3257+
return ctx.getAsyncIteratorNext();
3258+
}

lib/Sema/TypeChecker.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,11 @@ Type catchErrorType(DeclContext *dc, DoCatchStmt *stmt);
11691169
Type errorUnion(Type type1, Type type2,
11701170
llvm::function_ref<Type(Type)> simplifyType);
11711171

1172+
/// Retrieve the "next" function that should be used for iteration in a
1173+
/// for..in loop.
1174+
FuncDecl *getForEachIteratorNextFunction(
1175+
DeclContext *dc, SourceLoc loc, bool isAsync);
1176+
11721177
/// If an expression references 'self.init' or 'super.init' in an
11731178
/// initializer context, returns the implicit 'self' decl of the constructor.
11741179
/// Otherwise, return nil.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %target-swift-frontend -strict-concurrency=complete -dump-ast %s | %FileCheck %s
2+
// REQUIRES: concurrency, OS=macosx
3+
4+
@available(SwiftStdlib 5.1, *)
5+
func f<S: AsyncSequence>(s: S) async throws {
6+
// CHECK-NOT: nextElement()
7+
// CHECK: next()
8+
for try await x in s { }
9+
}

test/Concurrency/async_iterator_inference.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -strict-concurrency=complete -emit-sil -o /dev/null %s -verify
1+
// RUN: %target-swift-frontend -strict-concurrency=complete -emit-sil -o /dev/null %s -verify -disable-availability-checking
22
// REQUIRES: concurrency
33

44
@available(SwiftStdlib 5.1, *)

test/IRGen/async/weak_availability.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ public func f<S: AsyncSequence>(_ s: S) async throws -> Any.Type {
99
return Fn.self
1010
}
1111

12-
// MAYBE-AVAILABLE: @"$sScI11nextElement0B0QzSgyYa7FailureQzYKFTjTu" = extern_weak global
12+
// MAYBE-AVAILABLE: @"$sScI{{.*next.*}}YaKFTjTu" = extern_weak global
1313
// MAYBE-AVAILABLE: declare{{.*}} extern_weak{{.*}} @swift_getFunctionTypeMetadataGlobalActor

0 commit comments

Comments
 (0)