Skip to content

Commit b2b22e7

Browse files
authored
Merge pull request swiftlang#71957 from DougGregor/failure-assoc-type-seq
Always infer AsyncSequence.Failure from AsyncIteratorProtocol.Failure
2 parents 499cfed + 0652bb7 commit b2b22e7

File tree

4 files changed

+124
-18
lines changed

4 files changed

+124
-18
lines changed

lib/AST/ASTPrinter.cpp

+34
Original file line numberDiff line numberDiff line change
@@ -6374,6 +6374,32 @@ void Decl::printInherited(ASTPrinter &Printer, const PrintOptions &Opts) const {
63746374
printer.printInherited(this);
63756375
}
63766376

6377+
/// Determine whether this typealias is an inferred typealias "Failure" that
6378+
/// would conflict with another entity named failure in the same type.
6379+
static bool isConflictingFailureTypeWitness(
6380+
const TypeAliasDecl *typealias) {
6381+
if (!typealias->isImplicit())
6382+
return false;
6383+
6384+
ASTContext &ctx = typealias->getASTContext();
6385+
if (typealias->getName() != ctx.Id_Failure)
6386+
return false;
6387+
6388+
auto nominal = typealias->getDeclContext()->getSelfNominalTypeDecl();
6389+
if (!nominal)
6390+
return false;
6391+
6392+
// Look for another entity with the same name.
6393+
auto lookupResults = nominal->lookupDirect(
6394+
typealias->getName(), typealias->getLoc());
6395+
for (auto found : lookupResults) {
6396+
if (found != typealias)
6397+
return true;
6398+
}
6399+
6400+
return false;
6401+
}
6402+
63776403
bool Decl::shouldPrintInContext(const PrintOptions &PO) const {
63786404
// Skip getters/setters. They are part of the variable or subscript.
63796405
if (isa<AccessorDecl>(this))
@@ -6413,6 +6439,14 @@ bool Decl::shouldPrintInContext(const PrintOptions &PO) const {
64136439
return PO.PrintIfConfig;
64146440
}
64156441

6442+
// Prior to Swift 6, we shouldn't print the inferred associated type
6443+
// witness for AsyncSequence.Failure. It is always determined from the
6444+
// AsyncIteratorProtocol witness.
6445+
if (auto typealias = dyn_cast<TypeAliasDecl>(this)) {
6446+
if (isConflictingFailureTypeWitness(typealias))
6447+
return false;
6448+
}
6449+
64166450
// Print everything else.
64176451
return true;
64186452
}

lib/Sema/AssociatedTypeInference.cpp

+43-18
Original file line numberDiff line numberDiff line change
@@ -389,13 +389,49 @@ static void recordTypeWitness(NormalProtocolConformance *conformance,
389389
}
390390
}
391391

