Skip to content

Commit 4c990dc

Browse files
committed
Introduce an egregious source-compatibility hack for AsyncSequence.flatMap
Allow `AsyncSequence.flatMap` to be defined with "incorrect" availability, meaning that the function can refer to the `Failure` associated type in its where clause even though the function is back-deployed to before the `Failure` associated type was introduced. We believe this is safe, and that this notion can be generalized to any use of an associated type in a same-type constraint of a function (yes, it sounds weird), but for now introduce a narrower hack to see how things work in practice and whether it addresses all of the source-compatibility concerns we've uncovered.
1 parent a3ae340 commit 4c990dc

File tree

3 files changed

+47
-3
lines changed

3 files changed

+47
-3
lines changed

lib/Sema/TypeCheckAccess.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -2014,6 +2014,25 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
20142014
Where.withReason(reason), flags);
20152015
}
20162016

2017+
/// Identify the AsyncSequence.flatMap set of functions from the
2018+
/// _Concurrency module.
2019+
static bool isAsyncSequenceFlatMap(const GenericContext *gc) {
2020+
auto func = dyn_cast<FuncDecl>(gc);
2021+
if (!func)
2022+
return false;
2023+
2024+
auto proto = func->getDeclContext()->getSelfProtocolDecl();
2025+
if (!proto ||
2026+
!proto->isSpecificProtocol(KnownProtocolKind::AsyncSequence))
2027+
return false;
2028+
2029+
ASTContext &ctx = proto->getASTContext();
2030+
if (func->getModuleContext()->getName() != ctx.Id_Concurrency)
2031+
return false;
2032+
2033+
return !func->getName().isSimpleName("flatMap");
2034+
}
2035+
20172036
void checkGenericParams(const GenericContext *ownerCtx,
20182037
const ValueDecl *ownerDecl) {
20192038
if (!ownerCtx->isGenericContext())
@@ -2031,6 +2050,13 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
20312050
}
20322051

20332052
if (ownerCtx->getTrailingWhereClause()) {
2053+
// Ignore the where clause for AsyncSequence.flatMap from the
2054+
// _Concurrency module. This is an egregious hack to allow us to
2055+
// use overloading tricks to retain the behavior previously
2056+
// afforded by rethrowing conformances.
2057+
if (isAsyncSequenceFlatMap(ownerCtx))
2058+
return;
2059+
20342060
forAllRequirementTypes(WhereClauseOwner(
20352061
const_cast<GenericContext *>(ownerCtx)),
20362062
[&](Type type, TypeRepr *typeRepr) {

stdlib/public/Concurrency/AsyncFlatMapSequence.swift

-3
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ extension AsyncSequence {
7474
@preconcurrency
7575
@_alwaysEmitIntoClient
7676
@inlinable
77-
@available(SwiftStdlib 5.11, *)
7877
public __consuming func flatMap<SegmentOfResult: AsyncSequence>(
7978
_ transform: @Sendable @escaping (Element) async -> SegmentOfResult
8079
) -> AsyncFlatMapSequence<Self, SegmentOfResult>
@@ -111,7 +110,6 @@ extension AsyncSequence {
111110
@preconcurrency
112111
@_alwaysEmitIntoClient
113112
@inlinable
114-
@available(SwiftStdlib 5.11, *)
115113
public __consuming func flatMap<SegmentOfResult: AsyncSequence>(
116114
_ transform: @Sendable @escaping (Element) async -> SegmentOfResult
117115
) -> AsyncFlatMapSequence<Self, SegmentOfResult>
@@ -148,7 +146,6 @@ extension AsyncSequence {
148146
@preconcurrency
149147
@_alwaysEmitIntoClient
150148
@inlinable
151-
@available(SwiftStdlib 5.11, *)
152149
public __consuming func flatMap<SegmentOfResult: AsyncSequence>(
153150
_ transform: @Sendable @escaping (Element) async -> SegmentOfResult
154151
) -> AsyncFlatMapSequence<Self, SegmentOfResult>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-swift-frontend -typecheck %s -verify
2+
3+
// REQUIRES: concurrency
4+
5+
@available(SwiftStdlib 5.1, *)
6+
struct MyAsyncSequence<Element>: AsyncSequence {
7+
struct AsyncIterator: AsyncIteratorProtocol {
8+
mutating func next() -> Element? { nil }
9+
}
10+
11+
func makeAsyncIterator() -> AsyncIterator { .init() }
12+
}
13+
14+
@available(SwiftStdlib 5.1, *)
15+
func testMe(ms: MyAsyncSequence<String>) {
16+
let flatMS = ms.flatMap { string in
17+
return MyAsyncSequence<[Character]>()
18+
}
19+
20+
let _: AsyncFlatMapSequence<MyAsyncSequence<String>, MyAsyncSequence<[Character]>> = flatMS
21+
}

0 commit comments

Comments
 (0)