mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Always infer AsyncSequence.Failure from AsyncIteratorProtocol.Failure
The newly-introduced associated type `AsyncSequence.Failure` must always be equivalent to the `Failure` type of the `AsyncIteratorProtocol`. If the `AsyncSequence` type itself defines a nested `Failure` type (say, for another purpose), associated type inference would pick it and reject the `AsyncSequence`, causing a source compatibility problem. Work around the issue in two ways. First, always infer the type witness for `AsyncSequence.Failure` from the type witness for `AsyncIteratorProtocol.Failure`, so they can't be out of sync. This means that we'll never even consider a nested `Failure` type in the `AsyncSequence`-conforming type. This hack only applies prior to Swift 6. Second, when we have inferred a `Failure` type and there is already something else called `Failure` within that same nominal type, don't print the inferred typelias into a module interface because it will cause a conflict. Fixes rdar://123543633.
This commit is contained in:
@@ -6374,6 +6374,32 @@ void Decl::printInherited(ASTPrinter &Printer, const PrintOptions &Opts) const {
|
||||
printer.printInherited(this);
|
||||
}
|
||||
|
||||
/// Determine whether this typealias is an inferred typealias "Failure" that
|
||||
/// would conflict with another entity named failure in the same type.
|
||||
static bool isConflictingFailureTypeWitness(
|
||||
const TypeAliasDecl *typealias) {
|
||||
if (!typealias->isImplicit())
|
||||
return false;
|
||||
|
||||
ASTContext &ctx = typealias->getASTContext();
|
||||
if (typealias->getName() != ctx.Id_Failure)
|
||||
return false;
|
||||
|
||||
auto nominal = typealias->getDeclContext()->getSelfNominalTypeDecl();
|
||||
if (!nominal)
|
||||
return false;
|
||||
|
||||
// Look for another entity with the same name.
|
||||
auto lookupResults = nominal->lookupDirect(
|
||||
typealias->getName(), typealias->getLoc());
|
||||
for (auto found : lookupResults) {
|
||||
if (found != typealias)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decl::shouldPrintInContext(const PrintOptions &PO) const {
|
||||
// Skip getters/setters. They are part of the variable or subscript.
|
||||
if (isa<AccessorDecl>(this))
|
||||
@@ -6413,6 +6439,14 @@ bool Decl::shouldPrintInContext(const PrintOptions &PO) const {
|
||||
return PO.PrintIfConfig;
|
||||
}
|
||||
|
||||
// Prior to Swift 6, we shouldn't print the inferred associated type
|
||||
// witness for AsyncSequence.Failure. It is always determined from the
|
||||
// AsyncIteratorProtocol witness.
|
||||
if (auto typealias = dyn_cast<TypeAliasDecl>(this)) {
|
||||
if (isConflictingFailureTypeWitness(typealias))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Print everything else.
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -389,6 +389,35 @@ static void recordTypeWitness(NormalProtocolConformance *conformance,
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine whether this is the AsyncIteratorProtocol.Failure associated type.
|
||||
static bool isAsyncIteratorProtocolFailure(AssociatedTypeDecl *assocType) {
|
||||
auto proto = assocType->getProtocol();
|
||||
if (!proto->isSpecificProtocol(KnownProtocolKind::AsyncIteratorProtocol))
|
||||
return false;
|
||||
|
||||
return assocType->getName() == assocType->getASTContext().Id_Failure;
|
||||
}
|
||||
|
||||
/// Determine whether this is the AsyncSequence.Failure associated type.
|
||||
static bool isAsyncSequenceFailure(AssociatedTypeDecl *assocType) {
|
||||
auto proto = assocType->getProtocol();
|
||||
if (!proto->isSpecificProtocol(KnownProtocolKind::AsyncSequence))
|
||||
return false;
|
||||
|
||||
return assocType->getName() == assocType->getASTContext().Id_Failure;
|
||||
}
|
||||
|
||||
/// Determine whether this is the AsyncIteratorProtocol.Failure or
|
||||
/// AsyncSequence.Failure associated type.
|
||||
static bool isAsyncIteratorOrSequenceFailure(AssociatedTypeDecl *assocType) {
|
||||
auto proto = assocType->getProtocol();
|
||||
if (!proto->isSpecificProtocol(KnownProtocolKind::AsyncIteratorProtocol) &&
|
||||
!proto->isSpecificProtocol(KnownProtocolKind::AsyncSequence))
|
||||
return false;
|
||||
|
||||
return assocType->getName() == assocType->getASTContext().Id_Failure;
|
||||
}
|
||||
|
||||
/// Attempt to resolve a type witness via member name lookup.
|
||||
static ResolveWitnessResult resolveTypeWitnessViaLookup(
|
||||
NormalProtocolConformance *conformance,
|
||||
@@ -396,6 +425,13 @@ static ResolveWitnessResult resolveTypeWitnessViaLookup(
|
||||
auto *dc = conformance->getDeclContext();
|
||||
auto &ctx = dc->getASTContext();
|
||||
|
||||
// Prior to Swift 6, don't look for a named type witness for
|
||||
// AsyncSequence.Failure. We'll always infer it from
|
||||
// AsyncIteratorProtocol.Failure.
|
||||
if (isAsyncSequenceFailure(assocType) &&
|
||||
!ctx.LangOpts.isSwiftVersionAtLeast(6))
|
||||
return ResolveWitnessResult::Missing;
|
||||
|
||||
// Conformances constructed by the ClangImporter should have explicit type
|
||||
// witnesses already.
|
||||
if (isa<ClangModuleUnit>(dc->getModuleScopeContext())) {
|
||||
@@ -1816,17 +1852,6 @@ next_witness:;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Determine whether this is AsyncIteratorProtocol.Failure or
|
||||
/// AsyncSequenceProtoco.Failure associated type.
|
||||
static bool isAsyncIteratorProtocolFailure(AssociatedTypeDecl *assocType) {
|
||||
auto proto = assocType->getProtocol();
|
||||
if (!proto->isSpecificProtocol(KnownProtocolKind::AsyncIteratorProtocol) &&
|
||||
!proto->isSpecificProtocol(KnownProtocolKind::AsyncSequence))
|
||||
return false;
|
||||
|
||||
return assocType->getName() == assocType->getASTContext().Id_Failure;
|
||||
}
|
||||
|
||||
/// Determine whether this is AsyncIteratorProtocol.next() function.
|
||||
static bool isAsyncIteratorProtocolNext(ValueDecl *req) {
|
||||
auto proto = dyn_cast<ProtocolDecl>(req->getDeclContext());
|
||||
@@ -1834,7 +1859,8 @@ static bool isAsyncIteratorProtocolNext(ValueDecl *req) {
|
||||
!proto->isSpecificProtocol(KnownProtocolKind::AsyncIteratorProtocol))
|
||||
return false;
|
||||
|
||||
return req->getName().getBaseName() == req->getASTContext().Id_next;
|
||||
return req->getName().getBaseName() == req->getASTContext().Id_next &&
|
||||
req->getName().getArgumentNames().empty();
|
||||
}
|
||||
|
||||
InferredAssociatedTypes
|
||||
@@ -2537,8 +2563,7 @@ std::optional<AbstractTypeWitness>
|
||||
AssociatedTypeInference::computeFailureTypeWitness(
|
||||
AssociatedTypeDecl *assocType,
|
||||
ArrayRef<std::pair<ValueDecl *, ValueDecl *>> valueWitnesses) const {
|
||||
// Inference only applies to AsyncIteratorProtocol.Failure and
|
||||
// AsyncSequence.Failure.
|
||||
// Inference only applies to AsyncIteratorProtocol.Failure.
|
||||
if (!isAsyncIteratorProtocolFailure(assocType))
|
||||
return std::nullopt;
|
||||
|
||||
@@ -2582,7 +2607,7 @@ AssociatedTypeInference::computeDefaultTypeWitness(
|
||||
AssociatedTypeDecl *assocType) const {
|
||||
// Ignore the default for AsyncIteratorProtocol.Failure and
|
||||
// AsyncSequence.Failure.
|
||||
if (isAsyncIteratorProtocolFailure(assocType))
|
||||
if (isAsyncIteratorOrSequenceFailure(assocType))
|
||||
return std::nullopt;
|
||||
|
||||
// Go find a default definition.
|
||||
@@ -2683,8 +2708,8 @@ AssociatedTypeInference::computeAbstractTypeWitness(
|
||||
|
||||
// Don't consider the generic parameter names for AsyncSequence.Failure or
|
||||
// AsyncIteratorProtocol.Failure; we always rely on inference from next() or
|
||||
// next(_:).
|
||||
if (isAsyncIteratorProtocolFailure(assocType)) {
|
||||
// next(isolation:).
|
||||
if (isAsyncIteratorOrSequenceFailure(assocType)) {
|
||||
// If this is specifically AsyncSequence.Failure with the older associated
|
||||
// type inference implementation, our abstract witness is
|
||||
// "AsyncIterator.Failure". The new implementation is smart enough to do
|
||||
@@ -2727,7 +2752,7 @@ Type AssociatedTypeInference::computeGenericParamWitness(
|
||||
if (auto genericSig = dc->getGenericSignatureOfContext()) {
|
||||
// Ignore the generic parameters for AsyncIteratorProtocol.Failure and
|
||||
// AsyncSequence.Failure.
|
||||
if (!isAsyncIteratorProtocolFailure(assocType)) {
|
||||
if (!isAsyncIteratorOrSequenceFailure(assocType)) {
|
||||
for (auto *gp : genericSig.getInnermostGenericParams()) {
|
||||
// Packs cannot witness associated type requirements.
|
||||
if (gp->isParameterPack())
|
||||
|
||||
@@ -99,3 +99,23 @@ func forTryAwaitReturningExistentialType() async throws {
|
||||
for try await _ in S().seq() { // Ok
|
||||
}
|
||||
}
|
||||
|
||||
@available(SwiftStdlib 5.1, *)
|
||||
public struct ReaderSeq: AsyncSequence, Sendable {
|
||||
public enum Failure: Error {
|
||||
case x
|
||||
}
|
||||
|
||||
public typealias Element = Int
|
||||
|
||||
public func makeAsyncIterator() -> Reader {}
|
||||
|
||||
public actor Reader: AsyncIteratorProtocol {
|
||||
public func next() async throws -> Element? {}
|
||||
}
|
||||
}
|
||||
|
||||
@available(SwiftStdlib 5.1, *)
|
||||
func test1() -> Error {
|
||||
return ReaderSeq.Failure.x
|
||||
}
|
||||
|
||||
@@ -25,3 +25,30 @@ public struct SequenceAdapter<Base: AsyncSequence>: AsyncSequence {
|
||||
// CHECK: @available(
|
||||
// CHECK-NEXT: public typealias Failure = Base.Failure
|
||||
}
|
||||
|
||||
// CHECK: @available(
|
||||
// CHECK-NEXT: public struct OtherSequenceAdapte
|
||||
@available(SwiftStdlib 5.1, *)
|
||||
public struct OtherSequenceAdapter<Base: AsyncSequence>: AsyncSequence {
|
||||
// CHECK: public typealias Element = Base.Element
|
||||
// CHECK-NOT: public typealias Failure
|
||||
// CHECK: public struct Failure
|
||||
|
||||
// CHECK-LABEL: public struct AsyncIterator
|
||||
// CHECK: @available{{.*}}macOS 10.15
|
||||
// CHECK: @available(
|
||||
// CHECK-NEXT: public typealias Failure = Base.Failure
|
||||
public typealias Element = Base.Element
|
||||
|
||||
public struct Failure: Error { }
|
||||
|
||||
// CHECK-NOT: public typealias Failure
|
||||
public struct AsyncIterator: AsyncIteratorProtocol {
|
||||
public mutating func next() async rethrows -> Base.Element? { nil }
|
||||
}
|
||||
|
||||
// CHECK: public func makeAsyncIterator
|
||||
public func makeAsyncIterator() -> AsyncIterator { AsyncIterator() }
|
||||
|
||||
// CHECK-NOT: public typealias Failure
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user