392+
/// Determine whether this is the AsyncIteratorProtocol.Failure associated type.
393+
static bool isAsyncIteratorProtocolFailure(AssociatedTypeDecl *assocType) {
394+
auto proto = assocType->getProtocol();
395+
if (!proto->isSpecificProtocol(KnownProtocolKind::AsyncIteratorProtocol))
396+
return false;
397+
398+
return assocType->getName() == assocType->getASTContext().Id_Failure;
399+
}
400+
401+
/// Determine whether this is the AsyncSequence.Failure associated type.
402+
static bool isAsyncSequenceFailure(AssociatedTypeDecl *assocType) {
403+
auto proto = assocType->getProtocol();
404+
if (!proto->isSpecificProtocol(KnownProtocolKind::AsyncSequence))
405+
return false;
406+
407+
return assocType->getName() == assocType->getASTContext().Id_Failure;
408+
}
409+
410+
/// Determine whether this is the AsyncIteratorProtocol.Failure or
411+
/// AsyncSequence.Failure associated type.
412+
static bool isAsyncIteratorOrSequenceFailure(AssociatedTypeDecl *assocType) {
413+
auto proto = assocType->getProtocol();
414+
if (!proto->isSpecificProtocol(KnownProtocolKind::AsyncIteratorProtocol) &&
415+
!proto->isSpecificProtocol(KnownProtocolKind::AsyncSequence))
416+
return false;
417+
418+
return assocType->getName() == assocType->getASTContext().Id_Failure;
419+
}
420+
392421
/// Attempt to resolve a type witness via member name lookup.
393422
static ResolveWitnessResult resolveTypeWitnessViaLookup(
394423
NormalProtocolConformance *conformance,
395424
AssociatedTypeDecl *assocType) {
396425
auto *dc = conformance->getDeclContext();
397426
auto &ctx = dc->getASTContext();
398427

428+
// Prior to Swift 6, don't look for a named type witness for
429+
// AsyncSequence.Failure. We'll always infer it from
430+
// AsyncIteratorProtocol.Failure.
431+
if (isAsyncSequenceFailure(assocType) &&
432+
!ctx.LangOpts.isSwiftVersionAtLeast(6))
433+
return ResolveWitnessResult::Missing;
434+
399435
// Conformances constructed by the ClangImporter should have explicit type
400436
// witnesses already.
401437
if (isa<ClangModuleUnit>(dc->getModuleScopeContext())) {
@@ -1816,25 +1852,15 @@ next_witness:;
18161852
return result;
18171853
}
18181854

1819-
/// Determine whether this is AsyncIteratorProtocol.Failure or
1820-
/// AsyncSequenceProtoco.Failure associated type.
1821-
static bool isAsyncIteratorProtocolFailure(AssociatedTypeDecl *assocType) {
1822-
auto proto = assocType->getProtocol();
1823-
if (!proto->isSpecificProtocol(KnownProtocolKind::AsyncIteratorProtocol) &&
1824-
!proto->isSpecificProtocol(KnownProtocolKind::AsyncSequence))
1825-
return false;
1826-
1827-
return assocType->getName() == assocType->getASTContext().Id_Failure;
1828-
}
1829-
18301855
/// Determine whether this is AsyncIteratorProtocol.next() function.
18311856
static bool isAsyncIteratorProtocolNext(ValueDecl *req) {
18321857
auto proto = dyn_cast<ProtocolDecl>(req->getDeclContext());
18331858
if (!proto ||
18341859
!proto->isSpecificProtocol(KnownProtocolKind::AsyncIteratorProtocol))
18351860
return false;
18361861

1837-
return req->getName().getBaseName() == req->getASTContext().Id_next;
1862+
return req->getName().getBaseName() == req->getASTContext().Id_next &&
1863+
req->getName().getArgumentNames().empty();
18381864
}
18391865

18401866
InferredAssociatedTypes
@@ -2537,8 +2563,7 @@ std::optional<AbstractTypeWitness>
25372563
AssociatedTypeInference::computeFailureTypeWitness(
25382564
AssociatedTypeDecl *assocType,
25392565
ArrayRef<std::pair<ValueDecl *, ValueDecl *>> valueWitnesses) const {
2540-
// Inference only applies to AsyncIteratorProtocol.Failure and
2541-
// AsyncSequence.Failure.
2566+
// Inference only applies to AsyncIteratorProtocol.Failure.
25422567
if (!isAsyncIteratorProtocolFailure(assocType))
25432568
return std::nullopt;
25442569

@@ -2582,7 +2607,7 @@ AssociatedTypeInference::computeDefaultTypeWitness(
25822607
AssociatedTypeDecl *assocType) const {
25832608
// Ignore the default for AsyncIteratorProtocol.Failure and
25842609
// AsyncSequence.Failure.
2585-
if (isAsyncIteratorProtocolFailure(assocType))
2610+
if (isAsyncIteratorOrSequenceFailure(assocType))
25862611
return std::nullopt;
25872612

25882613
// Go find a default definition.
@@ -2683,8 +2708,8 @@ AssociatedTypeInference::computeAbstractTypeWitness(
26832708

26842709
// Don't consider the generic parameter names for AsyncSequence.Failure or
26852710
// AsyncIteratorProtocol.Failure; we always rely on inference from next() or
2686-
// next(_:).
2687-
if (isAsyncIteratorProtocolFailure(assocType)) {
2711+
// next(isolation:).
2712+
if (isAsyncIteratorOrSequenceFailure(assocType)) {
26882713
// If this is specifically AsyncSequence.Failure with the older associated
26892714
// type inference implementation, our abstract witness is
26902715
// "AsyncIterator.Failure". The new implementation is smart enough to do
@@ -2727,7 +2752,7 @@ Type AssociatedTypeInference::computeGenericParamWitness(
27272752
if (auto genericSig = dc->getGenericSignatureOfContext()) {
27282753
// Ignore the generic parameters for AsyncIteratorProtocol.Failure and
27292754
// AsyncSequence.Failure.
2730-
if (!isAsyncIteratorProtocolFailure(assocType)) {
2755+
if (!isAsyncIteratorOrSequenceFailure(assocType)) {
27312756
for (auto *gp : genericSig.getInnermostGenericParams()) {
27322757
// Packs cannot witness associated type requirements.
27332758
if (gp->isParameterPack())

test/Concurrency/async_sequence_syntax.swift

+20
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,23 @@ func forTryAwaitReturningExistentialType() async throws {
9999
for try await _ in S().seq() { // Ok
100100
}
101101
}
102+
103+
@available(SwiftStdlib 5.1, *)
104+
public struct ReaderSeq: AsyncSequence, Sendable {
105+
public enum Failure: Error {
106+
case x
107+
}
108+
109+
public typealias Element = Int
110+
111+
public func makeAsyncIterator() -> Reader {}
112+
113+
public actor Reader: AsyncIteratorProtocol {
114+
public func next() async throws -> Element? {}
115+
}
116+
}
117+
118+
@available(SwiftStdlib 5.1, *)
119+
func test1() -> Error {
120+
return ReaderSeq.Failure.x
121+
}

test/ModuleInterface/async_sequence_conformance.swift

+27
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,30 @@ public struct SequenceAdapter<Base: AsyncSequence>: AsyncSequence {
2525
// CHECK: @available(
2626
// CHECK-NEXT: public typealias Failure = Base.Failure
2727
}
28+
29+
// CHECK: @available(
30+
// CHECK-NEXT: public struct OtherSequenceAdapte
31+
@available(SwiftStdlib 5.1, *)
32+
public struct OtherSequenceAdapter<Base: AsyncSequence>: AsyncSequence {
33+
// CHECK: public typealias Element = Base.Element
34+
// CHECK-NOT: public typealias Failure
35+
// CHECK: public struct Failure
36+
37+
// CHECK-LABEL: public struct AsyncIterator
38+
// CHECK: @available{{.*}}macOS 10.15
39+
// CHECK: @available(
40+
// CHECK-NEXT: public typealias Failure = Base.Failure
41+
public typealias Element = Base.Element
42+
43+
public struct Failure: Error { }
44+
45+
// CHECK-NOT: public typealias Failure
46+
public struct AsyncIterator: AsyncIteratorProtocol {
47+
public mutating func next() async rethrows -> Base.Element? { nil }
48+
}
49+
50+
// CHECK: public func makeAsyncIterator
51+
public func makeAsyncIterator() -> AsyncIterator { AsyncIterator() }
52+
53+
// CHECK-NOT: public typealias Failure
54+
}

0 commit comments

Comments
 (0)