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);
|
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 {
|
bool Decl::shouldPrintInContext(const PrintOptions &PO) const {
|
||||||
// Skip getters/setters. They are part of the variable or subscript.
|
// Skip getters/setters. They are part of the variable or subscript.
|
||||||
if (isa<AccessorDecl>(this))
|
if (isa<AccessorDecl>(this))
|
||||||
@@ -6413,6 +6439,14 @@ bool Decl::shouldPrintInContext(const PrintOptions &PO) const {
|
|||||||
return PO.PrintIfConfig;
|
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.
|
// Print everything else.
|
||||||
return true;
|
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.
|
/// Attempt to resolve a type witness via member name lookup.
|
||||||
static ResolveWitnessResult resolveTypeWitnessViaLookup(
|
static ResolveWitnessResult resolveTypeWitnessViaLookup(
|
||||||
NormalProtocolConformance *conformance,
|
NormalProtocolConformance *conformance,
|
||||||
@@ -396,6 +425,13 @@ static ResolveWitnessResult resolveTypeWitnessViaLookup(
|
|||||||
auto *dc = conformance->getDeclContext();
|
auto *dc = conformance->getDeclContext();
|
||||||
auto &ctx = dc->getASTContext();
|
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
|
// Conformances constructed by the ClangImporter should have explicit type
|
||||||
// witnesses already.
|
// witnesses already.
|
||||||
if (isa<ClangModuleUnit>(dc->getModuleScopeContext())) {
|
if (isa<ClangModuleUnit>(dc->getModuleScopeContext())) {
|
||||||
@@ -1816,17 +1852,6 @@ next_witness:;
|
|||||||
return result;
|
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.
|
/// Determine whether this is AsyncIteratorProtocol.next() function.
|
||||||
static bool isAsyncIteratorProtocolNext(ValueDecl *req) {
|
static bool isAsyncIteratorProtocolNext(ValueDecl *req) {
|
||||||
auto proto = dyn_cast<ProtocolDecl>(req->getDeclContext());
|
auto proto = dyn_cast<ProtocolDecl>(req->getDeclContext());
|
||||||
@@ -1834,7 +1859,8 @@ static bool isAsyncIteratorProtocolNext(ValueDecl *req) {
|
|||||||
!proto->isSpecificProtocol(KnownProtocolKind::AsyncIteratorProtocol))
|
!proto->isSpecificProtocol(KnownProtocolKind::AsyncIteratorProtocol))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return req->getName().getBaseName() == req->getASTContext().Id_next;
|
return req->getName().getBaseName() == req->getASTContext().Id_next &&
|
||||||
|
req->getName().getArgumentNames().empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
InferredAssociatedTypes
|
InferredAssociatedTypes
|
||||||
@@ -2537,8 +2563,7 @@ std::optional<AbstractTypeWitness>
|
|||||||
AssociatedTypeInference::computeFailureTypeWitness(
|
AssociatedTypeInference::computeFailureTypeWitness(
|
||||||
AssociatedTypeDecl *assocType,
|
AssociatedTypeDecl *assocType,
|
||||||
ArrayRef<std::pair<ValueDecl *, ValueDecl *>> valueWitnesses) const {
|
ArrayRef<std::pair<ValueDecl *, ValueDecl *>> valueWitnesses) const {
|
||||||
// Inference only applies to AsyncIteratorProtocol.Failure and
|
// Inference only applies to AsyncIteratorProtocol.Failure.
|
||||||
// AsyncSequence.Failure.
|
|
||||||
if (!isAsyncIteratorProtocolFailure(assocType))
|
if (!isAsyncIteratorProtocolFailure(assocType))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
@@ -2582,7 +2607,7 @@ AssociatedTypeInference::computeDefaultTypeWitness(
|
|||||||
AssociatedTypeDecl *assocType) const {
|
AssociatedTypeDecl *assocType) const {
|
||||||
// Ignore the default for AsyncIteratorProtocol.Failure and
|
// Ignore the default for AsyncIteratorProtocol.Failure and
|
||||||
// AsyncSequence.Failure.
|
// AsyncSequence.Failure.
|
||||||
if (isAsyncIteratorProtocolFailure(assocType))
|
if (isAsyncIteratorOrSequenceFailure(assocType))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
// Go find a default definition.
|
// Go find a default definition.
|
||||||
@@ -2683,8 +2708,8 @@ AssociatedTypeInference::computeAbstractTypeWitness(
|
|||||||
|
|
||||||
// Don't consider the generic parameter names for AsyncSequence.Failure or
|
// Don't consider the generic parameter names for AsyncSequence.Failure or
|
||||||
// AsyncIteratorProtocol.Failure; we always rely on inference from next() or
|
// AsyncIteratorProtocol.Failure; we always rely on inference from next() or
|
||||||
// next(_:).
|
// next(isolation:).
|
||||||
if (isAsyncIteratorProtocolFailure(assocType)) {
|
if (isAsyncIteratorOrSequenceFailure(assocType)) {
|
||||||
// If this is specifically AsyncSequence.Failure with the older associated
|
// If this is specifically AsyncSequence.Failure with the older associated
|
||||||
// type inference implementation, our abstract witness is
|
// type inference implementation, our abstract witness is
|
||||||
// "AsyncIterator.Failure". The new implementation is smart enough to do
|
// "AsyncIterator.Failure". The new implementation is smart enough to do
|
||||||
@@ -2727,7 +2752,7 @@ Type AssociatedTypeInference::computeGenericParamWitness(
|
|||||||
if (auto genericSig = dc->getGenericSignatureOfContext()) {
|
if (auto genericSig = dc->getGenericSignatureOfContext()) {
|
||||||
// Ignore the generic parameters for AsyncIteratorProtocol.Failure and
|
// Ignore the generic parameters for AsyncIteratorProtocol.Failure and
|
||||||
// AsyncSequence.Failure.
|
// AsyncSequence.Failure.
|
||||||
if (!isAsyncIteratorProtocolFailure(assocType)) {
|
if (!isAsyncIteratorOrSequenceFailure(assocType)) {
|
||||||
for (auto *gp : genericSig.getInnermostGenericParams()) {
|
for (auto *gp : genericSig.getInnermostGenericParams()) {
|
||||||
// Packs cannot witness associated type requirements.
|
// Packs cannot witness associated type requirements.
|
||||||
if (gp->isParameterPack())
|
if (gp->isParameterPack())
|
||||||
|
|||||||
@@ -99,3 +99,23 @@ func forTryAwaitReturningExistentialType() async throws {
|
|||||||
for try await _ in S().seq() { // Ok
|
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: @available(
|
||||||
// CHECK-NEXT: public typealias Failure = Base.Failure
|
// 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