diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 7644b237921bd..c0e01f88650d4 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -1823,6 +1823,45 @@ class ValueWitnessRequest void cacheResult(Witness value) const; }; +struct PreCheckFunctionBuilderDescriptor { + AnyFunctionRef Fn; + +private: + // NOTE: Since source tooling (e.g. code completion) might replace the body, + // we need to take the body into account to calculate 'hash_value' and '=='. + // Also, we cannot 'getBody()' inside 'hash_value' and '==' because it invokes + // another request (even if it's cached). + BraceStmt *Body; + +public: + PreCheckFunctionBuilderDescriptor(AnyFunctionRef Fn) + : Fn(Fn), Body(Fn.getBody()) {} + + friend llvm::hash_code + hash_value(const PreCheckFunctionBuilderDescriptor &owner) { + return llvm::hash_combine(owner.Fn, owner.Body); + } + + friend bool operator==(const PreCheckFunctionBuilderDescriptor &lhs, + const PreCheckFunctionBuilderDescriptor &rhs) { + return lhs.Fn == rhs.Fn && lhs.Body == rhs.Body; + } + + friend bool operator!=(const PreCheckFunctionBuilderDescriptor &lhs, + const PreCheckFunctionBuilderDescriptor &rhs) { + return !(lhs == rhs); + } + + friend SourceLoc extractNearestSourceLoc(PreCheckFunctionBuilderDescriptor d) { + return extractNearestSourceLoc(d.Fn); + } + + friend void simple_display(llvm::raw_ostream &out, + const PreCheckFunctionBuilderDescriptor &d) { + simple_display(out, d.Fn); + } +}; + enum class FunctionBuilderBodyPreCheck : uint8_t { /// There were no problems pre-checking the closure. Okay, @@ -1836,8 +1875,8 @@ enum class FunctionBuilderBodyPreCheck : uint8_t { class PreCheckFunctionBuilderRequest : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -1846,8 +1885,8 @@ class PreCheckFunctionBuilderRequest friend SimpleRequest; // Evaluation. - FunctionBuilderBodyPreCheck evaluate(Evaluator &evaluator, AnyFunctionRef fn, - BraceStmt *body) const; + FunctionBuilderBodyPreCheck + evaluate(Evaluator &evaluator, PreCheckFunctionBuilderDescriptor owner) const; public: // Separate caching. diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 153482b0dfb2c..86845581c4103 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -231,7 +231,7 @@ SWIFT_REQUEST(TypeChecker, HasUserDefinedDesignatedInitRequest, SWIFT_REQUEST(TypeChecker, HasMemberwiseInitRequest, bool(StructDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, PreCheckFunctionBuilderRequest, - FunctionBuilderClosurePreCheck(AnyFunctionRef, BraceStmt *), + FunctionBuilderClosurePreCheck(PreCheckFunctionBuilderDescriptor), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ResolveImplicitMemberRequest, evaluator::SideEffect(NominalTypeDecl *, ImplicitMemberAction), diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index a6821bcf9cd4e..9bc3ab2b06046 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -1464,7 +1464,7 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( // If we encountered an error or there was an explicit result type, // bail out and report that to the caller. auto &ctx = func->getASTContext(); - auto request = PreCheckFunctionBuilderRequest{func, func->getBody()}; + auto request = PreCheckFunctionBuilderRequest{AnyFunctionRef(func)}; switch (evaluateOrDefault( ctx.evaluator, request, FunctionBuilderBodyPreCheck::Error)) { case FunctionBuilderBodyPreCheck::Okay: @@ -1592,7 +1592,7 @@ ConstraintSystem::matchFunctionBuilder( // Pre-check the body: pre-check any expressions in it and look // for return statements. - auto request = PreCheckFunctionBuilderRequest{fn, fn.getBody()}; + auto request = PreCheckFunctionBuilderRequest{fn}; switch (evaluateOrDefault(getASTContext().evaluator, request, FunctionBuilderBodyPreCheck::Error)) { case FunctionBuilderBodyPreCheck::Okay: @@ -1712,11 +1712,11 @@ class PreCheckFunctionBuilderApplication : public ASTWalker { if (HasError) return FunctionBuilderBodyPreCheck::Error; + assert(oldBody == newBody && "pre-check walk wasn't in-place?"); + if (hasReturnStmt()) return FunctionBuilderBodyPreCheck::HasReturnStmt; - assert(oldBody == newBody && "pre-check walk wasn't in-place?"); - return FunctionBuilderBodyPreCheck::Okay; } @@ -1754,23 +1754,16 @@ class PreCheckFunctionBuilderApplication : public ASTWalker { } -FunctionBuilderBodyPreCheck -PreCheckFunctionBuilderRequest::evaluate(Evaluator &eval, AnyFunctionRef fn, - BraceStmt *body) const { - // NOTE: 'body' is passed only for the request evaluater caching key. - // Since source tooling (e.g. code completion) might replace the body, - // the function alone is not sufficient for the key. - assert(fn.getBody() == body && - "body must be the current body of the function"); - +FunctionBuilderBodyPreCheck PreCheckFunctionBuilderRequest::evaluate( + Evaluator &evaluator, PreCheckFunctionBuilderDescriptor owner) const { // We don't want to do the precheck if it will already have happened in // the enclosing expression. bool skipPrecheck = false; if (auto closure = dyn_cast_or_null( - fn.getAbstractClosureExpr())) + owner.Fn.getAbstractClosureExpr())) skipPrecheck = shouldTypeCheckInEnclosingExpression(closure); - return PreCheckFunctionBuilderApplication(fn, false).run(); + return PreCheckFunctionBuilderApplication(owner.Fn, false).run(); } std::vector TypeChecker::findReturnStatements(AnyFunctionRef fn) {