From 1b7e579182a7f360133b4fa149e7e5f34ed34eb9 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Fri, 18 Sep 2020 13:50:18 -0700 Subject: [PATCH 01/40] Initial draft of async sequences --- include/swift/AST/ASTContext.h | 3 + include/swift/AST/KnownIdentifiers.def | 3 + include/swift/AST/KnownProtocols.def | 3 + include/swift/AST/Stmt.h | 7 +- lib/AST/ASTContext.cpp | 35 ++- lib/IRGen/GenMeta.cpp | 2 + lib/Parse/ParseStmt.cpp | 9 +- lib/SILGen/SILGenStmt.cpp | 231 ++++++++++++++++++ lib/Sema/BuilderTransform.cpp | 3 +- lib/Sema/CSApply.cpp | 4 +- lib/Sema/CSGen.cpp | 15 +- lib/Sema/TypeCheckConstraints.cpp | 4 +- stdlib/public/Concurrency/AsyncSequence.swift | 12 + stdlib/public/Darwin/CMakeLists.txt | 1 - 14 files changed, 320 insertions(+), 12 deletions(-) create mode 100644 stdlib/public/Concurrency/AsyncSequence.swift diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 46125e348f464..9a706aad677fe 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -522,6 +522,9 @@ class ASTContext final { /// Get Sequence.makeIterator(). FuncDecl *getSequenceMakeIterator() const; + /// Get AsyncSequence.makeGenerator(). + FuncDecl *getAsyncSequenceMakeGenerator() const; + /// Check whether the standard library provides all the correct /// intrinsic support for Optional. /// diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 4f77e9a98b409..8fdee73f2b202 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -50,6 +50,7 @@ IDENTIFIER(CoreGraphics) IDENTIFIER(CoreMedia) IDENTIFIER(CGFloat) IDENTIFIER(CoreFoundation) +IDENTIFIER_(Concurrency) IDENTIFIER(CVarArg) IDENTIFIER(Darwin) IDENTIFIER(dealloc) @@ -93,7 +94,9 @@ IDENTIFIER(KeyedEncodingContainer) IDENTIFIER(keyedBy) IDENTIFIER(keyPath) IDENTIFIER(makeIterator) +IDENTIFIER(makeGenerator) IDENTIFIER(Iterator) +IDENTIFIER(Generator) IDENTIFIER(load) IDENTIFIER(main) IDENTIFIER_WITH_NAME(MainEntryPoint, "$main") diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index 1d4a8484b726c..7bd54efd06e9d 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -88,6 +88,9 @@ PROTOCOL(StringInterpolationProtocol) PROTOCOL(AdditiveArithmetic) PROTOCOL(Differentiable) +PROTOCOL(AsyncSequence) +PROTOCOL(GeneratorProtocol) + PROTOCOL(FloatingPoint) EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "Array", false) diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 63907e2820ab4..30ba8bb8899a1 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -726,6 +726,7 @@ class RepeatWhileStmt : public LabeledStmt { /// \endcode class ForEachStmt : public LabeledStmt { SourceLoc ForLoc; + bool IsAsync; Pattern *Pat; SourceLoc InLoc; Expr *Sequence; @@ -741,12 +742,12 @@ class ForEachStmt : public LabeledStmt { Expr *convertElementExpr = nullptr; public: - ForEachStmt(LabeledStmtInfo LabelInfo, SourceLoc ForLoc, Pattern *Pat, + ForEachStmt(LabeledStmtInfo LabelInfo, SourceLoc ForLoc, bool IsAsync, Pattern *Pat, SourceLoc InLoc, Expr *Sequence, SourceLoc WhereLoc, Expr *WhereExpr, BraceStmt *Body, Optional implicit = None) : LabeledStmt(StmtKind::ForEach, getDefaultImplicitFlag(implicit, ForLoc), LabelInfo), - ForLoc(ForLoc), Pat(nullptr), InLoc(InLoc), Sequence(Sequence), + ForLoc(ForLoc), IsAsync(IsAsync), Pat(nullptr), InLoc(InLoc), Sequence(Sequence), WhereLoc(WhereLoc), WhereExpr(WhereExpr), Body(Body) { setPattern(Pat); } @@ -778,6 +779,8 @@ class ForEachStmt : public LabeledStmt { /// getWhereLoc - Retrieve the location of the 'where' keyword. SourceLoc getWhereLoc() const { return WhereLoc; } + + bool isAsync() const { return IsAsync; } /// getPattern - Retrieve the pattern describing the iteration variables. /// These variables will only be visible within the body of the loop. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index d07f1748654b2..1d7e53d0567f3 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -206,6 +206,9 @@ struct ASTContext::Implementation { /// The declaration of 'Sequence.makeIterator()'. FuncDecl *MakeIterator = nullptr; + /// The declaration of 'AsyncSequence.makeGenerator()'. + FuncDecl *MakeGenerator = nullptr; + /// The declaration of Swift.Optional.Some. EnumElementDecl *OptionalSomeDecl = nullptr; @@ -772,6 +775,31 @@ FuncDecl *ASTContext::getSequenceMakeIterator() const { return nullptr; } +FuncDecl *ASTContext::getAsyncSequenceMakeGenerator() const { + if (getImpl().MakeGenerator) { + return getImpl().MakeGenerator; + } + + auto proto = getProtocol(KnownProtocolKind::AsyncSequence); + if (!proto) + return nullptr; + + for (auto result : proto->lookupDirect(Id_makeGenerator)) { + if (result->getDeclContext() != proto) + continue; + + if (auto func = dyn_cast(result)) { + if (func->getParameters()->size() != 0) + continue; + + getImpl().MakeGenerator = func; + return func; + } + } + + return nullptr; +} + #define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \ DECL_CLASS *ASTContext::get##NAME##Decl() const { \ if (getImpl().NAME##Decl) \ @@ -942,7 +970,12 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { case KnownProtocolKind::Differentiable: M = getLoadedModule(Id_Differentiation); break; - case KnownProtocolKind::Actor: + case KnownProtocolKind::AsyncSequence: + case KnownProtocolKind::GeneratorProtocol: + M = getLoadedModule(Id_Concurrency); + break; + case KnownProtocolKind::AsyncSequence: + case KnownProtocolKind::GeneratorProtocol: M = getLoadedModule(Id_Concurrency); break; default: diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 2fcdf3c72cc08..b3039de8b29ab 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -5080,7 +5080,9 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { // The other known protocols aren't special at runtime. case KnownProtocolKind::Sequence: + case KnownProtocolKind::AsyncSequence: case KnownProtocolKind::IteratorProtocol: + case KnownProtocolKind::GeneratorProtocol: case KnownProtocolKind::RawRepresentable: case KnownProtocolKind::Equatable: case KnownProtocolKind::Hashable: diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index b0b16fdab34b7..955297f0564de 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -2124,6 +2124,13 @@ ParserResult Parser::parseStmtForEach(LabeledStmtInfo LabelInfo) { // lookahead to resolve what is going on. bool IsCStyleFor = isStmtForCStyle(*this); auto StartOfControl = Tok.getLoc(); + bool IsAsync = false; + + if (shouldParseExperimentalConcurrency() && + Tok.isContextualKeyword("await")) { + consumeToken(); + IsAsync = true; + } // Parse the pattern. This is either 'case ' or just a // normal pattern. @@ -2218,7 +2225,7 @@ ParserResult Parser::parseStmtForEach(LabeledStmtInfo LabelInfo) { return makeParserResult( Status, - new (Context) ForEachStmt(LabelInfo, ForLoc, pattern.get(), InLoc, + new (Context) ForEachStmt(LabelInfo, ForLoc, IsAsync, pattern.get(), InLoc, Container.get(), WhereLoc, Where.getPtrOrNull(), Body.get())); } diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 93a8cf34880ff..a20a393d0712d 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -181,6 +181,8 @@ namespace { #define STMT(ID, BASE) void visit##ID##Stmt(ID##Stmt *S); #include "swift/AST/StmtNodes.def" + void visitAsyncForEachStmt(ForEachStmt *S); + ASTContext &getASTContext() { return SGF.getASTContext(); } SILBasicBlock *createBasicBlock() { return SGF.createBasicBlock(); } @@ -932,10 +934,239 @@ void StmtEmitter::visitRepeatWhileStmt(RepeatWhileStmt *S) { SGF.BreakContinueDestStack.pop_back(); } +void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { + + // Dig out information about the sequence conformance. + auto sequenceConformance = S->getSequenceConformance(); + Type sequenceType = S->getSequence()->getType(); + + auto asyncSequenceProto = + SGF.getASTContext().getProtocol(KnownProtocolKind::AsyncSequence); + auto sequenceSubs = SubstitutionMap::getProtocolSubstitutions( + asyncSequenceProto, sequenceType, sequenceConformance); + + // Emit the 'generator' variable that we'll be using for iteration. + LexicalScope OuterForScope(SGF, CleanupLocation(S)); + { + auto initialization = + SGF.emitInitializationForVarDecl(S->getIteratorVar(), false); + SILLocation loc = SILLocation(S->getSequence()); + + // Compute the reference to the AsyncSequence's makeGenerator(). + FuncDecl *makeGeneratorReq = + SGF.getASTContext().getAsyncSequenceMakeGenerator(); + ConcreteDeclRef makeGeneratorRef(makeGeneratorReq, sequenceSubs); + + // Call makeGenerator(). + RValue result = SGF.emitApplyMethod( + loc, makeGeneratorRef, ArgumentSource(S->getSequence()), + PreparedArguments(ArrayRef({})), + SGFContext(initialization.get())); + if (!result.isInContext()) { + ArgumentSource(SILLocation(S->getSequence()), + std::move(result).ensurePlusOne(SGF, loc)) + .forwardInto(SGF, initialization.get()); + } + } + + // If we ever reach an unreachable point, stop emitting statements. + // This will need revision if we ever add goto. + if (!SGF.B.hasValidInsertionPoint()) return; + + // If generator's optional result is address-only, create a stack allocation + // to hold the results. This will be initialized on every entry into the loop + // header and consumed by the loop body. On loop exit, the terminating value + // will be in the buffer. + CanType optTy; + if (S->getConvertElementExpr()) { + optTy = S->getConvertElementExpr()->getType()->getCanonicalType(); + } else { + optTy = OptionalType::get(S->getSequenceConformance().getTypeWitnessByName( + S->getSequence()->getType(), + SGF.getASTContext().Id_Element)) + ->getCanonicalType(); + } + auto &optTL = SGF.getTypeLowering(optTy); + SILValue addrOnlyBuf; + ManagedValue nextBufOrValue; + + if (optTL.isAddressOnly() && SGF.silConv.useLoweredAddresses()) + addrOnlyBuf = SGF.emitTemporaryAllocation(S, optTL.getLoweredType()); + + // Create a new basic block and jump into it. + JumpDest loopDest = createJumpDest(S->getBody()); + SGF.B.emitBlock(loopDest.getBlock(), S); + + // Set the destinations for 'break' and 'continue'. + JumpDest endDest = createJumpDest(S->getBody()); + SGF.BreakContinueDestStack.push_back({ S, endDest, loopDest }); + + // Compute the reference to the the generator's next(). + auto generatorProto = + SGF.getASTContext().getProtocol(KnownProtocolKind::GeneratorProtocol); + ValueDecl *generatorNextReq = generatorProto->getSingleRequirement( + DeclName(SGF.getASTContext(), SGF.getASTContext().Id_next, + ArrayRef())); + auto generatorAssocType = + asyncSequenceProto->getAssociatedType(SGF.getASTContext().Id_Generator); + auto generatorMemberRef = DependentMemberType::get( + asyncSequenceProto->getSelfInterfaceType(), generatorAssocType); + auto generatorType = sequenceConformance.getAssociatedType( + sequenceType, generatorMemberRef); + auto generatorConformance = sequenceConformance.getAssociatedConformance( + sequenceType, generatorMemberRef, generatorProto); + auto generatorSubs = SubstitutionMap::getProtocolSubstitutions( + generatorProto, generatorType, generatorConformance); + ConcreteDeclRef generatorNextRef(generatorNextReq, generatorSubs); + + auto buildArgumentSource = [&]() { + if (cast(generatorNextRef.getDecl())->getSelfAccessKind() == + SelfAccessKind::Mutating) { + LValue lv = + SGF.emitLValue(S->getIteratorVarRef(), SGFAccessKind::ReadWrite); + return ArgumentSource(S, std::move(lv)); + } + LValue lv = + SGF.emitLValue(S->getIteratorVarRef(), SGFAccessKind::OwnedObjectRead); + return ArgumentSource( + S, SGF.emitLoadOfLValue(S->getIteratorVarRef(), std::move(lv), + SGFContext().withFollowingSideEffects())); + }; + + auto buildElementRValue = [&](SILLocation loc, SGFContext ctx) { + RValue result; + result = SGF.emitApplyMethod( + loc, generatorNextRef, buildArgumentSource(), + PreparedArguments(ArrayRef({})), + S->getElementExpr() ? SGFContext() : ctx); + if (S->getElementExpr()) { + SILGenFunction::OpaqueValueRAII pushOpaqueValue( + SGF, S->getElementExpr(), + std::move(result).getAsSingleValue(SGF, loc)); + result = SGF.emitRValue(S->getConvertElementExpr(), ctx); + } + return result; + }; + // Then emit the loop destination block. + // + // Advance the generator. Use a scope to ensure that any temporary stack + // allocations in the subexpression are immediately released. + if (optTL.isAddressOnly() && SGF.silConv.useLoweredAddresses()) { + // Create the initialization outside of the innerForScope so that the + // innerForScope doesn't clean it up. + auto nextInit = SGF.useBufferAsTemporary(addrOnlyBuf, optTL); + { + ArgumentScope innerForScope(SGF, SILLocation(S)); + SILLocation loc = SILLocation(S); + RValue result = buildElementRValue(loc, SGFContext(nextInit.get())); + if (!result.isInContext()) { + ArgumentSource(SILLocation(S->getSequence()), + std::move(result).ensurePlusOne(SGF, loc)) + .forwardInto(SGF, nextInit.get()); + } + innerForScope.pop(); + } + nextBufOrValue = nextInit->getManagedAddress(); + } else { + ArgumentScope innerForScope(SGF, SILLocation(S)); + nextBufOrValue = innerForScope.popPreservingValue( + buildElementRValue(SILLocation(S), SGFContext()) + .getAsSingleValue(SGF, SILLocation(S))); + } + + SILBasicBlock *failExitingBlock = createBasicBlock(); + SwitchEnumBuilder switchEnumBuilder(SGF.B, S, nextBufOrValue); + + switchEnumBuilder.addOptionalSomeCase( + createBasicBlock(), loopDest.getBlock(), + [&](ManagedValue inputValue, SwitchCaseFullExpr &&scope) { + SGF.emitProfilerIncrement(S->getBody()); + + // Emit the loop body. + // The declared variable(s) for the current element are destroyed + // at the end of each loop iteration. + { + Scope innerForScope(SGF.Cleanups, CleanupLocation(S->getBody())); + // Emit the initialization for the pattern. If any of the bound + // patterns + // fail (because this is a 'for case' pattern with a refutable + // pattern, + // the code should jump to the continue block. + InitializationPtr initLoopVars = + SGF.emitPatternBindingInitialization(S->getPattern(), loopDest); + + // If we had a loadable "next" generator value, we know it is present. + // Get the value out of the optional, and wrap it up with a cleanup so + // that any exits out of this scope properly clean it up. + // + // *NOTE* If we do not have an address only value, then inputValue is + // *already properly unwrapped. + if (optTL.isAddressOnly() && SGF.silConv.useLoweredAddresses()) { + inputValue = SGF.emitUncheckedGetOptionalValueFrom( + S, inputValue, optTL, SGFContext(initLoopVars.get())); + } + + if (!inputValue.isInContext()) + RValue(SGF, S, optTy.getOptionalObjectType(), inputValue) + .forwardInto(SGF, S, initLoopVars.get()); + + // Now that the pattern has been initialized, check any where + // condition. + // If it fails, loop around as if 'continue' happened. + if (auto *Where = S->getWhere()) { + auto cond = SGF.emitCondition(Where, /*invert*/ true); + // If self is null, branch to the epilog. + cond.enterTrue(SGF); + SGF.Cleanups.emitBranchAndCleanups(loopDest, Where, {}); + cond.exitTrue(SGF); + cond.complete(SGF); + } + + visit(S->getBody()); + } + + // If we emitted an unreachable in the body, we will not have a valid + // insertion point. Just return early. + if (!SGF.B.hasValidInsertionPoint()) { + scope.unreachableExit(); + return; + } + + // Otherwise, associate the loop body's closing brace with this branch. + RegularLocation L(S->getBody()); + L.pointToEnd(); + scope.exitAndBranch(L); + }, + SGF.loadProfilerCount(S->getBody())); + + // We add loop fail block, just to be defensive about intermediate + // transformations performing cleanups at scope.exit(). We still jump to the + // contBlock. + switchEnumBuilder.addOptionalNoneCase( + createBasicBlock(), failExitingBlock, + [&](ManagedValue inputValue, SwitchCaseFullExpr &&scope) { + assert(!inputValue && "None should not be passed an argument!"); + scope.exitAndBranch(S); + }, + SGF.loadProfilerCount(S)); + + std::move(switchEnumBuilder).emit(); + + SGF.B.emitBlock(failExitingBlock); + emitOrDeleteBlock(SGF, endDest, S); + SGF.BreakContinueDestStack.pop_back(); +} + void StmtEmitter::visitForEachStmt(ForEachStmt *S) { + if (S->isAsync()) { + visitAsyncForEachStmt(S); + return; + } + // Dig out information about the sequence conformance. auto sequenceConformance = S->getSequenceConformance(); Type sequenceType = S->getSequence()->getType(); + auto sequenceProto = SGF.getASTContext().getProtocol(KnownProtocolKind::Sequence); auto sequenceSubs = SubstitutionMap::getProtocolSubstitutions( diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index f833a705e452d..a927e0f65bf0b 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -774,7 +774,8 @@ class BuilderClosureVisitor // take care of this. auto sequenceProto = TypeChecker::getProtocol( dc->getASTContext(), forEachStmt->getForLoc(), - KnownProtocolKind::Sequence); + forEachStmt->isAsync() ? + KnownProtocolKind::AsyncSequence : KnownProtocolKind::Sequence); if (!sequenceProto) { if (!unhandledNode) unhandledNode = forEachStmt; diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 4581ecf8e39b7..655205f7fecb7 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7966,7 +7966,9 @@ static Optional applySolutionToForEachStmt( // Get the conformance of the sequence type to the Sequence protocol. auto stmt = forEachStmtInfo.stmt; auto sequenceProto = TypeChecker::getProtocol( - cs.getASTContext(), stmt->getForLoc(), KnownProtocolKind::Sequence); + cs.getASTContext(), stmt->getForLoc(), + stmt->getAwaitLoc().isValid() ? + KnownProtocolKind::AsyncSequence : KnownProtocolKind::Sequence); auto sequenceConformance = TypeChecker::conformsToProtocol( forEachStmtInfo.sequenceType, sequenceProto, cs.DC); assert(!sequenceConformance.isInvalid() && diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index f4aeb8cd3849a..48e7fce0084e7 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3655,6 +3655,7 @@ generateForEachStmtConstraints( ConstraintSystem &cs, SolutionApplicationTarget target, Expr *sequence) { auto forEachStmtInfo = target.getForEachStmtInfo(); ForEachStmt *stmt = forEachStmtInfo.stmt; + bool isAsync = stmt->isAsync(); auto locator = cs.getConstraintLocator(sequence); auto contextualLocator = @@ -3662,7 +3663,9 @@ generateForEachStmtConstraints( // The expression type must conform to the Sequence protocol. auto sequenceProto = TypeChecker::getProtocol( - cs.getASTContext(), stmt->getForLoc(), KnownProtocolKind::Sequence); + cs.getASTContext(), stmt->getForLoc(), + isAsync ? + KnownProtocolKind::AsyncSequence : KnownProtocolKind::Sequence); if (!sequenceProto) { return None; } @@ -3708,18 +3711,22 @@ generateForEachStmtConstraints( // Determine the iterator type. auto iteratorAssocType = - sequenceProto->getAssociatedType(cs.getASTContext().Id_Iterator); + sequenceProto->getAssociatedType(isAsync ? + cs.getASTContext().Id_Generator : cs.getASTContext().Id_Iterator); Type iteratorType = DependentMemberType::get(sequenceType, iteratorAssocType); // The iterator type must conform to IteratorProtocol. ProtocolDecl *iteratorProto = TypeChecker::getProtocol( cs.getASTContext(), stmt->getForLoc(), - KnownProtocolKind::IteratorProtocol); + isAsync ? + KnownProtocolKind::GeneratorProtocol : KnownProtocolKind::IteratorProtocol); if (!iteratorProto) return None; // Reference the makeIterator witness. - FuncDecl *makeIterator = ctx.getSequenceMakeIterator(); + FuncDecl *makeIterator = isAsync ? + ctx.getAsyncSequenceMakeGenerator() : ctx.getSequenceMakeIterator(); + Type makeIteratorType = cs.createTypeVariable(locator, TVO_CanBindToNoEscape); cs.addValueWitnessConstraint( diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 302040a68ad11..90e3b52a02f01 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -562,7 +562,9 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { }; auto sequenceProto = TypeChecker::getProtocol( - dc->getASTContext(), stmt->getForLoc(), KnownProtocolKind::Sequence); + dc->getASTContext(), stmt->getForLoc(), + stmt->isAsync() ? + KnownProtocolKind::AsyncSequence : KnownProtocolKind::Sequence); if (!sequenceProto) return failed(); diff --git a/stdlib/public/Concurrency/AsyncSequence.swift b/stdlib/public/Concurrency/AsyncSequence.swift new file mode 100644 index 0000000000000..068feef4e20e0 --- /dev/null +++ b/stdlib/public/Concurrency/AsyncSequence.swift @@ -0,0 +1,12 @@ +import Swift + +public protocol GeneratorProtocol { + associatedtype Element + + mutating func next() async -> Element? +} + +public protocol AsyncSequence { + associatedtype Generator: GeneratorProtocol + func makeGenerator() -> Generator +} diff --git a/stdlib/public/Darwin/CMakeLists.txt b/stdlib/public/Darwin/CMakeLists.txt index b8be8dfc71188..76584f5773293 100644 --- a/stdlib/public/Darwin/CMakeLists.txt +++ b/stdlib/public/Darwin/CMakeLists.txt @@ -19,7 +19,6 @@ set(all_overlays Dispatch Foundation ObjectiveC - XCTest ) if(DEFINED SWIFT_OVERLAY_TARGETS) From a23038a26059834243eeb843e5337693937e4976 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Fri, 18 Sep 2020 13:52:37 -0700 Subject: [PATCH 02/40] Adjust AsyncSequence associated type requirements --- stdlib/public/Concurrency/AsyncSequence.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stdlib/public/Concurrency/AsyncSequence.swift b/stdlib/public/Concurrency/AsyncSequence.swift index 068feef4e20e0..993656ea09453 100644 --- a/stdlib/public/Concurrency/AsyncSequence.swift +++ b/stdlib/public/Concurrency/AsyncSequence.swift @@ -7,6 +7,8 @@ public protocol GeneratorProtocol { } public protocol AsyncSequence { - associatedtype Generator: GeneratorProtocol + associatedtype Element + associatedtype Generator: GeneratorProtocol where Generator.Element == Element + func makeGenerator() -> Generator -} +} \ No newline at end of file From ea8d39609efc28cf243c284802966d30ddb69ddb Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 9 Dec 2020 13:10:04 -0800 Subject: [PATCH 03/40] Add a draft implementation of AsyncSequence and associated functionality --- stdlib/public/Concurrency/AllSatisfy.swift | 25 ++++ .../Concurrency/AsyncCompactMapSequence.swift | 115 ++++++++++++++++ .../Concurrency/AsyncConcatSequence.swift | 65 +++++++++ .../Concurrency/AsyncDropWhileSequence.swift | 125 ++++++++++++++++++ .../Concurrency/AsyncFilterSequence.swift | 115 ++++++++++++++++ .../Concurrency/AsyncFlatMapSequence.swift | 120 +++++++++++++++++ .../Concurrency/AsyncIteratorProtocol.swift | 19 +++ .../public/Concurrency/AsyncMapSequence.swift | 105 +++++++++++++++ .../AsyncPrefixWhileSequence.swift | 111 ++++++++++++++++ stdlib/public/Concurrency/AsyncSequence.swift | 25 ++-- stdlib/public/Concurrency/Contains.swift | 31 +++++ stdlib/public/Concurrency/Count.swift | 24 ++++ stdlib/public/Concurrency/First.swift | 30 +++++ stdlib/public/Concurrency/MinMax.swift | 43 ++++++ stdlib/public/Concurrency/Reduce.swift | 34 +++++ 15 files changed, 977 insertions(+), 10 deletions(-) create mode 100644 stdlib/public/Concurrency/AllSatisfy.swift create mode 100644 stdlib/public/Concurrency/AsyncCompactMapSequence.swift create mode 100644 stdlib/public/Concurrency/AsyncConcatSequence.swift create mode 100644 stdlib/public/Concurrency/AsyncDropWhileSequence.swift create mode 100644 stdlib/public/Concurrency/AsyncFilterSequence.swift create mode 100644 stdlib/public/Concurrency/AsyncFlatMapSequence.swift create mode 100644 stdlib/public/Concurrency/AsyncIteratorProtocol.swift create mode 100644 stdlib/public/Concurrency/AsyncMapSequence.swift create mode 100644 stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift create mode 100644 stdlib/public/Concurrency/Contains.swift create mode 100644 stdlib/public/Concurrency/Count.swift create mode 100644 stdlib/public/Concurrency/First.swift create mode 100644 stdlib/public/Concurrency/MinMax.swift create mode 100644 stdlib/public/Concurrency/Reduce.swift diff --git a/stdlib/public/Concurrency/AllSatisfy.swift b/stdlib/public/Concurrency/AllSatisfy.swift new file mode 100644 index 0000000000000..da23ebeb41b7d --- /dev/null +++ b/stdlib/public/Concurrency/AllSatisfy.swift @@ -0,0 +1,25 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift + +extension AsyncSequence { + public func allSatisfy(_ predicate: (Element) async throws -> Bool) async throws /*rethrows*/ -> Bool { + var it = makeAsyncIterator() + while let element = await try it.next() { + if !(await try predicate(element)) { + return false + } + } + return true + } +} diff --git a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift new file mode 100644 index 0000000000000..17f3d8b8b7a9b --- /dev/null +++ b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift @@ -0,0 +1,115 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift + +extension AsyncSequence { + public func compactMap(_ transform: @escaping (Element) async -> ElementOfResult?) -> AsyncCompactMapSequence { + return AsyncCompactMapSequence(self, transform: transform) + } + + public func compactMap(_ transform: @escaping (Element) async throws -> ElementOfResult?) -> AsyncTryCompactMapSequence { + return AsyncTryCompactMapSequence(self, transform: transform) + } +} + +public struct AsyncCompactMapSequence: AsyncSequence where Upstream: AsyncSequence, Upstream.Element == ElementOfResult? { + public typealias Element = ElementOfResult + public typealias AsyncIterator = Iterator + + public struct Iterator: AsyncIteratorProtocol { + var upstreamIterator: Upstream.AsyncIterator? + let transform: (Upstream.Element) async -> ElementOfResult? + + init(_ upstreamIterator: Upstream.AsyncIterator, transform: @escaping (Upstream.Element) async -> ElementOfResult?) { + self.upstreamIterator = upstreamIterator + self.transform = transform + } + + public mutating func next() async throws /*rethrows*/ -> ElementOfResult? { + while true { + guard let item = await try upstreamIterator?.next() else { + return nil + } + if let transformed = await transform(item) { + return transformed + } + } + } + + public mutating func cancel() { + upstreamIterator?.cancel() + upstreamIterator = nil + } + } + + public let upstream: Upstream + public let transform: (Upstream.Element) async -> ElementOfResult? + + public init(_ upstream: Upstream, transform: @escaping (Upstream.Element) async -> ElementOfResult?) { + self.upstream = upstream + self.transform = transform + } + + public func makeAsyncIterator() -> Iterator { + return Iterator(upstream.makeAsyncIterator(), transform: transform) + } +} + +public struct AsyncTryCompactMapSequence: AsyncSequence where Upstream: AsyncSequence, Upstream.Element == ElementOfResult? { + public typealias Element = ElementOfResult + public typealias AsyncIterator = Iterator + + public struct Iterator: AsyncIteratorProtocol { + var upstreamIterator: Upstream.AsyncIterator? + let transform: (Upstream.Element) async throws -> ElementOfResult? + + init(_ upstreamIterator: Upstream.AsyncIterator, transform: @escaping (Upstream.Element) async throws -> ElementOfResult?) { + self.upstreamIterator = upstreamIterator + self.transform = transform + } + + public mutating func next() async throws /*rethrows*/ -> ElementOfResult? { + while true { + guard let item = await try upstreamIterator?.next() else { + return nil + } + do { + if let transformed = await try transform(item) { + return transformed + } + } catch { + upstreamIterator?.cancel() + upstreamIterator = nil + throw error + } + } + } + + public mutating func cancel() { + upstreamIterator?.cancel() + upstreamIterator = nil + } + } + + public let upstream: Upstream + public let transform: (Upstream.Element) async throws -> ElementOfResult? + + public init(_ upstream: Upstream, transform: @escaping (Upstream.Element) async throws -> ElementOfResult?) { + self.upstream = upstream + self.transform = transform + } + + public func makeAsyncIterator() -> Iterator { + return Iterator(upstream.makeAsyncIterator(), transform: transform) + } +} diff --git a/stdlib/public/Concurrency/AsyncConcatSequence.swift b/stdlib/public/Concurrency/AsyncConcatSequence.swift new file mode 100644 index 0000000000000..b7494e4573e17 --- /dev/null +++ b/stdlib/public/Concurrency/AsyncConcatSequence.swift @@ -0,0 +1,65 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift + +extension AsyncSequence { + public func append(_ suffix: Suffix) -> AsyncConcatSequence where Suffix: AsyncSequence, Suffix.Element == Element { + return AsyncConcatSequence(prefix: self, suffix: suffix) + } + + public func prepend(_ prefix: Prefix) -> AsyncConcatSequence where Prefix: AsyncSequence, Prefix.Element == Element { + return AsyncConcatSequence(prefix: prefix, suffix: self) + } +} + +public struct AsyncConcatSequence: AsyncSequence where Prefix: AsyncSequence, Suffix: AsyncSequence, Prefix.Element == Suffix.Element { + public typealias Element = Prefix.Element + public typealias AsyncIterator = Iterator + + public struct Iterator: AsyncIteratorProtocol { + var prefixIterator: Prefix.AsyncIterator? + var suffixIterator: Suffix.AsyncIterator? + + init(_ prefixIterator: Prefix.AsyncIterator, _ suffixIterator: Suffix.AsyncIterator) { + self.prefixIterator = prefixIterator + self.suffixIterator = suffixIterator + } + + public mutating func next() async throws /*rethrows*/ -> Prefix.Element? { + if let item = await try prefixIterator?.next() { + return item + } + prefixIterator = nil + return await try suffixIterator?.next() + } + + public mutating func cancel() { + prefixIterator?.cancel() + prefixIterator = nil + suffixIterator?.cancel() + suffixIterator = nil + } + } + + public let prefix: Prefix + public let suffix: Suffix + + public init(prefix: Prefix, suffix: Suffix) { + self.prefix = prefix + self.suffix = suffix + } + + public func makeAsyncIterator() -> Iterator { + return Iterator(prefix.makeAsyncIterator(), suffix.makeAsyncIterator()) + } +} diff --git a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift new file mode 100644 index 0000000000000..5d3bf983ddfa0 --- /dev/null +++ b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift @@ -0,0 +1,125 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift + +extension AsyncSequence { + public func drop(while predicate: @escaping (Element) async -> Bool) -> AsyncDropWhileSequence { + return AsyncDropWhileSequence(self, predicate: predicate) + } + + public func drop(while predicate: @escaping (Element) async throws -> Bool) -> AsyncTryDropWhileSequence { + return AsyncTryDropWhileSequence(self, predicate: predicate) + } +} + +public struct AsyncDropWhileSequence: AsyncSequence where Upstream: AsyncSequence { + public typealias Element = Upstream.Element + public typealias AsyncIterator = Iterator + + public struct Iterator: AsyncIteratorProtocol { + var upstreamIterator: Upstream.AsyncIterator? + var predicate: ((Element) async -> Bool)? + + init(_ upstreamIterator: Upstream.AsyncIterator, predicate: @escaping (Element) async -> Bool) { + self.upstreamIterator = upstreamIterator + self.predicate = predicate + } + + public mutating func next() async throws /*rethrows*/ -> Upstream.Element? { + while true { + guard let item = await try upstreamIterator?.next() else { + return nil + } + if let predicate = self.predicate { + if !(await predicate(item)) { + self.predicate = nil + return item + } + } else { + return item + } + } + } + + public mutating func cancel() { + upstreamIterator?.cancel() + upstreamIterator = nil + } + } + + public let upstream: Upstream + public let predicate: (Element) async -> Bool + + public init(_ upstream: Upstream, predicate: @escaping (Element) async -> Bool) { + self.upstream = upstream + self.predicate = predicate + } + + public func makeAsyncIterator() -> Iterator { + return Iterator(upstream.makeAsyncIterator(), predicate: predicate) + } +} + +public struct AsyncTryDropWhileSequence: AsyncSequence where Upstream: AsyncSequence { + public typealias Element = Upstream.Element + public typealias AsyncIterator = Iterator + + public struct Iterator: AsyncIteratorProtocol { + var upstreamIterator: Upstream.AsyncIterator? + var predicate: ((Element) async throws -> Bool)? + + init(_ upstreamIterator: Upstream.AsyncIterator, predicate: @escaping (Element) async throws -> Bool) { + self.upstreamIterator = upstreamIterator + self.predicate = predicate + } + + public mutating func next() async throws -> Upstream.Element? { + while true { + guard let item = await try upstreamIterator?.next() else { + return nil + } + if let predicate = self.predicate { + do { + if !(await try predicate(item)) { + self.predicate = nil + return item + } + } catch { + upstreamIterator?.cancel() + upstreamIterator = nil + throw error + } + } else { + return item + } + } + } + + public mutating func cancel() { + upstreamIterator?.cancel() + upstreamIterator = nil + } + } + + public let upstream: Upstream + public let predicate: (Element) async throws -> Bool + + public init(_ upstream: Upstream, predicate: @escaping (Element) async throws -> Bool) { + self.upstream = upstream + self.predicate = predicate + } + + public func makeAsyncIterator() -> Iterator { + return Iterator(upstream.makeAsyncIterator(), predicate: predicate) + } +} diff --git a/stdlib/public/Concurrency/AsyncFilterSequence.swift b/stdlib/public/Concurrency/AsyncFilterSequence.swift new file mode 100644 index 0000000000000..a6813a1b073e1 --- /dev/null +++ b/stdlib/public/Concurrency/AsyncFilterSequence.swift @@ -0,0 +1,115 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift + +extension AsyncSequence { + public func filter(_ predicate: @escaping (Element) async -> Bool) -> AsyncFilterSequence { + return AsyncFilterSequence(self, predicate: predicate) + } + + public func filter(_ predicate: @escaping (Element) async throws -> Bool) -> AsyncTryFilterSequence { + return AsyncTryFilterSequence(self, predicate: predicate) + } +} + +public struct AsyncFilterSequence: AsyncSequence where Upstream: AsyncSequence { + public typealias AsyncIterator = Iterator + public typealias Element = Upstream.Element + + public let upstream: Upstream + public let predicate: (Element) async -> Bool + + public struct Iterator: AsyncIteratorProtocol { + var upstreamIterator: Upstream.AsyncIterator? + let predicate: (Element) async -> Bool + + init(_ upstreamIterator: Upstream.AsyncIterator, + predicate: @escaping (Element) async -> Bool + ) { + self.upstreamIterator = upstreamIterator + self.predicate = predicate + } + + public mutating func next() async throws /*rethrows*/ -> Upstream.Element? { + guard let item = await try upstreamIterator?.next() else { + return nil + } + guard await predicate(item) else { + upstreamIterator?.cancel() + upstreamIterator = nil + return nil + } + return item + } + + public mutating func cancel() { + upstreamIterator?.cancel() + upstreamIterator = nil + } + } + + public init(_ upstream: Upstream, predicate: @escaping (Upstream.Element) async -> Bool) { + self.upstream = upstream + self.predicate = predicate + } + + public func makeAsyncIterator() -> Iterator { + return Iterator(upstream.makeAsyncIterator(), predicate: predicate) + } +} + +public struct AsyncTryFilterSequence: AsyncSequence where Upstream: AsyncSequence { + public typealias AsyncIterator = Iterator + public typealias Element = Upstream.Element + + public let upstream: Upstream + public let predicate: (Element) async throws -> Bool + + public struct Iterator: AsyncIteratorProtocol { + var upstreamIterator: Upstream.AsyncIterator? + let predicate: (Element) async throws -> Bool + + init(_ upstreamIterator: Upstream.AsyncIterator, + predicate: @escaping (Element) async throws -> Bool + ) { + self.upstreamIterator = upstreamIterator + self.predicate = predicate + } + + public mutating func next() async throws -> Upstream.Element? { + guard let item = await try upstreamIterator?.next() else { + return nil + } + guard await try predicate(item) else { + upstreamIterator?.cancel() + upstreamIterator = nil + return nil + } + return item + } + + public mutating func cancel() { + upstreamIterator?.cancel() + upstreamIterator = nil + } + } + + public init(_ upstream: Upstream, predicate: @escaping (Upstream.Element) async throws -> Bool) { + self.upstream = upstream + self.predicate = predicate + } + + public func makeAsyncIterator() -> Iterator { + return Iterator(upstream.makeAsyncIterator(), predicate: predicate) + } +} diff --git a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift new file mode 100644 index 0000000000000..a867b058c5f5f --- /dev/null +++ b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift @@ -0,0 +1,120 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift + +extension AsyncSequence { + public func flatMap(_ transform: @escaping (Element) async -> SegmentOfResult) -> AsyncFlatMapSequence where SegmentOfResult: AsyncSequence { + return AsyncFlatMapSequence(self, transform: transform) + } + + public func flatMap(_ transform: @escaping (Element) async throws -> SegmentOfResult) -> AsyncTryFlatMapSequence where SegmentOfResult: AsyncSequence { + return AsyncTryFlatMapSequence(self, transform: transform) + } +} + +public struct AsyncFlatMapSequence: AsyncSequence where Upstream: AsyncSequence { + public typealias Element = SegmentOfResult.Element + public typealias AsyncIterator = Iterator + + public struct Iterator: AsyncIteratorProtocol { + var upstreamIterator: Upstream.AsyncIterator? + let transform: (Upstream.Element) async -> SegmentOfResult + var currentIterator: SegmentOfResult.AsyncIterator? + + init(_ upstreamIterator: Upstream.AsyncIterator, transform: @escaping (Upstream.Element) async -> SegmentOfResult) { + self.upstreamIterator = upstreamIterator + self.transform = transform + } + + public mutating func next() async throws /*rethrows*/ -> SegmentOfResult.Element? { + if let item = await try currentIterator?.next() { + return item + } else { + guard let item = await try upstreamIterator?.next() else { + return nil + } + let segment = await transform(item) + currentIterator = segment.makeAsyncIterator() + return await try currentIterator?.next() + } + } + + public mutating func cancel() { + upstreamIterator?.cancel() + upstreamIterator = nil + currentIterator?.cancel() + currentIterator = nil + } + } + + public let upstream: Upstream + public let transform: (Upstream.Element) async -> SegmentOfResult + + public init(_ upstream: Upstream, transform: @escaping (Upstream.Element) async -> SegmentOfResult) { + self.upstream = upstream + self.transform = transform + } + + public func makeAsyncIterator() -> Iterator { + return Iterator(upstream.makeAsyncIterator(), transform: transform) + } +} + +public struct AsyncTryFlatMapSequence: AsyncSequence where Upstream: AsyncSequence { + public typealias Element = SegmentOfResult.Element + public typealias AsyncIterator = Iterator + + public struct Iterator: AsyncIteratorProtocol { + var upstreamIterator: Upstream.AsyncIterator? + let transform: (Upstream.Element) async throws -> SegmentOfResult + var currentIterator: SegmentOfResult.AsyncIterator? + + init(_ upstreamIterator: Upstream.AsyncIterator, transform: @escaping (Upstream.Element) async throws -> SegmentOfResult) { + self.upstreamIterator = upstreamIterator + self.transform = transform + } + + public mutating func next() async throws -> SegmentOfResult.Element? { + if let item = await try currentIterator?.next() { + return item + } else { + guard let item = await try upstreamIterator?.next() else { + return nil + } + let segment = await try transform(item) + currentIterator = segment.makeAsyncIterator() + return await try currentIterator?.next() + } + } + + public mutating func cancel() { + upstreamIterator?.cancel() + upstreamIterator = nil + currentIterator?.cancel() + currentIterator = nil + } + } + + public let upstream: Upstream + public let transform: (Upstream.Element) async throws -> SegmentOfResult + + public init(_ upstream: Upstream, transform: @escaping (Upstream.Element) async throws -> SegmentOfResult) { + self.upstream = upstream + self.transform = transform + } + + public func makeAsyncIterator() -> Iterator { + return Iterator(upstream.makeAsyncIterator(), transform: transform) + } +} + diff --git a/stdlib/public/Concurrency/AsyncIteratorProtocol.swift b/stdlib/public/Concurrency/AsyncIteratorProtocol.swift new file mode 100644 index 0000000000000..98d04a0d86f0e --- /dev/null +++ b/stdlib/public/Concurrency/AsyncIteratorProtocol.swift @@ -0,0 +1,19 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift + +public protocol AsyncIteratorProtocol { + associatedtype Element + mutating func next() async throws -> Element? + mutating func cancel() +} diff --git a/stdlib/public/Concurrency/AsyncMapSequence.swift b/stdlib/public/Concurrency/AsyncMapSequence.swift new file mode 100644 index 0000000000000..63d79fad1656d --- /dev/null +++ b/stdlib/public/Concurrency/AsyncMapSequence.swift @@ -0,0 +1,105 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift + +extension AsyncSequence { + public func map(_ transform: @escaping (Element) async -> Transformed) -> AsyncMapSequence { + return AsyncMapSequence(self, transform: transform) + } + + public func map(_ transform: @escaping (Element) async throws -> Transformed) -> AsyncTryMapSequence { + return AsyncTryMapSequence(self, transform: transform) + } +} + +public struct AsyncMapSequence: AsyncSequence where Upstream: AsyncSequence { + public typealias AsyncIterator = Iterator + public typealias Element = Transformed + + public struct Iterator: AsyncIteratorProtocol { + var upstreamIterator: Upstream.AsyncIterator? + let transform: (Upstream.Element) async -> Transformed + + init(_ upstreamIterator: Upstream.AsyncIterator, + transform: @escaping (Upstream.Element) async -> Transformed + ) { + self.upstreamIterator = upstreamIterator + self.transform = transform + } + + public mutating func next() async throws /*rethrows*/ -> Transformed? { + guard let item = await try upstreamIterator?.next() else { + return nil + } + return await transform(item) + } + + public mutating func cancel() { + upstreamIterator?.cancel() + upstreamIterator = nil + } + } + + public let upstream: Upstream + public let transform: (Upstream.Element) async -> Transformed + + public init(_ upstream: Upstream, transform: @escaping (Upstream.Element) async -> Transformed) { + self.upstream = upstream + self.transform = transform + } + + public func makeAsyncIterator() -> Iterator { + return Iterator(upstream.makeAsyncIterator(), transform: transform) + } +} + +public struct AsyncTryMapSequence: AsyncSequence where Upstream: AsyncSequence { + public typealias AsyncIterator = Iterator + public typealias Element = Transformed + + public struct Iterator: AsyncIteratorProtocol { + var upstreamIterator: Upstream.AsyncIterator? + let transform: (Upstream.Element) async throws -> Transformed + + init(_ upstreamIterator: Upstream.AsyncIterator, + transform: @escaping (Upstream.Element) async throws -> Transformed + ) { + self.upstreamIterator = upstreamIterator + self.transform = transform + } + + public mutating func next() async throws -> Transformed? { + guard let item = await try upstreamIterator?.next() else { + return nil + } + return await try transform(item) + } + + public mutating func cancel() { + upstreamIterator?.cancel() + upstreamIterator = nil + } + } + + public let upstream: Upstream + public let transform: (Upstream.Element) async throws -> Transformed + + public init(_ upstream: Upstream, transform: @escaping (Upstream.Element) async throws -> Transformed) { + self.upstream = upstream + self.transform = transform + } + + public func makeAsyncIterator() -> Iterator { + return Iterator(upstream.makeAsyncIterator(), transform: transform) + } +} diff --git a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift new file mode 100644 index 0000000000000..7a30ab7bc77e4 --- /dev/null +++ b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift @@ -0,0 +1,111 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift + +extension AsyncSequence { + public func prefix(while predicate: @escaping (Element) async -> Bool) -> AsyncPrefixWhileSequence { + return AsyncPrefixWhileSequence(self, predicate: predicate) + } + + public func prefix(while predicate: @escaping (Element) async throws -> Bool) -> AsyncTryPrefixWhileSequence { + return AsyncTryPrefixWhileSequence(self, predicate: predicate) + } +} + +public struct AsyncPrefixWhileSequence: AsyncSequence where Upstream: AsyncSequence { + public typealias Element = Upstream.Element + public typealias AsyncIterator = Iterator + + public struct Iterator: AsyncIteratorProtocol { + var upstreamIterator: Upstream.AsyncIterator? + var predicate: (Element) async -> Bool + + init(_ upstreamIterator: Upstream.AsyncIterator, predicate: @escaping (Element) async -> Bool) { + self.upstreamIterator = upstreamIterator + self.predicate = predicate + } + + public mutating func next() async throws /*rethrows*/ -> Element? { + guard let item = await try upstreamIterator?.next() else { + return nil + } + guard await predicate(item) else { + upstreamIterator?.cancel() + upstreamIterator = nil + return nil + } + return item + } + + public mutating func cancel() { + upstreamIterator?.cancel() + upstreamIterator = nil + } + } + + public let upstream: Upstream + public let predicate: (Element) async -> Bool + + public init(_ upstream: Upstream, predicate: @escaping (Element) async -> Bool) { + self.upstream = upstream + self.predicate = predicate + } + + public func makeAsyncIterator() -> Iterator { + return Iterator(upstream.makeAsyncIterator(), predicate: predicate) + } +} + +public struct AsyncTryPrefixWhileSequence: AsyncSequence where Upstream: AsyncSequence { + public typealias Element = Upstream.Element + public typealias AsyncIterator = Iterator + + public struct Iterator: AsyncIteratorProtocol { + var upstreamIterator: Upstream.AsyncIterator? + var predicate: (Element) async throws -> Bool + + init(_ upstreamIterator: Upstream.AsyncIterator, predicate: @escaping (Element) async throws -> Bool) { + self.upstreamIterator = upstreamIterator + self.predicate = predicate + } + + public mutating func next() async throws -> Element? { + guard let item = await try upstreamIterator?.next() else { + return nil + } + guard await try predicate(item) else { + upstreamIterator?.cancel() + upstreamIterator = nil + return nil + } + return item + } + + public mutating func cancel() { + upstreamIterator?.cancel() + upstreamIterator = nil + } + } + + public let upstream: Upstream + public let predicate: (Element) async throws -> Bool + + public init(_ upstream: Upstream, predicate: @escaping (Element) async throws -> Bool) { + self.upstream = upstream + self.predicate = predicate + } + + public func makeAsyncIterator() -> Iterator { + return Iterator(upstream.makeAsyncIterator(), predicate: predicate) + } +} diff --git a/stdlib/public/Concurrency/AsyncSequence.swift b/stdlib/public/Concurrency/AsyncSequence.swift index 993656ea09453..d5d91f78e0dde 100644 --- a/stdlib/public/Concurrency/AsyncSequence.swift +++ b/stdlib/public/Concurrency/AsyncSequence.swift @@ -1,14 +1,19 @@ -import Swift +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// -public protocol GeneratorProtocol { - associatedtype Element - - mutating func next() async -> Element? -} +import Swift public protocol AsyncSequence { + associatedtype AsyncIterator: AsyncIteratorProtocol where AsyncIterator.Element == Element associatedtype Element - associatedtype Generator: GeneratorProtocol where Generator.Element == Element - - func makeGenerator() -> Generator -} \ No newline at end of file + func makeAsyncIterator() -> AsyncIterator +} diff --git a/stdlib/public/Concurrency/Contains.swift b/stdlib/public/Concurrency/Contains.swift new file mode 100644 index 0000000000000..c34adfcca8b76 --- /dev/null +++ b/stdlib/public/Concurrency/Contains.swift @@ -0,0 +1,31 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift + +extension AsyncSequence { + public func contains(where predicate: (Element) async throws -> Bool) async throws /*rethrows*/ -> Bool { + var it = makeAsyncIterator() + while let e = await try it.next() { + if await try predicate(e) { + return true + } + } + return false + } +} + +extension AsyncSequence where Element: Equatable { + public func contains(_ element: Element) async throws /*rethrows*/ -> Bool { + return await try contains { $0 == element } + } +} diff --git a/stdlib/public/Concurrency/Count.swift b/stdlib/public/Concurrency/Count.swift new file mode 100644 index 0000000000000..a51d92ea305f0 --- /dev/null +++ b/stdlib/public/Concurrency/Count.swift @@ -0,0 +1,24 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift + +extension AsyncSequence { + public func count() async throws /*rethrows*/ -> Int { + var count = 0 + var it = makeAsyncIterator() + while await try it.next() != nil { + count += 1 + } + return count + } +} diff --git a/stdlib/public/Concurrency/First.swift b/stdlib/public/Concurrency/First.swift new file mode 100644 index 0000000000000..9bee6e7d058aa --- /dev/null +++ b/stdlib/public/Concurrency/First.swift @@ -0,0 +1,30 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift + +extension AsyncSequence { + public func first(where predicate: (Element) async throws -> Bool) async throws /*rethrows*/ -> Element? { + var it = makeAsyncIterator() + while let element = await try it.next() { + if await try predicate(element) { + return element + } + } + return nil + } + + public func first() async throws /*rethrows*/ -> Element? { + var it = makeAsyncIterator() + return await try it.next() + } +} diff --git a/stdlib/public/Concurrency/MinMax.swift b/stdlib/public/Concurrency/MinMax.swift new file mode 100644 index 0000000000000..67371069190fb --- /dev/null +++ b/stdlib/public/Concurrency/MinMax.swift @@ -0,0 +1,43 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift + +extension AsyncSequence { + public func min(by areInIncreasingOrder: (Element, Element) async throws -> Bool) async throws /*rethrows*/ -> Element? { + var it = makeAsyncIterator() + guard var result = await try it.next() else { return nil } + while let e = await try it.next() { + if await try areInIncreasingOrder(e, result) { result = e } + } + return result + } + + public func max(by areInIncreasingOrder: (Element, Element) async throws -> Bool) async throws /*rethrows*/ -> Element? { + var it = makeAsyncIterator() + guard var result = await try it.next() else { return nil } + while let e = await try it.next() { + if await try areInIncreasingOrder(result, e) { result = e } + } + return result + } +} + +extension AsyncSequence where Element: Comparable { + public func min() async throws /*rethrows*/ -> Element? { + return await try min(by: <) + } + + public func max() async throws /*rethrows*/ -> Element? { + return await try max(by: <) + } +} diff --git a/stdlib/public/Concurrency/Reduce.swift b/stdlib/public/Concurrency/Reduce.swift new file mode 100644 index 0000000000000..251595b6eca37 --- /dev/null +++ b/stdlib/public/Concurrency/Reduce.swift @@ -0,0 +1,34 @@ +////===----------------------------------------------------------------------===// +//// +//// This source file is part of the Swift.org open source project +//// +//// Copyright (c) 2020 Apple Inc. and the Swift project authors +//// Licensed under Apache License v2.0 with Runtime Library Exception +//// +//// See https://swift.org/LICENSE.txt for license information +//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +//// +////===----------------------------------------------------------------------===// + +import Swift + +extension AsyncSequence { + public func reduce(_ initialResult: Result, _ nextPartialResult: (_ partialResult: Result, Element) async throws -> Result) async throws /*rethrows*/ -> Result { + var accumulator = initialResult + var it = makeAsyncIterator() + while let element = await try it.next() { + accumulator = await try nextPartialResult(accumulator, element) + } + return accumulator + } + + public func reduce(into initialResult: __owned Result, _ updateAccumulatingResult: (_ partialResult: inout Result, Element) async throws -> Void) async throws /*rethrows*/ -> Result { + var accumulator = initialResult + var it = makeAsyncIterator() + while let element = await try it.next() { + await try updateAccumulatingResult(&accumulator, element) + } + return accumulator + } + +} From db778e78cc87474c40ead952da8c5b6947605739 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 9 Dec 2020 15:12:33 -0800 Subject: [PATCH 04/40] Correct merge damage and rename from GeneratorProtocol to AsyncIteratorProtocol --- include/swift/AST/KnownIdentifiers.def | 1 - include/swift/AST/KnownProtocols.def | 2 +- lib/AST/ASTContext.cpp | 7 ++----- lib/IRGen/GenMeta.cpp | 2 +- lib/SILGen/SILGenStmt.cpp | 2 +- lib/Sema/CSGen.cpp | 2 +- 6 files changed, 6 insertions(+), 10 deletions(-) diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 8fdee73f2b202..feceee45626e5 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -50,7 +50,6 @@ IDENTIFIER(CoreGraphics) IDENTIFIER(CoreMedia) IDENTIFIER(CGFloat) IDENTIFIER(CoreFoundation) -IDENTIFIER_(Concurrency) IDENTIFIER(CVarArg) IDENTIFIER(Darwin) IDENTIFIER(dealloc) diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index 7bd54efd06e9d..af489b2d4a963 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -89,7 +89,7 @@ PROTOCOL(AdditiveArithmetic) PROTOCOL(Differentiable) PROTOCOL(AsyncSequence) -PROTOCOL(GeneratorProtocol) +PROTOCOL(AsyncIteratorProtocol) PROTOCOL(FloatingPoint) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 1d7e53d0567f3..b5c24b3ddf867 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -970,12 +970,9 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { case KnownProtocolKind::Differentiable: M = getLoadedModule(Id_Differentiation); break; + case KnownProtocolKind::Actor: case KnownProtocolKind::AsyncSequence: - case KnownProtocolKind::GeneratorProtocol: - M = getLoadedModule(Id_Concurrency); - break; - case KnownProtocolKind::AsyncSequence: - case KnownProtocolKind::GeneratorProtocol: + case KnownProtocolKind::AsyncIteratorProtocol: M = getLoadedModule(Id_Concurrency); break; default: diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index b3039de8b29ab..4fc6a50a8a4cc 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -5082,7 +5082,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { case KnownProtocolKind::Sequence: case KnownProtocolKind::AsyncSequence: case KnownProtocolKind::IteratorProtocol: - case KnownProtocolKind::GeneratorProtocol: + case KnownProtocolKind::AsyncIteratorProtocol: case KnownProtocolKind::RawRepresentable: case KnownProtocolKind::Equatable: case KnownProtocolKind::Hashable: diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index a20a393d0712d..725fb85fe28de 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -1003,7 +1003,7 @@ void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { // Compute the reference to the the generator's next(). auto generatorProto = - SGF.getASTContext().getProtocol(KnownProtocolKind::GeneratorProtocol); + SGF.getASTContext().getProtocol(KnownProtocolKind::AsyncIteratorProtocol); ValueDecl *generatorNextReq = generatorProto->getSingleRequirement( DeclName(SGF.getASTContext(), SGF.getASTContext().Id_next, ArrayRef())); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 48e7fce0084e7..9f5f64ddd55df 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3719,7 +3719,7 @@ generateForEachStmtConstraints( ProtocolDecl *iteratorProto = TypeChecker::getProtocol( cs.getASTContext(), stmt->getForLoc(), isAsync ? - KnownProtocolKind::GeneratorProtocol : KnownProtocolKind::IteratorProtocol); + KnownProtocolKind::AsyncIteratorProtocol : KnownProtocolKind::IteratorProtocol); if (!iteratorProto) return None; From 9096c482f2b9d3bb70aa784a74cc7865a4a9080e Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Thu, 10 Dec 2020 13:19:14 -0800 Subject: [PATCH 05/40] Add AsyncSequence types to the cmake lists --- stdlib/public/Concurrency/CMakeLists.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index 3ed283311ad41..d3831c60e8881 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -38,6 +38,20 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I Actor.swift CheckedContinuation.swift GlobalExecutor.cpp + AllSatisfy.swift + AsyncCompactMapSequence.swift + AsyncDropWhileSequence.swift + AsyncFilterSequence.swift + AsyncFlatMapSequence.swift + AsyncIteratorProtocol.swift + AsyncMapSequence.swift + AsyncPrefixWhileSequence.swift + AsyncSequence.swift + Contains.swift + Count.swift + First.swift + MinMax.Swift + Reduce.swift PartialAsyncTask.swift Task.cpp Task.swift From ae676a843c4132aca31186496a566846048beea1 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Thu, 17 Dec 2020 12:30:52 -0800 Subject: [PATCH 06/40] Add cancellation support --- include/swift/AST/ASTContext.h | 4 +- include/swift/AST/KnownIdentifiers.def | 5 ++- lib/AST/ASTContext.cpp | 15 +++---- lib/SILGen/SILGenStmt.cpp | 54 ++++++++++++++++++++++---- lib/Sema/CSGen.cpp | 4 +- 5 files changed, 62 insertions(+), 20 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 9a706aad677fe..bc9254895dd0d 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -522,8 +522,8 @@ class ASTContext final { /// Get Sequence.makeIterator(). FuncDecl *getSequenceMakeIterator() const; - /// Get AsyncSequence.makeGenerator(). - FuncDecl *getAsyncSequenceMakeGenerator() const; + /// Get AsyncSequence.makeAsyncIterator(). + FuncDecl *getAsyncSequenceMakeAsyncIterator() const; /// Check whether the standard library provides all the correct /// intrinsic support for Optional. diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index feceee45626e5..705b1aea57d33 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -40,6 +40,7 @@ IDENTIFIER(buildIf) IDENTIFIER(buildLimitedAvailability) IDENTIFIER(buildOptional) IDENTIFIER(callAsFunction) +IDENTIFIER(cancel) IDENTIFIER(Change) IDENTIFIER_WITH_NAME(code_, "_code") IDENTIFIER(CodingKeys) @@ -93,9 +94,9 @@ IDENTIFIER(KeyedEncodingContainer) IDENTIFIER(keyedBy) IDENTIFIER(keyPath) IDENTIFIER(makeIterator) -IDENTIFIER(makeGenerator) +IDENTIFIER(makeAsyncIterator) IDENTIFIER(Iterator) -IDENTIFIER(Generator) +IDENTIFIER(AsyncIterator) IDENTIFIER(load) IDENTIFIER(main) IDENTIFIER_WITH_NAME(MainEntryPoint, "$main") diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index b5c24b3ddf867..689e14d550be5 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -65,6 +65,7 @@ #include #include + using namespace swift; #define DEBUG_TYPE "ASTContext" @@ -206,8 +207,8 @@ struct ASTContext::Implementation { /// The declaration of 'Sequence.makeIterator()'. FuncDecl *MakeIterator = nullptr; - /// The declaration of 'AsyncSequence.makeGenerator()'. - FuncDecl *MakeGenerator = nullptr; + /// The declaration of 'AsyncSequence.makeAsyncIterator()'. + FuncDecl *MakeAsyncIterator = nullptr; /// The declaration of Swift.Optional.Some. EnumElementDecl *OptionalSomeDecl = nullptr; @@ -775,16 +776,16 @@ FuncDecl *ASTContext::getSequenceMakeIterator() const { return nullptr; } -FuncDecl *ASTContext::getAsyncSequenceMakeGenerator() const { - if (getImpl().MakeGenerator) { - return getImpl().MakeGenerator; +FuncDecl *ASTContext::getAsyncSequenceMakeAsyncIterator() const { + if (getImpl().MakeAsyncIterator) { + return getImpl().MakeAsyncIterator; } auto proto = getProtocol(KnownProtocolKind::AsyncSequence); if (!proto) return nullptr; - for (auto result : proto->lookupDirect(Id_makeGenerator)) { + for (auto result : proto->lookupDirect(Id_makeAsyncIterator)) { if (result->getDeclContext() != proto) continue; @@ -792,7 +793,7 @@ FuncDecl *ASTContext::getAsyncSequenceMakeGenerator() const { if (func->getParameters()->size() != 0) continue; - getImpl().MakeGenerator = func; + getImpl().MakeAsyncIterator = func; return func; } } diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 725fb85fe28de..a427a5651a8fd 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -934,6 +934,33 @@ void StmtEmitter::visitRepeatWhileStmt(RepeatWhileStmt *S) { SGF.BreakContinueDestStack.pop_back(); } +namespace { + class CancelCleanup : public Cleanup { + SILLocation loc; + std::function iteratorGen; + ConcreteDeclRef generatorCancelRef; + public: + CancelCleanup(SILLocation loc, std::function iteratorGen, + ConcreteDeclRef generatorCancelRef) : + loc(loc), iteratorGen(iteratorGen), + generatorCancelRef(generatorCancelRef) { } + + void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind) override { + FormalEvaluationScope scope(SGF); + SGF.emitApplyMethod(loc, generatorCancelRef, iteratorGen(), + PreparedArguments(ArrayRef({})), + SGFContext()); + } + + void dump(SILGenFunction &) const override { +#ifndef NDEBUG + llvm::errs() << "CancelCleanup\n" + << "State: " << getState() << "\n"; +#endif + } + }; +} + void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { // Dig out information about the sequence conformance. @@ -954,7 +981,7 @@ void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { // Compute the reference to the AsyncSequence's makeGenerator(). FuncDecl *makeGeneratorReq = - SGF.getASTContext().getAsyncSequenceMakeGenerator(); + SGF.getASTContext().getAsyncSequenceMakeAsyncIterator(); ConcreteDeclRef makeGeneratorRef(makeGeneratorReq, sequenceSubs); // Call makeGenerator(). @@ -997,18 +1024,17 @@ void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { JumpDest loopDest = createJumpDest(S->getBody()); SGF.B.emitBlock(loopDest.getBlock(), S); - // Set the destinations for 'break' and 'continue'. - JumpDest endDest = createJumpDest(S->getBody()); - SGF.BreakContinueDestStack.push_back({ S, endDest, loopDest }); - - // Compute the reference to the the generator's next(). + // Compute the reference to the the generator's next() && cancel(). auto generatorProto = SGF.getASTContext().getProtocol(KnownProtocolKind::AsyncIteratorProtocol); ValueDecl *generatorNextReq = generatorProto->getSingleRequirement( DeclName(SGF.getASTContext(), SGF.getASTContext().Id_next, ArrayRef())); + ValueDecl *generatorCancelReq = generatorProto->getSingleRequirement( + DeclName(SGF.getASTContext(), SGF.getASTContext().Id_cancel, + ArrayRef())); auto generatorAssocType = - asyncSequenceProto->getAssociatedType(SGF.getASTContext().Id_Generator); + asyncSequenceProto->getAssociatedType(SGF.getASTContext().Id_AsyncIterator); auto generatorMemberRef = DependentMemberType::get( asyncSequenceProto->getSelfInterfaceType(), generatorAssocType); auto generatorType = sequenceConformance.getAssociatedType( @@ -1018,6 +1044,12 @@ void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { auto generatorSubs = SubstitutionMap::getProtocolSubstitutions( generatorProto, generatorType, generatorConformance); ConcreteDeclRef generatorNextRef(generatorNextReq, generatorSubs); + ConcreteDeclRef generatorCancelRef(generatorCancelReq, generatorSubs); + + // Set the destinations for 'break' and 'continue'. + JumpDest endDest = createJumpDest(S->getBody()); + SGF.BreakContinueDestStack.push_back({ S, endDest, loopDest }); + auto buildArgumentSource = [&]() { if (cast(generatorNextRef.getDecl())->getSelfAccessKind() == @@ -1047,6 +1079,10 @@ void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { } return result; }; + + SGF.Cleanups.pushCleanup(SILLocation(S->getSequence()), + buildArgumentSource, generatorCancelRef); + auto cancelCleanup = SGF.Cleanups.getTopCleanup(); // Then emit the loop destination block. // // Advance the generator. Use a scope to ensure that any temporary stack @@ -1146,6 +1182,10 @@ void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { createBasicBlock(), failExitingBlock, [&](ManagedValue inputValue, SwitchCaseFullExpr &&scope) { assert(!inputValue && "None should not be passed an argument!"); + // cancelCleanup.setState(SGF, CleanupState::Dormant); + + SGF.Cleanups.forwardCleanup(cancelCleanup); + scope.exitAndBranch(S); }, SGF.loadProfilerCount(S)); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 9f5f64ddd55df..4f2e4dca72fc9 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3712,7 +3712,7 @@ generateForEachStmtConstraints( // Determine the iterator type. auto iteratorAssocType = sequenceProto->getAssociatedType(isAsync ? - cs.getASTContext().Id_Generator : cs.getASTContext().Id_Iterator); + cs.getASTContext().Id_AsyncIterator : cs.getASTContext().Id_Iterator); Type iteratorType = DependentMemberType::get(sequenceType, iteratorAssocType); // The iterator type must conform to IteratorProtocol. @@ -3725,7 +3725,7 @@ generateForEachStmtConstraints( // Reference the makeIterator witness. FuncDecl *makeIterator = isAsync ? - ctx.getAsyncSequenceMakeGenerator() : ctx.getSequenceMakeIterator(); + ctx.getAsyncSequenceMakeAsyncIterator() : ctx.getSequenceMakeIterator(); Type makeIteratorType = cs.createTypeVariable(locator, TVO_CanBindToNoEscape); From faa1da72c7d34fc67d9bb13737fb187e41585c63 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Fri, 4 Dec 2020 15:23:21 -0800 Subject: [PATCH 07/40] [DRAFT] Implementation of protocol conformance rethrowing --- include/swift/AST/ASTTypeIDZone.def | 2 + include/swift/AST/ASTTypeIDs.h | 2 + include/swift/AST/Decl.h | 82 +++++++++ include/swift/AST/Requirement.h | 1 + include/swift/AST/TypeCheckRequests.h | 39 +++++ include/swift/AST/TypeCheckerTypeIDZone.def | 7 +- lib/AST/Decl.cpp | 26 +++ lib/AST/TypeCheckRequests.cpp | 25 +++ lib/Sema/TypeCheckAttr.cpp | 30 +--- lib/Sema/TypeCheckDecl.cpp | 144 ++++++++++++++++ lib/Sema/TypeCheckEffects.cpp | 177 +++++++++++++++++--- lib/Sema/TypeCheckProtocol.cpp | 23 ++- 12 files changed, 501 insertions(+), 57 deletions(-) diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def index f6c5d333af3fb..c076ecf16d8da 100644 --- a/include/swift/AST/ASTTypeIDZone.def +++ b/include/swift/AST/ASTTypeIDZone.def @@ -31,6 +31,8 @@ SWIFT_TYPEID(PropertyWrapperTypeInfo) SWIFT_TYPEID(Requirement) SWIFT_TYPEID(ResilienceExpansion) SWIFT_TYPEID(FragileFunctionKind) +SWIFT_TYPEID(FunctionRethrowingKind) +SWIFT_TYPEID(ProtocolRethrowsRequirementList) SWIFT_TYPEID(TangentPropertyInfo) SWIFT_TYPEID(SymbolSourceMap) SWIFT_TYPEID(Type) diff --git a/include/swift/AST/ASTTypeIDs.h b/include/swift/AST/ASTTypeIDs.h index 6d217514e20b2..f647bad61928c 100644 --- a/include/swift/AST/ASTTypeIDs.h +++ b/include/swift/AST/ASTTypeIDs.h @@ -64,6 +64,8 @@ class ProtocolDecl; class Requirement; enum class ResilienceExpansion : unsigned; struct FragileFunctionKind; +enum class FunctionRethrowingKind : uint8_t; +class ProtocolRethrowsRequirementList; class SourceFile; class SymbolSourceMap; struct TangentPropertyInfo; diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 54ce64eb5de44..418aabfa2281f 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3870,6 +3870,66 @@ enum class KnownDerivableProtocolKind : uint8_t { Actor, }; +class ProtocolRethrowsRequirementList { +public: + typedef std::pair Entry; + +private: + ArrayRef entries; + +public: + ProtocolRethrowsRequirementList(ArrayRef entries) : entries(entries) {} + ProtocolRethrowsRequirementList() : entries() {} + + typedef const Entry *const_iterator; + typedef const_iterator iterator; + + const_iterator begin() const { return entries.begin(); } + const_iterator end() const { return entries.end(); } + + size_t size() const { return entries.size(); } + + void print(raw_ostream &OS) const; + + SWIFT_DEBUG_DUMP; + + friend bool operator==(const ProtocolRethrowsRequirementList &lhs, + const ProtocolRethrowsRequirementList &rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + auto lhsIter = lhs.begin(); + auto rhsIter = rhs.begin(); + while (lhsIter != lhs.end() && rhsIter != rhs.end()) { + if (lhsIter->first->isEqual(rhsIter->first)) { + return false; + } + if (lhsIter->second != rhsIter->second) { + return false; + } + } + return true; + } + + friend bool operator!=(const ProtocolRethrowsRequirementList &lhs, + const ProtocolRethrowsRequirementList &rhs) { + return !(lhs == rhs); + } + + friend llvm::hash_code hash_value( + const ProtocolRethrowsRequirementList &list) { + return llvm::hash_combine(list.size()); // it is good enought for + // llvm::hash_code hash; + // for (auto entry : list) { + // hash = llvm::hash_combine(hash, entry.first->getCanonicalType()); + // hash = llvm::hash_combine(hash, entry.second); + // } + // return hash; + } +}; + +void simple_display(raw_ostream &out, const ProtocolRethrowsRequirementList reqs); + /// ProtocolDecl - A declaration of a protocol, for example: /// /// protocol Drawable { @@ -4051,6 +4111,9 @@ class ProtocolDecl final : public NominalTypeDecl { /// contain 'Self' in 'parameter' or 'other' position. bool existentialTypeSupported() const; + ProtocolRethrowsRequirementList getRethrowingRequirements() const; + bool isRethrowingProtocol() const; + private: void computeKnownProtocolKind() const; @@ -5460,6 +5523,23 @@ class ImportAsMemberStatus { } }; +enum class FunctionRethrowingKind : uint8_t { + /// The function is not throwing + None, + + /// The function rethrows by closure + ByClosure, + + /// The function rethrows by conformance + ByConformance, + + /// The function throws + Throws, + + /// The function throwing determinate is invalid + Invalid +}; + /// Base class for function-like declarations. class AbstractFunctionDecl : public GenericContext, public ValueDecl { friend class NeedsNewVTableEntryRequest; @@ -5663,6 +5743,8 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// Returns true if the function body throws. bool hasThrows() const { return Bits.AbstractFunctionDecl.Throws; } + FunctionRethrowingKind getRethrowingKind() const; + // FIXME: Hack that provides names with keyword arguments for accessors. DeclName getEffectiveFullName() const; diff --git a/include/swift/AST/Requirement.h b/include/swift/AST/Requirement.h index 61a9c220dc544..3e87a8f989df4 100644 --- a/include/swift/AST/Requirement.h +++ b/include/swift/AST/Requirement.h @@ -46,6 +46,7 @@ enum class RequirementKind : unsigned { // when adding enumerators. }; + /// A single requirement placed on the type parameters (or associated /// types thereof) of a class Requirement { diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 2e0f6c0696817..732d2c1441a32 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -311,6 +311,25 @@ class ExistentialTypeSupportedRequest : void cacheResult(bool value) const; }; +class ProtocolRethrowsRequirementsRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + ProtocolRethrowsRequirementList + evaluate(Evaluator &evaluator, ProtocolDecl *decl) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + /// Determine whether the given declaration is 'final'. class IsFinalRequest : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + FunctionRethrowingKind evaluate(Evaluator &evaluator, AbstractFunctionDecl *decl) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + +void simple_display(llvm::raw_ostream &out, FunctionRethrowingKind value); + /// Request the custom attribute which attaches a result builder to the /// given declaration. class AttachedResultBuilderRequest : diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 4c24a181a1888..69f4fe22d1399 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -204,8 +204,10 @@ SWIFT_REQUEST(TypeChecker, RequiresOpaqueAccessorsRequest, bool(VarDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, RequiresOpaqueModifyCoroutineRequest, bool(AbstractStorageDecl *), SeparatelyCached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, FragileFunctionKindRequest, +SWIFT_REQUEST(TypeChecker, FunctionRethrowingKindRequest, FragileFunctionKind(DeclContext *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, FragileFunctionKindRequest, + FunctionRethrowingKind(AbstractFunctionDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, SelfAccessKindRequest, SelfAccessKind(FuncDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, StorageImplInfoRequest, @@ -260,6 +262,9 @@ SWIFT_REQUEST(TypeChecker, ResolveImplicitMemberRequest, SWIFT_REQUEST(TypeChecker, ResolveTypeEraserTypeRequest, Type(ProtocolDecl *, TypeEraserAttr *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, ProtocolRethrowsRequirementsRequest, + ProtocolRethrowsRequirementList(ProtocolDecl *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ResolveTypeRequest, Type (const TypeResolution *, TypeRepr *, GenericParamList *), Uncached, NoLocationInfo) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 448702258dab5..1b95a4af2433f 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4929,6 +4929,26 @@ bool ProtocolDecl::existentialTypeSupported() const { ExistentialTypeSupportedRequest{const_cast(this)}, true); } +void swift::simple_display(llvm::raw_ostream &out, const ProtocolRethrowsRequirementList list) { + for (auto entry : list) { + simple_display(out, entry.first); + simple_display(out, entry.second); + } +} + + +ProtocolRethrowsRequirementList +ProtocolDecl::getRethrowingRequirements() const { + return evaluateOrDefault(getASTContext().evaluator, + ProtocolRethrowsRequirementsRequest{const_cast(this)}, + ProtocolRethrowsRequirementList()); +} + +bool +ProtocolDecl::isRethrowingProtocol() const { + return getRethrowingRequirements().size() > 0; +} + StringRef ProtocolDecl::getObjCRuntimeName( llvm::SmallVectorImpl &buffer) const { // If there is an 'objc' attribute with a name, use that name. @@ -6769,6 +6789,12 @@ bool AbstractFunctionDecl::canBeAsyncHandler() const { false); } +FunctionRethrowingKind AbstractFunctionDecl::getRethrowingKind() const { + return evaluateOrDefault(getASTContext().evaluator, + FunctionRethrowingKindRequest{const_cast(this)}, + FunctionRethrowingKind::Invalid); +} + BraceStmt *AbstractFunctionDecl::getBody(bool canSynthesize) const { if ((getBodyKind() == BodyKind::Synthesize || getBodyKind() == BodyKind::Unparsed) && diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 5cd9930133df2..a9d2414efbdc2 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -278,6 +278,31 @@ void ExistentialTypeSupportedRequest::cacheResult(bool value) const { decl->setCachedExistentialTypeSupported(value); } +//----------------------------------------------------------------------------// +// getRethrowingKind computation. +//----------------------------------------------------------------------------// + +void swift::simple_display(llvm::raw_ostream &out, + FunctionRethrowingKind kind) { + switch (kind) { + case FunctionRethrowingKind::None: + out << "non-throwing"; + break; + case FunctionRethrowingKind::ByClosure: + out << "by closure"; + break; + case FunctionRethrowingKind::ByConformance: + out << "by conformance"; + break; + case FunctionRethrowingKind::Throws: + out << "throws"; + break; + case FunctionRethrowingKind::Invalid: + out << "invalid"; + break; + } +} + //----------------------------------------------------------------------------// // isFinal computation. //----------------------------------------------------------------------------// diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 2b87eed617852..181079fc11b51 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2134,38 +2134,12 @@ void AttributeChecker::visitRequiredAttr(RequiredAttr *attr) { } } -static bool hasThrowingFunctionParameter(CanType type) { - // Only consider throwing function types. - if (auto fnType = dyn_cast(type)) { - return fnType->getExtInfo().isThrowing(); - } - - // Look through tuples. - if (auto tuple = dyn_cast(type)) { - for (auto eltType : tuple.getElementTypes()) { - if (hasThrowingFunctionParameter(eltType)) - return true; - } - return false; - } - - // Suppress diagnostics in the presence of errors. - if (type->hasError()) { - return true; - } - - return false; -} - void AttributeChecker::visitRethrowsAttr(RethrowsAttr *attr) { // 'rethrows' only applies to functions that take throwing functions // as parameters. auto fn = cast(D); - for (auto param : *fn->getParameters()) { - if (hasThrowingFunctionParameter(param->getType() - ->lookThroughAllOptionalTypes() - ->getCanonicalType())) - return; + if (fn->getRethrowingKind() != FunctionRethrowingKind::Invalid) { + return; } diagnose(attr->getLocation(), diag::rethrows_without_throwing_parameter); diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index f5b253baa1d89..6e09fe1f6a69a 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -696,6 +696,150 @@ ExistentialTypeSupportedRequest::evaluate(Evaluator &evaluator, return true; } +static bool hasThrowingFunctionClosureParameter(CanType type) { + // Only consider throwing function types. + if (auto fnType = dyn_cast(type)) { + return fnType->getExtInfo().isThrowing(); + } + + // Look through tuples. + if (auto tuple = dyn_cast(type)) { + for (auto eltType : tuple.getElementTypes()) { + auto elt = eltType->lookThroughAllOptionalTypes()->getCanonicalType(); + if (hasThrowingFunctionClosureParameter(elt)) + return true; + } + return false; + } + + // Suppress diagnostics in the presence of errors. + if (type->hasError()) { + return true; + } + + return false; +} + +static FunctionRethrowingKind +getTypeThrowingKind(Type interfaceTy, GenericSignature genericSig) { + if (interfaceTy->isTypeParameter()) { + for (auto proto : genericSig->getRequiredProtocols(interfaceTy)) { + if (proto->isRethrowingProtocol()) { + return FunctionRethrowingKind::ByConformance; + } + } + } else if (auto NTD = interfaceTy->getNominalOrBoundGenericNominal()) { + if (auto genericSig = NTD->getGenericSignature()) { + for (auto req : genericSig->getRequirements()) { + if (req.getKind() == RequirementKind::Conformance) { + if (req.getSecondType()->castTo() + ->getDecl() + ->isRethrowingProtocol()) { + return FunctionRethrowingKind::ByConformance; + } + } + } + } + } + return FunctionRethrowingKind::Invalid; +} + +static FunctionRethrowingKind +getParameterThrowingKind(AbstractFunctionDecl *decl, + GenericSignature genericSig) { + FunctionRethrowingKind kind = FunctionRethrowingKind::Invalid; + // check all parameters to determine if any are closures that throw + + for (auto param : *decl->getParameters()) { + auto interfaceTy = param->getInterfaceType(); + if (hasThrowingFunctionClosureParameter(interfaceTy + ->lookThroughAllOptionalTypes() + ->getCanonicalType())) { + // closure rethrowing supersedes conformance rethrowing + return FunctionRethrowingKind::ByClosure; + } + + if (kind == FunctionRethrowingKind::Invalid) { + kind = getTypeThrowingKind(interfaceTy, genericSig); + } + } + + return kind; +} + +ProtocolRethrowsRequirementList +ProtocolRethrowsRequirementsRequest::evaluate(Evaluator &evaluator, + ProtocolDecl *decl) const { + SmallVector, 2> found; + + // check if immediate members of protocol are 'rethrows' + for (auto member : decl->getMembers()) { + auto fnDecl = dyn_cast(member); + // it must be a function + // it must have a rethrows attribute + // it must not have any parameters that are closures that cause rethrowing + if (!fnDecl || + !fnDecl->getAttrs().hasAttribute()) { + continue; + } + + GenericSignature genericSig = fnDecl->getGenericSignature(); + auto kind = getParameterThrowingKind(fnDecl, genericSig); + // skip closure based rethrowing cases + if (kind == FunctionRethrowingKind::ByClosure) { + continue; + } + // we now have a protocol member that has a rethrows and no closure + // parameters contributing to it's rethrowing-ness + found.push_back( + std::pair(decl->getSelfInterfaceType(), fnDecl)); + } + llvm::DenseSet checkedProtocols; + checkedProtocols.insert(decl); + + // check associated conformances of associated types or inheritance + for (auto requirement : decl->getRequirementSignature()) { + if (requirement.getKind() != RequirementKind::Conformance) { + continue; + } + auto protoTy = requirement.getSecondType()->castTo(); + auto proto = protoTy->getDecl(); + if (checkedProtocols.count(proto) != 0) { + continue; + } + checkedProtocols.insert(proto); + for (auto entry : proto->getRethrowingRequirements()) { + found.emplace_back(requirement.getFirstType(), entry.second); + } + } + ASTContext &ctx = decl->getASTContext(); + return ProtocolRethrowsRequirementList(ctx.AllocateCopy(found)); +} + +FunctionRethrowingKind +FunctionRethrowingKindRequest::evaluate(Evaluator &evaluator, + AbstractFunctionDecl *decl) const { + if (decl->getAttrs().hasAttribute()) { + GenericSignature genericSig = decl->getGenericSignature(); + FunctionRethrowingKind kind = getParameterThrowingKind(decl, genericSig); + // since we have checked all arguments, if we still havent found anything + // check the self parameter + if (kind == FunctionRethrowingKind::Invalid && + decl->hasImplicitSelfDecl()) { + auto selfParam = decl->getImplicitSelfDecl(); + if (selfParam) { + auto interfaceTy = selfParam->getInterfaceType(); + kind = getTypeThrowingKind(interfaceTy, genericSig); + } + } + + return kind; + } else if (decl->hasThrows()) { + return FunctionRethrowingKind::Throws; + } + return FunctionRethrowingKind::None; +} + bool IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { if (isa(decl)) diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 81d405bb1465f..1e6318abfd5b1 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -22,6 +22,7 @@ #include "swift/AST/Initializer.h" #include "swift/AST/Pattern.h" #include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/ProtocolConformance.h" using namespace swift; @@ -44,33 +45,44 @@ class AbstractFunction { unsigned TheKind : 2; unsigned IsRethrows : 1; unsigned ParamCount : 2; + FunctionRethrowingKind rethrowingKind; + ConcreteDeclRef declRef; public: - explicit AbstractFunction(Kind kind, Expr *fn) + explicit AbstractFunction(Kind kind, Expr *fn, ConcreteDeclRef declRef) : TheKind(kind), IsRethrows(false), - ParamCount(1) { + ParamCount(1), + rethrowingKind(FunctionRethrowingKind::Invalid), + declRef(declRef) { TheExpr = fn; } - explicit AbstractFunction(AbstractFunctionDecl *fn) + explicit AbstractFunction(AbstractFunctionDecl *fn, ConcreteDeclRef declRef) : TheKind(Kind::Function), IsRethrows(fn->getAttrs().hasAttribute()), - ParamCount(fn->getNumCurryLevels()) { + ParamCount(fn->getNumCurryLevels()), + rethrowingKind(fn->getRethrowingKind()), + declRef(declRef) { TheFunction = fn; } - explicit AbstractFunction(AbstractClosureExpr *closure) + explicit AbstractFunction(AbstractClosureExpr *closure, + ConcreteDeclRef declRef) : TheKind(Kind::Closure), IsRethrows(false), - ParamCount(1) { + ParamCount(1), + rethrowingKind(FunctionRethrowingKind::Invalid), + declRef(declRef) { TheClosure = closure; } - explicit AbstractFunction(ParamDecl *parameter) + explicit AbstractFunction(ParamDecl *parameter, ConcreteDeclRef declRef) : TheKind(Kind::Parameter), IsRethrows(false), - ParamCount(1) { + ParamCount(1), + rethrowingKind(FunctionRethrowingKind::Invalid), + declRef(declRef) { TheParameter = parameter; } @@ -79,6 +91,8 @@ class AbstractFunction { /// Whether the function is marked 'rethrows'. bool isBodyRethrows() const { return IsRethrows; } + FunctionRethrowingKind getRethrowingKind() const { return rethrowingKind; } + unsigned getNumArgumentsForFullApply() const { return ParamCount; } @@ -116,18 +130,34 @@ class AbstractFunction { return TheExpr; } + ConcreteDeclRef getDeclRef() { + return declRef; + } + + ModuleDecl *getModuleContext() { + assert(getKind() == Kind::Function); + return TheFunction->getModuleContext(); + } + static AbstractFunction decomposeApply(ApplyExpr *apply, SmallVectorImpl &args) { Expr *fn; + ConcreteDeclRef declRef; do { args.push_back(apply->getArg()); - fn = apply->getFn()->getValueProvidingExpr(); + auto applyFn = apply->getFn(); + if (!declRef) { + if (auto DRE = dyn_cast(applyFn)) { + declRef = DRE->getDeclRef(); + } + } + fn = applyFn->getValueProvidingExpr(); } while ((apply = dyn_cast(fn))); - return decomposeFunction(fn); + return decomposeFunction(fn, declRef); } - static AbstractFunction decomposeFunction(Expr *fn) { + static AbstractFunction decomposeFunction(Expr *fn, ConcreteDeclRef declRef = ConcreteDeclRef()) { assert(fn->getValueProvidingExpr() == fn); while (true) { @@ -158,25 +188,25 @@ class AbstractFunction { // Constructor delegation. if (auto otherCtorDeclRef = dyn_cast(fn)) { - return AbstractFunction(otherCtorDeclRef->getDecl()); + return AbstractFunction(otherCtorDeclRef->getDecl(), declRef); } // Normal function references. - if (auto declRef = dyn_cast(fn)) { - ValueDecl *decl = declRef->getDecl(); + if (auto DRE = dyn_cast(fn)) { + ValueDecl *decl = DRE->getDecl(); if (auto fn = dyn_cast(decl)) { - return AbstractFunction(fn); + return AbstractFunction(fn, declRef); } else if (auto param = dyn_cast(decl)) { - return AbstractFunction(param); + return AbstractFunction(param, declRef); } // Closures. } else if (auto closure = dyn_cast(fn)) { - return AbstractFunction(closure); + return AbstractFunction(closure, declRef); } // Everything else is opaque. - return AbstractFunction(Kind::Opaque, fn); + return AbstractFunction(Kind::Opaque, fn, declRef); } }; @@ -451,6 +481,90 @@ class ApplyClassifier { DeclContext *RethrowsDC = nullptr; bool inRethrowsContext() const { return RethrowsDC != nullptr; } + bool classifyWitnessAsThrows(ModuleDecl *module, + SubstitutionMap substitutions) { + + + for (auto conformanceRef : substitutions.getConformances()) { + if (!conformanceRef.isConcrete()) { + return true; + } + auto conformance = conformanceRef.getConcrete(); + auto DC = conformance->getDeclContext(); + auto requiredProtocol = conformanceRef.getRequirement(); + for (auto req : requiredProtocol->getRethrowingRequirements()) { + auto reqProtocol = cast(req.second->getDeclContext()); + auto reqConformanceRef = + conformance->getAssociatedConformance(req.first, reqProtocol); + if (!reqConformanceRef.isConcrete()) { + return true; + } + auto reqConformance = reqConformanceRef.getConcrete(); + auto reqTy = reqConformance->getType(); + auto declRef = reqConformance->getWitnessDeclRef(req.second); + auto witnessDecl = cast(declRef.getDecl()); + switch (witnessDecl->getRethrowingKind()) { + case FunctionRethrowingKind::ByConformance: + if (classifyWitnessAsThrows(module, + reqTy->getContextSubstitutionMap(module, DC))) { + return true; + } + break; + case FunctionRethrowingKind::None: + break; + case FunctionRethrowingKind::Throws: + return true; + default: + return true; // should return none + } + } + } + + return false; + // if (conformanceRef.isConcrete()) { + + // auto conformance = conformanceRef.getConcrete(); + + + // // SmallVector, 4> unsatisfied; + // // for (auto req : requirements) { + // // if (req.first->isEqual(requiredProtocol->getSelfInterfaceType())) { + // // auto declRef = conformance->getWitnessDeclRef(req.second); + // // auto witnessDecl = cast(declRef.getDecl()); + // // switch (witnessDecl->getRethrowingKind()) { + // // case FunctionRethrowingKind::ByConformance: + // // classification.merge( + // // classifyWitness(module, declRef.getSubstitutions(), isAsync)); + // // break; + // // case FunctionRethrowingKind::None: + // // classification.merge(isAsync ? + // // Classification::forAsync() : Classification()); + // // break; + // // case FunctionRethrowingKind::Throws: + // // classification.merge( + // // Classification::forThrow( + // // PotentialThrowReason::forThrowingApply(), isAsync)); + // // break; + // // default: + // // classification.merge(Classification::forInvalidCode()); + // // break; + // // } + // // } else { + // // unsatisfied.push_back(req); + // // } + // // } + + // } else { + // auto protocol = conformanceRef.getAbstract(); + // if (protocol->isRethrowingProtocol()) { + // // NOT TRUE, but eh close enough.... + // classification.merge(Classification::forRethrowingOnly( + // PotentialThrowReason::forThrowingApply(), isAsync)); + // } + // } + + } + /// Check to see if the given function application throws or is async. Classification classifyApply(ApplyExpr *E) { // An apply expression is a potential throw site if the function throws. @@ -464,14 +578,30 @@ class ApplyClassifier { if (!fnType) return Classification::forInvalidCode(); bool isAsync = fnType->isAsync() || E->implicitlyAsync(); - - // If the function doesn't throw at all, we're done here. - if (!fnType->isThrowing()) - return isAsync ? Classification::forAsync() : Classification(); - // Decompose the application. SmallVector args; auto fnRef = AbstractFunction::decomposeApply(E, args); + auto rethrowsKind = fnRef.getRethrowingKind(); + + // If the function doesn't throw at all, we're done here. + if (rethrowsKind == FunctionRethrowingKind::None) { + return isAsync ? Classification::forAsync() : Classification(); + } else if (rethrowsKind == FunctionRethrowingKind::ByConformance) { + auto substitutions = fnRef.getDeclRef().getSubstitutions(); + if (classifyWitnessAsThrows(fnRef.getModuleContext(), substitutions)) { + return Classification::forRethrowingOnly( + PotentialThrowReason::forThrowingApply(), isAsync); + } else { + return isAsync ? Classification::forAsync() : Classification(); + } + } else if (rethrowsKind == FunctionRethrowingKind::Throws) { + return Classification::forThrow( + PotentialThrowReason::forThrowingApply(), isAsync); + } + + // If the function doesn't throw at all, we're done here. + if (!fnType->isThrowing()) + return isAsync ? Classification::forAsync() : Classification(); // If any of the arguments didn't type check, fail. for (auto arg : args) { @@ -1967,6 +2097,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker } ShouldRecurse_t checkTry(TryExpr *E) { + // Walk the operand. ContextScope scope(*this, None); scope.enterTry(); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index a11cea294dcf2..39252f8b0d532 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -558,12 +558,25 @@ swift::matchWitness( return RequirementMatch(witness, MatchKind::MutatingConflict); // If the requirement is rethrows, the witness must either be - // rethrows or be non-throwing. + // rethrows or be non-throwing if the requirement is not by conformance + // else the witness can be by conformance, throwing or non throwing if (reqAttrs.hasAttribute() && - !witnessAttrs.hasAttribute() && - cast(witness)->hasThrows()) - return RequirementMatch(witness, MatchKind::RethrowsConflict); - + !witnessAttrs.hasAttribute()) { + auto reqRethrowingKind = funcReq->getRethrowingKind(); + auto witnessRethrowingKind = funcWitness->getRethrowingKind(); + if (reqRethrowingKind == FunctionRethrowingKind::ByConformance) { + switch (witnessRethrowingKind) { + case FunctionRethrowingKind::ByConformance: + case FunctionRethrowingKind::Throws: + case FunctionRethrowingKind::None: + break; + default: + return RequirementMatch(witness, MatchKind::RethrowsConflict); + } + } else if (cast(witness)->hasThrows()) { + return RequirementMatch(witness, MatchKind::RethrowsConflict); + } + } // We want to decompose the parameters to handle them separately. decomposeFunctionType = true; } else if (auto *witnessASD = dyn_cast(witness)) { From df70e942d66c70f4df09fd2355b632b3fbb7df7d Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Sat, 5 Dec 2020 16:06:10 -0800 Subject: [PATCH 08/40] Account for ASTVerifier passes to ensure throwing and by conformance rethrowing verifies appropriately --- lib/AST/ASTVerifier.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index 200a485eec0e4..6a1a15583fe6e 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -1803,11 +1803,20 @@ class Verifier : public ASTWalker { Out << "\n"; abort(); } else if (E->throws() && !FT->isThrowing()) { - Out << "apply expression is marked as throwing, but function operand" - "does not have a throwing function type\n"; - E->dump(Out); - Out << "\n"; - abort(); + FunctionRethrowingKind rethrowingKind = FunctionRethrowingKind::Invalid; + if (auto DRE = dyn_cast(E->getFn())) { + if (auto fnDecl = dyn_cast(DRE->getDecl())) { + rethrowingKind = fnDecl->getRethrowingKind(); + } + } + if (rethrowingKind != FunctionRethrowingKind::ByConformance && + rethrowingKind != FunctionRethrowingKind::Throws) { + Out << "apply expression is marked as throwing, but function operand" + "does not have a throwing function type\n"; + E->dump(Out); + Out << "\n"; + abort(); + } } if (E->isSuper() != E->getArg()->isSuperExpr()) { From d8cef33fb482aeb75806ad5068fc9a32f43bee4c Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Mon, 7 Dec 2020 07:37:48 -0800 Subject: [PATCH 09/40] Remove commented out code --- lib/Sema/TypeCheckEffects.cpp | 42 ----------------------------------- 1 file changed, 42 deletions(-) diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 1e6318abfd5b1..103a836e1fa07 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -521,48 +521,6 @@ class ApplyClassifier { } return false; - // if (conformanceRef.isConcrete()) { - - // auto conformance = conformanceRef.getConcrete(); - - - // // SmallVector, 4> unsatisfied; - // // for (auto req : requirements) { - // // if (req.first->isEqual(requiredProtocol->getSelfInterfaceType())) { - // // auto declRef = conformance->getWitnessDeclRef(req.second); - // // auto witnessDecl = cast(declRef.getDecl()); - // // switch (witnessDecl->getRethrowingKind()) { - // // case FunctionRethrowingKind::ByConformance: - // // classification.merge( - // // classifyWitness(module, declRef.getSubstitutions(), isAsync)); - // // break; - // // case FunctionRethrowingKind::None: - // // classification.merge(isAsync ? - // // Classification::forAsync() : Classification()); - // // break; - // // case FunctionRethrowingKind::Throws: - // // classification.merge( - // // Classification::forThrow( - // // PotentialThrowReason::forThrowingApply(), isAsync)); - // // break; - // // default: - // // classification.merge(Classification::forInvalidCode()); - // // break; - // // } - // // } else { - // // unsatisfied.push_back(req); - // // } - // // } - - // } else { - // auto protocol = conformanceRef.getAbstract(); - // if (protocol->isRethrowingProtocol()) { - // // NOT TRUE, but eh close enough.... - // classification.merge(Classification::forRethrowingOnly( - // PotentialThrowReason::forThrowingApply(), isAsync)); - // } - // } - } /// Check to see if the given function application throws or is async. From 0bf59699a18e0a7154ab927d57bebc05507844e3 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Mon, 7 Dec 2020 07:38:29 -0800 Subject: [PATCH 10/40] OtherConstructorDeclRefExpr can also be a source of a rethrowing kind function --- lib/AST/ASTVerifier.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index 6a1a15583fe6e..c9c8f74ae5253 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -1808,7 +1808,12 @@ class Verifier : public ASTWalker { if (auto fnDecl = dyn_cast(DRE->getDecl())) { rethrowingKind = fnDecl->getRethrowingKind(); } + } else if (auto OCDRE = dyn_cast(E->getFn())) { + if (auto fnDecl = dyn_cast(OCDRE->getDecl())) { + rethrowingKind = fnDecl->getRethrowingKind(); + } } + if (rethrowingKind != FunctionRethrowingKind::ByConformance && rethrowingKind != FunctionRethrowingKind::Throws) { Out << "apply expression is marked as throwing, but function operand" From f3f7b4f0a84175e44fb397459cc452d51bdd798f Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Mon, 7 Dec 2020 09:12:02 -0800 Subject: [PATCH 11/40] Re-order the checkApply logic to account for existing throwing calculations better --- lib/Sema/TypeCheckEffects.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 103a836e1fa07..fb970aa359592 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -539,12 +539,14 @@ class ApplyClassifier { // Decompose the application. SmallVector args; auto fnRef = AbstractFunction::decomposeApply(E, args); - auto rethrowsKind = fnRef.getRethrowingKind(); - // If the function doesn't throw at all, we're done here. - if (rethrowsKind == FunctionRethrowingKind::None) { - return isAsync ? Classification::forAsync() : Classification(); - } else if (rethrowsKind == FunctionRethrowingKind::ByConformance) { + // If any of the arguments didn't type check, fail. + for (auto arg : args) { + if (!arg->getType() || arg->getType()->hasError()) + return Classification::forInvalidCode(); + } + + if (fnRef.getRethrowingKind() == FunctionRethrowingKind::ByConformance) { auto substitutions = fnRef.getDeclRef().getSubstitutions(); if (classifyWitnessAsThrows(fnRef.getModuleContext(), substitutions)) { return Classification::forRethrowingOnly( @@ -552,19 +554,11 @@ class ApplyClassifier { } else { return isAsync ? Classification::forAsync() : Classification(); } - } else if (rethrowsKind == FunctionRethrowingKind::Throws) { - return Classification::forThrow( - PotentialThrowReason::forThrowingApply(), isAsync); } // If the function doesn't throw at all, we're done here. - if (!fnType->isThrowing()) + if (!fnType->isThrowing()) { return isAsync ? Classification::forAsync() : Classification(); - - // If any of the arguments didn't type check, fail. - for (auto arg : args) { - if (!arg->getType() || arg->getType()->hasError()) - return Classification::forInvalidCode(); } // If we're applying more arguments than the natural argument From c0b9d39e7df9d46f259d85b45e78da6176b5475c Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Mon, 7 Dec 2020 17:01:55 -0800 Subject: [PATCH 12/40] Extract rethrowing calculation into smaller functions --- lib/Sema/TypeCheckEffects.cpp | 74 +++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index fb970aa359592..5edd71affd358 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -481,6 +481,52 @@ class ApplyClassifier { DeclContext *RethrowsDC = nullptr; bool inRethrowsContext() const { return RethrowsDC != nullptr; } + bool classifyRequirement(ModuleDecl *module, + ProtocolConformance *reqConformance, + ValueDecl *requiredFn) { + auto DC = reqConformance->getDeclContext(); + auto reqTy = reqConformance->getType(); + auto declRef = reqConformance->getWitnessDeclRef(requiredFn); + auto witnessDecl = cast(declRef.getDecl()); + switch (witnessDecl->getRethrowingKind()) { + case FunctionRethrowingKind::ByConformance: + if (classifyWitnessAsThrows(module, + reqTy->getContextSubstitutionMap(module, DC))) { + return true; + } + break; + case FunctionRethrowingKind::None: + break; + case FunctionRethrowingKind::Throws: + return true; + default: + return true; + } + return false; + } + + bool classifyTypeRequirement(ModuleDecl *module, Type protoType, + ValueDecl *requiredFn, + ProtocolConformance *conformance, + ProtocolDecl *requiredProtocol) { + auto reqProtocol = cast(requiredFn->getDeclContext()); + ProtocolConformance *reqConformance; + + if(protoType->isEqual(reqProtocol->getSelfInterfaceType()) && + requiredProtocol == reqProtocol) { + reqConformance = conformance; + } else { + auto reqConformanceRef = + conformance->getAssociatedConformance(protoType, reqProtocol); + if (!reqConformanceRef.isConcrete()) { + return true; + } + reqConformance = reqConformanceRef.getConcrete(); + } + + return classifyRequirement(module, reqConformance, requiredFn); + } + bool classifyWitnessAsThrows(ModuleDecl *module, SubstitutionMap substitutions) { @@ -490,33 +536,13 @@ class ApplyClassifier { return true; } auto conformance = conformanceRef.getConcrete(); - auto DC = conformance->getDeclContext(); + auto requiredProtocol = conformanceRef.getRequirement(); for (auto req : requiredProtocol->getRethrowingRequirements()) { - auto reqProtocol = cast(req.second->getDeclContext()); - auto reqConformanceRef = - conformance->getAssociatedConformance(req.first, reqProtocol); - if (!reqConformanceRef.isConcrete()) { + if (classifyTypeRequirement(module, req.first, req.second, + conformance, requiredProtocol)) { return true; } - auto reqConformance = reqConformanceRef.getConcrete(); - auto reqTy = reqConformance->getType(); - auto declRef = reqConformance->getWitnessDeclRef(req.second); - auto witnessDecl = cast(declRef.getDecl()); - switch (witnessDecl->getRethrowingKind()) { - case FunctionRethrowingKind::ByConformance: - if (classifyWitnessAsThrows(module, - reqTy->getContextSubstitutionMap(module, DC))) { - return true; - } - break; - case FunctionRethrowingKind::None: - break; - case FunctionRethrowingKind::Throws: - return true; - default: - return true; // should return none - } } } @@ -551,8 +577,6 @@ class ApplyClassifier { if (classifyWitnessAsThrows(fnRef.getModuleContext(), substitutions)) { return Classification::forRethrowingOnly( PotentialThrowReason::forThrowingApply(), isAsync); - } else { - return isAsync ? Classification::forAsync() : Classification(); } } From de8ddbe0d7b1f6cc4084a424047bedaf8f574d30 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Mon, 7 Dec 2020 17:02:20 -0800 Subject: [PATCH 13/40] Allow for closures and protocol conformances to contribute to throwing --- lib/Sema/TypeCheckDecl.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 6e09fe1f6a69a..83496428240b1 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -749,21 +749,23 @@ getParameterThrowingKind(AbstractFunctionDecl *decl, GenericSignature genericSig) { FunctionRethrowingKind kind = FunctionRethrowingKind::Invalid; // check all parameters to determine if any are closures that throw - + bool foundThrowingClosure = false; for (auto param : *decl->getParameters()) { auto interfaceTy = param->getInterfaceType(); if (hasThrowingFunctionClosureParameter(interfaceTy ->lookThroughAllOptionalTypes() ->getCanonicalType())) { - // closure rethrowing supersedes conformance rethrowing - return FunctionRethrowingKind::ByClosure; + foundThrowingClosure = true; } if (kind == FunctionRethrowingKind::Invalid) { kind = getTypeThrowingKind(interfaceTy, genericSig); } } - + if (kind == FunctionRethrowingKind::Invalid && + foundThrowingClosure) { + return FunctionRethrowingKind::ByClosure; + } return kind; } From 8e5b1ddd658697c9fb4832567b5f8643e3219f63 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Mon, 7 Dec 2020 17:02:39 -0800 Subject: [PATCH 14/40] Add unit tests for conformance based rethrowing --- test/attr/attr_rethrows_protocol.swift | 75 ++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 test/attr/attr_rethrows_protocol.swift diff --git a/test/attr/attr_rethrows_protocol.swift b/test/attr/attr_rethrows_protocol.swift new file mode 100644 index 0000000000000..3efda60597cd5 --- /dev/null +++ b/test/attr/attr_rethrows_protocol.swift @@ -0,0 +1,75 @@ +// RUN: %target-typecheck-verify-swift + +protocol RethrowingProtocol { + func source() rethrows +} + +struct Rethrows: RethrowingProtocol { + var other: Source + func source() rethrows { } +} + +struct Throws: RethrowingProtocol { + func source() throws { } +} + +struct ThrowsWithSource: RethrowingProtocol { + var other: Source + func source() throws { } +} + +struct NonThrows: RethrowingProtocol { + func source() { } +} + +struct NonThrowsWithSource: RethrowingProtocol { + var other: Source + func source() { } +} + +protocol InvalidRethrowingProtocol { + func source() rethrows // expected-note{{}} +} + +struct InvalidRethrows : InvalidRethrowingProtocol { + // expected-error@-1{{type 'InvalidRethrows' does not conform to protocol 'InvalidRethrowingProtocol'}} + func source() rethrows { } // expected-note{{}} + // expected-error@-1{{'rethrows' function must take a throwing function argument}} +} + +func freeFloatingRethrowing(_ r: R) rethrows { } + +func freeFloatingRethrowingFromExistential(_ r: RethrowingProtocol) rethrows { } + +func invalidFreeFloatingRethrows() rethrows { + // expected-error@-1{{'rethrows' function must take a throwing function argument}} +} + +let rethrowingFromThrows = Rethrows(other: Throws()) +try rethrowingFromThrows.source() + +protocol HasAssociatedRethrowerWithEnclosedRethrow { + associatedtype Rethrower: RethrowingProtocol + + func source() rethrows +} + +protocol HasAssociatedRethrower { + associatedtype Rethrower: RethrowingProtocol + + func makeRethrower() -> Rethrower +} + +func freeFloatingRethrowing(_ r: R) rethrows { } + +protocol InheritsRethrowing: RethrowingProtocol {} + +func freeFloatingInheritedRethrowingFunction(_ r: I) rethrows { } +func freeFloatingInheritedRethrowingFunctionFromExistential(_ r: InheritsRethrowing) rethrows { } + +func closureAndRethrowing(_ r: R, _ closure: () throws -> Void) rethrows { } + +closureAndRethrowing(NonThrows()) { } +try closureAndRethrowing(NonThrows()) { } // expected-warning{{no calls to throwing functions occur within 'try' expression}} +try closureAndRethrowing(Throws()) { } +try closureAndRethrowing(NonThrows()) { () throws -> Void in } From 92229b6737cfe0fcd45ce6f8fda5ea84e6cc5f0c Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Tue, 8 Dec 2020 15:28:17 -0800 Subject: [PATCH 15/40] Restrict rethrowing requirements to only protocols marked with @rethrows --- include/swift/AST/Attr.def | 4 ++++ include/swift/AST/DiagnosticsSema.def | 2 ++ lib/Parse/ParseDecl.cpp | 20 +++++++++++++++----- lib/Sema/TypeCheckAttr.cpp | 14 ++++++++++++-- lib/Sema/TypeCheckDecl.cpp | 11 +++++++++-- lib/Sema/TypeCheckDeclOverride.cpp | 2 ++ test/attr/attr_rethrows_protocol.swift | 3 +++ 7 files changed, 47 insertions(+), 9 deletions(-) diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 0166ef3545cc8..a56ef80cb13ec 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -359,6 +359,10 @@ SIMPLE_DECL_ATTR(rethrows, Rethrows, RejectByParser | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, 57) +SIMPLE_DECL_ATTR(rethrows, AtRethrows, + OnProtocol | + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + 58) DECL_ATTR(_swift_native_objc_runtime_base, SwiftNativeObjCRuntimeBase, OnClass | UserInaccessible | diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 5dc8ccb4d52e6..3c8f161b30769 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2981,6 +2981,8 @@ ERROR(override_rethrows_with_non_rethrows,none, "be 'rethrows'", (bool)) ERROR(rethrows_without_throwing_parameter,none, "'rethrows' function must take a throwing function argument", ()) +ERROR(rethrows_attr_on_non_protocol,none, + "@rethrows may only be used on 'protocol' declarations", ()) ERROR(autoclosure_function_type,none, "@autoclosure attribute only applies to function types", diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index cba6397192e29..78798edf4c7a0 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2691,7 +2691,8 @@ ParserStatus Parser::parseDeclAttribute( // If this not an identifier, the attribute is malformed. if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_in) && - Tok.isNot(tok::kw_inout)) { + Tok.isNot(tok::kw_inout) && + Tok.isNot(tok::kw_rethrows)) { if (Tok.is(tok::code_complete)) { if (CodeCompletion) { @@ -2712,7 +2713,7 @@ ParserStatus Parser::parseDeclAttribute( // If the attribute follows the new representation, switch // over to the alternate parsing path. DeclAttrKind DK = DeclAttribute::getAttrKindFromString(Tok.getText()); - + if (DK == DAK_Rethrows) { DK = DAK_AtRethrows; } auto checkInvalidAttrName = [&](StringRef invalidName, StringRef correctName, DeclAttrKind kind, @@ -3637,7 +3638,8 @@ static void skipAttribute(Parser &P) { // Parse the attribute name, which can be qualified, have // generic arguments, and so on. do { - if (!P.consumeIf(tok::identifier) && !P.consumeIf(tok::code_complete)) + if (!(P.consumeIf(tok::identifier) || P.consumeIf(tok::kw_rethrows)) && + !P.consumeIf(tok::code_complete)) return; if (P.startsWithLess(P.Tok)) { @@ -3656,8 +3658,16 @@ static void skipAttribute(Parser &P) { } bool Parser::isStartOfSwiftDecl() { - // If this is obviously not the start of a decl, then we're done. - if (!isKeywordPossibleDeclStart(Tok)) return false; + if (Tok.is(tok::at_sign) && peekToken().is(tok::kw_rethrows)) { + // @rethrows does not follow the general rule of @ so + // it is needed to short circuit this else there will be an infinite + // loop on invalid attributes of just rethrows + } else if (!isKeywordPossibleDeclStart(Tok)) { + // If this is obviously not the start of a decl, then we're done. + return false; + } + + // When 'init' appears inside another 'init', it's likely the user wants to // invoke an initializer but forgets to prefix it with 'self.' or 'super.' diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 181079fc11b51..2116896825f91 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -215,6 +215,7 @@ class AttributeChecker : public AttributeVisitor { void visitNSCopyingAttr(NSCopyingAttr *attr); void visitRequiredAttr(RequiredAttr *attr); void visitRethrowsAttr(RethrowsAttr *attr); + void visitAtRethrowsAttr(AtRethrowsAttr *attr); void checkApplicationMainAttribute(DeclAttribute *attr, Identifier Id_ApplicationDelegate, @@ -2137,8 +2138,8 @@ void AttributeChecker::visitRequiredAttr(RequiredAttr *attr) { void AttributeChecker::visitRethrowsAttr(RethrowsAttr *attr) { // 'rethrows' only applies to functions that take throwing functions // as parameters. - auto fn = cast(D); - if (fn->getRethrowingKind() != FunctionRethrowingKind::Invalid) { + auto fn = dyn_cast(D); + if (fn && fn->getRethrowingKind() != FunctionRethrowingKind::Invalid) { return; } @@ -2146,6 +2147,15 @@ void AttributeChecker::visitRethrowsAttr(RethrowsAttr *attr) { attr->setInvalid(); } +void AttributeChecker::visitAtRethrowsAttr(AtRethrowsAttr *attr) { + if (isa(D)) { + return; + } + + diagnose(attr->getLocation(), diag::rethrows_attr_on_non_protocol); + attr->setInvalid(); +} + /// Collect all used generic parameter types from a given type. static void collectUsedGenericParameters( Type Ty, SmallPtrSetImpl &ConstrainedGenericParams) { diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 83496428240b1..486eca8163651 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -773,6 +773,14 @@ ProtocolRethrowsRequirementList ProtocolRethrowsRequirementsRequest::evaluate(Evaluator &evaluator, ProtocolDecl *decl) const { SmallVector, 2> found; + llvm::DenseSet checkedProtocols; + + ASTContext &ctx = decl->getASTContext(); + + // only allow rethrowing requirements to be determined from marked protocols + if (decl->getAttrs().hasAttribute()) { + return ProtocolRethrowsRequirementList(ctx.AllocateCopy(found)); + } // check if immediate members of protocol are 'rethrows' for (auto member : decl->getMembers()) { @@ -796,7 +804,6 @@ ProtocolRethrowsRequirementsRequest::evaluate(Evaluator &evaluator, found.push_back( std::pair(decl->getSelfInterfaceType(), fnDecl)); } - llvm::DenseSet checkedProtocols; checkedProtocols.insert(decl); // check associated conformances of associated types or inheritance @@ -814,7 +821,7 @@ ProtocolRethrowsRequirementsRequest::evaluate(Evaluator &evaluator, found.emplace_back(requirement.getFirstType(), entry.second); } } - ASTContext &ctx = decl->getASTContext(); + return ProtocolRethrowsRequirementList(ctx.AllocateCopy(found)); } diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index af8d60e4755b1..bb952521f2935 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1515,6 +1515,8 @@ namespace { UNINTERESTING_ATTR(ActorIndependent) UNINTERESTING_ATTR(GlobalActor) UNINTERESTING_ATTR(Async) + + UNINTERESTING_ATTR(AtRethrows) #undef UNINTERESTING_ATTR void visitAvailableAttr(AvailableAttr *attr) { diff --git a/test/attr/attr_rethrows_protocol.swift b/test/attr/attr_rethrows_protocol.swift index 3efda60597cd5..c8bbb3b6a37e4 100644 --- a/test/attr/attr_rethrows_protocol.swift +++ b/test/attr/attr_rethrows_protocol.swift @@ -1,5 +1,6 @@ // RUN: %target-typecheck-verify-swift +@rethrows protocol RethrowingProtocol { func source() rethrows } @@ -48,12 +49,14 @@ func invalidFreeFloatingRethrows() rethrows { let rethrowingFromThrows = Rethrows(other: Throws()) try rethrowingFromThrows.source() +@rethrows protocol HasAssociatedRethrowerWithEnclosedRethrow { associatedtype Rethrower: RethrowingProtocol func source() rethrows } +@rethrows protocol HasAssociatedRethrower { associatedtype Rethrower: RethrowingProtocol From e8c4cd0e9465a343d98bcfbd997b14f16517bf32 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 16 Dec 2020 14:19:22 -0800 Subject: [PATCH 16/40] Correct logic for gating of `@rethrows` and adjust the determinates to be based upon throws and not rethrows spelling --- lib/Sema/TypeCheckDecl.cpp | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 486eca8163651..cc9c120b5949a 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -778,7 +778,7 @@ ProtocolRethrowsRequirementsRequest::evaluate(Evaluator &evaluator, ASTContext &ctx = decl->getASTContext(); // only allow rethrowing requirements to be determined from marked protocols - if (decl->getAttrs().hasAttribute()) { + if (!decl->getAttrs().hasAttribute()) { return ProtocolRethrowsRequirementList(ctx.AllocateCopy(found)); } @@ -789,7 +789,7 @@ ProtocolRethrowsRequirementsRequest::evaluate(Evaluator &evaluator, // it must have a rethrows attribute // it must not have any parameters that are closures that cause rethrowing if (!fnDecl || - !fnDecl->getAttrs().hasAttribute()) { + !fnDecl->hasThrows()) { continue; } @@ -828,22 +828,25 @@ ProtocolRethrowsRequirementsRequest::evaluate(Evaluator &evaluator, FunctionRethrowingKind FunctionRethrowingKindRequest::evaluate(Evaluator &evaluator, AbstractFunctionDecl *decl) const { - if (decl->getAttrs().hasAttribute()) { - GenericSignature genericSig = decl->getGenericSignature(); - FunctionRethrowingKind kind = getParameterThrowingKind(decl, genericSig); - // since we have checked all arguments, if we still havent found anything - // check the self parameter - if (kind == FunctionRethrowingKind::Invalid && - decl->hasImplicitSelfDecl()) { - auto selfParam = decl->getImplicitSelfDecl(); - if (selfParam) { - auto interfaceTy = selfParam->getInterfaceType(); - kind = getTypeThrowingKind(interfaceTy, genericSig); + if (decl->hasThrows()) { + if (auto proto = dyn_cast(decl->getDeclContext())) { + if (proto->isRethrowingProtocol()) { + GenericSignature genericSig = decl->getGenericSignature(); + FunctionRethrowingKind kind = getParameterThrowingKind(decl, genericSig); + // since we have checked all arguments, if we still havent found anything + // check the self parameter + if (kind == FunctionRethrowingKind::Invalid && + decl->hasImplicitSelfDecl()) { + auto selfParam = decl->getImplicitSelfDecl(); + if (selfParam) { + auto interfaceTy = selfParam->getInterfaceType(); + kind = getTypeThrowingKind(interfaceTy, genericSig); + } + } + + return kind; } } - - return kind; - } else if (decl->hasThrows()) { return FunctionRethrowingKind::Throws; } return FunctionRethrowingKind::None; From 019d8ad92ff4dd62219515f377840b921461ea5e Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Thu, 24 Dec 2020 15:13:03 -0800 Subject: [PATCH 17/40] Attempt to unify the async sequence features together --- lib/Sema/TypeCheckConcurrency.cpp | 7 ++-- lib/Sema/TypeCheckDecl.cpp | 33 ++++++++++--------- lib/Sema/TypeCheckEffects.cpp | 11 ++++++- stdlib/public/Concurrency/AllSatisfy.swift | 2 +- .../Concurrency/AsyncCompactMapSequence.swift | 4 +-- .../Concurrency/AsyncConcatSequence.swift | 2 +- .../Concurrency/AsyncDropWhileSequence.swift | 2 +- .../Concurrency/AsyncFilterSequence.swift | 2 +- .../Concurrency/AsyncFlatMapSequence.swift | 2 +- .../Concurrency/AsyncIteratorProtocol.swift | 1 + .../public/Concurrency/AsyncMapSequence.swift | 2 +- .../AsyncPrefixWhileSequence.swift | 2 +- stdlib/public/Concurrency/AsyncSequence.swift | 1 + stdlib/public/Concurrency/Contains.swift | 6 ++-- stdlib/public/Concurrency/Count.swift | 2 +- stdlib/public/Concurrency/First.swift | 4 +-- stdlib/public/Concurrency/MinMax.swift | 8 ++--- stdlib/public/Concurrency/Reduce.swift | 4 +-- 18 files changed, 55 insertions(+), 40 deletions(-) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 8b15e80643a41..0236744d516fb 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -820,9 +820,9 @@ namespace { if (isAsyncCall(outerCall)) { // This call is a partial application within an async call. // If the partial application take a value inout, it is bad. - if (InOutExpr *inoutArg = dyn_cast( - call->getArg()->getSemanticsProvidingExpr())) - diagnoseInOutArg(outerCall, inoutArg, true); + // if (InOutExpr *inoutArg = dyn_cast( + // call->getArg()->getSemanticsProvidingExpr())) + // diagnoseInOutArg(outerCall, inoutArg, true); } } @@ -958,6 +958,7 @@ namespace { /// \returns true if we diagnosed the entity, \c false otherwise. bool diagnoseInOutArg(const ApplyExpr *call, const InOutExpr *arg, bool isPartialApply) { + // check that the call is actually async if (!isAsyncCall(call)) return false; diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index cc9c120b5949a..c281691d82b19 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -829,23 +829,26 @@ FunctionRethrowingKind FunctionRethrowingKindRequest::evaluate(Evaluator &evaluator, AbstractFunctionDecl *decl) const { if (decl->hasThrows()) { - if (auto proto = dyn_cast(decl->getDeclContext())) { - if (proto->isRethrowingProtocol()) { - GenericSignature genericSig = decl->getGenericSignature(); - FunctionRethrowingKind kind = getParameterThrowingKind(decl, genericSig); - // since we have checked all arguments, if we still havent found anything - // check the self parameter - if (kind == FunctionRethrowingKind::Invalid && - decl->hasImplicitSelfDecl()) { - auto selfParam = decl->getImplicitSelfDecl(); - if (selfParam) { - auto interfaceTy = selfParam->getInterfaceType(); - kind = getTypeThrowingKind(interfaceTy, genericSig); - } + auto proto = dyn_cast(decl->getDeclContext()); + bool fromRethrow = proto != nullptr ? proto->isRethrowingProtocol() : false; + bool markedRethrows = decl->getAttrs().hasAttribute(); + if (fromRethrow && !markedRethrows) { + return FunctionRethrowingKind::ByConformance; + } + if (markedRethrows) { + GenericSignature genericSig = decl->getGenericSignature(); + FunctionRethrowingKind kind = getParameterThrowingKind(decl, genericSig); + // since we have checked all arguments, if we still havent found anything + // check the self parameter + if (kind == FunctionRethrowingKind::Invalid && + decl->hasImplicitSelfDecl()) { + auto selfParam = decl->getImplicitSelfDecl(); + if (selfParam) { + auto interfaceTy = selfParam->getInterfaceType(); + kind = getTypeThrowingKind(interfaceTy, genericSig); } - - return kind; } + return kind; } return FunctionRethrowingKind::Throws; } diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 5edd71affd358..27d0ab8dfc20e 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -578,6 +578,13 @@ class ApplyClassifier { return Classification::forRethrowingOnly( PotentialThrowReason::forThrowingApply(), isAsync); } + } else if (fnRef.isBodyRethrows() && + fnRef.getRethrowingKind() == FunctionRethrowingKind::Throws) { + return Classification::forThrow(PotentialThrowReason::forThrowingApply(), + isAsync); + } else if (fnRef.isBodyRethrows() && + fnRef.getRethrowingKind() == FunctionRethrowingKind::None) { + return isAsync ? Classification::forAsync() : Classification(); } // If the function doesn't throw at all, we're done here. @@ -1072,7 +1079,8 @@ class Context { if (!fn) return false; - return fn->getAttrs().hasAttribute(); + + return fn->getRethrowingKind() == FunctionRethrowingKind::ByClosure; } /// Whether this is an autoclosure. @@ -2074,6 +2082,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker ShouldRecurse_t checkTry(TryExpr *E) { + // Walk the operand. ContextScope scope(*this, None); scope.enterTry(); diff --git a/stdlib/public/Concurrency/AllSatisfy.swift b/stdlib/public/Concurrency/AllSatisfy.swift index da23ebeb41b7d..99b9f0b9490cf 100644 --- a/stdlib/public/Concurrency/AllSatisfy.swift +++ b/stdlib/public/Concurrency/AllSatisfy.swift @@ -13,7 +13,7 @@ import Swift extension AsyncSequence { - public func allSatisfy(_ predicate: (Element) async throws -> Bool) async throws /*rethrows*/ -> Bool { + public func allSatisfy(_ predicate: (Element) async throws -> Bool) async rethrows -> Bool { var it = makeAsyncIterator() while let element = await try it.next() { if !(await try predicate(element)) { diff --git a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift index 17f3d8b8b7a9b..bdd7da0a2dd40 100644 --- a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift @@ -35,7 +35,7 @@ public struct AsyncCompactMapSequence: AsyncSequence self.transform = transform } - public mutating func next() async throws /*rethrows*/ -> ElementOfResult? { + public mutating func next() async rethrows -> ElementOfResult? { while true { guard let item = await try upstreamIterator?.next() else { return nil @@ -78,7 +78,7 @@ public struct AsyncTryCompactMapSequence: AsyncSequen self.transform = transform } - public mutating func next() async throws /*rethrows*/ -> ElementOfResult? { + public mutating func next() async throws -> ElementOfResult? { while true { guard let item = await try upstreamIterator?.next() else { return nil diff --git a/stdlib/public/Concurrency/AsyncConcatSequence.swift b/stdlib/public/Concurrency/AsyncConcatSequence.swift index b7494e4573e17..1880397ea366c 100644 --- a/stdlib/public/Concurrency/AsyncConcatSequence.swift +++ b/stdlib/public/Concurrency/AsyncConcatSequence.swift @@ -35,7 +35,7 @@ public struct AsyncConcatSequence: AsyncSequence where Prefix: A self.suffixIterator = suffixIterator } - public mutating func next() async throws /*rethrows*/ -> Prefix.Element? { + public mutating func next() async rethrows -> Prefix.Element? { if let item = await try prefixIterator?.next() { return item } diff --git a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift index 5d3bf983ddfa0..43c954159814b 100644 --- a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift +++ b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift @@ -35,7 +35,7 @@ public struct AsyncDropWhileSequence: AsyncSequence where Upstream: As self.predicate = predicate } - public mutating func next() async throws /*rethrows*/ -> Upstream.Element? { + public mutating func next() async rethrows -> Upstream.Element? { while true { guard let item = await try upstreamIterator?.next() else { return nil diff --git a/stdlib/public/Concurrency/AsyncFilterSequence.swift b/stdlib/public/Concurrency/AsyncFilterSequence.swift index a6813a1b073e1..f6d22d220fcba 100644 --- a/stdlib/public/Concurrency/AsyncFilterSequence.swift +++ b/stdlib/public/Concurrency/AsyncFilterSequence.swift @@ -40,7 +40,7 @@ public struct AsyncFilterSequence: AsyncSequence where Upstream: Async self.predicate = predicate } - public mutating func next() async throws /*rethrows*/ -> Upstream.Element? { + public mutating func next() async rethrows -> Upstream.Element? { guard let item = await try upstreamIterator?.next() else { return nil } diff --git a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift index a867b058c5f5f..323ec956514af 100644 --- a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift @@ -36,7 +36,7 @@ public struct AsyncFlatMapSequence: As self.transform = transform } - public mutating func next() async throws /*rethrows*/ -> SegmentOfResult.Element? { + public mutating func next() async rethrows -> SegmentOfResult.Element? { if let item = await try currentIterator?.next() { return item } else { diff --git a/stdlib/public/Concurrency/AsyncIteratorProtocol.swift b/stdlib/public/Concurrency/AsyncIteratorProtocol.swift index 98d04a0d86f0e..3013377d0a387 100644 --- a/stdlib/public/Concurrency/AsyncIteratorProtocol.swift +++ b/stdlib/public/Concurrency/AsyncIteratorProtocol.swift @@ -12,6 +12,7 @@ import Swift +@rethrows public protocol AsyncIteratorProtocol { associatedtype Element mutating func next() async throws -> Element? diff --git a/stdlib/public/Concurrency/AsyncMapSequence.swift b/stdlib/public/Concurrency/AsyncMapSequence.swift index 63d79fad1656d..f5c6867dc19c7 100644 --- a/stdlib/public/Concurrency/AsyncMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncMapSequence.swift @@ -37,7 +37,7 @@ public struct AsyncMapSequence: AsyncSequence where Upstr self.transform = transform } - public mutating func next() async throws /*rethrows*/ -> Transformed? { + public mutating func next() async rethrows -> Transformed? { guard let item = await try upstreamIterator?.next() else { return nil } diff --git a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift index 7a30ab7bc77e4..963c6b25ad91d 100644 --- a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift +++ b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift @@ -35,7 +35,7 @@ public struct AsyncPrefixWhileSequence: AsyncSequence where Upstream: self.predicate = predicate } - public mutating func next() async throws /*rethrows*/ -> Element? { + public mutating func next() async rethrows -> Element? { guard let item = await try upstreamIterator?.next() else { return nil } diff --git a/stdlib/public/Concurrency/AsyncSequence.swift b/stdlib/public/Concurrency/AsyncSequence.swift index d5d91f78e0dde..070d622f9f8b8 100644 --- a/stdlib/public/Concurrency/AsyncSequence.swift +++ b/stdlib/public/Concurrency/AsyncSequence.swift @@ -12,6 +12,7 @@ import Swift +@rethrows public protocol AsyncSequence { associatedtype AsyncIterator: AsyncIteratorProtocol where AsyncIterator.Element == Element associatedtype Element diff --git a/stdlib/public/Concurrency/Contains.swift b/stdlib/public/Concurrency/Contains.swift index c34adfcca8b76..3915eef819401 100644 --- a/stdlib/public/Concurrency/Contains.swift +++ b/stdlib/public/Concurrency/Contains.swift @@ -13,7 +13,7 @@ import Swift extension AsyncSequence { - public func contains(where predicate: (Element) async throws -> Bool) async throws /*rethrows*/ -> Bool { + public func contains(where predicate: (Element) async throws -> Bool) async rethrows -> Bool { var it = makeAsyncIterator() while let e = await try it.next() { if await try predicate(e) { @@ -25,7 +25,7 @@ extension AsyncSequence { } extension AsyncSequence where Element: Equatable { - public func contains(_ element: Element) async throws /*rethrows*/ -> Bool { - return await try contains { $0 == element } + public func contains(_ element: Element) async rethrows -> Bool { + return await contains { $0 == element } } } diff --git a/stdlib/public/Concurrency/Count.swift b/stdlib/public/Concurrency/Count.swift index a51d92ea305f0..372b2cb0ea811 100644 --- a/stdlib/public/Concurrency/Count.swift +++ b/stdlib/public/Concurrency/Count.swift @@ -13,7 +13,7 @@ import Swift extension AsyncSequence { - public func count() async throws /*rethrows*/ -> Int { + public func count() async rethrows -> Int { var count = 0 var it = makeAsyncIterator() while await try it.next() != nil { diff --git a/stdlib/public/Concurrency/First.swift b/stdlib/public/Concurrency/First.swift index 9bee6e7d058aa..2d12aa3ffd87c 100644 --- a/stdlib/public/Concurrency/First.swift +++ b/stdlib/public/Concurrency/First.swift @@ -13,7 +13,7 @@ import Swift extension AsyncSequence { - public func first(where predicate: (Element) async throws -> Bool) async throws /*rethrows*/ -> Element? { + public func first(where predicate: (Element) async throws -> Bool) async rethrows -> Element? { var it = makeAsyncIterator() while let element = await try it.next() { if await try predicate(element) { @@ -23,7 +23,7 @@ extension AsyncSequence { return nil } - public func first() async throws /*rethrows*/ -> Element? { + public func first() async rethrows -> Element? { var it = makeAsyncIterator() return await try it.next() } diff --git a/stdlib/public/Concurrency/MinMax.swift b/stdlib/public/Concurrency/MinMax.swift index 67371069190fb..2cb7ce7719be9 100644 --- a/stdlib/public/Concurrency/MinMax.swift +++ b/stdlib/public/Concurrency/MinMax.swift @@ -13,7 +13,7 @@ import Swift extension AsyncSequence { - public func min(by areInIncreasingOrder: (Element, Element) async throws -> Bool) async throws /*rethrows*/ -> Element? { + public func min(by areInIncreasingOrder: (Element, Element) async throws -> Bool) async rethrows -> Element? { var it = makeAsyncIterator() guard var result = await try it.next() else { return nil } while let e = await try it.next() { @@ -22,7 +22,7 @@ extension AsyncSequence { return result } - public func max(by areInIncreasingOrder: (Element, Element) async throws -> Bool) async throws /*rethrows*/ -> Element? { + public func max(by areInIncreasingOrder: (Element, Element) async throws -> Bool) async rethrows -> Element? { var it = makeAsyncIterator() guard var result = await try it.next() else { return nil } while let e = await try it.next() { @@ -33,11 +33,11 @@ extension AsyncSequence { } extension AsyncSequence where Element: Comparable { - public func min() async throws /*rethrows*/ -> Element? { + public func min() async rethrows -> Element? { return await try min(by: <) } - public func max() async throws /*rethrows*/ -> Element? { + public func max() async rethrows -> Element? { return await try max(by: <) } } diff --git a/stdlib/public/Concurrency/Reduce.swift b/stdlib/public/Concurrency/Reduce.swift index 251595b6eca37..686b19588d346 100644 --- a/stdlib/public/Concurrency/Reduce.swift +++ b/stdlib/public/Concurrency/Reduce.swift @@ -13,7 +13,7 @@ import Swift extension AsyncSequence { - public func reduce(_ initialResult: Result, _ nextPartialResult: (_ partialResult: Result, Element) async throws -> Result) async throws /*rethrows*/ -> Result { + public func reduce(_ initialResult: Result, _ nextPartialResult: (_ partialResult: Result, Element) async throws -> Result) async rethrows -> Result { var accumulator = initialResult var it = makeAsyncIterator() while let element = await try it.next() { @@ -22,7 +22,7 @@ extension AsyncSequence { return accumulator } - public func reduce(into initialResult: __owned Result, _ updateAccumulatingResult: (_ partialResult: inout Result, Element) async throws -> Void) async throws /*rethrows*/ -> Result { + public func reduce(into initialResult: __owned Result, _ updateAccumulatingResult: (_ partialResult: inout Result, Element) async throws -> Void) async rethrows -> Result { var accumulator = initialResult var it = makeAsyncIterator() while let element = await try it.next() { From 10feb16b8af0314feda8afb0fbd4c798aaadeef4 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 6 Jan 2021 14:05:25 -0800 Subject: [PATCH 18/40] Reorder try await to latest syntax --- stdlib/public/Concurrency/AllSatisfy.swift | 4 ++-- .../Concurrency/AsyncCompactMapSequence.swift | 6 +++--- .../public/Concurrency/AsyncConcatSequence.swift | 4 ++-- .../Concurrency/AsyncDropWhileSequence.swift | 6 +++--- .../public/Concurrency/AsyncFilterSequence.swift | 6 +++--- .../Concurrency/AsyncFlatMapSequence.swift | 14 +++++++------- stdlib/public/Concurrency/AsyncMapSequence.swift | 6 +++--- .../Concurrency/AsyncPrefixWhileSequence.swift | 6 +++--- stdlib/public/Concurrency/Contains.swift | 4 ++-- stdlib/public/Concurrency/Count.swift | 2 +- stdlib/public/Concurrency/First.swift | 6 +++--- stdlib/public/Concurrency/MinMax.swift | 16 ++++++++-------- stdlib/public/Concurrency/Reduce.swift | 8 ++++---- 13 files changed, 44 insertions(+), 44 deletions(-) diff --git a/stdlib/public/Concurrency/AllSatisfy.swift b/stdlib/public/Concurrency/AllSatisfy.swift index 99b9f0b9490cf..03a06188d5848 100644 --- a/stdlib/public/Concurrency/AllSatisfy.swift +++ b/stdlib/public/Concurrency/AllSatisfy.swift @@ -15,8 +15,8 @@ import Swift extension AsyncSequence { public func allSatisfy(_ predicate: (Element) async throws -> Bool) async rethrows -> Bool { var it = makeAsyncIterator() - while let element = await try it.next() { - if !(await try predicate(element)) { + while let element = try await it.next() { + if !(try await predicate(element)) { return false } } diff --git a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift index bdd7da0a2dd40..908465e178d24 100644 --- a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift @@ -37,7 +37,7 @@ public struct AsyncCompactMapSequence: AsyncSequence public mutating func next() async rethrows -> ElementOfResult? { while true { - guard let item = await try upstreamIterator?.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } if let transformed = await transform(item) { @@ -80,11 +80,11 @@ public struct AsyncTryCompactMapSequence: AsyncSequen public mutating func next() async throws -> ElementOfResult? { while true { - guard let item = await try upstreamIterator?.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } do { - if let transformed = await try transform(item) { + if let transformed = try await transform(item) { return transformed } } catch { diff --git a/stdlib/public/Concurrency/AsyncConcatSequence.swift b/stdlib/public/Concurrency/AsyncConcatSequence.swift index 1880397ea366c..baea73dd47a34 100644 --- a/stdlib/public/Concurrency/AsyncConcatSequence.swift +++ b/stdlib/public/Concurrency/AsyncConcatSequence.swift @@ -36,11 +36,11 @@ public struct AsyncConcatSequence: AsyncSequence where Prefix: A } public mutating func next() async rethrows -> Prefix.Element? { - if let item = await try prefixIterator?.next() { + if let item = try await prefixIterator?.next() { return item } prefixIterator = nil - return await try suffixIterator?.next() + return try await suffixIterator?.next() } public mutating func cancel() { diff --git a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift index 43c954159814b..6d314a923c1da 100644 --- a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift +++ b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift @@ -37,7 +37,7 @@ public struct AsyncDropWhileSequence: AsyncSequence where Upstream: As public mutating func next() async rethrows -> Upstream.Element? { while true { - guard let item = await try upstreamIterator?.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } if let predicate = self.predicate { @@ -85,12 +85,12 @@ public struct AsyncTryDropWhileSequence: AsyncSequence where Upstream: public mutating func next() async throws -> Upstream.Element? { while true { - guard let item = await try upstreamIterator?.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } if let predicate = self.predicate { do { - if !(await try predicate(item)) { + if !(try await predicate(item)) { self.predicate = nil return item } diff --git a/stdlib/public/Concurrency/AsyncFilterSequence.swift b/stdlib/public/Concurrency/AsyncFilterSequence.swift index f6d22d220fcba..ecfa5cc5e7c16 100644 --- a/stdlib/public/Concurrency/AsyncFilterSequence.swift +++ b/stdlib/public/Concurrency/AsyncFilterSequence.swift @@ -41,7 +41,7 @@ public struct AsyncFilterSequence: AsyncSequence where Upstream: Async } public mutating func next() async rethrows -> Upstream.Element? { - guard let item = await try upstreamIterator?.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } guard await predicate(item) else { @@ -87,10 +87,10 @@ public struct AsyncTryFilterSequence: AsyncSequence where Upstream: As } public mutating func next() async throws -> Upstream.Element? { - guard let item = await try upstreamIterator?.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } - guard await try predicate(item) else { + guard try await predicate(item) else { upstreamIterator?.cancel() upstreamIterator = nil return nil diff --git a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift index 323ec956514af..1ebcb2883adf4 100644 --- a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift @@ -37,15 +37,15 @@ public struct AsyncFlatMapSequence: As } public mutating func next() async rethrows -> SegmentOfResult.Element? { - if let item = await try currentIterator?.next() { + if let item = try await currentIterator?.next() { return item } else { - guard let item = await try upstreamIterator?.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } let segment = await transform(item) currentIterator = segment.makeAsyncIterator() - return await try currentIterator?.next() + return try await currentIterator?.next() } } @@ -85,15 +85,15 @@ public struct AsyncTryFlatMapSequence: } public mutating func next() async throws -> SegmentOfResult.Element? { - if let item = await try currentIterator?.next() { + if let item = try await currentIterator?.next() { return item } else { - guard let item = await try upstreamIterator?.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } - let segment = await try transform(item) + let segment = try await transform(item) currentIterator = segment.makeAsyncIterator() - return await try currentIterator?.next() + return try await currentIterator?.next() } } diff --git a/stdlib/public/Concurrency/AsyncMapSequence.swift b/stdlib/public/Concurrency/AsyncMapSequence.swift index f5c6867dc19c7..55fa5f6ecf249 100644 --- a/stdlib/public/Concurrency/AsyncMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncMapSequence.swift @@ -38,7 +38,7 @@ public struct AsyncMapSequence: AsyncSequence where Upstr } public mutating func next() async rethrows -> Transformed? { - guard let item = await try upstreamIterator?.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } return await transform(item) @@ -79,10 +79,10 @@ public struct AsyncTryMapSequence: AsyncSequence where Up } public mutating func next() async throws -> Transformed? { - guard let item = await try upstreamIterator?.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } - return await try transform(item) + return try await transform(item) } public mutating func cancel() { diff --git a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift index 963c6b25ad91d..b6b48fe08fcfb 100644 --- a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift +++ b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift @@ -36,7 +36,7 @@ public struct AsyncPrefixWhileSequence: AsyncSequence where Upstream: } public mutating func next() async rethrows -> Element? { - guard let item = await try upstreamIterator?.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } guard await predicate(item) else { @@ -80,10 +80,10 @@ public struct AsyncTryPrefixWhileSequence: AsyncSequence where Upstrea } public mutating func next() async throws -> Element? { - guard let item = await try upstreamIterator?.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } - guard await try predicate(item) else { + guard try await predicate(item) else { upstreamIterator?.cancel() upstreamIterator = nil return nil diff --git a/stdlib/public/Concurrency/Contains.swift b/stdlib/public/Concurrency/Contains.swift index 3915eef819401..e13478866ae23 100644 --- a/stdlib/public/Concurrency/Contains.swift +++ b/stdlib/public/Concurrency/Contains.swift @@ -15,8 +15,8 @@ import Swift extension AsyncSequence { public func contains(where predicate: (Element) async throws -> Bool) async rethrows -> Bool { var it = makeAsyncIterator() - while let e = await try it.next() { - if await try predicate(e) { + while let e = try await it.next() { + if try await predicate(e) { return true } } diff --git a/stdlib/public/Concurrency/Count.swift b/stdlib/public/Concurrency/Count.swift index 372b2cb0ea811..60a0ad5395c0c 100644 --- a/stdlib/public/Concurrency/Count.swift +++ b/stdlib/public/Concurrency/Count.swift @@ -16,7 +16,7 @@ extension AsyncSequence { public func count() async rethrows -> Int { var count = 0 var it = makeAsyncIterator() - while await try it.next() != nil { + while try await it.next() != nil { count += 1 } return count diff --git a/stdlib/public/Concurrency/First.swift b/stdlib/public/Concurrency/First.swift index 2d12aa3ffd87c..9a4471488a520 100644 --- a/stdlib/public/Concurrency/First.swift +++ b/stdlib/public/Concurrency/First.swift @@ -15,8 +15,8 @@ import Swift extension AsyncSequence { public func first(where predicate: (Element) async throws -> Bool) async rethrows -> Element? { var it = makeAsyncIterator() - while let element = await try it.next() { - if await try predicate(element) { + while let element = try await it.next() { + if try await predicate(element) { return element } } @@ -25,6 +25,6 @@ extension AsyncSequence { public func first() async rethrows -> Element? { var it = makeAsyncIterator() - return await try it.next() + return try await it.next() } } diff --git a/stdlib/public/Concurrency/MinMax.swift b/stdlib/public/Concurrency/MinMax.swift index 2cb7ce7719be9..8e7edbc993e65 100644 --- a/stdlib/public/Concurrency/MinMax.swift +++ b/stdlib/public/Concurrency/MinMax.swift @@ -15,18 +15,18 @@ import Swift extension AsyncSequence { public func min(by areInIncreasingOrder: (Element, Element) async throws -> Bool) async rethrows -> Element? { var it = makeAsyncIterator() - guard var result = await try it.next() else { return nil } - while let e = await try it.next() { - if await try areInIncreasingOrder(e, result) { result = e } + guard var result = try await it.next() else { return nil } + while let e = try await it.next() { + if try await areInIncreasingOrder(e, result) { result = e } } return result } public func max(by areInIncreasingOrder: (Element, Element) async throws -> Bool) async rethrows -> Element? { var it = makeAsyncIterator() - guard var result = await try it.next() else { return nil } - while let e = await try it.next() { - if await try areInIncreasingOrder(result, e) { result = e } + guard var result = try await it.next() else { return nil } + while let e = try await it.next() { + if try await areInIncreasingOrder(result, e) { result = e } } return result } @@ -34,10 +34,10 @@ extension AsyncSequence { extension AsyncSequence where Element: Comparable { public func min() async rethrows -> Element? { - return await try min(by: <) + return try await min(by: <) } public func max() async rethrows -> Element? { - return await try max(by: <) + return try await max(by: <) } } diff --git a/stdlib/public/Concurrency/Reduce.swift b/stdlib/public/Concurrency/Reduce.swift index 686b19588d346..23a4f534766b4 100644 --- a/stdlib/public/Concurrency/Reduce.swift +++ b/stdlib/public/Concurrency/Reduce.swift @@ -16,8 +16,8 @@ extension AsyncSequence { public func reduce(_ initialResult: Result, _ nextPartialResult: (_ partialResult: Result, Element) async throws -> Result) async rethrows -> Result { var accumulator = initialResult var it = makeAsyncIterator() - while let element = await try it.next() { - accumulator = await try nextPartialResult(accumulator, element) + while let element = try await it.next() { + accumulator = try await nextPartialResult(accumulator, element) } return accumulator } @@ -25,8 +25,8 @@ extension AsyncSequence { public func reduce(into initialResult: __owned Result, _ updateAccumulatingResult: (_ partialResult: inout Result, Element) async throws -> Void) async rethrows -> Result { var accumulator = initialResult var it = makeAsyncIterator() - while let element = await try it.next() { - await try updateAccumulatingResult(&accumulator, element) + while let element = try await it.next() { + try await updateAccumulatingResult(&accumulator, element) } return accumulator } From d0f2e06c65596dcbb61a766fd720e202d7c9dd16 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 6 Jan 2021 14:05:42 -0800 Subject: [PATCH 19/40] revert back to the inout diagnosis --- lib/Sema/TypeCheckConcurrency.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 0236744d516fb..c8ae6377ebd85 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -820,9 +820,9 @@ namespace { if (isAsyncCall(outerCall)) { // This call is a partial application within an async call. // If the partial application take a value inout, it is bad. - // if (InOutExpr *inoutArg = dyn_cast( - // call->getArg()->getSemanticsProvidingExpr())) - // diagnoseInOutArg(outerCall, inoutArg, true); + if (InOutExpr *inoutArg = dyn_cast( + call->getArg()->getSemanticsProvidingExpr())) + diagnoseInOutArg(outerCall, inoutArg, true); } } From 3faaf3c63749b19d444380810a46b2aebda06241 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Thu, 7 Jan 2021 08:53:33 -0800 Subject: [PATCH 20/40] House mutations in local scope --- .../Concurrency/AsyncCompactMapSequence.swift | 19 ++++-- .../Concurrency/AsyncDropWhileSequence.swift | 21 +++++-- .../Concurrency/AsyncFilterSequence.swift | 27 +++++--- .../Concurrency/AsyncFlatMapSequence.swift | 62 ++++++++++++++----- .../public/Concurrency/AsyncMapSequence.swift | 14 ++++- .../AsyncPrefixWhileSequence.swift | 23 +++++-- 6 files changed, 128 insertions(+), 38 deletions(-) diff --git a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift index 908465e178d24..700bb1165bc79 100644 --- a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift @@ -37,7 +37,12 @@ public struct AsyncCompactMapSequence: AsyncSequence public mutating func next() async rethrows -> ElementOfResult? { while true { - guard let item = try await upstreamIterator?.next() else { + guard var upstreamIterator = self.upstreamIterator else { + return nil + } + defer { self.upstreamIterator = upstreamIterator } + + guard let item = try await upstreamIterator.next() else { return nil } if let transformed = await transform(item) { @@ -80,16 +85,22 @@ public struct AsyncTryCompactMapSequence: AsyncSequen public mutating func next() async throws -> ElementOfResult? { while true { - guard let item = try await upstreamIterator?.next() else { + guard var upstreamIterator = self.upstreamIterator else { + return nil + } + + guard let item = try await upstreamIterator.next() else { return nil } do { if let transformed = try await transform(item) { + self.upstreamIterator = upstreamIterator return transformed } + self.upstreamIterator = upstreamIterator } catch { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() + self.upstreamIterator = nil throw error } } diff --git a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift index 6d314a923c1da..f1ed0debe358c 100644 --- a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift +++ b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift @@ -37,7 +37,12 @@ public struct AsyncDropWhileSequence: AsyncSequence where Upstream: As public mutating func next() async rethrows -> Upstream.Element? { while true { - guard let item = try await upstreamIterator?.next() else { + guard var upstreamIterator = self.upstreamIterator else { + return nil + } + defer { self.upstreamIterator = upstreamIterator } + + guard let item = try await upstreamIterator.next() else { return nil } if let predicate = self.predicate { @@ -85,21 +90,29 @@ public struct AsyncTryDropWhileSequence: AsyncSequence where Upstream: public mutating func next() async throws -> Upstream.Element? { while true { - guard let item = try await upstreamIterator?.next() else { + guard var upstreamIterator = self.upstreamIterator else { + return nil + } + + guard let item = try await upstreamIterator.next() else { + self.upstreamIterator = upstreamIterator return nil } if let predicate = self.predicate { do { if !(try await predicate(item)) { self.predicate = nil + self.upstreamIterator = upstreamIterator return item } + self.upstreamIterator = upstreamIterator } catch { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() + self.upstreamIterator = nil throw error } } else { + self.upstreamIterator = upstreamIterator return item } } diff --git a/stdlib/public/Concurrency/AsyncFilterSequence.swift b/stdlib/public/Concurrency/AsyncFilterSequence.swift index ecfa5cc5e7c16..19993851b54be 100644 --- a/stdlib/public/Concurrency/AsyncFilterSequence.swift +++ b/stdlib/public/Concurrency/AsyncFilterSequence.swift @@ -41,14 +41,20 @@ public struct AsyncFilterSequence: AsyncSequence where Upstream: Async } public mutating func next() async rethrows -> Upstream.Element? { - guard let item = try await upstreamIterator?.next() else { + guard var upstreamIterator = self.upstreamIterator else { + return nil + } + + guard let item = try await upstreamIterator.next() else { + self.upstreamIterator = upstreamIterator return nil } guard await predicate(item) else { - upstreamIterator?.cancel() - upstreamIterator = nil - return nil + upstreamIterator.cancel() + self.upstreamIterator = nil + return nil } + self.upstreamIterator = upstreamIterator return item } @@ -87,14 +93,19 @@ public struct AsyncTryFilterSequence: AsyncSequence where Upstream: As } public mutating func next() async throws -> Upstream.Element? { - guard let item = try await upstreamIterator?.next() else { + guard var upstreamIterator = self.upstreamIterator else { + return nil + } + + guard let item = try await upstreamIterator.next() else { return nil } guard try await predicate(item) else { - upstreamIterator?.cancel() - upstreamIterator = nil - return nil + upstreamIterator.cancel() + self.upstreamIterator = nil + return nil } + self.upstreamIterator = upstreamIterator return item } diff --git a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift index 1ebcb2883adf4..aa5bcedcd876f 100644 --- a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift @@ -35,17 +35,34 @@ public struct AsyncFlatMapSequence: As self.upstreamIterator = upstreamIterator self.transform = transform } + + mutating func nextUpstream() async rethrows -> SegmentOfResult.Element? { + guard var upstreamIterator = self.upstreamIterator else { + return nil + } + + guard let item = try await upstreamIterator.next() else { + self.upstreamIterator = upstreamIterator + return nil + } + let segment = await transform(item) + var currentIterator = segment.makeAsyncIterator() + self.currentIterator = currentIterator + self.upstreamIterator = upstreamIterator + return try await currentIterator.next() + } public mutating func next() async rethrows -> SegmentOfResult.Element? { - if let item = try await currentIterator?.next() { + guard var currentIterator = self.currentIterator else { + return try await nextUpstream() + } + + if let item = try await currentIterator.next() { + self.currentIterator = currentIterator return item } else { - guard let item = try await upstreamIterator?.next() else { - return nil - } - let segment = await transform(item) - currentIterator = segment.makeAsyncIterator() - return try await currentIterator?.next() + self.currentIterator = currentIterator + return try await nextUpstream() } } @@ -83,17 +100,34 @@ public struct AsyncTryFlatMapSequence: self.upstreamIterator = upstreamIterator self.transform = transform } + + mutating func nextUpstream() async throws -> SegmentOfResult.Element? { + guard var upstreamIterator = self.upstreamIterator else { + return nil + } + + guard let item = try await upstreamIterator.next() else { + self.upstreamIterator = upstreamIterator + return nil + } + let segment = try await transform(item) + var currentIterator = segment.makeAsyncIterator() + self.currentIterator = currentIterator + self.upstreamIterator = upstreamIterator + return try await currentIterator.next() + } public mutating func next() async throws -> SegmentOfResult.Element? { - if let item = try await currentIterator?.next() { + guard var currentIterator = self.currentIterator else { + return try await nextUpstream() + } + + if let item = try await currentIterator.next() { + self.currentIterator = currentIterator return item } else { - guard let item = try await upstreamIterator?.next() else { - return nil - } - let segment = try await transform(item) - currentIterator = segment.makeAsyncIterator() - return try await currentIterator?.next() + self.currentIterator = currentIterator + return try await nextUpstream() } } diff --git a/stdlib/public/Concurrency/AsyncMapSequence.swift b/stdlib/public/Concurrency/AsyncMapSequence.swift index 55fa5f6ecf249..fdf086a1fd8ed 100644 --- a/stdlib/public/Concurrency/AsyncMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncMapSequence.swift @@ -38,7 +38,12 @@ public struct AsyncMapSequence: AsyncSequence where Upstr } public mutating func next() async rethrows -> Transformed? { - guard let item = try await upstreamIterator?.next() else { + guard var upstreamIterator = self.upstreamIterator else { + return nil + } + defer { self.upstreamIterator = upstreamIterator } + + guard let item = try await upstreamIterator.next() else { return nil } return await transform(item) @@ -79,7 +84,12 @@ public struct AsyncTryMapSequence: AsyncSequence where Up } public mutating func next() async throws -> Transformed? { - guard let item = try await upstreamIterator?.next() else { + guard var upstreamIterator = self.upstreamIterator else { + return nil + } + defer { self.upstreamIterator = upstreamIterator } + + guard let item = try await upstreamIterator.next() else { return nil } return try await transform(item) diff --git a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift index b6b48fe08fcfb..e8ab3de577ef5 100644 --- a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift +++ b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift @@ -36,14 +36,19 @@ public struct AsyncPrefixWhileSequence: AsyncSequence where Upstream: } public mutating func next() async rethrows -> Element? { - guard let item = try await upstreamIterator?.next() else { + guard var upstreamIterator = self.upstreamIterator else { + return nil + } + + guard let item = try await upstreamIterator.next() else { return nil } guard await predicate(item) else { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() + self.upstreamIterator = nil return nil } + self.upstreamIterator = upstreamIterator return item } @@ -80,14 +85,20 @@ public struct AsyncTryPrefixWhileSequence: AsyncSequence where Upstrea } public mutating func next() async throws -> Element? { - guard let item = try await upstreamIterator?.next() else { + guard var upstreamIterator = self.upstreamIterator else { + return nil + } + + guard let item = try await upstreamIterator.next() else { + self.upstreamIterator = upstreamIterator return nil } guard try await predicate(item) else { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() + self.upstreamIterator = nil return nil } + self.upstreamIterator = upstreamIterator return item } From 2c8e6b1094b2ec40111fa89deff375c7d0d7cf75 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Thu, 7 Jan 2021 15:23:33 -0800 Subject: [PATCH 21/40] Revert "House mutations in local scope" This reverts commit d91f1b25b59fff8e4be107c808895ff3f293b394. --- .../Concurrency/AsyncCompactMapSequence.swift | 19 ++---- .../Concurrency/AsyncDropWhileSequence.swift | 21 ++----- .../Concurrency/AsyncFilterSequence.swift | 27 +++----- .../Concurrency/AsyncFlatMapSequence.swift | 62 +++++-------------- .../public/Concurrency/AsyncMapSequence.swift | 14 +---- .../AsyncPrefixWhileSequence.swift | 23 ++----- 6 files changed, 38 insertions(+), 128 deletions(-) diff --git a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift index 700bb1165bc79..908465e178d24 100644 --- a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift @@ -37,12 +37,7 @@ public struct AsyncCompactMapSequence: AsyncSequence public mutating func next() async rethrows -> ElementOfResult? { while true { - guard var upstreamIterator = self.upstreamIterator else { - return nil - } - defer { self.upstreamIterator = upstreamIterator } - - guard let item = try await upstreamIterator.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } if let transformed = await transform(item) { @@ -85,22 +80,16 @@ public struct AsyncTryCompactMapSequence: AsyncSequen public mutating func next() async throws -> ElementOfResult? { while true { - guard var upstreamIterator = self.upstreamIterator else { - return nil - } - - guard let item = try await upstreamIterator.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } do { if let transformed = try await transform(item) { - self.upstreamIterator = upstreamIterator return transformed } - self.upstreamIterator = upstreamIterator } catch { - upstreamIterator.cancel() - self.upstreamIterator = nil + upstreamIterator?.cancel() + upstreamIterator = nil throw error } } diff --git a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift index f1ed0debe358c..6d314a923c1da 100644 --- a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift +++ b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift @@ -37,12 +37,7 @@ public struct AsyncDropWhileSequence: AsyncSequence where Upstream: As public mutating func next() async rethrows -> Upstream.Element? { while true { - guard var upstreamIterator = self.upstreamIterator else { - return nil - } - defer { self.upstreamIterator = upstreamIterator } - - guard let item = try await upstreamIterator.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } if let predicate = self.predicate { @@ -90,29 +85,21 @@ public struct AsyncTryDropWhileSequence: AsyncSequence where Upstream: public mutating func next() async throws -> Upstream.Element? { while true { - guard var upstreamIterator = self.upstreamIterator else { - return nil - } - - guard let item = try await upstreamIterator.next() else { - self.upstreamIterator = upstreamIterator + guard let item = try await upstreamIterator?.next() else { return nil } if let predicate = self.predicate { do { if !(try await predicate(item)) { self.predicate = nil - self.upstreamIterator = upstreamIterator return item } - self.upstreamIterator = upstreamIterator } catch { - upstreamIterator.cancel() - self.upstreamIterator = nil + upstreamIterator?.cancel() + upstreamIterator = nil throw error } } else { - self.upstreamIterator = upstreamIterator return item } } diff --git a/stdlib/public/Concurrency/AsyncFilterSequence.swift b/stdlib/public/Concurrency/AsyncFilterSequence.swift index 19993851b54be..ecfa5cc5e7c16 100644 --- a/stdlib/public/Concurrency/AsyncFilterSequence.swift +++ b/stdlib/public/Concurrency/AsyncFilterSequence.swift @@ -41,20 +41,14 @@ public struct AsyncFilterSequence: AsyncSequence where Upstream: Async } public mutating func next() async rethrows -> Upstream.Element? { - guard var upstreamIterator = self.upstreamIterator else { - return nil - } - - guard let item = try await upstreamIterator.next() else { - self.upstreamIterator = upstreamIterator + guard let item = try await upstreamIterator?.next() else { return nil } guard await predicate(item) else { - upstreamIterator.cancel() - self.upstreamIterator = nil - return nil + upstreamIterator?.cancel() + upstreamIterator = nil + return nil } - self.upstreamIterator = upstreamIterator return item } @@ -93,19 +87,14 @@ public struct AsyncTryFilterSequence: AsyncSequence where Upstream: As } public mutating func next() async throws -> Upstream.Element? { - guard var upstreamIterator = self.upstreamIterator else { - return nil - } - - guard let item = try await upstreamIterator.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } guard try await predicate(item) else { - upstreamIterator.cancel() - self.upstreamIterator = nil - return nil + upstreamIterator?.cancel() + upstreamIterator = nil + return nil } - self.upstreamIterator = upstreamIterator return item } diff --git a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift index aa5bcedcd876f..1ebcb2883adf4 100644 --- a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift @@ -35,34 +35,17 @@ public struct AsyncFlatMapSequence: As self.upstreamIterator = upstreamIterator self.transform = transform } - - mutating func nextUpstream() async rethrows -> SegmentOfResult.Element? { - guard var upstreamIterator = self.upstreamIterator else { - return nil - } - - guard let item = try await upstreamIterator.next() else { - self.upstreamIterator = upstreamIterator - return nil - } - let segment = await transform(item) - var currentIterator = segment.makeAsyncIterator() - self.currentIterator = currentIterator - self.upstreamIterator = upstreamIterator - return try await currentIterator.next() - } public mutating func next() async rethrows -> SegmentOfResult.Element? { - guard var currentIterator = self.currentIterator else { - return try await nextUpstream() - } - - if let item = try await currentIterator.next() { - self.currentIterator = currentIterator + if let item = try await currentIterator?.next() { return item } else { - self.currentIterator = currentIterator - return try await nextUpstream() + guard let item = try await upstreamIterator?.next() else { + return nil + } + let segment = await transform(item) + currentIterator = segment.makeAsyncIterator() + return try await currentIterator?.next() } } @@ -100,34 +83,17 @@ public struct AsyncTryFlatMapSequence: self.upstreamIterator = upstreamIterator self.transform = transform } - - mutating func nextUpstream() async throws -> SegmentOfResult.Element? { - guard var upstreamIterator = self.upstreamIterator else { - return nil - } - - guard let item = try await upstreamIterator.next() else { - self.upstreamIterator = upstreamIterator - return nil - } - let segment = try await transform(item) - var currentIterator = segment.makeAsyncIterator() - self.currentIterator = currentIterator - self.upstreamIterator = upstreamIterator - return try await currentIterator.next() - } public mutating func next() async throws -> SegmentOfResult.Element? { - guard var currentIterator = self.currentIterator else { - return try await nextUpstream() - } - - if let item = try await currentIterator.next() { - self.currentIterator = currentIterator + if let item = try await currentIterator?.next() { return item } else { - self.currentIterator = currentIterator - return try await nextUpstream() + guard let item = try await upstreamIterator?.next() else { + return nil + } + let segment = try await transform(item) + currentIterator = segment.makeAsyncIterator() + return try await currentIterator?.next() } } diff --git a/stdlib/public/Concurrency/AsyncMapSequence.swift b/stdlib/public/Concurrency/AsyncMapSequence.swift index fdf086a1fd8ed..55fa5f6ecf249 100644 --- a/stdlib/public/Concurrency/AsyncMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncMapSequence.swift @@ -38,12 +38,7 @@ public struct AsyncMapSequence: AsyncSequence where Upstr } public mutating func next() async rethrows -> Transformed? { - guard var upstreamIterator = self.upstreamIterator else { - return nil - } - defer { self.upstreamIterator = upstreamIterator } - - guard let item = try await upstreamIterator.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } return await transform(item) @@ -84,12 +79,7 @@ public struct AsyncTryMapSequence: AsyncSequence where Up } public mutating func next() async throws -> Transformed? { - guard var upstreamIterator = self.upstreamIterator else { - return nil - } - defer { self.upstreamIterator = upstreamIterator } - - guard let item = try await upstreamIterator.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } return try await transform(item) diff --git a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift index e8ab3de577ef5..b6b48fe08fcfb 100644 --- a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift +++ b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift @@ -36,19 +36,14 @@ public struct AsyncPrefixWhileSequence: AsyncSequence where Upstream: } public mutating func next() async rethrows -> Element? { - guard var upstreamIterator = self.upstreamIterator else { - return nil - } - - guard let item = try await upstreamIterator.next() else { + guard let item = try await upstreamIterator?.next() else { return nil } guard await predicate(item) else { - upstreamIterator.cancel() - self.upstreamIterator = nil + upstreamIterator?.cancel() + upstreamIterator = nil return nil } - self.upstreamIterator = upstreamIterator return item } @@ -85,20 +80,14 @@ public struct AsyncTryPrefixWhileSequence: AsyncSequence where Upstrea } public mutating func next() async throws -> Element? { - guard var upstreamIterator = self.upstreamIterator else { - return nil - } - - guard let item = try await upstreamIterator.next() else { - self.upstreamIterator = upstreamIterator + guard let item = try await upstreamIterator?.next() else { return nil } guard try await predicate(item) else { - upstreamIterator.cancel() - self.upstreamIterator = nil + upstreamIterator?.cancel() + upstreamIterator = nil return nil } - self.upstreamIterator = upstreamIterator return item } From 51fec5bda7d13337019136bfb7c2673a1bde0424 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Mon, 11 Jan 2021 09:13:30 -0800 Subject: [PATCH 22/40] Adjust for inout diagnostics and fall back to original mutation strategy --- include/swift/AST/DiagnosticsSema.def | 2 + lib/IRGen/IRGenSIL.cpp | 2 +- lib/Sema/TypeCheckConcurrency.cpp | 2 + lib/Sema/TypeCheckEffects.cpp | 19 ++++++++- .../Concurrency/AsyncCompactMapSequence.swift | 21 +++++----- .../Concurrency/AsyncConcatSequence.swift | 20 +++++----- .../Concurrency/AsyncDropWhileSequence.swift | 21 +++++----- .../Concurrency/AsyncFilterSequence.swift | 28 ++++++------- .../Concurrency/AsyncFlatMapSequence.swift | 40 +++++++++---------- .../Concurrency/AsyncIteratorProtocol.swift | 29 ++++++++++++++ .../public/Concurrency/AsyncMapSequence.swift | 18 ++++----- .../AsyncPrefixWhileSequence.swift | 24 +++++------ 12 files changed, 126 insertions(+), 100 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 3c8f161b30769..097afd0fb5676 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4054,6 +4054,8 @@ NOTE(because_rethrows_argument_throws,none, NOTE(because_rethrows_default_argument_throws,none, "call is to 'rethrows' function, but a defaulted argument function" " can throw", ()) +NOTE(because_rethrows_default_conformance_throws,none, + "call is to 'rethrows' function, but a conformance can throw", ()) ERROR(throwing_call_in_nonthrowing_autoclosure,none, "call can throw, but it is executed in a non-throwing " diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 31bef947ca330..d229224b6d004 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -4360,7 +4360,7 @@ void IRGenSILFunction::visitDebugValueAddrInst(DebugValueAddrInst *i) { break; } assert(llvm::isa(Storage) && - "arg expected to be load from inside %swift.context"); + "arg expected to be load from inside swift.context"); #endif Indirection = CoroIndirectValue; } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index c8ae6377ebd85..cc5181a1f361f 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -965,6 +965,8 @@ namespace { Expr *subArg = arg->getSubExpr(); ValueDecl *valueDecl = nullptr; + if (auto binding = dyn_cast(subArg)) + subArg = binding->getSubExpr(); if (LookupExpr *baseArg = dyn_cast(subArg)) { while (LookupExpr *nextLayer = dyn_cast(baseArg->getBase())) baseArg = nextLayer; diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 27d0ab8dfc20e..56ef316c90c7e 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -309,6 +309,10 @@ class PotentialThrowReason { /// The function is 'rethrows', and it was passed a default /// argument that was not rethrowing-only in this context. CallRethrowsWithDefaultThrowingArgument, + + /// The the function is 'rethrows', and it is a member that + /// is a conformance to a rethrowing protocol. + CallRethrowsWithConformance, }; static StringRef kindToString(Kind k) { @@ -320,6 +324,8 @@ class PotentialThrowReason { return "CallRethrowsWithExplicitThrowingArgument"; case Kind::CallRethrowsWithDefaultThrowingArgument: return "CallRethrowsWithDefaultThrowingArgument"; + case Kind::CallRethrowsWithConformance: + return "CallRethrowsWithConformance"; } } @@ -337,6 +343,11 @@ class PotentialThrowReason { static PotentialThrowReason forDefaultArgument() { return PotentialThrowReason(Kind::CallRethrowsWithDefaultThrowingArgument); } + static PotentialThrowReason forRethrowsConformance(Expr *E) { + PotentialThrowReason result(Kind::CallRethrowsWithConformance); + result.TheExpression = E; + return result; + } static PotentialThrowReason forThrowingApply() { return PotentialThrowReason(Kind::CallThrows); } @@ -353,7 +364,8 @@ class PotentialThrowReason { bool isThrow() const { return getKind() == Kind::Throw; } bool isRethrowsCall() const { return (getKind() == Kind::CallRethrowsWithExplicitThrowingArgument || - getKind() == Kind::CallRethrowsWithDefaultThrowingArgument); + getKind() == Kind::CallRethrowsWithDefaultThrowingArgument || + getKind() == Kind::CallRethrowsWithConformance); } /// If this was built with forRethrowsArgument, return the expression. @@ -576,7 +588,7 @@ class ApplyClassifier { auto substitutions = fnRef.getDeclRef().getSubstitutions(); if (classifyWitnessAsThrows(fnRef.getModuleContext(), substitutions)) { return Classification::forRethrowingOnly( - PotentialThrowReason::forThrowingApply(), isAsync); + PotentialThrowReason::forRethrowsConformance(E), isAsync); } } else if (fnRef.isBodyRethrows() && fnRef.getRethrowingKind() == FunctionRethrowingKind::Throws) { @@ -1256,6 +1268,9 @@ class Context { case PotentialThrowReason::Kind::CallRethrowsWithDefaultThrowingArgument: Diags.diagnose(loc, diag::because_rethrows_default_argument_throws); return; + case PotentialThrowReason::Kind::CallRethrowsWithConformance: + Diags.diagnose(loc, diag::because_rethrows_default_conformance_throws); + return; } llvm_unreachable("bad reason kind"); } diff --git a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift index 908465e178d24..d728f6114a16f 100644 --- a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift @@ -27,17 +27,17 @@ public struct AsyncCompactMapSequence: AsyncSequence public typealias AsyncIterator = Iterator public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: Upstream.AsyncIterator? + var upstreamIterator: _OptionalAsyncIterator let transform: (Upstream.Element) async -> ElementOfResult? init(_ upstreamIterator: Upstream.AsyncIterator, transform: @escaping (Upstream.Element) async -> ElementOfResult?) { - self.upstreamIterator = upstreamIterator + self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) self.transform = transform } public mutating func next() async rethrows -> ElementOfResult? { while true { - guard let item = try await upstreamIterator?.next() else { + guard let item = try await upstreamIterator.next() else { return nil } if let transformed = await transform(item) { @@ -47,8 +47,7 @@ public struct AsyncCompactMapSequence: AsyncSequence } public mutating func cancel() { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() } } @@ -70,17 +69,17 @@ public struct AsyncTryCompactMapSequence: AsyncSequen public typealias AsyncIterator = Iterator public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: Upstream.AsyncIterator? + var upstreamIterator: _OptionalAsyncIterator let transform: (Upstream.Element) async throws -> ElementOfResult? init(_ upstreamIterator: Upstream.AsyncIterator, transform: @escaping (Upstream.Element) async throws -> ElementOfResult?) { - self.upstreamIterator = upstreamIterator + self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) self.transform = transform } public mutating func next() async throws -> ElementOfResult? { while true { - guard let item = try await upstreamIterator?.next() else { + guard let item = try await upstreamIterator.next() else { return nil } do { @@ -88,16 +87,14 @@ public struct AsyncTryCompactMapSequence: AsyncSequen return transformed } } catch { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() throw error } } } public mutating func cancel() { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() } } diff --git a/stdlib/public/Concurrency/AsyncConcatSequence.swift b/stdlib/public/Concurrency/AsyncConcatSequence.swift index baea73dd47a34..f838364564d20 100644 --- a/stdlib/public/Concurrency/AsyncConcatSequence.swift +++ b/stdlib/public/Concurrency/AsyncConcatSequence.swift @@ -27,27 +27,25 @@ public struct AsyncConcatSequence: AsyncSequence where Prefix: A public typealias AsyncIterator = Iterator public struct Iterator: AsyncIteratorProtocol { - var prefixIterator: Prefix.AsyncIterator? - var suffixIterator: Suffix.AsyncIterator? + var prefixIterator: _OptionalAsyncIterator + var suffixIterator: _OptionalAsyncIterator init(_ prefixIterator: Prefix.AsyncIterator, _ suffixIterator: Suffix.AsyncIterator) { - self.prefixIterator = prefixIterator - self.suffixIterator = suffixIterator + self.prefixIterator = _OptionalAsyncIterator(prefixIterator) + self.suffixIterator = _OptionalAsyncIterator(suffixIterator) } public mutating func next() async rethrows -> Prefix.Element? { - if let item = try await prefixIterator?.next() { + if let item = try await prefixIterator.next() { return item } - prefixIterator = nil - return try await suffixIterator?.next() + prefixIterator = .none + return try await suffixIterator.next() } public mutating func cancel() { - prefixIterator?.cancel() - prefixIterator = nil - suffixIterator?.cancel() - suffixIterator = nil + prefixIterator.cancel() + suffixIterator.cancel() } } diff --git a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift index 6d314a923c1da..b5e97f83a14b9 100644 --- a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift +++ b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift @@ -27,17 +27,17 @@ public struct AsyncDropWhileSequence: AsyncSequence where Upstream: As public typealias AsyncIterator = Iterator public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: Upstream.AsyncIterator? + var upstreamIterator: _OptionalAsyncIterator var predicate: ((Element) async -> Bool)? init(_ upstreamIterator: Upstream.AsyncIterator, predicate: @escaping (Element) async -> Bool) { - self.upstreamIterator = upstreamIterator + self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) self.predicate = predicate } public mutating func next() async rethrows -> Upstream.Element? { while true { - guard let item = try await upstreamIterator?.next() else { + guard let item = try await upstreamIterator.next() else { return nil } if let predicate = self.predicate { @@ -52,8 +52,7 @@ public struct AsyncDropWhileSequence: AsyncSequence where Upstream: As } public mutating func cancel() { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() } } @@ -75,17 +74,17 @@ public struct AsyncTryDropWhileSequence: AsyncSequence where Upstream: public typealias AsyncIterator = Iterator public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: Upstream.AsyncIterator? + var upstreamIterator: _OptionalAsyncIterator var predicate: ((Element) async throws -> Bool)? init(_ upstreamIterator: Upstream.AsyncIterator, predicate: @escaping (Element) async throws -> Bool) { - self.upstreamIterator = upstreamIterator + self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) self.predicate = predicate } public mutating func next() async throws -> Upstream.Element? { while true { - guard let item = try await upstreamIterator?.next() else { + guard let item = try await upstreamIterator.next() else { return nil } if let predicate = self.predicate { @@ -95,8 +94,7 @@ public struct AsyncTryDropWhileSequence: AsyncSequence where Upstream: return item } } catch { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() throw error } } else { @@ -106,8 +104,7 @@ public struct AsyncTryDropWhileSequence: AsyncSequence where Upstream: } public mutating func cancel() { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() } } diff --git a/stdlib/public/Concurrency/AsyncFilterSequence.swift b/stdlib/public/Concurrency/AsyncFilterSequence.swift index ecfa5cc5e7c16..cf646cc9bfe40 100644 --- a/stdlib/public/Concurrency/AsyncFilterSequence.swift +++ b/stdlib/public/Concurrency/AsyncFilterSequence.swift @@ -30,31 +30,29 @@ public struct AsyncFilterSequence: AsyncSequence where Upstream: Async public let predicate: (Element) async -> Bool public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: Upstream.AsyncIterator? + var upstreamIterator: _OptionalAsyncIterator let predicate: (Element) async -> Bool init(_ upstreamIterator: Upstream.AsyncIterator, predicate: @escaping (Element) async -> Bool ) { - self.upstreamIterator = upstreamIterator + self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) self.predicate = predicate } public mutating func next() async rethrows -> Upstream.Element? { - guard let item = try await upstreamIterator?.next() else { + guard let item = try await upstreamIterator.next() else { return nil } guard await predicate(item) else { - upstreamIterator?.cancel() - upstreamIterator = nil - return nil + upstreamIterator.cancel() + return nil } return item } public mutating func cancel() { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() } } @@ -76,31 +74,29 @@ public struct AsyncTryFilterSequence: AsyncSequence where Upstream: As public let predicate: (Element) async throws -> Bool public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: Upstream.AsyncIterator? + var upstreamIterator: _OptionalAsyncIterator let predicate: (Element) async throws -> Bool init(_ upstreamIterator: Upstream.AsyncIterator, predicate: @escaping (Element) async throws -> Bool ) { - self.upstreamIterator = upstreamIterator + self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) self.predicate = predicate } public mutating func next() async throws -> Upstream.Element? { - guard let item = try await upstreamIterator?.next() else { + guard let item = try await upstreamIterator.next() else { return nil } guard try await predicate(item) else { - upstreamIterator?.cancel() - upstreamIterator = nil - return nil + upstreamIterator.cancel() + return nil } return item } public mutating func cancel() { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() } } diff --git a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift index 1ebcb2883adf4..9975bcbc4c859 100644 --- a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift @@ -27,33 +27,31 @@ public struct AsyncFlatMapSequence: As public typealias AsyncIterator = Iterator public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: Upstream.AsyncIterator? + var upstreamIterator: _OptionalAsyncIterator let transform: (Upstream.Element) async -> SegmentOfResult - var currentIterator: SegmentOfResult.AsyncIterator? + var currentIterator: _OptionalAsyncIterator = .none init(_ upstreamIterator: Upstream.AsyncIterator, transform: @escaping (Upstream.Element) async -> SegmentOfResult) { - self.upstreamIterator = upstreamIterator + self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) self.transform = transform } public mutating func next() async rethrows -> SegmentOfResult.Element? { - if let item = try await currentIterator?.next() { + if let item = try await currentIterator.next() { return item } else { - guard let item = try await upstreamIterator?.next() else { + guard let item = try await upstreamIterator.next() else { return nil } let segment = await transform(item) - currentIterator = segment.makeAsyncIterator() - return try await currentIterator?.next() + currentIterator = _OptionalAsyncIterator(segment.makeAsyncIterator()) + return try await currentIterator.next() } } public mutating func cancel() { - upstreamIterator?.cancel() - upstreamIterator = nil - currentIterator?.cancel() - currentIterator = nil + upstreamIterator.cancel() + currentIterator.cancel() } } @@ -75,33 +73,31 @@ public struct AsyncTryFlatMapSequence: public typealias AsyncIterator = Iterator public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: Upstream.AsyncIterator? + var upstreamIterator: _OptionalAsyncIterator let transform: (Upstream.Element) async throws -> SegmentOfResult - var currentIterator: SegmentOfResult.AsyncIterator? + var currentIterator: _OptionalAsyncIterator = .none init(_ upstreamIterator: Upstream.AsyncIterator, transform: @escaping (Upstream.Element) async throws -> SegmentOfResult) { - self.upstreamIterator = upstreamIterator + self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) self.transform = transform } public mutating func next() async throws -> SegmentOfResult.Element? { - if let item = try await currentIterator?.next() { + if let item = try await currentIterator.next() { return item } else { - guard let item = try await upstreamIterator?.next() else { + guard let item = try await upstreamIterator.next() else { return nil } let segment = try await transform(item) - currentIterator = segment.makeAsyncIterator() - return try await currentIterator?.next() + currentIterator = _OptionalAsyncIterator(segment.makeAsyncIterator()) + return try await currentIterator.next() } } public mutating func cancel() { - upstreamIterator?.cancel() - upstreamIterator = nil - currentIterator?.cancel() - currentIterator = nil + upstreamIterator.cancel() + currentIterator.cancel() } } diff --git a/stdlib/public/Concurrency/AsyncIteratorProtocol.swift b/stdlib/public/Concurrency/AsyncIteratorProtocol.swift index 3013377d0a387..5bf13f1c4b8ef 100644 --- a/stdlib/public/Concurrency/AsyncIteratorProtocol.swift +++ b/stdlib/public/Concurrency/AsyncIteratorProtocol.swift @@ -18,3 +18,32 @@ public protocol AsyncIteratorProtocol { mutating func next() async throws -> Element? mutating func cancel() } + +internal enum _OptionalAsyncIterator: AsyncIteratorProtocol { + case none + case some(AsyncIterator) + + init(_ iterator: AsyncIterator) { + self = .some(iterator) + } + + mutating func next() async rethrows -> AsyncIterator.Element? { + switch self { + case .some(var iterator): + defer { self = .some(iterator) } + return try await iterator.next() + default: + return nil + } + } + + mutating func cancel() { + switch self { + case .some(var iterator): + iterator.cancel() + self = .none + default: + break + } + } +} \ No newline at end of file diff --git a/stdlib/public/Concurrency/AsyncMapSequence.swift b/stdlib/public/Concurrency/AsyncMapSequence.swift index 55fa5f6ecf249..ca8fa0faa6e3e 100644 --- a/stdlib/public/Concurrency/AsyncMapSequence.swift +++ b/stdlib/public/Concurrency/AsyncMapSequence.swift @@ -27,26 +27,25 @@ public struct AsyncMapSequence: AsyncSequence where Upstr public typealias Element = Transformed public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: Upstream.AsyncIterator? + var upstreamIterator: _OptionalAsyncIterator let transform: (Upstream.Element) async -> Transformed init(_ upstreamIterator: Upstream.AsyncIterator, transform: @escaping (Upstream.Element) async -> Transformed ) { - self.upstreamIterator = upstreamIterator + self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) self.transform = transform } public mutating func next() async rethrows -> Transformed? { - guard let item = try await upstreamIterator?.next() else { + guard let item = try await upstreamIterator.next() else { return nil } return await transform(item) } public mutating func cancel() { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() } } @@ -68,26 +67,25 @@ public struct AsyncTryMapSequence: AsyncSequence where Up public typealias Element = Transformed public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: Upstream.AsyncIterator? + var upstreamIterator: _OptionalAsyncIterator let transform: (Upstream.Element) async throws -> Transformed init(_ upstreamIterator: Upstream.AsyncIterator, transform: @escaping (Upstream.Element) async throws -> Transformed ) { - self.upstreamIterator = upstreamIterator + self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) self.transform = transform } public mutating func next() async throws -> Transformed? { - guard let item = try await upstreamIterator?.next() else { + guard let item = try await upstreamIterator.next() else { return nil } return try await transform(item) } public mutating func cancel() { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() } } diff --git a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift index b6b48fe08fcfb..e14ea1932abdd 100644 --- a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift +++ b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift @@ -27,29 +27,27 @@ public struct AsyncPrefixWhileSequence: AsyncSequence where Upstream: public typealias AsyncIterator = Iterator public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: Upstream.AsyncIterator? + var upstreamIterator: _OptionalAsyncIterator var predicate: (Element) async -> Bool init(_ upstreamIterator: Upstream.AsyncIterator, predicate: @escaping (Element) async -> Bool) { - self.upstreamIterator = upstreamIterator + self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) self.predicate = predicate } public mutating func next() async rethrows -> Element? { - guard let item = try await upstreamIterator?.next() else { + guard let item = try await upstreamIterator.next() else { return nil } guard await predicate(item) else { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() return nil } return item } public mutating func cancel() { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() } } @@ -71,29 +69,27 @@ public struct AsyncTryPrefixWhileSequence: AsyncSequence where Upstrea public typealias AsyncIterator = Iterator public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: Upstream.AsyncIterator? + var upstreamIterator: _OptionalAsyncIterator var predicate: (Element) async throws -> Bool init(_ upstreamIterator: Upstream.AsyncIterator, predicate: @escaping (Element) async throws -> Bool) { - self.upstreamIterator = upstreamIterator + self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) self.predicate = predicate } public mutating func next() async throws -> Element? { - guard let item = try await upstreamIterator?.next() else { + guard let item = try await upstreamIterator.next() else { return nil } guard try await predicate(item) else { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() return nil } return item } public mutating func cancel() { - upstreamIterator?.cancel() - upstreamIterator = nil + upstreamIterator.cancel() } } From d682168ee43e5fff43443e7af8eb9067f0f03a2f Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Tue, 12 Jan 2021 10:56:33 -0800 Subject: [PATCH 23/40] Convert async flag to source locations and add initial try support to for await in syntax --- include/swift/AST/Stmt.h | 12 +++++++----- lib/Parse/ParseStmt.cpp | 14 ++++++++++---- lib/SILGen/SILGenStmt.cpp | 2 +- lib/Sema/BuilderTransform.cpp | 2 +- lib/Sema/CSGen.cpp | 2 +- lib/Sema/TypeCheckConstraints.cpp | 2 +- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 30ba8bb8899a1..029dadb177989 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -726,7 +726,8 @@ class RepeatWhileStmt : public LabeledStmt { /// \endcode class ForEachStmt : public LabeledStmt { SourceLoc ForLoc; - bool IsAsync; + SourceLoc TryLoc; + SourceLoc AwaitLoc; Pattern *Pat; SourceLoc InLoc; Expr *Sequence; @@ -742,12 +743,12 @@ class ForEachStmt : public LabeledStmt { Expr *convertElementExpr = nullptr; public: - ForEachStmt(LabeledStmtInfo LabelInfo, SourceLoc ForLoc, bool IsAsync, Pattern *Pat, - SourceLoc InLoc, Expr *Sequence, SourceLoc WhereLoc, + ForEachStmt(LabeledStmtInfo LabelInfo, SourceLoc ForLoc, SourceLoc TryLoc, SourceLoc AwaitLoc, + Pattern *Pat, SourceLoc InLoc, Expr *Sequence, SourceLoc WhereLoc, Expr *WhereExpr, BraceStmt *Body, Optional implicit = None) : LabeledStmt(StmtKind::ForEach, getDefaultImplicitFlag(implicit, ForLoc), LabelInfo), - ForLoc(ForLoc), IsAsync(IsAsync), Pat(nullptr), InLoc(InLoc), Sequence(Sequence), + ForLoc(ForLoc), TryLoc(TryLoc), AwaitLoc(AwaitLoc), Pat(nullptr), InLoc(InLoc), Sequence(Sequence), WhereLoc(WhereLoc), WhereExpr(WhereExpr), Body(Body) { setPattern(Pat); } @@ -780,7 +781,8 @@ class ForEachStmt : public LabeledStmt { /// getWhereLoc - Retrieve the location of the 'where' keyword. SourceLoc getWhereLoc() const { return WhereLoc; } - bool isAsync() const { return IsAsync; } + SourceLoc getAwaitLoc() const { return AwaitLoc; } + SourceLoc getTryLoc() const { return TryLoc; } /// getPattern - Retrieve the pattern describing the iteration variables. /// These variables will only be visible within the body of the loop. diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 955297f0564de..36414a83fedfa 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -2124,12 +2124,18 @@ ParserResult Parser::parseStmtForEach(LabeledStmtInfo LabelInfo) { // lookahead to resolve what is going on. bool IsCStyleFor = isStmtForCStyle(*this); auto StartOfControl = Tok.getLoc(); - bool IsAsync = false; + SourceLoc AwaitLoc; + SourceLoc TryLoc; if (shouldParseExperimentalConcurrency() && Tok.isContextualKeyword("await")) { - consumeToken(); - IsAsync = true; + AwaitLoc = consumeToken(); + } if (shouldParseExperimentalConcurrency() && + Tok.is(tok::kw_try)) { + TryLoc = consumeToken(); + if (Tok.isContextualKeyword("await")) { + AwaitLoc = consumeToken(); + } } // Parse the pattern. This is either 'case ' or just a @@ -2225,7 +2231,7 @@ ParserResult Parser::parseStmtForEach(LabeledStmtInfo LabelInfo) { return makeParserResult( Status, - new (Context) ForEachStmt(LabelInfo, ForLoc, IsAsync, pattern.get(), InLoc, + new (Context) ForEachStmt(LabelInfo, ForLoc, TryLoc, AwaitLoc, pattern.get(), InLoc, Container.get(), WhereLoc, Where.getPtrOrNull(), Body.get())); } diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index a427a5651a8fd..3c5bb1ec0ecd0 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -1198,7 +1198,7 @@ void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { } void StmtEmitter::visitForEachStmt(ForEachStmt *S) { - if (S->isAsync()) { + if (S->getAwaitLoc().isValid()) { visitAsyncForEachStmt(S); return; } diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index a927e0f65bf0b..f461ff9105178 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -774,7 +774,7 @@ class BuilderClosureVisitor // take care of this. auto sequenceProto = TypeChecker::getProtocol( dc->getASTContext(), forEachStmt->getForLoc(), - forEachStmt->isAsync() ? + forEachStmt->getAwaitLoc().isValid() ? KnownProtocolKind::AsyncSequence : KnownProtocolKind::Sequence); if (!sequenceProto) { if (!unhandledNode) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 4f2e4dca72fc9..95de559e37a82 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3655,7 +3655,7 @@ generateForEachStmtConstraints( ConstraintSystem &cs, SolutionApplicationTarget target, Expr *sequence) { auto forEachStmtInfo = target.getForEachStmtInfo(); ForEachStmt *stmt = forEachStmtInfo.stmt; - bool isAsync = stmt->isAsync(); + bool isAsync = stmt->getAwaitLoc().isValid(); auto locator = cs.getConstraintLocator(sequence); auto contextualLocator = diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 90e3b52a02f01..feee4a37fbb82 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -563,7 +563,7 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { auto sequenceProto = TypeChecker::getProtocol( dc->getASTContext(), stmt->getForLoc(), - stmt->isAsync() ? + stmt->getAwaitLoc().isValid() ? KnownProtocolKind::AsyncSequence : KnownProtocolKind::Sequence); if (!sequenceProto) return failed(); From 7431ec4fd12e8d00102fa367a0a90aa05b394b8c Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Tue, 12 Jan 2021 12:29:42 -0800 Subject: [PATCH 24/40] Fix case typo of MinMax.swift --- stdlib/public/Concurrency/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index d3831c60e8881..001a3bde6b995 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -50,7 +50,7 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I Contains.swift Count.swift First.swift - MinMax.Swift + MinMax.swift Reduce.swift PartialAsyncTask.swift Task.cpp From 8aa204b6dcecec7da0a47ccb8d7799f435186f60 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 13 Jan 2021 15:56:07 -0800 Subject: [PATCH 25/40] Adjust rethrowing tests to account for changes associated with @rethrows --- test/attr/attr_rethrows_protocol.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/attr/attr_rethrows_protocol.swift b/test/attr/attr_rethrows_protocol.swift index c8bbb3b6a37e4..4bf4891b7b35e 100644 --- a/test/attr/attr_rethrows_protocol.swift +++ b/test/attr/attr_rethrows_protocol.swift @@ -2,7 +2,7 @@ @rethrows protocol RethrowingProtocol { - func source() rethrows + func source() throws } struct Rethrows: RethrowingProtocol { @@ -29,12 +29,11 @@ struct NonThrowsWithSource: RethrowingProtocol { } protocol InvalidRethrowingProtocol { - func source() rethrows // expected-note{{}} + func source() throws } struct InvalidRethrows : InvalidRethrowingProtocol { - // expected-error@-1{{type 'InvalidRethrows' does not conform to protocol 'InvalidRethrowingProtocol'}} - func source() rethrows { } // expected-note{{}} + func source() rethrows { } // expected-error@-1{{'rethrows' function must take a throwing function argument}} } @@ -53,7 +52,7 @@ try rethrowingFromThrows.source() protocol HasAssociatedRethrowerWithEnclosedRethrow { associatedtype Rethrower: RethrowingProtocol - func source() rethrows + func source() throws } @rethrows @@ -65,6 +64,7 @@ protocol HasAssociatedRethrower { func freeFloatingRethrowing(_ r: R) rethrows { } +@rethrows protocol InheritsRethrowing: RethrowingProtocol {} func freeFloatingInheritedRethrowingFunction(_ r: I) rethrows { } From 307630f0cc130c962dfaee5f1291d9f415479bef Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 13 Jan 2021 15:56:40 -0800 Subject: [PATCH 26/40] Allow parsing and diagnostics associated with try applied to for await in syntax --- include/swift/AST/Module.h | 2 + include/swift/AST/ProtocolConformanceRef.h | 2 + lib/AST/Module.cpp | 14 +++++ lib/AST/ProtocolConformance.cpp | 59 ++++++++++++++++++ lib/Sema/TypeCheckConstraints.cpp | 17 ++++++ lib/Sema/TypeCheckEffects.cpp | 70 +--------------------- 6 files changed, 95 insertions(+), 69 deletions(-) diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 5cff707bd07e2..f9d5c914a0a95 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -730,6 +730,8 @@ class ModuleDecl : public DeclContext, public TypeDecl { SourceRange getSourceRange() const { return SourceRange(); } + bool classifyWitnessAsThrows(SubstitutionMap substitutions); + static bool classof(const DeclContext *DC) { if (auto D = DC->getAsDecl()) return classof(D); diff --git a/include/swift/AST/ProtocolConformanceRef.h b/include/swift/AST/ProtocolConformanceRef.h index e155092f58db6..2b27d4edd9a75 100644 --- a/include/swift/AST/ProtocolConformanceRef.h +++ b/include/swift/AST/ProtocolConformanceRef.h @@ -170,6 +170,8 @@ class ProtocolConformanceRef { /// Get any additional requirements that are required for this conformance to /// be satisfied. ArrayRef getConditionalRequirements() const; + + bool classifyAsThrows(ModuleDecl *module); }; } // end namespace swift diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index d206d2f250640..80bda8f67cfe9 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -835,6 +835,20 @@ void ModuleDecl::getPrecedenceGroups( FORWARD(getPrecedenceGroups, (results)); } +bool ModuleDecl::classifyWitnessAsThrows(SubstitutionMap substitutions) { + for (auto conformanceRef : substitutions.getConformances()) { + if (!conformanceRef.isConcrete()) { + return true; + } + + if (conformanceRef.classifyAsThrows(this)) { + return true; + } + } + + return false; +} + void SourceFile::getPrecedenceGroups( SmallVectorImpl &results) const { getCache().getPrecedenceGroups(results); diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 62b4791e29c88..02b5087840e12 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -185,6 +185,65 @@ ProtocolConformanceRef::getWitnessByName(Type type, DeclName name) const { return getConcrete()->getWitnessDeclRef(requirement); } + +static bool classifyRequirement(ModuleDecl *module, + ProtocolConformance *reqConformance, + ValueDecl *requiredFn) { + auto DC = reqConformance->getDeclContext(); + auto reqTy = reqConformance->getType(); + auto declRef = reqConformance->getWitnessDeclRef(requiredFn); + auto witnessDecl = cast(declRef.getDecl()); + switch (witnessDecl->getRethrowingKind()) { + case FunctionRethrowingKind::ByConformance: + if (module->classifyWitnessAsThrows( + reqTy->getContextSubstitutionMap(module, DC))) { + return true; + } + break; + case FunctionRethrowingKind::None: + break; + case FunctionRethrowingKind::Throws: + return true; + default: + return true; + } + return false; +} + +static bool classifyTypeRequirement(ModuleDecl *module, Type protoType, + ValueDecl *requiredFn, + ProtocolConformance *conformance, + ProtocolDecl *requiredProtocol) { + auto reqProtocol = cast(requiredFn->getDeclContext()); + ProtocolConformance *reqConformance; + + if(protoType->isEqual(reqProtocol->getSelfInterfaceType()) && + requiredProtocol == reqProtocol) { + reqConformance = conformance; + } else { + auto reqConformanceRef = + conformance->getAssociatedConformance(protoType, reqProtocol); + if (!reqConformanceRef.isConcrete()) { + return true; + } + reqConformance = reqConformanceRef.getConcrete(); + } + + return classifyRequirement(module, reqConformance, requiredFn); +} + +bool ProtocolConformanceRef::classifyAsThrows(ModuleDecl *module) { + auto conformance = getConcrete(); + auto requiredProtocol = getRequirement(); + for (auto req : requiredProtocol->getRethrowingRequirements()) { + if (classifyTypeRequirement(module, req.first, req.second, + conformance, requiredProtocol)) { + return true; + } + } + return false; +} + void *ProtocolConformance::operator new(size_t bytes, ASTContext &context, AllocationArena arena, unsigned alignment) { diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index feee4a37fbb82..d863d9c6aba7c 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -589,6 +589,23 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { if (!typeCheckExpression(target)) return failed(); + // check to see if the sequence expr is throwing (and async), if so require the stmt to have a try loc + if (stmt->getAwaitLoc().isValid()) { + auto Ty = sequence->getType(); + if (Ty.isNull()) { return failed(); } + auto context = sequence->getType()->getNominalOrBoundGenericNominal(); + if (!context) { return failed(); } + auto module = context->getParentModule(); + auto conformanceRef = module->lookupConformance(Ty, sequenceProto); + + if (conformanceRef.classifyAsThrows(module) && stmt->getTryLoc().isInvalid()) { + auto &diags = dc->getASTContext().Diags; + diags.diagnose(stmt->getAwaitLoc(), diag::throwing_call_unhandled); + + return failed(); + } + } + return false; } diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 56ef316c90c7e..2a0e434773015 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -493,74 +493,6 @@ class ApplyClassifier { DeclContext *RethrowsDC = nullptr; bool inRethrowsContext() const { return RethrowsDC != nullptr; } - bool classifyRequirement(ModuleDecl *module, - ProtocolConformance *reqConformance, - ValueDecl *requiredFn) { - auto DC = reqConformance->getDeclContext(); - auto reqTy = reqConformance->getType(); - auto declRef = reqConformance->getWitnessDeclRef(requiredFn); - auto witnessDecl = cast(declRef.getDecl()); - switch (witnessDecl->getRethrowingKind()) { - case FunctionRethrowingKind::ByConformance: - if (classifyWitnessAsThrows(module, - reqTy->getContextSubstitutionMap(module, DC))) { - return true; - } - break; - case FunctionRethrowingKind::None: - break; - case FunctionRethrowingKind::Throws: - return true; - default: - return true; - } - return false; - } - - bool classifyTypeRequirement(ModuleDecl *module, Type protoType, - ValueDecl *requiredFn, - ProtocolConformance *conformance, - ProtocolDecl *requiredProtocol) { - auto reqProtocol = cast(requiredFn->getDeclContext()); - ProtocolConformance *reqConformance; - - if(protoType->isEqual(reqProtocol->getSelfInterfaceType()) && - requiredProtocol == reqProtocol) { - reqConformance = conformance; - } else { - auto reqConformanceRef = - conformance->getAssociatedConformance(protoType, reqProtocol); - if (!reqConformanceRef.isConcrete()) { - return true; - } - reqConformance = reqConformanceRef.getConcrete(); - } - - return classifyRequirement(module, reqConformance, requiredFn); - } - - bool classifyWitnessAsThrows(ModuleDecl *module, - SubstitutionMap substitutions) { - - - for (auto conformanceRef : substitutions.getConformances()) { - if (!conformanceRef.isConcrete()) { - return true; - } - auto conformance = conformanceRef.getConcrete(); - - auto requiredProtocol = conformanceRef.getRequirement(); - for (auto req : requiredProtocol->getRethrowingRequirements()) { - if (classifyTypeRequirement(module, req.first, req.second, - conformance, requiredProtocol)) { - return true; - } - } - } - - return false; - } - /// Check to see if the given function application throws or is async. Classification classifyApply(ApplyExpr *E) { // An apply expression is a potential throw site if the function throws. @@ -586,7 +518,7 @@ class ApplyClassifier { if (fnRef.getRethrowingKind() == FunctionRethrowingKind::ByConformance) { auto substitutions = fnRef.getDeclRef().getSubstitutions(); - if (classifyWitnessAsThrows(fnRef.getModuleContext(), substitutions)) { + if (fnRef.getModuleContext()->classifyWitnessAsThrows(substitutions)) { return Classification::forRethrowingOnly( PotentialThrowReason::forRethrowsConformance(E), isAsync); } From 0763e2dec07ccc2dbe246ed497d73826cd8da5d5 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 13 Jan 2021 16:16:11 -0800 Subject: [PATCH 27/40] Correct the code-completion for @rethrows --- test/IDE/complete_decl_attribute.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index b5125597727c9..7469d3c703769 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -239,6 +239,7 @@ struct _S { // ON_MEMBER_LAST-DAG: Keyword/None: inlinable[#Declaration Attribute#]; name=inlinable // ON_MEMBER_LAST-DAG: Keyword/None: objcMembers[#Declaration Attribute#]; name=objcMembers // ON_MEMBER_LAST-DAG: Keyword/None: NSApplicationMain[#Declaration Attribute#]; name=NSApplicationMain +// ON_MEMBER_LAST-DAG: Keyword/None: rethrows[#Declaration Attribute#]; name=rethrows // ON_MEMBER_LAST-DAG: Keyword/None: warn_unqualified_access[#Declaration Attribute#]; name=warn_unqualified_access // ON_MEMBER_LAST-DAG: Keyword/None: usableFromInline[#Declaration Attribute#]; name=usableFromInline // ON_MEMBER_LAST-DAG: Keyword/None: discardableResult[#Declaration Attribute#]; name=discardableResult From 0d940e7f0f73bf900293e4ab6afcaac471212a4a Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Thu, 14 Jan 2021 10:36:50 -0800 Subject: [PATCH 28/40] Additional corrections for the code-completion for @rethrows this time for the last in the list --- test/IDE/complete_decl_attribute.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index 7469d3c703769..a2585d76ecfb9 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -287,6 +287,7 @@ func dummy2() {} // KEYWORD_LAST-NEXT: Keyword/None: inlinable[#Declaration Attribute#]; name=inlinable{{$}} // KEYWORD_LAST-NEXT: Keyword/None: objcMembers[#Declaration Attribute#]; name=objcMembers{{$}} // KEYWORD_LAST-NEXT: Keyword/None: NSApplicationMain[#Declaration Attribute#]; name=NSApplicationMain{{$}} +// KEYWORD_LAST-NEXT: Keyword/None: rethrows[#Declaration Attribute#]; name=rethrows{{$}} // KEYWORD_LAST-NEXT: Keyword/None: warn_unqualified_access[#Declaration Attribute#]; name=warn_unqualified_access // KEYWORD_LAST-NEXT: Keyword/None: usableFromInline[#Declaration Attribute#]; name=usableFromInline{{$}} // KEYWORD_LAST-NEXT: Keyword/None: discardableResult[#Declaration Attribute#]; name=discardableResult From 6a640a2de4d6baa0a33740e74b278c4c8cb9ad3b Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Thu, 14 Jan 2021 15:59:45 -0800 Subject: [PATCH 29/40] Handle throwing cases of iteration of async sequences --- lib/SILGen/SILGenApply.cpp | 15 +++++++++++++-- lib/SILGen/SILGenStmt.cpp | 7 ++----- lib/Sema/TypeCheckEffects.cpp | 16 ++++++++++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 12477f287b1a8..8c268473bbaf5 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -4969,7 +4969,18 @@ RValue SILGenFunction::emitApplyMethod(SILLocation loc, ConcreteDeclRef declRef, .asForeign(requiresForeignEntryPoint(declRef.getDecl())); auto declRefConstant = getConstantInfo(getTypeExpansionContext(), callRef); auto subs = declRef.getSubstitutions(); - + bool throws; + bool markedAsRethrows = call->getAttrs().hasAttribute(); + FunctionRethrowingKind rethrowingKind = call->getRethrowingKind(); + if (rethrowingKind == FunctionRethrowingKind::ByConformance) { + throws = call->getModuleContext()->classifyWitnessAsThrows(subs); + } else if (markedAsRethrows && + rethrowingKind == FunctionRethrowingKind::Throws) { + throws = true; + } else { + throws = false; + } + // Scope any further writeback just within this operation. FormalEvaluationScope writebackScope(*this); @@ -5002,7 +5013,7 @@ RValue SILGenFunction::emitApplyMethod(SILLocation loc, ConcreteDeclRef declRef, // Form the call emission. CallEmission emission(*this, std::move(*callee), std::move(writebackScope)); emission.addSelfParam(loc, std::move(self), substFormalType.getParams()[0]); - emission.addCallSite(loc, std::move(args), /*throws*/ false); + emission.addCallSite(loc, std::move(args), throws); return emission.apply(C); } diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 3c5bb1ec0ecd0..ceb79f86a23c1 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -979,12 +979,12 @@ void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { SGF.emitInitializationForVarDecl(S->getIteratorVar(), false); SILLocation loc = SILLocation(S->getSequence()); - // Compute the reference to the AsyncSequence's makeGenerator(). + // Compute the reference to the AsyncSequence's makeAsyncSequence(). FuncDecl *makeGeneratorReq = SGF.getASTContext().getAsyncSequenceMakeAsyncIterator(); ConcreteDeclRef makeGeneratorRef(makeGeneratorReq, sequenceSubs); - // Call makeGenerator(). + // Call makeAsyncSequence(). RValue result = SGF.emitApplyMethod( loc, makeGeneratorRef, ArgumentSource(S->getSequence()), PreparedArguments(ArrayRef({})), @@ -1182,10 +1182,7 @@ void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { createBasicBlock(), failExitingBlock, [&](ManagedValue inputValue, SwitchCaseFullExpr &&scope) { assert(!inputValue && "None should not be passed an argument!"); - // cancelCleanup.setState(SGF, CleanupState::Dormant); - SGF.Cleanups.forwardCleanup(cancelCleanup); - scope.exitAndBranch(S); }, SGF.loadProfilerCount(S)); diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 2a0e434773015..2f5d3c1865bcb 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -274,6 +274,8 @@ class EffectsHandlingWalker : public ASTWalker { recurse = asImpl().checkDoCatch(doCatch); } else if (auto thr = dyn_cast(S)) { recurse = asImpl().checkThrow(thr); + } else if (auto forEach = dyn_cast(S)) { + recurse = asImpl().checkForEach(forEach); } return {bool(recurse), S}; } @@ -287,6 +289,10 @@ class EffectsHandlingWalker : public ASTWalker { } return ShouldNotRecurse; } + + ShouldRecurse_t checkForEach(ForEachStmt *S) { + return ShouldRecurse; + } }; /// A potential reason why something might throw. @@ -2082,6 +2088,16 @@ class CheckEffectsCoverage : public EffectsHandlingWalker scope.preserveCoverageFromOptionalOrForcedTryOperand(); return ShouldNotRecurse; } + + ShouldRecurse_t checkForEach(ForEachStmt *S) { + if (S->getTryLoc().isValid() && + !Flags.has(ContextFlags::IsTryCovered)) { + checkThrowAsyncSite(S, /*requiresTry*/ false, + Classification::forThrow(PotentialThrowReason::forThrow(), + /*async*/false)); + } + return ShouldRecurse; + } }; // Find nested functions and perform effects checking on them. From 0f2422d4dec7e88c65d917cbdbe471c0acdfbd0b Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Fri, 15 Jan 2021 11:10:16 -0800 Subject: [PATCH 30/40] restore building XCTest --- stdlib/public/Darwin/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/public/Darwin/CMakeLists.txt b/stdlib/public/Darwin/CMakeLists.txt index 76584f5773293..b8be8dfc71188 100644 --- a/stdlib/public/Darwin/CMakeLists.txt +++ b/stdlib/public/Darwin/CMakeLists.txt @@ -19,6 +19,7 @@ set(all_overlays Dispatch Foundation ObjectiveC + XCTest ) if(DEFINED SWIFT_OVERLAY_TARGETS) From ac18c22c50b596e51a9263bb2df9097107663cd6 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Tue, 19 Jan 2021 08:18:20 -0800 Subject: [PATCH 31/40] First wave of feedback fixes --- include/swift/AST/DiagnosticsSema.def | 2 +- include/swift/AST/Requirement.h | 1 - lib/AST/ASTContext.cpp | 1 - lib/AST/ProtocolConformance.cpp | 15 +++++++++------ lib/Sema/TypeCheckConstraints.cpp | 6 ++++-- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 097afd0fb5676..3e0f5ac5824a5 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4055,7 +4055,7 @@ NOTE(because_rethrows_default_argument_throws,none, "call is to 'rethrows' function, but a defaulted argument function" " can throw", ()) NOTE(because_rethrows_default_conformance_throws,none, - "call is to 'rethrows' function, but a conformance can throw", ()) + "call is to 'rethrows' function, but a conformance has a throwing witness", ()) ERROR(throwing_call_in_nonthrowing_autoclosure,none, "call can throw, but it is executed in a non-throwing " diff --git a/include/swift/AST/Requirement.h b/include/swift/AST/Requirement.h index 3e87a8f989df4..61a9c220dc544 100644 --- a/include/swift/AST/Requirement.h +++ b/include/swift/AST/Requirement.h @@ -46,7 +46,6 @@ enum class RequirementKind : unsigned { // when adding enumerators. }; - /// A single requirement placed on the type parameters (or associated /// types thereof) of a class Requirement { diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 689e14d550be5..4e7601e0e3bc1 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -65,7 +65,6 @@ #include #include - using namespace swift; #define DEBUG_TYPE "ASTContext" diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 02b5087840e12..c5e5d58e0785c 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -187,8 +187,8 @@ ProtocolConformanceRef::getWitnessByName(Type type, DeclName name) const { static bool classifyRequirement(ModuleDecl *module, - ProtocolConformance *reqConformance, - ValueDecl *requiredFn) { + ProtocolConformance *reqConformance, + ValueDecl *requiredFn) { auto DC = reqConformance->getDeclContext(); auto reqTy = reqConformance->getType(); auto declRef = reqConformance->getWitnessDeclRef(requiredFn); @@ -210,10 +210,13 @@ static bool classifyRequirement(ModuleDecl *module, return false; } +// classify the type requirements of a given prottocol type with a function +// requirement as throws or not. This will detect if the signature of the +// function is throwing or not depending on associated types. static bool classifyTypeRequirement(ModuleDecl *module, Type protoType, - ValueDecl *requiredFn, - ProtocolConformance *conformance, - ProtocolDecl *requiredProtocol) { + ValueDecl *requiredFn, + ProtocolConformance *conformance, + ProtocolDecl *requiredProtocol) { auto reqProtocol = cast(requiredFn->getDeclContext()); ProtocolConformance *reqConformance; @@ -222,7 +225,7 @@ static bool classifyTypeRequirement(ModuleDecl *module, Type protoType, reqConformance = conformance; } else { auto reqConformanceRef = - conformance->getAssociatedConformance(protoType, reqProtocol); + conformance->getAssociatedConformance(protoType, reqProtocol); if (!reqConformanceRef.isConcrete()) { return true; } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index d863d9c6aba7c..9a7baff5bd06a 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -589,7 +589,8 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { if (!typeCheckExpression(target)) return failed(); - // check to see if the sequence expr is throwing (and async), if so require the stmt to have a try loc + // check to see if the sequence expr is throwing (and async), if so require + // the stmt to have a try loc if (stmt->getAwaitLoc().isValid()) { auto Ty = sequence->getType(); if (Ty.isNull()) { return failed(); } @@ -598,7 +599,8 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { auto module = context->getParentModule(); auto conformanceRef = module->lookupConformance(Ty, sequenceProto); - if (conformanceRef.classifyAsThrows(module) && stmt->getTryLoc().isInvalid()) { + if (conformanceRef.classifyAsThrows(module) && + stmt->getTryLoc().isInvalid()) { auto &diags = dc->getASTContext().Diags; diags.diagnose(stmt->getAwaitLoc(), diag::throwing_call_unhandled); From 21ce9b7461b8f12d40590e207d3155f74a88e70e Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Tue, 19 Jan 2021 17:18:12 -0800 Subject: [PATCH 32/40] Rework constraints checking for async sequence for-try-await-in checking --- lib/Sema/TypeCheckConstraints.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 9a7baff5bd06a..88b4b325097e3 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -593,10 +593,30 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { // the stmt to have a try loc if (stmt->getAwaitLoc().isValid()) { auto Ty = sequence->getType(); - if (Ty.isNull()) { return failed(); } - auto context = sequence->getType()->getNominalOrBoundGenericNominal(); - if (!context) { return failed(); } - auto module = context->getParentModule(); + if (Ty.isNull()) { + auto DRE = dyn_cast(sequence); + if (DRE) { + Ty = DRE->getDecl()->getInterfaceType(); + } + if (Ty.isNull()) { + return failed(); + } + } + auto context = Ty->getNominalOrBoundGenericNominal(); + if (!context) { + // if no nominal type can be determined then we must consider this to be + // a potential throwing source and concequently this must have a valid try + // location to account for that potential ambiguity. + if (stmt->getTryLoc().isInvalid()) { + auto &diags = dc->getASTContext().Diags; + diags.diagnose(stmt->getAwaitLoc(), diag::throwing_call_unhandled); + return failed(); + } else { + return false; + } + + } + auto module = dc->getParentModule(); auto conformanceRef = module->lookupConformance(Ty, sequenceProto); if (conformanceRef.classifyAsThrows(module) && From 48d4bb5a9bc603033eaf7242aa8bd6330c7c2e26 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Tue, 19 Jan 2021 17:18:43 -0800 Subject: [PATCH 33/40] Allow testing of for-await-in parsing and silgen testing and add unit tests for both --- test/Parse/foreach_async.swift | 51 ++++++ test/SILGen/foreach_async.swift | 228 ++++++++++++++++++++++++++ utils/gyb_syntax_support/StmtNodes.py | 7 +- 3 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 test/Parse/foreach_async.swift create mode 100644 test/SILGen/foreach_async.swift diff --git a/test/Parse/foreach_async.swift b/test/Parse/foreach_async.swift new file mode 100644 index 0000000000000..52df6387400a1 --- /dev/null +++ b/test/Parse/foreach_async.swift @@ -0,0 +1,51 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency + +struct AsyncRange: AsyncSequence, AsyncIteratorProtocol where Bound.Stride : SignedInteger { + var range: Range.Iterator + typealias Element = Bound + mutating func next() async -> Element? { return range.next() } + func cancel() { } + + func makeAsyncIterator() -> Self { return self } +} + +struct AsyncIntRange : AsyncSequence, AsyncIteratorProtocol { + typealias Element = (Int, Int) + func next() async -> (Int, Int)? {} + + func cancel() { } + + typealias AsyncIterator = AsyncIntRange + func makeAsyncIterator() -> AsyncIntRange { return self } +} + +func for_each(r: AsyncRange, iir: AsyncIntRange) async { // expected-note {{'r' declared here}} + var sum = 0 + + // Simple foreach loop, using the variable in the body + for await i in r { + sum = sum + i + } + // Check scoping of variable introduced with foreach loop + i = 0 // expected-error{{cannot find 'i' in scope; did you mean 'r'?}} + + // For-each loops with two variables and varying degrees of typedness + for await (i, j) in iir { + sum = sum + i + j + } + for await (i, j) in iir { + sum = sum + i + j + } + for await (i, j) : (Int, Int) in iir { + sum = sum + i + j + } + + // Parse errors + // FIXME: Bad diagnostics; should be just 'expected 'in' after for-each patter'. + for await i r { // expected-error {{found an unexpected second identifier in constant declaration}} + } // expected-note @-1 {{join the identifiers together}} + // expected-note @-2 {{join the identifiers together with camel-case}} + // expected-error @-3 {{expected 'in' after for-each pattern}} + // expected-error @-4 {{expected Sequence expression for for-each loop}} + for await i in r sum = sum + i; // expected-error{{expected '{' to start the body of for-each loop}} +} diff --git a/test/SILGen/foreach_async.swift b/test/SILGen/foreach_async.swift new file mode 100644 index 0000000000000..3d21fb057e633 --- /dev/null +++ b/test/SILGen/foreach_async.swift @@ -0,0 +1,228 @@ +// RUN: %target-swift-emit-silgen %s -module-name foreach_async -swift-version 5 -enable-experimental-concurrency | %FileCheck %s +// REQUIRES: concurrency + +////////////////// +// Declarations // +////////////////// + +class C {} + +@_silgen_name("loopBodyEnd") +func loopBodyEnd() -> () + +@_silgen_name("condition") +func condition() -> Bool + +@_silgen_name("loopContinueEnd") +func loopContinueEnd() -> () + +@_silgen_name("loopBreakEnd") +func loopBreakEnd() -> () + +@_silgen_name("funcEnd") +func funcEnd() -> () + +struct TrivialStruct { + var value: Int32 +} + +struct NonTrivialStruct { + var value: C +} + +struct GenericStruct { + var value: T + var value2: C +} + +protocol P {} +protocol ClassP : AnyObject {} + +protocol GenericCollection : Collection { + +} + +struct AsyncLazySequence: AsyncSequence { + typealias Element = S.Element + typealias AsyncIterator = Iterator + + struct Iterator: AsyncIteratorProtocol { + typealias Element = S.Element + + var iterator: S.Iterator? + + mutating func next() async -> S.Element? { + return iterator?.next() + } + + mutating func cancel() { + iterator = nil + } + } + + var sequence: S + + func makeAsyncIterator() -> Iterator { + return Iterator(iterator: sequence.makeIterator()) + } +} + +/////////// +// Tests // +/////////// + +//===----------------------------------------------------------------------===// +// Trivial Struct +//===----------------------------------------------------------------------===// + +// CHECK-LABEL: sil hidden [ossa] @$s13foreach_async13trivialStructyyAA17AsyncLazySequenceVySaySiGGYF : $@convention(thin) @async (@guaranteed AsyncLazySequence>) -> () { +// CHECK: bb0([[SOURCE:%.*]] : @guaranteed $AsyncLazySequence>): +// CHECK: [[ITERATOR_BOX:%.*]] = alloc_box ${ var AsyncLazySequence>.Iterator }, var, name "$x$generator" +// CHECK: [[PROJECT_ITERATOR_BOX:%.*]] = project_box [[ITERATOR_BOX]] +// CHECK: br [[LOOP_DEST:bb[0-9]+]] + +// CHECK: [[LOOP_DEST]]: +// CHECK: [[NEXT_RESULT:%.*]] = alloc_stack $Optional +// CHECK: [[MUTATION:%.*]] = begin_access +// CHECK: [[WITNESS_METHOD:%.*]] = witness_method $AsyncLazySequence>.Iterator, #AsyncIteratorProtocol.next : (inout Self) -> () async throws -> Self.Element? : $@convention(witness_method: AsyncIteratorProtocol) @async <τ_0_0 where τ_0_0 : AsyncIteratorProtocol> (@inout τ_0_0) -> (@out Optional<τ_0_0.Element>, @error Error) +// CHECK: try_apply [[WITNESS_METHOD]].Iterator>([[NEXT_RESULT]], [[MUTATION]]) : $@convention(witness_method: AsyncIteratorProtocol) @async <τ_0_0 where τ_0_0 : AsyncIteratorProtocol> (@inout τ_0_0) -> (@out Optional<τ_0_0.Element>, @error Error), normal [[NORMAL_BB:bb[0-9]+]], error [[ERROR_BB:bb[0-9]+]] + +// CHECK: [[NORMAL_BB]]([[VAR:%.*]] : $()): +// CHECK: end_access [[MUTATION]] +// CHECK: switch_enum [[IND_VAR:%.*]] : $Optional, case #Optional.some!enumelt: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[NONE_BB:bb[0-9]+]] + +// CHECK: [[SOME_BB]]([[VAR:%.*]] : $Int): +// CHECK: loopBodyEnd +// CHECK: br [[LOOP_DEST]] + +// CHECK: [[NONE_BB]]: +// CHECK: funcEnd +// CHECK return + +// CHECK: [[ERROR_BB]]([[VAR:%.*]] : @owned $Error): +// CHECK: unreachable +// CHECK: } // end sil function '$s13foreach_async13trivialStructyyAA17AsyncLazySequenceVySaySiGGYF' +func trivialStruct(_ xx: AsyncLazySequence<[Int]>) async { + for await x in xx { + loopBodyEnd() + } + funcEnd() +} + +// CHECK-LABEL: sil hidden [ossa] @$s13foreach_async21trivialStructContinueyyAA17AsyncLazySequenceVySaySiGGYF : $@convention(thin) @async (@guaranteed AsyncLazySequence>) -> () { +// CHECK: bb0([[SOURCE:%.*]] : @guaranteed $AsyncLazySequence>): +// CHECK: [[ITERATOR_BOX:%.*]] = alloc_box ${ var AsyncLazySequence>.Iterator }, var, name "$x$generator" +// CHECK: [[PROJECT_ITERATOR_BOX:%.*]] = project_box [[ITERATOR_BOX]] +// CHECK: br [[LOOP_DEST:bb[0-9]+]] + +// CHECK: [[LOOP_DEST]]: +// CHECK: [[NEXT_RESULT:%.*]] = alloc_stack $Optional +// CHECK: [[MUTATION:%.*]] = begin_access +// CHECK: [[WITNESS_METHOD:%.*]] = witness_method $AsyncLazySequence>.Iterator, #AsyncIteratorProtocol.next : (inout Self) -> () async throws -> Self.Element? : $@convention(witness_method: AsyncIteratorProtocol) @async <τ_0_0 where τ_0_0 : AsyncIteratorProtocol> (@inout τ_0_0) -> (@out Optional<τ_0_0.Element>, @error Error) +// CHECK: try_apply [[WITNESS_METHOD]].Iterator>([[NEXT_RESULT]], [[MUTATION]]) : $@convention(witness_method: AsyncIteratorProtocol) @async <τ_0_0 where τ_0_0 : AsyncIteratorProtocol> (@inout τ_0_0) -> (@out Optional<τ_0_0.Element>, @error Error), normal [[NORMAL_BB:bb[0-9]+]], error [[ERROR_BB:bb[0-9]+]] + +// CHECK: [[NORMAL_BB]]([[VAR:%.*]] : $()): +// CHECK: end_access [[MUTATION]] +// CHECK: switch_enum [[IND_VAR:%.*]] : $Optional, case #Optional.some!enumelt: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[NONE_BB:bb[0-9]+]] + +// CHECK: [[SOME_BB]]([[VAR:%.*]] : $Int): +// CHECK: condition +// CHECK: cond_br [[VAR:%.*]], [[COND_TRUE:bb[0-9]+]], [[COND_FALSE:bb[0-9]+]] + +// CHECK: [[COND_TRUE]]: +// CHECK: loopContinueEnd +// CHECK: br [[LOOP_DEST]] + +// CHECK: [[COND_FALSE]]: +// CHECK: loopBodyEnd +// CHECK: br [[LOOP_DEST]] + +// CHECK: [[NONE_BB]]: +// CHECK: funcEnd +// CHECK return + +// CHECK: [[ERROR_BB]]([[VAR:%.*]] : @owned $Error): +// CHECK: unreachable +// CHECK: } // end sil function '$s13foreach_async21trivialStructContinueyyAA17AsyncLazySequenceVySaySiGGYF' + +func trivialStructContinue(_ xx: AsyncLazySequence<[Int]>) async { + for await x in xx { + if (condition()) { + loopContinueEnd() + continue + } + loopBodyEnd() + } + + funcEnd() +} + +// TODO: Write this test +func trivialStructBreak(_ xx: AsyncLazySequence<[Int]>) async { + for await x in xx { + if (condition()) { + loopBreakEnd() + break + } + loopBodyEnd() + } + + funcEnd() +} + +// CHECK-LABEL: sil hidden [ossa] @$s13foreach_async26trivialStructContinueBreakyyAA17AsyncLazySequenceVySaySiGGYF : $@convention(thin) @async (@guaranteed AsyncLazySequence>) -> () +// CHECK: bb0([[SOURCE:%.*]] : @guaranteed $AsyncLazySequence>): +// CHECK: [[ITERATOR_BOX:%.*]] = alloc_box ${ var AsyncLazySequence>.Iterator }, var, name "$x$generator" +// CHECK: [[PROJECT_ITERATOR_BOX:%.*]] = project_box [[ITERATOR_BOX]] +// CHECK: br [[LOOP_DEST:bb[0-9]+]] + +// CHECK: [[LOOP_DEST]]: +// CHECK: [[NEXT_RESULT:%.*]] = alloc_stack $Optional +// CHECK: [[MUTATION:%.*]] = begin_access +// CHECK: [[WITNESS_METHOD:%.*]] = witness_method $AsyncLazySequence>.Iterator, #AsyncIteratorProtocol.next : (inout Self) -> () async throws -> Self.Element? : $@convention(witness_method: AsyncIteratorProtocol) @async <τ_0_0 where τ_0_0 : AsyncIteratorProtocol> (@inout τ_0_0) -> (@out Optional<τ_0_0.Element>, @error Error) +// CHECK: try_apply [[WITNESS_METHOD]].Iterator>([[NEXT_RESULT]], [[MUTATION]]) : $@convention(witness_method: AsyncIteratorProtocol) @async <τ_0_0 where τ_0_0 : AsyncIteratorProtocol> (@inout τ_0_0) -> (@out Optional<τ_0_0.Element>, @error Error), normal [[NORMAL_BB:bb[0-9]+]], error [[ERROR_BB:bb[0-9]+]] + +// CHECK: [[NORMAL_BB]]([[VAR:%.*]] : $()): +// CHECK: end_access [[MUTATION]] +// CHECK: switch_enum [[IND_VAR:%.*]] : $Optional, case #Optional.some!enumelt: [[SOME_BB:bb[0-9]+]], case #Optional.none!enumelt: [[NONE_BB:bb[0-9]+]] + +// CHECK: [[SOME_BB]]([[VAR:%.*]] : $Int): +// CHECK: condition +// CHECK: cond_br [[VAR:%.*]], [[COND_TRUE:bb[0-9]+]], [[COND_FALSE:bb[0-9]+]] + +// CHECK: [[COND_TRUE]]: +// CHECK: loopBreakEnd +// CHECK: cancel +// CHECK: br [[LOOP_EXIT:bb[0-9]+]] + +// CHECK: [[COND_FALSE]]: +// CHECK: condition +// CHECK: cond_br [[VAR:%.*]], [[COND_TRUE2:bb[0-9]+]], [[COND_FALSE2:bb[0-9]+]] + +// CHECK: [[COND_TRUE2]]: +// CHECK: loopContinueEnd +// CHECK: br [[LOOP_DEST]] + +// CHECK: [[COND_FALSE2]]: +// CHECK: br [[LOOP_DEST]] + +// CHECK: [[LOOP_EXIT]]: +// CHECK: return + +// CHECK: } // end sil function '$s13foreach_async26trivialStructContinueBreakyyAA17AsyncLazySequenceVySaySiGGYF' +func trivialStructContinueBreak(_ xx: AsyncLazySequence<[Int]>) async { + for await x in xx { + if (condition()) { + loopBreakEnd() + break + } + + if (condition()) { + loopContinueEnd() + continue + } + loopBodyEnd() + } + + funcEnd() +} diff --git a/utils/gyb_syntax_support/StmtNodes.py b/utils/gyb_syntax_support/StmtNodes.py index f662f3975a5bf..72b9fd8a268c1 100644 --- a/utils/gyb_syntax_support/StmtNodes.py +++ b/utils/gyb_syntax_support/StmtNodes.py @@ -74,7 +74,7 @@ Child('GuardResult', kind='Expr'), ]), - # for-in-stmt -> label? ':'? 'for' 'case'? pattern 'in' expr 'where'? + # for-in-stmt -> label? ':'? 'for' 'try'? 'await'? 'case'? pattern 'in' expr 'where'? # expr code-block ';'? Node('ForInStmt', kind='Stmt', traits=['WithCodeBlock', 'Labeled'], @@ -84,6 +84,11 @@ Child('LabelColon', kind='ColonToken', is_optional=True), Child('ForKeyword', kind='ForToken'), + Child('TryKeyword', kind='TryToken', + is_optional=True), + Child('AwaitKeyword', kind='IdentifierToken', + classification='Keyword', + text_choices=['await'], is_optional=True), Child('CaseKeyword', kind='CaseToken', is_optional=True), Child('Pattern', kind='Pattern'), From a0f62360bca76d44ae46521501880a56d32bb571 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 20 Jan 2021 10:59:00 -0800 Subject: [PATCH 34/40] Remove async sequence operators for now --- stdlib/public/Concurrency/AllSatisfy.swift | 25 ---- .../Concurrency/AsyncCompactMapSequence.swift | 112 ---------------- .../Concurrency/AsyncConcatSequence.swift | 63 --------- .../Concurrency/AsyncDropWhileSequence.swift | 122 ------------------ .../Concurrency/AsyncFilterSequence.swift | 111 ---------------- .../Concurrency/AsyncFlatMapSequence.swift | 116 ----------------- .../Concurrency/AsyncIteratorProtocol.swift | 29 ----- .../public/Concurrency/AsyncMapSequence.swift | 103 --------------- .../AsyncPrefixWhileSequence.swift | 107 --------------- stdlib/public/Concurrency/CMakeLists.txt | 12 -- stdlib/public/Concurrency/Contains.swift | 31 ----- stdlib/public/Concurrency/Count.swift | 24 ---- stdlib/public/Concurrency/First.swift | 30 ----- stdlib/public/Concurrency/MinMax.swift | 43 ------ stdlib/public/Concurrency/Reduce.swift | 34 ----- 15 files changed, 962 deletions(-) delete mode 100644 stdlib/public/Concurrency/AllSatisfy.swift delete mode 100644 stdlib/public/Concurrency/AsyncCompactMapSequence.swift delete mode 100644 stdlib/public/Concurrency/AsyncConcatSequence.swift delete mode 100644 stdlib/public/Concurrency/AsyncDropWhileSequence.swift delete mode 100644 stdlib/public/Concurrency/AsyncFilterSequence.swift delete mode 100644 stdlib/public/Concurrency/AsyncFlatMapSequence.swift delete mode 100644 stdlib/public/Concurrency/AsyncMapSequence.swift delete mode 100644 stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift delete mode 100644 stdlib/public/Concurrency/Contains.swift delete mode 100644 stdlib/public/Concurrency/Count.swift delete mode 100644 stdlib/public/Concurrency/First.swift delete mode 100644 stdlib/public/Concurrency/MinMax.swift delete mode 100644 stdlib/public/Concurrency/Reduce.swift diff --git a/stdlib/public/Concurrency/AllSatisfy.swift b/stdlib/public/Concurrency/AllSatisfy.swift deleted file mode 100644 index 03a06188d5848..0000000000000 --- a/stdlib/public/Concurrency/AllSatisfy.swift +++ /dev/null @@ -1,25 +0,0 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2020 Apple Inc. and the Swift project authors -//// Licensed under Apache License v2.0 with Runtime Library Exception -//// -//// See https://swift.org/LICENSE.txt for license information -//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -//// -////===----------------------------------------------------------------------===// - -import Swift - -extension AsyncSequence { - public func allSatisfy(_ predicate: (Element) async throws -> Bool) async rethrows -> Bool { - var it = makeAsyncIterator() - while let element = try await it.next() { - if !(try await predicate(element)) { - return false - } - } - return true - } -} diff --git a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift b/stdlib/public/Concurrency/AsyncCompactMapSequence.swift deleted file mode 100644 index d728f6114a16f..0000000000000 --- a/stdlib/public/Concurrency/AsyncCompactMapSequence.swift +++ /dev/null @@ -1,112 +0,0 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2020 Apple Inc. and the Swift project authors -//// Licensed under Apache License v2.0 with Runtime Library Exception -//// -//// See https://swift.org/LICENSE.txt for license information -//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -//// -////===----------------------------------------------------------------------===// - -import Swift - -extension AsyncSequence { - public func compactMap(_ transform: @escaping (Element) async -> ElementOfResult?) -> AsyncCompactMapSequence { - return AsyncCompactMapSequence(self, transform: transform) - } - - public func compactMap(_ transform: @escaping (Element) async throws -> ElementOfResult?) -> AsyncTryCompactMapSequence { - return AsyncTryCompactMapSequence(self, transform: transform) - } -} - -public struct AsyncCompactMapSequence: AsyncSequence where Upstream: AsyncSequence, Upstream.Element == ElementOfResult? { - public typealias Element = ElementOfResult - public typealias AsyncIterator = Iterator - - public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: _OptionalAsyncIterator - let transform: (Upstream.Element) async -> ElementOfResult? - - init(_ upstreamIterator: Upstream.AsyncIterator, transform: @escaping (Upstream.Element) async -> ElementOfResult?) { - self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) - self.transform = transform - } - - public mutating func next() async rethrows -> ElementOfResult? { - while true { - guard let item = try await upstreamIterator.next() else { - return nil - } - if let transformed = await transform(item) { - return transformed - } - } - } - - public mutating func cancel() { - upstreamIterator.cancel() - } - } - - public let upstream: Upstream - public let transform: (Upstream.Element) async -> ElementOfResult? - - public init(_ upstream: Upstream, transform: @escaping (Upstream.Element) async -> ElementOfResult?) { - self.upstream = upstream - self.transform = transform - } - - public func makeAsyncIterator() -> Iterator { - return Iterator(upstream.makeAsyncIterator(), transform: transform) - } -} - -public struct AsyncTryCompactMapSequence: AsyncSequence where Upstream: AsyncSequence, Upstream.Element == ElementOfResult? { - public typealias Element = ElementOfResult - public typealias AsyncIterator = Iterator - - public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: _OptionalAsyncIterator - let transform: (Upstream.Element) async throws -> ElementOfResult? - - init(_ upstreamIterator: Upstream.AsyncIterator, transform: @escaping (Upstream.Element) async throws -> ElementOfResult?) { - self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) - self.transform = transform - } - - public mutating func next() async throws -> ElementOfResult? { - while true { - guard let item = try await upstreamIterator.next() else { - return nil - } - do { - if let transformed = try await transform(item) { - return transformed - } - } catch { - upstreamIterator.cancel() - throw error - } - } - } - - public mutating func cancel() { - upstreamIterator.cancel() - } - } - - public let upstream: Upstream - public let transform: (Upstream.Element) async throws -> ElementOfResult? - - public init(_ upstream: Upstream, transform: @escaping (Upstream.Element) async throws -> ElementOfResult?) { - self.upstream = upstream - self.transform = transform - } - - public func makeAsyncIterator() -> Iterator { - return Iterator(upstream.makeAsyncIterator(), transform: transform) - } -} diff --git a/stdlib/public/Concurrency/AsyncConcatSequence.swift b/stdlib/public/Concurrency/AsyncConcatSequence.swift deleted file mode 100644 index f838364564d20..0000000000000 --- a/stdlib/public/Concurrency/AsyncConcatSequence.swift +++ /dev/null @@ -1,63 +0,0 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2020 Apple Inc. and the Swift project authors -//// Licensed under Apache License v2.0 with Runtime Library Exception -//// -//// See https://swift.org/LICENSE.txt for license information -//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -//// -////===----------------------------------------------------------------------===// - -import Swift - -extension AsyncSequence { - public func append(_ suffix: Suffix) -> AsyncConcatSequence where Suffix: AsyncSequence, Suffix.Element == Element { - return AsyncConcatSequence(prefix: self, suffix: suffix) - } - - public func prepend(_ prefix: Prefix) -> AsyncConcatSequence where Prefix: AsyncSequence, Prefix.Element == Element { - return AsyncConcatSequence(prefix: prefix, suffix: self) - } -} - -public struct AsyncConcatSequence: AsyncSequence where Prefix: AsyncSequence, Suffix: AsyncSequence, Prefix.Element == Suffix.Element { - public typealias Element = Prefix.Element - public typealias AsyncIterator = Iterator - - public struct Iterator: AsyncIteratorProtocol { - var prefixIterator: _OptionalAsyncIterator - var suffixIterator: _OptionalAsyncIterator - - init(_ prefixIterator: Prefix.AsyncIterator, _ suffixIterator: Suffix.AsyncIterator) { - self.prefixIterator = _OptionalAsyncIterator(prefixIterator) - self.suffixIterator = _OptionalAsyncIterator(suffixIterator) - } - - public mutating func next() async rethrows -> Prefix.Element? { - if let item = try await prefixIterator.next() { - return item - } - prefixIterator = .none - return try await suffixIterator.next() - } - - public mutating func cancel() { - prefixIterator.cancel() - suffixIterator.cancel() - } - } - - public let prefix: Prefix - public let suffix: Suffix - - public init(prefix: Prefix, suffix: Suffix) { - self.prefix = prefix - self.suffix = suffix - } - - public func makeAsyncIterator() -> Iterator { - return Iterator(prefix.makeAsyncIterator(), suffix.makeAsyncIterator()) - } -} diff --git a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift b/stdlib/public/Concurrency/AsyncDropWhileSequence.swift deleted file mode 100644 index b5e97f83a14b9..0000000000000 --- a/stdlib/public/Concurrency/AsyncDropWhileSequence.swift +++ /dev/null @@ -1,122 +0,0 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2020 Apple Inc. and the Swift project authors -//// Licensed under Apache License v2.0 with Runtime Library Exception -//// -//// See https://swift.org/LICENSE.txt for license information -//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -//// -////===----------------------------------------------------------------------===// - -import Swift - -extension AsyncSequence { - public func drop(while predicate: @escaping (Element) async -> Bool) -> AsyncDropWhileSequence { - return AsyncDropWhileSequence(self, predicate: predicate) - } - - public func drop(while predicate: @escaping (Element) async throws -> Bool) -> AsyncTryDropWhileSequence { - return AsyncTryDropWhileSequence(self, predicate: predicate) - } -} - -public struct AsyncDropWhileSequence: AsyncSequence where Upstream: AsyncSequence { - public typealias Element = Upstream.Element - public typealias AsyncIterator = Iterator - - public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: _OptionalAsyncIterator - var predicate: ((Element) async -> Bool)? - - init(_ upstreamIterator: Upstream.AsyncIterator, predicate: @escaping (Element) async -> Bool) { - self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) - self.predicate = predicate - } - - public mutating func next() async rethrows -> Upstream.Element? { - while true { - guard let item = try await upstreamIterator.next() else { - return nil - } - if let predicate = self.predicate { - if !(await predicate(item)) { - self.predicate = nil - return item - } - } else { - return item - } - } - } - - public mutating func cancel() { - upstreamIterator.cancel() - } - } - - public let upstream: Upstream - public let predicate: (Element) async -> Bool - - public init(_ upstream: Upstream, predicate: @escaping (Element) async -> Bool) { - self.upstream = upstream - self.predicate = predicate - } - - public func makeAsyncIterator() -> Iterator { - return Iterator(upstream.makeAsyncIterator(), predicate: predicate) - } -} - -public struct AsyncTryDropWhileSequence: AsyncSequence where Upstream: AsyncSequence { - public typealias Element = Upstream.Element - public typealias AsyncIterator = Iterator - - public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: _OptionalAsyncIterator - var predicate: ((Element) async throws -> Bool)? - - init(_ upstreamIterator: Upstream.AsyncIterator, predicate: @escaping (Element) async throws -> Bool) { - self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) - self.predicate = predicate - } - - public mutating func next() async throws -> Upstream.Element? { - while true { - guard let item = try await upstreamIterator.next() else { - return nil - } - if let predicate = self.predicate { - do { - if !(try await predicate(item)) { - self.predicate = nil - return item - } - } catch { - upstreamIterator.cancel() - throw error - } - } else { - return item - } - } - } - - public mutating func cancel() { - upstreamIterator.cancel() - } - } - - public let upstream: Upstream - public let predicate: (Element) async throws -> Bool - - public init(_ upstream: Upstream, predicate: @escaping (Element) async throws -> Bool) { - self.upstream = upstream - self.predicate = predicate - } - - public func makeAsyncIterator() -> Iterator { - return Iterator(upstream.makeAsyncIterator(), predicate: predicate) - } -} diff --git a/stdlib/public/Concurrency/AsyncFilterSequence.swift b/stdlib/public/Concurrency/AsyncFilterSequence.swift deleted file mode 100644 index cf646cc9bfe40..0000000000000 --- a/stdlib/public/Concurrency/AsyncFilterSequence.swift +++ /dev/null @@ -1,111 +0,0 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2020 Apple Inc. and the Swift project authors -//// Licensed under Apache License v2.0 with Runtime Library Exception -//// -//// See https://swift.org/LICENSE.txt for license information -//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -//// -////===----------------------------------------------------------------------===// - -import Swift - -extension AsyncSequence { - public func filter(_ predicate: @escaping (Element) async -> Bool) -> AsyncFilterSequence { - return AsyncFilterSequence(self, predicate: predicate) - } - - public func filter(_ predicate: @escaping (Element) async throws -> Bool) -> AsyncTryFilterSequence { - return AsyncTryFilterSequence(self, predicate: predicate) - } -} - -public struct AsyncFilterSequence: AsyncSequence where Upstream: AsyncSequence { - public typealias AsyncIterator = Iterator - public typealias Element = Upstream.Element - - public let upstream: Upstream - public let predicate: (Element) async -> Bool - - public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: _OptionalAsyncIterator - let predicate: (Element) async -> Bool - - init(_ upstreamIterator: Upstream.AsyncIterator, - predicate: @escaping (Element) async -> Bool - ) { - self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) - self.predicate = predicate - } - - public mutating func next() async rethrows -> Upstream.Element? { - guard let item = try await upstreamIterator.next() else { - return nil - } - guard await predicate(item) else { - upstreamIterator.cancel() - return nil - } - return item - } - - public mutating func cancel() { - upstreamIterator.cancel() - } - } - - public init(_ upstream: Upstream, predicate: @escaping (Upstream.Element) async -> Bool) { - self.upstream = upstream - self.predicate = predicate - } - - public func makeAsyncIterator() -> Iterator { - return Iterator(upstream.makeAsyncIterator(), predicate: predicate) - } -} - -public struct AsyncTryFilterSequence: AsyncSequence where Upstream: AsyncSequence { - public typealias AsyncIterator = Iterator - public typealias Element = Upstream.Element - - public let upstream: Upstream - public let predicate: (Element) async throws -> Bool - - public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: _OptionalAsyncIterator - let predicate: (Element) async throws -> Bool - - init(_ upstreamIterator: Upstream.AsyncIterator, - predicate: @escaping (Element) async throws -> Bool - ) { - self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) - self.predicate = predicate - } - - public mutating func next() async throws -> Upstream.Element? { - guard let item = try await upstreamIterator.next() else { - return nil - } - guard try await predicate(item) else { - upstreamIterator.cancel() - return nil - } - return item - } - - public mutating func cancel() { - upstreamIterator.cancel() - } - } - - public init(_ upstream: Upstream, predicate: @escaping (Upstream.Element) async throws -> Bool) { - self.upstream = upstream - self.predicate = predicate - } - - public func makeAsyncIterator() -> Iterator { - return Iterator(upstream.makeAsyncIterator(), predicate: predicate) - } -} diff --git a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift b/stdlib/public/Concurrency/AsyncFlatMapSequence.swift deleted file mode 100644 index 9975bcbc4c859..0000000000000 --- a/stdlib/public/Concurrency/AsyncFlatMapSequence.swift +++ /dev/null @@ -1,116 +0,0 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2020 Apple Inc. and the Swift project authors -//// Licensed under Apache License v2.0 with Runtime Library Exception -//// -//// See https://swift.org/LICENSE.txt for license information -//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -//// -////===----------------------------------------------------------------------===// - -import Swift - -extension AsyncSequence { - public func flatMap(_ transform: @escaping (Element) async -> SegmentOfResult) -> AsyncFlatMapSequence where SegmentOfResult: AsyncSequence { - return AsyncFlatMapSequence(self, transform: transform) - } - - public func flatMap(_ transform: @escaping (Element) async throws -> SegmentOfResult) -> AsyncTryFlatMapSequence where SegmentOfResult: AsyncSequence { - return AsyncTryFlatMapSequence(self, transform: transform) - } -} - -public struct AsyncFlatMapSequence: AsyncSequence where Upstream: AsyncSequence { - public typealias Element = SegmentOfResult.Element - public typealias AsyncIterator = Iterator - - public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: _OptionalAsyncIterator - let transform: (Upstream.Element) async -> SegmentOfResult - var currentIterator: _OptionalAsyncIterator = .none - - init(_ upstreamIterator: Upstream.AsyncIterator, transform: @escaping (Upstream.Element) async -> SegmentOfResult) { - self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) - self.transform = transform - } - - public mutating func next() async rethrows -> SegmentOfResult.Element? { - if let item = try await currentIterator.next() { - return item - } else { - guard let item = try await upstreamIterator.next() else { - return nil - } - let segment = await transform(item) - currentIterator = _OptionalAsyncIterator(segment.makeAsyncIterator()) - return try await currentIterator.next() - } - } - - public mutating func cancel() { - upstreamIterator.cancel() - currentIterator.cancel() - } - } - - public let upstream: Upstream - public let transform: (Upstream.Element) async -> SegmentOfResult - - public init(_ upstream: Upstream, transform: @escaping (Upstream.Element) async -> SegmentOfResult) { - self.upstream = upstream - self.transform = transform - } - - public func makeAsyncIterator() -> Iterator { - return Iterator(upstream.makeAsyncIterator(), transform: transform) - } -} - -public struct AsyncTryFlatMapSequence: AsyncSequence where Upstream: AsyncSequence { - public typealias Element = SegmentOfResult.Element - public typealias AsyncIterator = Iterator - - public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: _OptionalAsyncIterator - let transform: (Upstream.Element) async throws -> SegmentOfResult - var currentIterator: _OptionalAsyncIterator = .none - - init(_ upstreamIterator: Upstream.AsyncIterator, transform: @escaping (Upstream.Element) async throws -> SegmentOfResult) { - self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) - self.transform = transform - } - - public mutating func next() async throws -> SegmentOfResult.Element? { - if let item = try await currentIterator.next() { - return item - } else { - guard let item = try await upstreamIterator.next() else { - return nil - } - let segment = try await transform(item) - currentIterator = _OptionalAsyncIterator(segment.makeAsyncIterator()) - return try await currentIterator.next() - } - } - - public mutating func cancel() { - upstreamIterator.cancel() - currentIterator.cancel() - } - } - - public let upstream: Upstream - public let transform: (Upstream.Element) async throws -> SegmentOfResult - - public init(_ upstream: Upstream, transform: @escaping (Upstream.Element) async throws -> SegmentOfResult) { - self.upstream = upstream - self.transform = transform - } - - public func makeAsyncIterator() -> Iterator { - return Iterator(upstream.makeAsyncIterator(), transform: transform) - } -} - diff --git a/stdlib/public/Concurrency/AsyncIteratorProtocol.swift b/stdlib/public/Concurrency/AsyncIteratorProtocol.swift index 5bf13f1c4b8ef..3013377d0a387 100644 --- a/stdlib/public/Concurrency/AsyncIteratorProtocol.swift +++ b/stdlib/public/Concurrency/AsyncIteratorProtocol.swift @@ -18,32 +18,3 @@ public protocol AsyncIteratorProtocol { mutating func next() async throws -> Element? mutating func cancel() } - -internal enum _OptionalAsyncIterator: AsyncIteratorProtocol { - case none - case some(AsyncIterator) - - init(_ iterator: AsyncIterator) { - self = .some(iterator) - } - - mutating func next() async rethrows -> AsyncIterator.Element? { - switch self { - case .some(var iterator): - defer { self = .some(iterator) } - return try await iterator.next() - default: - return nil - } - } - - mutating func cancel() { - switch self { - case .some(var iterator): - iterator.cancel() - self = .none - default: - break - } - } -} \ No newline at end of file diff --git a/stdlib/public/Concurrency/AsyncMapSequence.swift b/stdlib/public/Concurrency/AsyncMapSequence.swift deleted file mode 100644 index ca8fa0faa6e3e..0000000000000 --- a/stdlib/public/Concurrency/AsyncMapSequence.swift +++ /dev/null @@ -1,103 +0,0 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2020 Apple Inc. and the Swift project authors -//// Licensed under Apache License v2.0 with Runtime Library Exception -//// -//// See https://swift.org/LICENSE.txt for license information -//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -//// -////===----------------------------------------------------------------------===// - -import Swift - -extension AsyncSequence { - public func map(_ transform: @escaping (Element) async -> Transformed) -> AsyncMapSequence { - return AsyncMapSequence(self, transform: transform) - } - - public func map(_ transform: @escaping (Element) async throws -> Transformed) -> AsyncTryMapSequence { - return AsyncTryMapSequence(self, transform: transform) - } -} - -public struct AsyncMapSequence: AsyncSequence where Upstream: AsyncSequence { - public typealias AsyncIterator = Iterator - public typealias Element = Transformed - - public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: _OptionalAsyncIterator - let transform: (Upstream.Element) async -> Transformed - - init(_ upstreamIterator: Upstream.AsyncIterator, - transform: @escaping (Upstream.Element) async -> Transformed - ) { - self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) - self.transform = transform - } - - public mutating func next() async rethrows -> Transformed? { - guard let item = try await upstreamIterator.next() else { - return nil - } - return await transform(item) - } - - public mutating func cancel() { - upstreamIterator.cancel() - } - } - - public let upstream: Upstream - public let transform: (Upstream.Element) async -> Transformed - - public init(_ upstream: Upstream, transform: @escaping (Upstream.Element) async -> Transformed) { - self.upstream = upstream - self.transform = transform - } - - public func makeAsyncIterator() -> Iterator { - return Iterator(upstream.makeAsyncIterator(), transform: transform) - } -} - -public struct AsyncTryMapSequence: AsyncSequence where Upstream: AsyncSequence { - public typealias AsyncIterator = Iterator - public typealias Element = Transformed - - public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: _OptionalAsyncIterator - let transform: (Upstream.Element) async throws -> Transformed - - init(_ upstreamIterator: Upstream.AsyncIterator, - transform: @escaping (Upstream.Element) async throws -> Transformed - ) { - self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) - self.transform = transform - } - - public mutating func next() async throws -> Transformed? { - guard let item = try await upstreamIterator.next() else { - return nil - } - return try await transform(item) - } - - public mutating func cancel() { - upstreamIterator.cancel() - } - } - - public let upstream: Upstream - public let transform: (Upstream.Element) async throws -> Transformed - - public init(_ upstream: Upstream, transform: @escaping (Upstream.Element) async throws -> Transformed) { - self.upstream = upstream - self.transform = transform - } - - public func makeAsyncIterator() -> Iterator { - return Iterator(upstream.makeAsyncIterator(), transform: transform) - } -} diff --git a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift b/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift deleted file mode 100644 index e14ea1932abdd..0000000000000 --- a/stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift +++ /dev/null @@ -1,107 +0,0 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2020 Apple Inc. and the Swift project authors -//// Licensed under Apache License v2.0 with Runtime Library Exception -//// -//// See https://swift.org/LICENSE.txt for license information -//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -//// -////===----------------------------------------------------------------------===// - -import Swift - -extension AsyncSequence { - public func prefix(while predicate: @escaping (Element) async -> Bool) -> AsyncPrefixWhileSequence { - return AsyncPrefixWhileSequence(self, predicate: predicate) - } - - public func prefix(while predicate: @escaping (Element) async throws -> Bool) -> AsyncTryPrefixWhileSequence { - return AsyncTryPrefixWhileSequence(self, predicate: predicate) - } -} - -public struct AsyncPrefixWhileSequence: AsyncSequence where Upstream: AsyncSequence { - public typealias Element = Upstream.Element - public typealias AsyncIterator = Iterator - - public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: _OptionalAsyncIterator - var predicate: (Element) async -> Bool - - init(_ upstreamIterator: Upstream.AsyncIterator, predicate: @escaping (Element) async -> Bool) { - self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) - self.predicate = predicate - } - - public mutating func next() async rethrows -> Element? { - guard let item = try await upstreamIterator.next() else { - return nil - } - guard await predicate(item) else { - upstreamIterator.cancel() - return nil - } - return item - } - - public mutating func cancel() { - upstreamIterator.cancel() - } - } - - public let upstream: Upstream - public let predicate: (Element) async -> Bool - - public init(_ upstream: Upstream, predicate: @escaping (Element) async -> Bool) { - self.upstream = upstream - self.predicate = predicate - } - - public func makeAsyncIterator() -> Iterator { - return Iterator(upstream.makeAsyncIterator(), predicate: predicate) - } -} - -public struct AsyncTryPrefixWhileSequence: AsyncSequence where Upstream: AsyncSequence { - public typealias Element = Upstream.Element - public typealias AsyncIterator = Iterator - - public struct Iterator: AsyncIteratorProtocol { - var upstreamIterator: _OptionalAsyncIterator - var predicate: (Element) async throws -> Bool - - init(_ upstreamIterator: Upstream.AsyncIterator, predicate: @escaping (Element) async throws -> Bool) { - self.upstreamIterator = _OptionalAsyncIterator(upstreamIterator) - self.predicate = predicate - } - - public mutating func next() async throws -> Element? { - guard let item = try await upstreamIterator.next() else { - return nil - } - guard try await predicate(item) else { - upstreamIterator.cancel() - return nil - } - return item - } - - public mutating func cancel() { - upstreamIterator.cancel() - } - } - - public let upstream: Upstream - public let predicate: (Element) async throws -> Bool - - public init(_ upstream: Upstream, predicate: @escaping (Element) async throws -> Bool) { - self.upstream = upstream - self.predicate = predicate - } - - public func makeAsyncIterator() -> Iterator { - return Iterator(upstream.makeAsyncIterator(), predicate: predicate) - } -} diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index 001a3bde6b995..e826277c60e4e 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -38,20 +38,8 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I Actor.swift CheckedContinuation.swift GlobalExecutor.cpp - AllSatisfy.swift - AsyncCompactMapSequence.swift - AsyncDropWhileSequence.swift - AsyncFilterSequence.swift - AsyncFlatMapSequence.swift AsyncIteratorProtocol.swift - AsyncMapSequence.swift - AsyncPrefixWhileSequence.swift AsyncSequence.swift - Contains.swift - Count.swift - First.swift - MinMax.swift - Reduce.swift PartialAsyncTask.swift Task.cpp Task.swift diff --git a/stdlib/public/Concurrency/Contains.swift b/stdlib/public/Concurrency/Contains.swift deleted file mode 100644 index e13478866ae23..0000000000000 --- a/stdlib/public/Concurrency/Contains.swift +++ /dev/null @@ -1,31 +0,0 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2020 Apple Inc. and the Swift project authors -//// Licensed under Apache License v2.0 with Runtime Library Exception -//// -//// See https://swift.org/LICENSE.txt for license information -//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -//// -////===----------------------------------------------------------------------===// - -import Swift - -extension AsyncSequence { - public func contains(where predicate: (Element) async throws -> Bool) async rethrows -> Bool { - var it = makeAsyncIterator() - while let e = try await it.next() { - if try await predicate(e) { - return true - } - } - return false - } -} - -extension AsyncSequence where Element: Equatable { - public func contains(_ element: Element) async rethrows -> Bool { - return await contains { $0 == element } - } -} diff --git a/stdlib/public/Concurrency/Count.swift b/stdlib/public/Concurrency/Count.swift deleted file mode 100644 index 60a0ad5395c0c..0000000000000 --- a/stdlib/public/Concurrency/Count.swift +++ /dev/null @@ -1,24 +0,0 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2020 Apple Inc. and the Swift project authors -//// Licensed under Apache License v2.0 with Runtime Library Exception -//// -//// See https://swift.org/LICENSE.txt for license information -//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -//// -////===----------------------------------------------------------------------===// - -import Swift - -extension AsyncSequence { - public func count() async rethrows -> Int { - var count = 0 - var it = makeAsyncIterator() - while try await it.next() != nil { - count += 1 - } - return count - } -} diff --git a/stdlib/public/Concurrency/First.swift b/stdlib/public/Concurrency/First.swift deleted file mode 100644 index 9a4471488a520..0000000000000 --- a/stdlib/public/Concurrency/First.swift +++ /dev/null @@ -1,30 +0,0 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2020 Apple Inc. and the Swift project authors -//// Licensed under Apache License v2.0 with Runtime Library Exception -//// -//// See https://swift.org/LICENSE.txt for license information -//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -//// -////===----------------------------------------------------------------------===// - -import Swift - -extension AsyncSequence { - public func first(where predicate: (Element) async throws -> Bool) async rethrows -> Element? { - var it = makeAsyncIterator() - while let element = try await it.next() { - if try await predicate(element) { - return element - } - } - return nil - } - - public func first() async rethrows -> Element? { - var it = makeAsyncIterator() - return try await it.next() - } -} diff --git a/stdlib/public/Concurrency/MinMax.swift b/stdlib/public/Concurrency/MinMax.swift deleted file mode 100644 index 8e7edbc993e65..0000000000000 --- a/stdlib/public/Concurrency/MinMax.swift +++ /dev/null @@ -1,43 +0,0 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2020 Apple Inc. and the Swift project authors -//// Licensed under Apache License v2.0 with Runtime Library Exception -//// -//// See https://swift.org/LICENSE.txt for license information -//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -//// -////===----------------------------------------------------------------------===// - -import Swift - -extension AsyncSequence { - public func min(by areInIncreasingOrder: (Element, Element) async throws -> Bool) async rethrows -> Element? { - var it = makeAsyncIterator() - guard var result = try await it.next() else { return nil } - while let e = try await it.next() { - if try await areInIncreasingOrder(e, result) { result = e } - } - return result - } - - public func max(by areInIncreasingOrder: (Element, Element) async throws -> Bool) async rethrows -> Element? { - var it = makeAsyncIterator() - guard var result = try await it.next() else { return nil } - while let e = try await it.next() { - if try await areInIncreasingOrder(result, e) { result = e } - } - return result - } -} - -extension AsyncSequence where Element: Comparable { - public func min() async rethrows -> Element? { - return try await min(by: <) - } - - public func max() async rethrows -> Element? { - return try await max(by: <) - } -} diff --git a/stdlib/public/Concurrency/Reduce.swift b/stdlib/public/Concurrency/Reduce.swift deleted file mode 100644 index 23a4f534766b4..0000000000000 --- a/stdlib/public/Concurrency/Reduce.swift +++ /dev/null @@ -1,34 +0,0 @@ -////===----------------------------------------------------------------------===// -//// -//// This source file is part of the Swift.org open source project -//// -//// Copyright (c) 2020 Apple Inc. and the Swift project authors -//// Licensed under Apache License v2.0 with Runtime Library Exception -//// -//// See https://swift.org/LICENSE.txt for license information -//// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -//// -////===----------------------------------------------------------------------===// - -import Swift - -extension AsyncSequence { - public func reduce(_ initialResult: Result, _ nextPartialResult: (_ partialResult: Result, Element) async throws -> Result) async rethrows -> Result { - var accumulator = initialResult - var it = makeAsyncIterator() - while let element = try await it.next() { - accumulator = try await nextPartialResult(accumulator, element) - } - return accumulator - } - - public func reduce(into initialResult: __owned Result, _ updateAccumulatingResult: (_ partialResult: inout Result, Element) async throws -> Void) async rethrows -> Result { - var accumulator = initialResult - var it = makeAsyncIterator() - while let element = try await it.next() { - try await updateAccumulatingResult(&accumulator, element) - } - return accumulator - } - -} From d4d92490697b7c995a080f2cc2ae1e949208365f Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 20 Jan 2021 11:03:14 -0800 Subject: [PATCH 35/40] Back out cancellation of AsyncIteratorProtocols --- include/swift/AST/KnownIdentifiers.def | 1 - lib/SILGen/SILGenStmt.cpp | 35 ------------------- .../Concurrency/AsyncIteratorProtocol.swift | 1 - 3 files changed, 37 deletions(-) diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 705b1aea57d33..904b47c915214 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -40,7 +40,6 @@ IDENTIFIER(buildIf) IDENTIFIER(buildLimitedAvailability) IDENTIFIER(buildOptional) IDENTIFIER(callAsFunction) -IDENTIFIER(cancel) IDENTIFIER(Change) IDENTIFIER_WITH_NAME(code_, "_code") IDENTIFIER(CodingKeys) diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index ceb79f86a23c1..36e2088c70814 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -934,33 +934,6 @@ void StmtEmitter::visitRepeatWhileStmt(RepeatWhileStmt *S) { SGF.BreakContinueDestStack.pop_back(); } -namespace { - class CancelCleanup : public Cleanup { - SILLocation loc; - std::function iteratorGen; - ConcreteDeclRef generatorCancelRef; - public: - CancelCleanup(SILLocation loc, std::function iteratorGen, - ConcreteDeclRef generatorCancelRef) : - loc(loc), iteratorGen(iteratorGen), - generatorCancelRef(generatorCancelRef) { } - - void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind) override { - FormalEvaluationScope scope(SGF); - SGF.emitApplyMethod(loc, generatorCancelRef, iteratorGen(), - PreparedArguments(ArrayRef({})), - SGFContext()); - } - - void dump(SILGenFunction &) const override { -#ifndef NDEBUG - llvm::errs() << "CancelCleanup\n" - << "State: " << getState() << "\n"; -#endif - } - }; -} - void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { // Dig out information about the sequence conformance. @@ -1030,9 +1003,6 @@ void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { ValueDecl *generatorNextReq = generatorProto->getSingleRequirement( DeclName(SGF.getASTContext(), SGF.getASTContext().Id_next, ArrayRef())); - ValueDecl *generatorCancelReq = generatorProto->getSingleRequirement( - DeclName(SGF.getASTContext(), SGF.getASTContext().Id_cancel, - ArrayRef())); auto generatorAssocType = asyncSequenceProto->getAssociatedType(SGF.getASTContext().Id_AsyncIterator); auto generatorMemberRef = DependentMemberType::get( @@ -1044,7 +1014,6 @@ void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { auto generatorSubs = SubstitutionMap::getProtocolSubstitutions( generatorProto, generatorType, generatorConformance); ConcreteDeclRef generatorNextRef(generatorNextReq, generatorSubs); - ConcreteDeclRef generatorCancelRef(generatorCancelReq, generatorSubs); // Set the destinations for 'break' and 'continue'. JumpDest endDest = createJumpDest(S->getBody()); @@ -1080,9 +1049,6 @@ void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { return result; }; - SGF.Cleanups.pushCleanup(SILLocation(S->getSequence()), - buildArgumentSource, generatorCancelRef); - auto cancelCleanup = SGF.Cleanups.getTopCleanup(); // Then emit the loop destination block. // // Advance the generator. Use a scope to ensure that any temporary stack @@ -1182,7 +1148,6 @@ void StmtEmitter::visitAsyncForEachStmt(ForEachStmt *S) { createBasicBlock(), failExitingBlock, [&](ManagedValue inputValue, SwitchCaseFullExpr &&scope) { assert(!inputValue && "None should not be passed an argument!"); - SGF.Cleanups.forwardCleanup(cancelCleanup); scope.exitAndBranch(S); }, SGF.loadProfilerCount(S)); diff --git a/stdlib/public/Concurrency/AsyncIteratorProtocol.swift b/stdlib/public/Concurrency/AsyncIteratorProtocol.swift index 3013377d0a387..7613300cc96cf 100644 --- a/stdlib/public/Concurrency/AsyncIteratorProtocol.swift +++ b/stdlib/public/Concurrency/AsyncIteratorProtocol.swift @@ -16,5 +16,4 @@ import Swift public protocol AsyncIteratorProtocol { associatedtype Element mutating func next() async throws -> Element? - mutating func cancel() } From b25ccabeadef9e6755b81297abff3aff93ba6a04 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 20 Jan 2021 14:16:45 -0800 Subject: [PATCH 36/40] Restructure protocol conformance throws checking and cache results --- include/swift/AST/Module.h | 2 - include/swift/AST/ProtocolConformanceRef.h | 5 ++- include/swift/AST/TypeCheckRequests.h | 19 +++++++++ include/swift/AST/TypeCheckerTypeIDZone.def | 3 ++ lib/AST/Module.cpp | 14 ------ lib/AST/ProtocolConformance.cpp | 47 +++++++++++++++++---- lib/AST/SubstitutionMap.cpp | 1 + lib/SILGen/SILGenApply.cpp | 7 ++- lib/Sema/TypeCheckConstraints.cpp | 2 +- lib/Sema/TypeCheckEffects.cpp | 10 ++++- 10 files changed, 81 insertions(+), 29 deletions(-) diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index f9d5c914a0a95..5cff707bd07e2 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -730,8 +730,6 @@ class ModuleDecl : public DeclContext, public TypeDecl { SourceRange getSourceRange() const { return SourceRange(); } - bool classifyWitnessAsThrows(SubstitutionMap substitutions); - static bool classof(const DeclContext *DC) { if (auto D = DC->getAsDecl()) return classof(D); diff --git a/include/swift/AST/ProtocolConformanceRef.h b/include/swift/AST/ProtocolConformanceRef.h index 2b27d4edd9a75..77d67b0267278 100644 --- a/include/swift/AST/ProtocolConformanceRef.h +++ b/include/swift/AST/ProtocolConformanceRef.h @@ -171,9 +171,12 @@ class ProtocolConformanceRef { /// be satisfied. ArrayRef getConditionalRequirements() const; - bool classifyAsThrows(ModuleDecl *module); + bool classifyAsThrows() const; }; +void simple_display(llvm::raw_ostream &out, ProtocolConformanceRef conformanceRef); +SourceLoc extractNearestSourceLoc(const ProtocolConformanceRef conformanceRef); + } // end namespace swift #endif // LLVM_SWIFT_AST_PROTOCOLCONFORMANCEREF_H diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 732d2c1441a32..41bf7038006fc 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -330,6 +330,25 @@ class ProtocolRethrowsRequirementsRequest : bool isCached() const { return true; } }; +class ProtocolConformanceRefClassifyAsThrowsRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + bool + evaluate(Evaluator &evaluator, ProtocolConformanceRef conformanceRef) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + /// Determine whether the given declaration is 'final'. class IsFinalRequest : public SimpleRequest &results) const { getCache().getPrecedenceGroups(results); diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index c5e5d58e0785c..e80598602fb93 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -26,6 +26,7 @@ #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeWalker.h" #include "swift/AST/Types.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Statistic.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/Statistic.h" @@ -189,17 +190,18 @@ ProtocolConformanceRef::getWitnessByName(Type type, DeclName name) const { static bool classifyRequirement(ModuleDecl *module, ProtocolConformance *reqConformance, ValueDecl *requiredFn) { - auto DC = reqConformance->getDeclContext(); - auto reqTy = reqConformance->getType(); auto declRef = reqConformance->getWitnessDeclRef(requiredFn); auto witnessDecl = cast(declRef.getDecl()); switch (witnessDecl->getRethrowingKind()) { - case FunctionRethrowingKind::ByConformance: - if (module->classifyWitnessAsThrows( - reqTy->getContextSubstitutionMap(module, DC))) { - return true; + case FunctionRethrowingKind::ByConformance: { + auto substitutions = reqConformance->getSubstitutions(module); + for (auto conformanceRef : substitutions.getConformances()) { + if (conformanceRef.classifyAsThrows()) { + return true; + } } break; + } case FunctionRethrowingKind::None: break; case FunctionRethrowingKind::Throws: @@ -235,9 +237,12 @@ static bool classifyTypeRequirement(ModuleDecl *module, Type protoType, return classifyRequirement(module, reqConformance, requiredFn); } -bool ProtocolConformanceRef::classifyAsThrows(ModuleDecl *module) { - auto conformance = getConcrete(); - auto requiredProtocol = getRequirement(); +bool +ProtocolConformanceRefClassifyAsThrowsRequest::evaluate( + Evaluator &evaluator, ProtocolConformanceRef conformanceRef) const { + auto conformance = conformanceRef.getConcrete(); + auto requiredProtocol = conformanceRef.getRequirement(); + auto module = requiredProtocol->getModuleContext(); for (auto req : requiredProtocol->getRethrowingRequirements()) { if (classifyTypeRequirement(module, req.first, req.second, conformance, requiredProtocol)) { @@ -247,6 +252,13 @@ bool ProtocolConformanceRef::classifyAsThrows(ModuleDecl *module) { return false; } +bool ProtocolConformanceRef::classifyAsThrows() const { + if (!isConcrete()) { return true; } + return evaluateOrDefault(getRequirement()->getASTContext().evaluator, + ProtocolConformanceRefClassifyAsThrowsRequest{ *this }, + true); +} + void *ProtocolConformance::operator new(size_t bytes, ASTContext &context, AllocationArena arena, unsigned alignment) { @@ -1591,3 +1603,20 @@ void swift::simple_display(llvm::raw_ostream &out, const ProtocolConformance *conf) { conf->printName(out); } + +void swift::simple_display(llvm::raw_ostream &out, ProtocolConformanceRef conformanceRef) { + if (conformanceRef.isAbstract()) { + simple_display(out, conformanceRef.getAbstract()); + } else if (conformanceRef.isConcrete()) { + simple_display(out, conformanceRef.getConcrete()); + } +} + +SourceLoc swift::extractNearestSourceLoc(const ProtocolConformanceRef conformanceRef) { + if (conformanceRef.isAbstract()) { + return extractNearestSourceLoc(conformanceRef.getAbstract()); + } else if (conformanceRef.isConcrete()) { + return extractNearestSourceLoc(conformanceRef.getConcrete()->getProtocol()); + } + return SourceLoc(); +} diff --git a/lib/AST/SubstitutionMap.cpp b/lib/AST/SubstitutionMap.cpp index b9352b4314852..1d5fb19b20480 100644 --- a/lib/AST/SubstitutionMap.cpp +++ b/lib/AST/SubstitutionMap.cpp @@ -30,6 +30,7 @@ #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/AST/Types.h" #include "swift/Basic/Defer.h" #include "llvm/Support/Debug.h" diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 8c268473bbaf5..66ea0f16e62c5 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -4973,7 +4973,12 @@ RValue SILGenFunction::emitApplyMethod(SILLocation loc, ConcreteDeclRef declRef, bool markedAsRethrows = call->getAttrs().hasAttribute(); FunctionRethrowingKind rethrowingKind = call->getRethrowingKind(); if (rethrowingKind == FunctionRethrowingKind::ByConformance) { - throws = call->getModuleContext()->classifyWitnessAsThrows(subs); + for (auto conformanceRef : subs.getConformances()) { + if (conformanceRef.classifyAsThrows()) { + throws = true; + break; + } + } } else if (markedAsRethrows && rethrowingKind == FunctionRethrowingKind::Throws) { throws = true; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 88b4b325097e3..7356e604193d5 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -619,7 +619,7 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { auto module = dc->getParentModule(); auto conformanceRef = module->lookupConformance(Ty, sequenceProto); - if (conformanceRef.classifyAsThrows(module) && + if (conformanceRef.classifyAsThrows() && stmt->getTryLoc().isInvalid()) { auto &diags = dc->getASTContext().Diags; diags.diagnose(stmt->getAwaitLoc(), diag::throwing_call_unhandled); diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 2f5d3c1865bcb..75096d00039cc 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -524,7 +524,15 @@ class ApplyClassifier { if (fnRef.getRethrowingKind() == FunctionRethrowingKind::ByConformance) { auto substitutions = fnRef.getDeclRef().getSubstitutions(); - if (fnRef.getModuleContext()->classifyWitnessAsThrows(substitutions)) { + bool classifiedAsThrows = false; + for (auto conformanceRef : substitutions.getConformances()) { + if (conformanceRef.classifyAsThrows()) { + classifiedAsThrows = true; + break; + } + } + + if (classifiedAsThrows) { return Classification::forRethrowingOnly( PotentialThrowReason::forRethrowsConformance(E), isAsync); } From 18211829665b7093b668d01c915eb772b0c6fbe0 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 20 Jan 2021 15:14:18 -0800 Subject: [PATCH 37/40] remove some stray whitespaces --- lib/Sema/TypeCheckEffects.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 75096d00039cc..1c8fb97dd736a 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -134,11 +134,6 @@ class AbstractFunction { return declRef; } - ModuleDecl *getModuleContext() { - assert(getKind() == Kind::Function); - return TheFunction->getModuleContext(); - } - static AbstractFunction decomposeApply(ApplyExpr *apply, SmallVectorImpl &args) { Expr *fn; @@ -1037,7 +1032,6 @@ class Context { if (!fn) return false; - return fn->getRethrowingKind() == FunctionRethrowingKind::ByClosure; } @@ -2042,8 +2036,6 @@ class CheckEffectsCoverage : public EffectsHandlingWalker } ShouldRecurse_t checkTry(TryExpr *E) { - - // Walk the operand. ContextScope scope(*this, None); scope.enterTry(); From b186a7d361db2b31fb9cc86125ba9d887f1d5f9b Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 20 Jan 2021 15:28:05 -0800 Subject: [PATCH 38/40] Correct some merge damage --- include/swift/AST/TypeCheckerTypeIDZone.def | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 5783a0102718f..ae3cee5930803 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -204,9 +204,9 @@ SWIFT_REQUEST(TypeChecker, RequiresOpaqueAccessorsRequest, bool(VarDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, RequiresOpaqueModifyCoroutineRequest, bool(AbstractStorageDecl *), SeparatelyCached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, FunctionRethrowingKindRequest, - FragileFunctionKind(DeclContext *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, FragileFunctionKindRequest, + FragileFunctionKind(DeclContext *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, FunctionRethrowingKindRequest, FunctionRethrowingKind(AbstractFunctionDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, SelfAccessKindRequest, SelfAccessKind(FuncDecl *), SeparatelyCached, NoLocationInfo) From e07ad118eb1777c2e94712c76d72cc7f7a2b0e72 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Fri, 22 Jan 2021 10:45:57 -0800 Subject: [PATCH 39/40] Ensure the throwing determinate for applying for-await-in always has a valid value and adjust the for-await-in silgen test to reflect the cancel changes --- lib/SILGen/SILGenApply.cpp | 4 +--- test/SILGen/foreach_async.swift | 5 ----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 66ea0f16e62c5..552b969ff76e8 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -4969,7 +4969,7 @@ RValue SILGenFunction::emitApplyMethod(SILLocation loc, ConcreteDeclRef declRef, .asForeign(requiresForeignEntryPoint(declRef.getDecl())); auto declRefConstant = getConstantInfo(getTypeExpansionContext(), callRef); auto subs = declRef.getSubstitutions(); - bool throws; + bool throws = false; bool markedAsRethrows = call->getAttrs().hasAttribute(); FunctionRethrowingKind rethrowingKind = call->getRethrowingKind(); if (rethrowingKind == FunctionRethrowingKind::ByConformance) { @@ -4982,8 +4982,6 @@ RValue SILGenFunction::emitApplyMethod(SILLocation loc, ConcreteDeclRef declRef, } else if (markedAsRethrows && rethrowingKind == FunctionRethrowingKind::Throws) { throws = true; - } else { - throws = false; } // Scope any further writeback just within this operation. diff --git a/test/SILGen/foreach_async.swift b/test/SILGen/foreach_async.swift index 3d21fb057e633..b10b1dc481631 100644 --- a/test/SILGen/foreach_async.swift +++ b/test/SILGen/foreach_async.swift @@ -54,10 +54,6 @@ struct AsyncLazySequence: AsyncSequence { mutating func next() async -> S.Element? { return iterator?.next() } - - mutating func cancel() { - iterator = nil - } } var sequence: S @@ -192,7 +188,6 @@ func trivialStructBreak(_ xx: AsyncLazySequence<[Int]>) async { // CHECK: [[COND_TRUE]]: // CHECK: loopBreakEnd -// CHECK: cancel // CHECK: br [[LOOP_EXIT:bb[0-9]+]] // CHECK: [[COND_FALSE]]: From dafe88e1c87ecb2d6520b9a47155b50ba6dc2e7c Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Mon, 25 Jan 2021 08:36:27 -0800 Subject: [PATCH 40/40] Squelch the python linter for line length --- utils/gyb_syntax_support/StmtNodes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/gyb_syntax_support/StmtNodes.py b/utils/gyb_syntax_support/StmtNodes.py index 72b9fd8a268c1..974490bf15e09 100644 --- a/utils/gyb_syntax_support/StmtNodes.py +++ b/utils/gyb_syntax_support/StmtNodes.py @@ -74,7 +74,8 @@ Child('GuardResult', kind='Expr'), ]), - # for-in-stmt -> label? ':'? 'for' 'try'? 'await'? 'case'? pattern 'in' expr 'where'? + # for-in-stmt -> label? ':'? + # 'for' 'try'? 'await'? 'case'? pattern 'in' expr 'where'? # expr code-block ';'? Node('ForInStmt', kind='Stmt', traits=['WithCodeBlock', 'Labeled'],