From 4d104e45e080530cb3d3c8fcf5174a9295c4c262 Mon Sep 17 00:00:00 2001 From: Freddy Kellison-Linn Date: Sun, 24 Aug 2025 01:53:22 -0400 Subject: [PATCH 1/5] Implement async defer --- include/swift/AST/ASTBridging.h | 8 +++++--- include/swift/AST/Stmt.h | 17 +++++++++++------ lib/AST/ASTScopeCreation.cpp | 2 +- lib/AST/ASTVerifier.cpp | 6 ++++-- lib/AST/Bridging/StmtBridging.cpp | 9 ++++++--- lib/AST/Stmt.cpp | 26 ++++++++++++++------------ lib/ASTGen/Sources/ASTGen/Stmts.swift | 7 +++---- lib/IDE/SourceEntityWalker.cpp | 3 +-- lib/IDE/SyntaxModel.cpp | 10 ++++------ lib/Parse/ParseStmt.cpp | 26 +++++--------------------- lib/SILGen/SILGenStmt.cpp | 7 ++++--- lib/Sema/TypeCheckEffects.cpp | 17 ++--------------- lib/Sema/TypeCheckStmt.cpp | 3 +++ 13 files changed, 63 insertions(+), 78 deletions(-) diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 99f9d05e0eef3..e57424d35b8f7 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -2349,12 +2349,14 @@ BridgedContinueStmt BridgedContinueStmt_createParsed( BridgedDeclContext cDeclContext, swift::SourceLoc loc, swift::Identifier targetName, swift::SourceLoc targetLoc); -SWIFT_NAME("BridgedDeferStmt.createParsed(_:deferLoc:)") +SWIFT_NAME("BridgedDeferStmt.createParsed(_:deferLoc:body:)") BridgedDeferStmt BridgedDeferStmt_createParsed(BridgedDeclContext cDeclContext, - swift::SourceLoc deferLoc); + swift::SourceLoc deferLoc, + BridgedExpr body); SWIFT_NAME("getter:BridgedDeferStmt.tempDecl(self:)") -BridgedFuncDecl BridgedDeferStmt_getTempDecl(BridgedDeferStmt bridged); +BridgedPatternBindingDecl +BridgedDeferStmt_getTempDecl(BridgedDeferStmt bridged); SWIFT_NAME("BridgedDiscardStmt.createParsed(_:discardLoc:subExpr:)") BridgedDiscardStmt BridgedDiscardStmt_createParsed(BridgedASTContext cContext, diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index c8f9d4bb3e76d..f2ed224970574 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -394,31 +394,36 @@ class ThenStmt : public Stmt { class DeferStmt : public Stmt { SourceLoc DeferLoc; - /// This is the bound temp function. - FuncDecl *tempDecl; + /// This is the bound temp var for the defer closure. + PatternBindingDecl *tempDecl; + + /// This is the expr that makes up the body of the defer stmt + Expr *body; /// This is the invocation of the closure, which is to be emitted on any error /// paths. Expr *callExpr; DeferStmt(SourceLoc DeferLoc, - FuncDecl *tempDecl, Expr *callExpr) + PatternBindingDecl *tempDecl, Expr *body, + Expr *callExpr) : Stmt(StmtKind::Defer, /*implicit*/false), - DeferLoc(DeferLoc), tempDecl(tempDecl), + DeferLoc(DeferLoc), tempDecl(tempDecl), body(body), callExpr(callExpr) {} public: /// Create a 'defer' statement. This automatically creates the "temp decl" and /// the call expression. It's the caller's responsibility to populate the /// body of the func decl. - static DeferStmt *create(DeclContext *dc, SourceLoc deferLoc); + static DeferStmt *create(DeclContext *dc, SourceLoc deferLoc, Expr *body); SourceLoc getDeferLoc() const { return DeferLoc; } SourceLoc getStartLoc() const { return DeferLoc; } SourceLoc getEndLoc() const; - FuncDecl *getTempDecl() const { return tempDecl; } + PatternBindingDecl *getTempDecl() const { return tempDecl; } + Expr *getBody() const { return body; } Expr *getCallExpr() const { return callExpr; } void setCallExpr(Expr *E) { callExpr = E; } diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index d95675b40b24b..0963149bb8c10 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -510,7 +510,7 @@ class NodeAdder ASTScopeImpl *visitDeferStmt(DeferStmt *ds, ASTScopeImpl *p, ScopeCreator &scopeCreator) { - visitFuncDecl(ds->getTempDecl(), p, scopeCreator); + visitClosureExpr(dyn_cast(ds->getBody()), p, scopeCreator); return p; } diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index 039f5db5bd67b..e2b8d38ab6a99 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -1121,8 +1121,10 @@ class Verifier : public ASTWalker { verifyCheckedBase(S); } void verifyChecked(DeferStmt *S) { - auto FT = S->getTempDecl()->getInterfaceType()->castTo(); - assert(FT->isNoEscape() && "Defer statements must not escape"); + auto FT = S->getBody()->getType()->castTo(); + // TODO: Making these into PBDs made them get marked as escaping... are + // there downstream effects? + // assert(FT->isNoEscape() && "Defer statements must not escape"); (void)FT; verifyCheckedBase(S); } diff --git a/lib/AST/Bridging/StmtBridging.cpp b/lib/AST/Bridging/StmtBridging.cpp index e8c11bb98d0cf..a0c77630ab540 100644 --- a/lib/AST/Bridging/StmtBridging.cpp +++ b/lib/AST/Bridging/StmtBridging.cpp @@ -148,11 +148,14 @@ BridgedContinueStmt_createParsed(BridgedDeclContext cDeclContext, SourceLoc loc, } BridgedDeferStmt BridgedDeferStmt_createParsed(BridgedDeclContext cDeclContext, - SourceLoc deferLoc) { - return DeferStmt::create(cDeclContext.unbridged(), deferLoc); + SourceLoc deferLoc, + BridgedExpr body) { + return DeferStmt::create(cDeclContext.unbridged(), deferLoc, + body.unbridged()); } -BridgedFuncDecl BridgedDeferStmt_getTempDecl(BridgedDeferStmt bridged) { +BridgedPatternBindingDecl +BridgedDeferStmt_getTempDecl(BridgedDeferStmt bridged) { return bridged.unbridged()->getTempDecl(); } diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index f1460e2383ed6..ba22d9cd07bfd 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -363,35 +363,37 @@ SourceLoc ThrowStmt::getEndLoc() const { return SubExpr->getEndLoc(); } SourceLoc DiscardStmt::getEndLoc() const { return SubExpr->getEndLoc(); } -DeferStmt *DeferStmt::create(DeclContext *dc, SourceLoc deferLoc) { +DeferStmt *DeferStmt::create(DeclContext *dc, SourceLoc deferLoc, Expr *body) { ASTContext &ctx = dc->getASTContext(); auto params = ParameterList::createEmpty(ctx); - DeclName name(ctx, ctx.getIdentifier("$defer"), params); - auto *const funcDecl = FuncDecl::createImplicit( - ctx, StaticSpellingKind::None, name, /*NameLoc=*/deferLoc, - /*Async=*/false, - /*Throws=*/false, - /*ThrownType=*/Type(), - /*GenericParams=*/nullptr, params, TupleType::getEmpty(ctx), dc); + auto name = ctx.getIdentifier("$defer"); + + auto varDecl = new (ctx) VarDecl(/*isStatic=*/false, VarDecl::Introducer::Let, + /*NameLoc=*/deferLoc, name, dc); + + auto *pat = NamedPattern::createImplicit(ctx, varDecl, Type()); + + auto *pbd = PatternBindingDecl::createImplicit( + ctx, StaticSpellingKind::None, pat, body, dc); // Form the call, which will be emitted on any path that needs to run the // code. auto DRE = new (ctx) - DeclRefExpr(funcDecl, DeclNameLoc(deferLoc), + DeclRefExpr(varDecl, DeclNameLoc(deferLoc), /*Implicit*/ true, AccessSemantics::DirectToStorage); auto call = CallExpr::createImplicitEmpty(ctx, DRE); - return new (ctx) DeferStmt(deferLoc, funcDecl, call); + return new (ctx) DeferStmt(deferLoc, pbd, body, call); } SourceLoc DeferStmt::getEndLoc() const { - return tempDecl->getBody()->getEndLoc(); + return body->getEndLoc(); } /// Dig the original user's body of the defer out for AST fidelity. BraceStmt *DeferStmt::getBodyAsWritten() const { - return tempDecl->getBody(); + return dyn_cast(body)->getBody(); } bool LabeledStmt::isPossibleContinueTarget() const { diff --git a/lib/ASTGen/Sources/ASTGen/Stmts.swift b/lib/ASTGen/Sources/ASTGen/Stmts.swift index c97d8d558e3c4..b7f6c98b6354a 100644 --- a/lib/ASTGen/Sources/ASTGen/Stmts.swift +++ b/lib/ASTGen/Sources/ASTGen/Stmts.swift @@ -272,13 +272,12 @@ extension ASTGenVisitor { func generate(deferStmt node: DeferStmtSyntax) -> BridgedDeferStmt { let deferLoc = self.generateSourceLoc(node.deferKeyword) + let closure = self.generate(closureExpr: .init(statements: node.body.statements)) let stmt = BridgedDeferStmt.createParsed( self.declContext, - deferLoc: deferLoc + deferLoc: deferLoc, + body: closure ) - self.withDeclContext(stmt.tempDecl.asDeclContext) { - stmt.tempDecl.setParsedBody(self.generate(codeBlock: node.body)) - } return stmt } diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index f11281be71863..e020fcdc94ec6 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -274,8 +274,7 @@ ASTWalker::PreWalkResult SemaAnnotator::walkToStmtPre(Stmt *S) { if (auto *DeferS = dyn_cast(S)) { // Since 'DeferStmt::getTempDecl()' is marked as implicit, we manually // walk into the body. - if (auto *FD = DeferS->getTempDecl()) { - auto *Body = FD->getBody(); + if (auto *Body = DeferS->getBody()) { if (!Body) return Action::Stop(); diff --git a/lib/IDE/SyntaxModel.cpp b/lib/IDE/SyntaxModel.cpp index 8fbdddc7d45b0..ae218f8f53d1b 100644 --- a/lib/IDE/SyntaxModel.cpp +++ b/lib/IDE/SyntaxModel.cpp @@ -812,12 +812,10 @@ ASTWalker::PreWalkResult ModelASTWalker::walkToStmtPre(Stmt *S) { } else if (auto *DeferS = dyn_cast(S)) { // Since 'DeferStmt::getTempDecl()' is marked as implicit, we manually walk // into the body. - if (auto *FD = DeferS->getTempDecl()) { - if (auto *Body = FD->getBody()) { - auto *RetS = Body->walk(*this); - assert(RetS == Body); - (void)RetS; - } + if (auto *Body = DeferS->getBody()) { + auto *RetS = Body->walk(*this); + assert(RetS == Body); + (void)RetS; } // Already walked children. return Action::SkipChildren(DeferS); diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 35208bdca0393..c6b72c5640e76 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1081,28 +1081,12 @@ ParserResult Parser::parseStmtDefer() { // // As such, the body of the 'defer' is actually type checked within the // closure's DeclContext. - auto *DS = DeferStmt::create(CurDeclContext, DeferLoc); ParserStatus Status; - { - auto *tempDecl = DS->getTempDecl(); - // Change the DeclContext for any variables declared in the defer to be within - // the defer closure. - ParseFunctionBody cc(*this, tempDecl); - llvm::SaveAndRestore> T( - CurrentTokenHash, StableHasher::defaultHasher()); - - ParserResult Body = - parseBraceItemList(diag::expected_lbrace_after_defer); - if (Body.isNull()) - return nullptr; - Status |= Body; - - // Clone the current hasher and extract a Fingerprint. - StableHasher currentHash{*CurrentTokenHash}; - Fingerprint fp(std::move(currentHash)); - tempDecl->setBodyParsed(Body.get(), fp); - } - + ParserResult body = parseExprClosure(); + if (body.isNull()) + return nullptr; + auto *DS = DeferStmt::create(CurDeclContext, DeferLoc, body.get()); + Status |= body; return makeParserResult(Status, DS); } diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 173cecfb7cd96..8d5f8f64a35ba 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -928,13 +928,14 @@ void StmtEmitter::visitDeferStmt(DeferStmt *S) { // Emit the closure for the defer, along with its binding. // If the defer is at the top-level code, insert 'mark_escape_inst' // to the top-level code to check initialization of any captured globals. - FuncDecl *deferDecl = S->getTempDecl(); + PatternBindingDecl *deferDecl = S->getTempDecl(); + ClosureExpr *body = dyn_cast(S->getBody()); auto *Ctx = deferDecl->getDeclContext(); if (isa(Ctx) && SGF.isEmittingTopLevelCode()) { - auto Captures = deferDecl->getCaptureInfo(); + auto Captures = body->getCaptureInfo(); SGF.emitMarkFunctionEscapeForTopLevelCodeGlobals(S, std::move(Captures)); } - SGF.visitFuncDecl(deferDecl); + SGF.visitPatternBindingDecl(deferDecl); // Register a cleanup to invoke the closure on any exit paths. SGF.Cleanups.pushCleanup(S->getDeferLoc(), S->getCallExpr()); diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index c23bd67e24db1..6efd662c24e12 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -2854,9 +2854,6 @@ class Context { /// The guard expression controlling a catch. CatchGuard, - - /// A defer body - DeferBody, }; private: @@ -2999,10 +2996,6 @@ class Context { return Context(D->hasThrows(), D->isAsyncContext(), AnyFunctionRef(D), D); } - static Context forDeferBody(DeclContext *dc) { - return Context(Kind::DeferBody, dc); - } - static Context forInitializer(Initializer *init) { if (isa(init)) { return Context(Kind::DefaultArgument, init); @@ -3303,7 +3296,6 @@ class Context { case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: - case Kind::DeferBody: Diags.diagnose(E.getStartLoc(), diag::throwing_op_in_illegal_context, static_cast(getKind()), getEffectSourceName(reason)); return; @@ -3340,7 +3332,6 @@ class Context { case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: - case Kind::DeferBody: Diags.diagnose(S->getStartLoc(), diag::throw_in_illegal_context, static_cast(getKind())); return; @@ -3367,7 +3358,6 @@ class Context { case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: - case Kind::DeferBody: assert(!DiagnoseErrorOnTry); // Diagnosed at the call sites. return; @@ -3477,7 +3467,6 @@ class Context { case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: - case Kind::DeferBody: diagnoseAsyncInIllegalContext(Diags, node); return; } @@ -4639,7 +4628,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker ContextScope scope(*this, std::nullopt); scope.enterUnsafe(S->getDeferLoc()); - // Walk the call expression. We don't care about the rest. + S->getBody()->walk(*this); S->getCallExpr()->walk(*this); return ShouldNotRecurse; @@ -5004,9 +4993,7 @@ void TypeChecker::checkFunctionEffects(AbstractFunctionDecl *fn) { PrettyStackTraceDecl debugStack("checking effects handling for", fn); #endif - auto isDeferBody = isa(fn) && cast(fn)->isDeferBody(); - auto context = - isDeferBody ? Context::forDeferBody(fn) : Context::forFunction(fn); + auto context = Context::forFunction(fn); auto &ctx = fn->getASTContext(); CheckEffectsCoverage checker(ctx, context); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 08477dc6a42fa..61adf6cab52c0 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1366,6 +1366,9 @@ class StmtChecker : public StmtVisitor { TypeChecker::typeCheckDecl(DS->getTempDecl()); Expr *theCall = DS->getCallExpr(); + if (DS->getBody()->getType()->getAs()->isAsync()) { + theCall = AwaitExpr::createImplicit(Ctx, SourceLoc(), theCall); + } TypeChecker::typeCheckExpression(theCall, DC); DS->setCallExpr(theCall); From 3e085034f9d69e86efa8fc99a2f9864538bb3f91 Mon Sep 17 00:00:00 2001 From: Freddy Kellison-Linn Date: Sat, 30 Aug 2025 12:42:39 -0400 Subject: [PATCH 2/5] Use closure for defer body --- include/swift/AST/Decl.h | 3 -- include/swift/AST/DiagnosticsSema.def | 5 +++ include/swift/AST/Expr.h | 16 +++++++-- include/swift/SIL/SILFunction.h | 7 +--- lib/AST/Decl.cpp | 22 ++++-------- lib/AST/Stmt.cpp | 1 - lib/Parse/ParseStmt.cpp | 34 ++++++++++++++++--- lib/SIL/IR/SILFunction.cpp | 7 ++++ lib/SILGen/SILGenFunction.cpp | 6 ---- lib/SILOptimizer/Mandatory/FlowIsolation.cpp | 10 +++--- lib/Sema/CSGen.cpp | 10 ++++++ lib/Sema/CSSyntacticElement.cpp | 2 +- lib/Sema/PreCheckTarget.cpp | 4 +-- lib/Sema/TypeCheckAttr.cpp | 3 -- lib/Sema/TypeCheckAvailability.cpp | 16 +++------ lib/Sema/TypeCheckCaptures.cpp | 8 ----- lib/Sema/TypeCheckConcurrency.cpp | 35 +++++++++----------- lib/Sema/TypeCheckDecl.cpp | 6 ---- lib/Sema/TypeCheckEffects.cpp | 25 ++++++++++++++ lib/Sema/TypeCheckStmt.cpp | 15 +++++++-- test/expr/unary/async_await.swift | 2 +- test/stmt/defer.swift | 13 +++++++- 22 files changed, 152 insertions(+), 98 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 51376bbceaec4..3da70b9f95c60 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -8570,9 +8570,6 @@ class FuncDecl : public AbstractFunctionDecl { return false; } - /// True if the function is a defer body. - bool isDeferBody() const; - /// Perform basic checking to determine whether the @IBAction or /// @IBSegueAction attribute can be applied to this function. bool isPotentialIBActionTarget() const; diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index d7ffcbd2cdd0c..dc56c958a5d55 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5532,6 +5532,11 @@ ERROR(throwing_op_in_illegal_context,none, ERROR(throw_in_illegal_context,none, "errors cannot be thrown out of " EFFECTS_CONTEXT_KIND "0", (unsigned)) +ERROR(throwing_op_in_defer,none, + "%0 can throw, but errors cannot be thrown out of a defer body", (StringRef)) +ERROR(throw_in_defer_body,none, + "errors cannot be thrown out of a defer body", ()) + ERROR(throwing_operator_without_try,none, "operator can throw but expression is not marked with 'try'", ()) ERROR(throwing_interpolation_without_try,none, diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 74f58a18c406a..191337522643f 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -267,7 +267,7 @@ class alignas(8) Expr : public ASTAllocated { Kind : 2 ); - SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1+1+1+1, /// True if closure parameters were synthesized from anonymous closure /// variables. HasAnonymousClosureVars : 1, @@ -302,7 +302,10 @@ class alignas(8) Expr : public ASTAllocated { /// Whether this closure was type-checked as an argument to a macro. This /// is only populated after type-checking, and only exists for diagnostic /// logic. Do not add more uses of this. - IsMacroArgument : 1 + IsMacroArgument : 1, + + /// Whether this closure is serving as the body of a `defer` statement. + IsDeferBody : 1 ); SWIFT_INLINE_BITFIELD_FULL(BindOptionalExpr, Expr, 16, @@ -4529,6 +4532,15 @@ class ClosureExpr : public AbstractClosureExpr { ExplicitResultTypeAndBodyState.setInt(v); } + /// Whether this is a closure serving as the body of a `defer` statement. + bool isDeferBody() const { + return Bits.ClosureExpr.IsDeferBody; + } + + void setIsDeferBody(bool value = true) { + Bits.ClosureExpr.IsDeferBody = value; + } + static bool classof(const Expr *E) { return E->getKind() == ExprKind::Closure; } diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index c1ed6a24b16ad..7e753a5dd422b 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -1397,12 +1397,7 @@ class SILFunction /// deserialized without a DeclContext. This means that this is guaranteed to /// be correct for SILFunctions in Raw SIL that were not deserialized as /// canonical. Thus one can use it for diagnostics. - bool isDefer() const { - if (auto *dc = getDeclContext()) - if (auto *decl = dyn_cast_or_null(dc->getAsDecl())) - return decl->isDeferBody(); - return false; - } + bool isDefer() const; /// Returns true if this function belongs to a declaration that /// has `@_alwaysEmitIntoClient` attribute. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 02293df2a92c7..fc8ed00e3e83c 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2950,7 +2950,7 @@ static bool isPolymorphic(const AbstractStorageDecl *storage) { /// In Swift 5 and earlier, this was not true, meaning that property observers, /// etc, would be invoked in initializers or deinitializers if a property access /// happens within a defer, but not when outside the defer. -static bool deferMatchesEnclosingAccess(const FuncDecl *defer) { +static bool deferMatchesEnclosingAccess(const ClosureExpr *defer) { assert(defer->isDeferBody()); // In Swift 6+, then yes. @@ -2995,15 +2995,15 @@ static bool isDirectToStorageAccess(const DeclContext *UseDC, if (!var->hasStorage()) return false; + // Check if this is a closure representing a defer. + if (auto *CE = dyn_cast(UseDC)) + if (CE->isDeferBody() && deferMatchesEnclosingAccess(CE)) + return isDirectToStorageAccess(CE->getParent(), var, isAccessOnSelf); + auto *AFD = dyn_cast_or_null(UseDC); if (AFD == nullptr) return false; - // Check if this is a function representing a defer. - if (auto *func = dyn_cast(AFD)) - if (func->isDeferBody() && deferMatchesEnclosingAccess(func)) - return isDirectToStorageAccess(func->getParent(), var, isAccessOnSelf); - // The property reference is for immediate class, not a derived class. if (AFD->getParent()->getSelfNominalTypeDecl() != var->getDeclContext()->getSelfNominalTypeDecl()) @@ -11813,16 +11813,6 @@ PrecedenceGroupDecl *InfixOperatorDecl::getPrecedenceGroup() const { nullptr); } -bool ValueDecl::isDeferBody() const { - if (auto fn = dyn_cast(this)) - return fn->isDeferBody(); - return false; -} - -bool FuncDecl::isDeferBody() const { - return getBaseIdentifier() == getASTContext().getIdentifier("$defer"); -} - bool FuncDecl::isPotentialIBActionTarget() const { return isInstanceMember() && getDeclContext()->getSelfClassDecl() && diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index ba22d9cd07bfd..5b27e80027901 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -366,7 +366,6 @@ SourceLoc DiscardStmt::getEndLoc() const { return SubExpr->getEndLoc(); } DeferStmt *DeferStmt::create(DeclContext *dc, SourceLoc deferLoc, Expr *body) { ASTContext &ctx = dc->getASTContext(); - auto params = ParameterList::createEmpty(ctx); auto name = ctx.getIdentifier("$defer"); auto varDecl = new (ctx) VarDecl(/*isStatic=*/false, VarDecl::Introducer::Let, diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index c6b72c5640e76..0d06bf5b67743 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1082,11 +1082,35 @@ ParserResult Parser::parseStmtDefer() { // As such, the body of the 'defer' is actually type checked within the // closure's DeclContext. ParserStatus Status; - ParserResult body = parseExprClosure(); - if (body.isNull()) - return nullptr; - auto *DS = DeferStmt::create(CurDeclContext, DeferLoc, body.get()); - Status |= body; + DeferStmt *DS = nullptr; + auto *paramList = ParameterList::create(Context, SourceLoc(), + {}, SourceLoc()); + auto *closure = new (Context) ClosureExpr(DeclAttributes(), + SourceRange(), + nullptr, paramList, SourceLoc(), + SourceLoc(), nullptr, SourceLoc(), + SourceLoc(), nullptr, + CurDeclContext); + { + ParseFunctionBody cc(*this, closure); + llvm::SaveAndRestore> T( + CurrentTokenHash, StableHasher::defaultHasher()); + + ParserResult body = + parseBraceItemList(diag::expected_lbrace_after_defer); + + if (body.isNull()) + return nullptr; + Status |= body; + + closure->setBody(body.get()); + closure->setIsDeferBody(); + + DS = DeferStmt::create(CurDeclContext->getParent(), DeferLoc, closure); + // Clone the current hasher and extract a Fingerprint. + StableHasher currentHash{*CurrentTokenHash}; + Fingerprint fp(std::move(currentHash)); + } return makeParserResult(Status, DS); } diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index 7713ae57f345b..cbfda39ecb108 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -1149,6 +1149,13 @@ void SILFunction::eraseAllBlocks() { BlockList.clear(); } +bool SILFunction::isDefer() const { + if (auto *dc = getDeclContext()) + if (auto *CE = dyn_cast_or_null(dc)) + return CE->isDeferBody(); + return false; +} + void SILFunction::setGenericEnvironment(GenericEnvironment *env) { setGenericEnvironment(env, ArrayRef(), env ? env->getForwardingSubstitutionMap() diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 37b5205224f8b..6ff93db2a15bf 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -135,12 +135,6 @@ DeclName SILGenModule::getMagicFunctionName(DeclContext *dc) { // If this is an accessor, use the name of the storage. if (auto accessor = dyn_cast(absFunc)) return accessor->getStorage()->getName(); - if (auto func = dyn_cast(absFunc)) { - // If this is a defer body, use the parent name. - if (func->isDeferBody()) { - return getMagicFunctionName(func->getParent()); - } - } return absFunc->getName(); } diff --git a/lib/SILOptimizer/Mandatory/FlowIsolation.cpp b/lib/SILOptimizer/Mandatory/FlowIsolation.cpp index 41bd158bf2070..4774e5582b76b 100644 --- a/lib/SILOptimizer/Mandatory/FlowIsolation.cpp +++ b/lib/SILOptimizer/Mandatory/FlowIsolation.cpp @@ -130,9 +130,9 @@ struct Info { static bool isWithinDeinit(SILFunction *fn) { auto *astFn = fn->getDeclContext()->getAsDecl(); - if (auto *funcDecl = dyn_cast(astFn)) - if (funcDecl->isDeferBody()) - astFn = funcDecl->getParent()->getAsDecl(); + if (auto *CE = dyn_cast(fn->getDeclContext())) + if (CE->isDeferBody()) + astFn = CE->getParent()->getAsDecl(); return isa(astFn); } @@ -597,8 +597,8 @@ void AnalysisInfo::analyze(const SILArgument *selfParam) { // Check if the callee is a function representing a defer block. if (SILFunction *callee = apply.getCalleeFunction()) { if (auto *dc = callee->getDeclContext()) { - if (auto *decl = dyn_cast_or_null(dc->getAsDecl())) { - if (decl->isDeferBody()) { + if (auto *CE = dyn_cast_or_null(dc)) { + if (CE->isDeferBody()) { // If we need to analyze the defer first, do so. if (!haveDeferInfo(callee)) { diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 13634185ec393..7df6adcbb7b91 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2580,6 +2580,11 @@ namespace { // stand in for that parameter or return type, allowing it to be inferred // from context. Type resultTy = [&] { + // Defer bodies always return void + if (closure->isDeferBody()) { + return (Type) TupleType::getEmpty(CS.getASTContext()); + } + if (closure->hasExplicitResultType()) { if (auto declaredTy = closure->getExplicitResultType()) { return declaredTy; @@ -2639,6 +2644,11 @@ namespace { extInfo = extInfo.withSendable(); } + // Defer bodies never escape or throw + if (closure->isDeferBody()) { + extInfo = extInfo.withNoEscape().withThrows(false, Type()); + } + auto *fnTy = FunctionType::get(closureParams, resultTy, extInfo); return CS.replaceInferableTypesWithTypeVars( fnTy, CS.getConstraintLocator(closure))->castTo(); diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index 8981d5004397f..b4797d00b2255 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -1119,7 +1119,7 @@ class SyntacticElementConstraintGenerator } } - if (context.isSingleExpressionClosure(cs)) { + if (context.isSingleExpressionClosure(cs) && !context.getAsClosureExpr().get()->isDeferBody()) { // Generate constraints for the capture list first. // // TODO: This should be a conjunction connected to diff --git a/lib/Sema/PreCheckTarget.cpp b/lib/Sema/PreCheckTarget.cpp index 2326a7c621682..b1d5e2bde0942 100644 --- a/lib/Sema/PreCheckTarget.cpp +++ b/lib/Sema/PreCheckTarget.cpp @@ -412,8 +412,8 @@ static bool isValidForwardReference(ValueDecl *D, DeclContext *DC, // If we're inside of a 'defer' context, walk up to the parent // and check again. We don't want 'defer' bodies to forward // reference bindings in the immediate outer scope. - } while (isa(DC) && - cast(DC)->isDeferBody() && + } while (isa(DC) && + cast(DC)->isDeferBody() && (DC = DC->getParent())); } return true; diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index af34b013ca4b8..956cfd96e93e6 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -5658,9 +5658,6 @@ void TypeChecker::addImplicitDynamicAttribute(Decl *D) { return; if (auto *FD = dyn_cast(D)) { - // Don't add dynamic to defer bodies. - if (FD->isDeferBody()) - return; // Don't add dynamic to functions with a cdecl. if (FD->getAttrs().hasAttribute()) return; diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index f46a2c25eb65c..23ec52dd7fb66 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -147,11 +147,7 @@ static void computeExportContextBits(ASTContext &Ctx, Decl *D, bool *spi, D->isAvailableAsSPI()) *spi = true; - // Defer bodies are desugared to an implicit closure expression. We need to - // dilute the meaning of "implicit" to make sure we're still checking - // availability inside of defer statements. - const auto isDeferBody = isa(D) && cast(D)->isDeferBody(); - if (D->isImplicit() && !isDeferBody) + if (D->isImplicit()) *implicit = true; if (auto *PBD = dyn_cast(D)) { @@ -265,12 +261,10 @@ static bool shouldAllowReferenceToUnavailableInSwiftDeclaration( /// checking. static const DeclContext * getInnermostDeclContextForNoAsync(const DeclContext *DC) { - if (auto *D = DC->getAsDecl()) { - if (auto *FD = dyn_cast(D)) { - if (FD->isDeferBody()) - // If this is a defer body, we should delegate to its parent. - return getInnermostDeclContextForNoAsync(DC->getParent()); - } + if (auto *CE = dyn_cast(DC)) { + if (CE->isDeferBody()) + // If this is a defer body, we should delegate to its parent. + return getInnermostDeclContextForNoAsync(DC->getParent()); } return DC; } diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index be0054a709067..4d9b0af74424d 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -793,14 +793,6 @@ static bool shouldCaptureIsolationInLocalFunc(AbstractFunctionDecl *AFD, // (in which case it's needed for executor switching) or if we're in the // mode that forces an executor check in all synchronous functions. But // it's a simpler rule if we just do it unconditionally. - - // However, don't do it for the implicit functions that represent defer - // bodies, where it is both unnecessary and likely to lead to bad diagnostics. - // We already suppress the executor check in defer bodies. - if (auto FD = dyn_cast(AFD)) - if (FD->isDeferBody()) - return false; - return true; } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 93e2ae1ca154a..1500def361b99 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -2020,22 +2020,13 @@ static bool checkedByFlowIsolation(DeclContext const *refCxt, // NOTE: once flow-isolation can analyze calls to arbitrary local // functions, we should be using isActorInitOrDeInitContext instead // of this ugly loop. - AbstractFunctionDecl const* fnDecl = nullptr; - while (true) { - fnDecl = dyn_cast_or_null(refCxt->getAsDecl()); - if (!fnDecl) - break; - + while (isa(refCxt) && dyn_cast(refCxt)->isDeferBody()) { // go up one level if this context is a defer. - if (auto *d = dyn_cast(fnDecl)) { - if (d->isDeferBody()) { - refCxt = refCxt->getParent(); - continue; - } - } - break; + refCxt = refCxt->getParent(); } + AbstractFunctionDecl const* fnDecl = dyn_cast_or_null(refCxt->getAsDecl()); + if (memberAccessHasSpecialPermissionInSwift5(refCxt, baseActor, member, memberLoc, useKind)) return true; // then permit it now. @@ -2340,10 +2331,16 @@ static bool safeToDropGlobalActor( static FuncDecl *findAnnotatableFunction(DeclContext *dc) { auto fn = dyn_cast(dc); - if (!fn) return nullptr; - if (fn->isDeferBody()) - return findAnnotatableFunction(fn->getDeclContext()); - return fn; + if (fn) + return fn; + + // If 'dc' isn't a function, it might be a defer body--in that case pass to + // the parent DC. + auto CE = dyn_cast(dc); + if (!CE) return nullptr; + if (CE->isDeferBody()) + return findAnnotatableFunction(dc->getParent()); + return nullptr; } namespace { @@ -3684,8 +3681,8 @@ namespace { // Look through defers. // FIXME: should this be covered automatically by the logic below? - if (auto func = dyn_cast(dc)) - if (func->isDeferBody()) + if (auto CE = dyn_cast(dc)) + if (CE->isDeferBody()) continue; if (auto func = dyn_cast(dc)) { diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 6ecc1023c0354..5dbc031cf2f6a 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2545,12 +2545,6 @@ InterfaceTypeRequest::evaluate(Evaluator &eval, ValueDecl *D) const { infoBuilder = infoBuilder.withSendable(AFD->isSendable()); // 'throws' only applies to the innermost function. infoBuilder = infoBuilder.withThrows(AFD->hasThrows(), thrownTy); - // Defer bodies must not escape. - if (auto fd = dyn_cast(D)) { - infoBuilder = infoBuilder.withNoEscape(fd->isDeferBody()); - if (fd->hasSendingResult()) - infoBuilder = infoBuilder.withSendingResult(); - } // Lifetime dependencies only apply to the outer function type. if (!hasSelf && lifetimeDependenceInfo.has_value()) { diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 6efd662c24e12..6a97d0d351236 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -2970,6 +2970,20 @@ class Context { return isa(closure); } + bool isDeferBody() const { + if (!Function) + return false; + + if (ErrorHandlingIgnoresFunction) + return false; + + auto closure = dyn_cast_or_null(Function->getAbstractClosureExpr()); + if (!closure) + return false; + + return closure->isDeferBody(); + } + static Context forTopLevelCode(TopLevelCodeDecl *D) { // Top-level code implicitly handles errors. return Context(/*handlesErrors=*/true, @@ -3276,6 +3290,12 @@ class Context { return; } + if (isDeferBody()) { + Diags.diagnose(E.getStartLoc(), diag::throwing_op_in_defer, + getEffectSourceName(reason)); + return; + } + if (hasPolymorphicEffect(EffectKind::Throws)) { diagnoseThrowInLegalContext(Diags, E, isTryCovered, reason, diag::throwing_call_in_rethrows_function, @@ -3316,6 +3336,11 @@ class Context { return; } + if (isDeferBody()) { + Diags.diagnose(S->getStartLoc(), diag::throw_in_defer_body); + return; + } + if (hasPolymorphicEffect(EffectKind::Throws)) { Diags.diagnose(S->getStartLoc(), diag::throw_in_rethrows_function); return; diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 61adf6cab52c0..5debe675b298b 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -545,8 +545,8 @@ static void tryDiagnoseUnnecessaryCastOverOptionSet(ASTContext &Ctx, /// Whether the given enclosing context is a "defer" body. static bool isDefer(DeclContext *dc) { - if (auto *func = dyn_cast(dc)) - return func->isDeferBody(); + if (auto *CE = dyn_cast(dc)) + return CE->isDeferBody(); return false; } @@ -1365,6 +1365,10 @@ class StmtChecker : public StmtVisitor { Stmt *visitDeferStmt(DeferStmt *DS) { TypeChecker::typeCheckDecl(DS->getTempDecl()); + if (DS->getBody()->getType()->hasError()) { + return nullptr; + } + Expr *theCall = DS->getCallExpr(); if (DS->getBody()->getType()->getAs()->isAsync()) { theCall = AwaitExpr::createImplicit(Ctx, SourceLoc(), theCall); @@ -2764,6 +2768,13 @@ static void addImplicitReturnIfNeeded(BraceStmt *body, DeclContext *dc) { if (isa(SemanticExpr)) return; } + + // Don't add implicit return to defer bodies + if (auto *CE = dyn_cast(dc)) { + if (CE->isDeferBody()) { + return; + } + } makeResult(E); } } diff --git a/test/expr/unary/async_await.swift b/test/expr/unary/async_await.swift index 98bc9ba5ffe8d..08c4084b25cb0 100644 --- a/test/expr/unary/async_await.swift +++ b/test/expr/unary/async_await.swift @@ -19,7 +19,7 @@ func test2( defaulted: Int = await getInt() // expected-error{{'async' call cannot occur in a default argument}} ) async { defer { - _ = await getInt() // expected-error{{'async' call cannot occur in a defer body}} + _ = await getInt() // OK } print("foo") } diff --git a/test/stmt/defer.swift b/test/stmt/defer.swift index ac113a4b1baaa..709bc248fd44f 100644 --- a/test/stmt/defer.swift +++ b/test/stmt/defer.swift @@ -125,7 +125,7 @@ func throwingFunctionCalledInDefer() throws { class SomeDerivedClass: SomeTestClass { override init() { defer { - super.init() // expected-error {{initializer chaining ('super.init') cannot be nested in another expression}} + super.init() // expected-error {{initializer chaining ('super.init') cannot be nested in another statement}} } } } @@ -161,3 +161,14 @@ func badForwardReference() { let z2 = 0 } + +struct MutatingSelf { + var x: Int + mutating func f() { + defer { + _ = x // ok + } + + _ = x + } +} From 90db49d7b1edd4f12046d0d6c046b90fad57494f Mon Sep 17 00:00:00 2001 From: Freddy Kellison-Linn Date: Sat, 30 Aug 2025 12:44:07 -0400 Subject: [PATCH 3/5] Revert to main --- include/swift/AST/ASTBridging.h | 8 ++-- include/swift/AST/Decl.h | 3 ++ include/swift/AST/DiagnosticsSema.def | 5 --- include/swift/AST/Expr.h | 16 +------- include/swift/AST/Stmt.h | 17 +++----- include/swift/SIL/SILFunction.h | 7 +++- lib/AST/ASTScopeCreation.cpp | 2 +- lib/AST/ASTVerifier.cpp | 6 +-- lib/AST/Bridging/StmtBridging.cpp | 9 ++--- lib/AST/Decl.cpp | 22 +++++++--- lib/AST/Stmt.cpp | 27 ++++++------- lib/ASTGen/Sources/ASTGen/Stmts.swift | 7 ++-- lib/IDE/SourceEntityWalker.cpp | 3 +- lib/IDE/SyntaxModel.cpp | 10 +++-- lib/Parse/ParseStmt.cpp | 32 ++++++--------- lib/SIL/IR/SILFunction.cpp | 7 ---- lib/SILGen/SILGenFunction.cpp | 6 +++ lib/SILGen/SILGenStmt.cpp | 7 ++-- lib/SILOptimizer/Mandatory/FlowIsolation.cpp | 10 ++--- lib/Sema/CSGen.cpp | 10 ----- lib/Sema/CSSyntacticElement.cpp | 2 +- lib/Sema/PreCheckTarget.cpp | 4 +- lib/Sema/TypeCheckAttr.cpp | 3 ++ lib/Sema/TypeCheckAvailability.cpp | 16 +++++--- lib/Sema/TypeCheckCaptures.cpp | 8 ++++ lib/Sema/TypeCheckConcurrency.cpp | 35 ++++++++-------- lib/Sema/TypeCheckDecl.cpp | 6 +++ lib/Sema/TypeCheckEffects.cpp | 42 +++++++------------- lib/Sema/TypeCheckStmt.cpp | 18 +-------- test/expr/unary/async_await.swift | 2 +- test/stmt/defer.swift | 13 +----- 31 files changed, 162 insertions(+), 201 deletions(-) diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index e57424d35b8f7..99f9d05e0eef3 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -2349,14 +2349,12 @@ BridgedContinueStmt BridgedContinueStmt_createParsed( BridgedDeclContext cDeclContext, swift::SourceLoc loc, swift::Identifier targetName, swift::SourceLoc targetLoc); -SWIFT_NAME("BridgedDeferStmt.createParsed(_:deferLoc:body:)") +SWIFT_NAME("BridgedDeferStmt.createParsed(_:deferLoc:)") BridgedDeferStmt BridgedDeferStmt_createParsed(BridgedDeclContext cDeclContext, - swift::SourceLoc deferLoc, - BridgedExpr body); + swift::SourceLoc deferLoc); SWIFT_NAME("getter:BridgedDeferStmt.tempDecl(self:)") -BridgedPatternBindingDecl -BridgedDeferStmt_getTempDecl(BridgedDeferStmt bridged); +BridgedFuncDecl BridgedDeferStmt_getTempDecl(BridgedDeferStmt bridged); SWIFT_NAME("BridgedDiscardStmt.createParsed(_:discardLoc:subExpr:)") BridgedDiscardStmt BridgedDiscardStmt_createParsed(BridgedASTContext cContext, diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 3da70b9f95c60..51376bbceaec4 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -8570,6 +8570,9 @@ class FuncDecl : public AbstractFunctionDecl { return false; } + /// True if the function is a defer body. + bool isDeferBody() const; + /// Perform basic checking to determine whether the @IBAction or /// @IBSegueAction attribute can be applied to this function. bool isPotentialIBActionTarget() const; diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index dc56c958a5d55..d7ffcbd2cdd0c 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5532,11 +5532,6 @@ ERROR(throwing_op_in_illegal_context,none, ERROR(throw_in_illegal_context,none, "errors cannot be thrown out of " EFFECTS_CONTEXT_KIND "0", (unsigned)) -ERROR(throwing_op_in_defer,none, - "%0 can throw, but errors cannot be thrown out of a defer body", (StringRef)) -ERROR(throw_in_defer_body,none, - "errors cannot be thrown out of a defer body", ()) - ERROR(throwing_operator_without_try,none, "operator can throw but expression is not marked with 'try'", ()) ERROR(throwing_interpolation_without_try,none, diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 191337522643f..74f58a18c406a 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -267,7 +267,7 @@ class alignas(8) Expr : public ASTAllocated { Kind : 2 ); - SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1+1+1, /// True if closure parameters were synthesized from anonymous closure /// variables. HasAnonymousClosureVars : 1, @@ -302,10 +302,7 @@ class alignas(8) Expr : public ASTAllocated { /// Whether this closure was type-checked as an argument to a macro. This /// is only populated after type-checking, and only exists for diagnostic /// logic. Do not add more uses of this. - IsMacroArgument : 1, - - /// Whether this closure is serving as the body of a `defer` statement. - IsDeferBody : 1 + IsMacroArgument : 1 ); SWIFT_INLINE_BITFIELD_FULL(BindOptionalExpr, Expr, 16, @@ -4532,15 +4529,6 @@ class ClosureExpr : public AbstractClosureExpr { ExplicitResultTypeAndBodyState.setInt(v); } - /// Whether this is a closure serving as the body of a `defer` statement. - bool isDeferBody() const { - return Bits.ClosureExpr.IsDeferBody; - } - - void setIsDeferBody(bool value = true) { - Bits.ClosureExpr.IsDeferBody = value; - } - static bool classof(const Expr *E) { return E->getKind() == ExprKind::Closure; } diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index f2ed224970574..c8f9d4bb3e76d 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -394,36 +394,31 @@ class ThenStmt : public Stmt { class DeferStmt : public Stmt { SourceLoc DeferLoc; - /// This is the bound temp var for the defer closure. - PatternBindingDecl *tempDecl; - - /// This is the expr that makes up the body of the defer stmt - Expr *body; + /// This is the bound temp function. + FuncDecl *tempDecl; /// This is the invocation of the closure, which is to be emitted on any error /// paths. Expr *callExpr; DeferStmt(SourceLoc DeferLoc, - PatternBindingDecl *tempDecl, Expr *body, - Expr *callExpr) + FuncDecl *tempDecl, Expr *callExpr) : Stmt(StmtKind::Defer, /*implicit*/false), - DeferLoc(DeferLoc), tempDecl(tempDecl), body(body), + DeferLoc(DeferLoc), tempDecl(tempDecl), callExpr(callExpr) {} public: /// Create a 'defer' statement. This automatically creates the "temp decl" and /// the call expression. It's the caller's responsibility to populate the /// body of the func decl. - static DeferStmt *create(DeclContext *dc, SourceLoc deferLoc, Expr *body); + static DeferStmt *create(DeclContext *dc, SourceLoc deferLoc); SourceLoc getDeferLoc() const { return DeferLoc; } SourceLoc getStartLoc() const { return DeferLoc; } SourceLoc getEndLoc() const; - PatternBindingDecl *getTempDecl() const { return tempDecl; } - Expr *getBody() const { return body; } + FuncDecl *getTempDecl() const { return tempDecl; } Expr *getCallExpr() const { return callExpr; } void setCallExpr(Expr *E) { callExpr = E; } diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 7e753a5dd422b..c1ed6a24b16ad 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -1397,7 +1397,12 @@ class SILFunction /// deserialized without a DeclContext. This means that this is guaranteed to /// be correct for SILFunctions in Raw SIL that were not deserialized as /// canonical. Thus one can use it for diagnostics. - bool isDefer() const; + bool isDefer() const { + if (auto *dc = getDeclContext()) + if (auto *decl = dyn_cast_or_null(dc->getAsDecl())) + return decl->isDeferBody(); + return false; + } /// Returns true if this function belongs to a declaration that /// has `@_alwaysEmitIntoClient` attribute. diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 0963149bb8c10..d95675b40b24b 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -510,7 +510,7 @@ class NodeAdder ASTScopeImpl *visitDeferStmt(DeferStmt *ds, ASTScopeImpl *p, ScopeCreator &scopeCreator) { - visitClosureExpr(dyn_cast(ds->getBody()), p, scopeCreator); + visitFuncDecl(ds->getTempDecl(), p, scopeCreator); return p; } diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index e2b8d38ab6a99..039f5db5bd67b 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -1121,10 +1121,8 @@ class Verifier : public ASTWalker { verifyCheckedBase(S); } void verifyChecked(DeferStmt *S) { - auto FT = S->getBody()->getType()->castTo(); - // TODO: Making these into PBDs made them get marked as escaping... are - // there downstream effects? - // assert(FT->isNoEscape() && "Defer statements must not escape"); + auto FT = S->getTempDecl()->getInterfaceType()->castTo(); + assert(FT->isNoEscape() && "Defer statements must not escape"); (void)FT; verifyCheckedBase(S); } diff --git a/lib/AST/Bridging/StmtBridging.cpp b/lib/AST/Bridging/StmtBridging.cpp index a0c77630ab540..e8c11bb98d0cf 100644 --- a/lib/AST/Bridging/StmtBridging.cpp +++ b/lib/AST/Bridging/StmtBridging.cpp @@ -148,14 +148,11 @@ BridgedContinueStmt_createParsed(BridgedDeclContext cDeclContext, SourceLoc loc, } BridgedDeferStmt BridgedDeferStmt_createParsed(BridgedDeclContext cDeclContext, - SourceLoc deferLoc, - BridgedExpr body) { - return DeferStmt::create(cDeclContext.unbridged(), deferLoc, - body.unbridged()); + SourceLoc deferLoc) { + return DeferStmt::create(cDeclContext.unbridged(), deferLoc); } -BridgedPatternBindingDecl -BridgedDeferStmt_getTempDecl(BridgedDeferStmt bridged) { +BridgedFuncDecl BridgedDeferStmt_getTempDecl(BridgedDeferStmt bridged) { return bridged.unbridged()->getTempDecl(); } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index fc8ed00e3e83c..02293df2a92c7 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2950,7 +2950,7 @@ static bool isPolymorphic(const AbstractStorageDecl *storage) { /// In Swift 5 and earlier, this was not true, meaning that property observers, /// etc, would be invoked in initializers or deinitializers if a property access /// happens within a defer, but not when outside the defer. -static bool deferMatchesEnclosingAccess(const ClosureExpr *defer) { +static bool deferMatchesEnclosingAccess(const FuncDecl *defer) { assert(defer->isDeferBody()); // In Swift 6+, then yes. @@ -2995,15 +2995,15 @@ static bool isDirectToStorageAccess(const DeclContext *UseDC, if (!var->hasStorage()) return false; - // Check if this is a closure representing a defer. - if (auto *CE = dyn_cast(UseDC)) - if (CE->isDeferBody() && deferMatchesEnclosingAccess(CE)) - return isDirectToStorageAccess(CE->getParent(), var, isAccessOnSelf); - auto *AFD = dyn_cast_or_null(UseDC); if (AFD == nullptr) return false; + // Check if this is a function representing a defer. + if (auto *func = dyn_cast(AFD)) + if (func->isDeferBody() && deferMatchesEnclosingAccess(func)) + return isDirectToStorageAccess(func->getParent(), var, isAccessOnSelf); + // The property reference is for immediate class, not a derived class. if (AFD->getParent()->getSelfNominalTypeDecl() != var->getDeclContext()->getSelfNominalTypeDecl()) @@ -11813,6 +11813,16 @@ PrecedenceGroupDecl *InfixOperatorDecl::getPrecedenceGroup() const { nullptr); } +bool ValueDecl::isDeferBody() const { + if (auto fn = dyn_cast(this)) + return fn->isDeferBody(); + return false; +} + +bool FuncDecl::isDeferBody() const { + return getBaseIdentifier() == getASTContext().getIdentifier("$defer"); +} + bool FuncDecl::isPotentialIBActionTarget() const { return isInstanceMember() && getDeclContext()->getSelfClassDecl() && diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 5b27e80027901..f1460e2383ed6 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -363,36 +363,35 @@ SourceLoc ThrowStmt::getEndLoc() const { return SubExpr->getEndLoc(); } SourceLoc DiscardStmt::getEndLoc() const { return SubExpr->getEndLoc(); } -DeferStmt *DeferStmt::create(DeclContext *dc, SourceLoc deferLoc, Expr *body) { +DeferStmt *DeferStmt::create(DeclContext *dc, SourceLoc deferLoc) { ASTContext &ctx = dc->getASTContext(); - auto name = ctx.getIdentifier("$defer"); - - auto varDecl = new (ctx) VarDecl(/*isStatic=*/false, VarDecl::Introducer::Let, - /*NameLoc=*/deferLoc, name, dc); - - auto *pat = NamedPattern::createImplicit(ctx, varDecl, Type()); - - auto *pbd = PatternBindingDecl::createImplicit( - ctx, StaticSpellingKind::None, pat, body, dc); + auto params = ParameterList::createEmpty(ctx); + DeclName name(ctx, ctx.getIdentifier("$defer"), params); + auto *const funcDecl = FuncDecl::createImplicit( + ctx, StaticSpellingKind::None, name, /*NameLoc=*/deferLoc, + /*Async=*/false, + /*Throws=*/false, + /*ThrownType=*/Type(), + /*GenericParams=*/nullptr, params, TupleType::getEmpty(ctx), dc); // Form the call, which will be emitted on any path that needs to run the // code. auto DRE = new (ctx) - DeclRefExpr(varDecl, DeclNameLoc(deferLoc), + DeclRefExpr(funcDecl, DeclNameLoc(deferLoc), /*Implicit*/ true, AccessSemantics::DirectToStorage); auto call = CallExpr::createImplicitEmpty(ctx, DRE); - return new (ctx) DeferStmt(deferLoc, pbd, body, call); + return new (ctx) DeferStmt(deferLoc, funcDecl, call); } SourceLoc DeferStmt::getEndLoc() const { - return body->getEndLoc(); + return tempDecl->getBody()->getEndLoc(); } /// Dig the original user's body of the defer out for AST fidelity. BraceStmt *DeferStmt::getBodyAsWritten() const { - return dyn_cast(body)->getBody(); + return tempDecl->getBody(); } bool LabeledStmt::isPossibleContinueTarget() const { diff --git a/lib/ASTGen/Sources/ASTGen/Stmts.swift b/lib/ASTGen/Sources/ASTGen/Stmts.swift index b7f6c98b6354a..c97d8d558e3c4 100644 --- a/lib/ASTGen/Sources/ASTGen/Stmts.swift +++ b/lib/ASTGen/Sources/ASTGen/Stmts.swift @@ -272,12 +272,13 @@ extension ASTGenVisitor { func generate(deferStmt node: DeferStmtSyntax) -> BridgedDeferStmt { let deferLoc = self.generateSourceLoc(node.deferKeyword) - let closure = self.generate(closureExpr: .init(statements: node.body.statements)) let stmt = BridgedDeferStmt.createParsed( self.declContext, - deferLoc: deferLoc, - body: closure + deferLoc: deferLoc ) + self.withDeclContext(stmt.tempDecl.asDeclContext) { + stmt.tempDecl.setParsedBody(self.generate(codeBlock: node.body)) + } return stmt } diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index e020fcdc94ec6..f11281be71863 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -274,7 +274,8 @@ ASTWalker::PreWalkResult SemaAnnotator::walkToStmtPre(Stmt *S) { if (auto *DeferS = dyn_cast(S)) { // Since 'DeferStmt::getTempDecl()' is marked as implicit, we manually // walk into the body. - if (auto *Body = DeferS->getBody()) { + if (auto *FD = DeferS->getTempDecl()) { + auto *Body = FD->getBody(); if (!Body) return Action::Stop(); diff --git a/lib/IDE/SyntaxModel.cpp b/lib/IDE/SyntaxModel.cpp index ae218f8f53d1b..8fbdddc7d45b0 100644 --- a/lib/IDE/SyntaxModel.cpp +++ b/lib/IDE/SyntaxModel.cpp @@ -812,10 +812,12 @@ ASTWalker::PreWalkResult ModelASTWalker::walkToStmtPre(Stmt *S) { } else if (auto *DeferS = dyn_cast(S)) { // Since 'DeferStmt::getTempDecl()' is marked as implicit, we manually walk // into the body. - if (auto *Body = DeferS->getBody()) { - auto *RetS = Body->walk(*this); - assert(RetS == Body); - (void)RetS; + if (auto *FD = DeferS->getTempDecl()) { + if (auto *Body = FD->getBody()) { + auto *RetS = Body->walk(*this); + assert(RetS == Body); + (void)RetS; + } } // Already walked children. return Action::SkipChildren(DeferS); diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 0d06bf5b67743..35208bdca0393 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1081,36 +1081,28 @@ ParserResult Parser::parseStmtDefer() { // // As such, the body of the 'defer' is actually type checked within the // closure's DeclContext. + auto *DS = DeferStmt::create(CurDeclContext, DeferLoc); ParserStatus Status; - DeferStmt *DS = nullptr; - auto *paramList = ParameterList::create(Context, SourceLoc(), - {}, SourceLoc()); - auto *closure = new (Context) ClosureExpr(DeclAttributes(), - SourceRange(), - nullptr, paramList, SourceLoc(), - SourceLoc(), nullptr, SourceLoc(), - SourceLoc(), nullptr, - CurDeclContext); { - ParseFunctionBody cc(*this, closure); + auto *tempDecl = DS->getTempDecl(); + // Change the DeclContext for any variables declared in the defer to be within + // the defer closure. + ParseFunctionBody cc(*this, tempDecl); llvm::SaveAndRestore> T( - CurrentTokenHash, StableHasher::defaultHasher()); + CurrentTokenHash, StableHasher::defaultHasher()); - ParserResult body = - parseBraceItemList(diag::expected_lbrace_after_defer); - - if (body.isNull()) + ParserResult Body = + parseBraceItemList(diag::expected_lbrace_after_defer); + if (Body.isNull()) return nullptr; - Status |= body; - - closure->setBody(body.get()); - closure->setIsDeferBody(); + Status |= Body; - DS = DeferStmt::create(CurDeclContext->getParent(), DeferLoc, closure); // Clone the current hasher and extract a Fingerprint. StableHasher currentHash{*CurrentTokenHash}; Fingerprint fp(std::move(currentHash)); + tempDecl->setBodyParsed(Body.get(), fp); } + return makeParserResult(Status, DS); } diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index cbfda39ecb108..7713ae57f345b 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -1149,13 +1149,6 @@ void SILFunction::eraseAllBlocks() { BlockList.clear(); } -bool SILFunction::isDefer() const { - if (auto *dc = getDeclContext()) - if (auto *CE = dyn_cast_or_null(dc)) - return CE->isDeferBody(); - return false; -} - void SILFunction::setGenericEnvironment(GenericEnvironment *env) { setGenericEnvironment(env, ArrayRef(), env ? env->getForwardingSubstitutionMap() diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 6ff93db2a15bf..37b5205224f8b 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -135,6 +135,12 @@ DeclName SILGenModule::getMagicFunctionName(DeclContext *dc) { // If this is an accessor, use the name of the storage. if (auto accessor = dyn_cast(absFunc)) return accessor->getStorage()->getName(); + if (auto func = dyn_cast(absFunc)) { + // If this is a defer body, use the parent name. + if (func->isDeferBody()) { + return getMagicFunctionName(func->getParent()); + } + } return absFunc->getName(); } diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 8d5f8f64a35ba..173cecfb7cd96 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -928,14 +928,13 @@ void StmtEmitter::visitDeferStmt(DeferStmt *S) { // Emit the closure for the defer, along with its binding. // If the defer is at the top-level code, insert 'mark_escape_inst' // to the top-level code to check initialization of any captured globals. - PatternBindingDecl *deferDecl = S->getTempDecl(); - ClosureExpr *body = dyn_cast(S->getBody()); + FuncDecl *deferDecl = S->getTempDecl(); auto *Ctx = deferDecl->getDeclContext(); if (isa(Ctx) && SGF.isEmittingTopLevelCode()) { - auto Captures = body->getCaptureInfo(); + auto Captures = deferDecl->getCaptureInfo(); SGF.emitMarkFunctionEscapeForTopLevelCodeGlobals(S, std::move(Captures)); } - SGF.visitPatternBindingDecl(deferDecl); + SGF.visitFuncDecl(deferDecl); // Register a cleanup to invoke the closure on any exit paths. SGF.Cleanups.pushCleanup(S->getDeferLoc(), S->getCallExpr()); diff --git a/lib/SILOptimizer/Mandatory/FlowIsolation.cpp b/lib/SILOptimizer/Mandatory/FlowIsolation.cpp index 4774e5582b76b..41bd158bf2070 100644 --- a/lib/SILOptimizer/Mandatory/FlowIsolation.cpp +++ b/lib/SILOptimizer/Mandatory/FlowIsolation.cpp @@ -130,9 +130,9 @@ struct Info { static bool isWithinDeinit(SILFunction *fn) { auto *astFn = fn->getDeclContext()->getAsDecl(); - if (auto *CE = dyn_cast(fn->getDeclContext())) - if (CE->isDeferBody()) - astFn = CE->getParent()->getAsDecl(); + if (auto *funcDecl = dyn_cast(astFn)) + if (funcDecl->isDeferBody()) + astFn = funcDecl->getParent()->getAsDecl(); return isa(astFn); } @@ -597,8 +597,8 @@ void AnalysisInfo::analyze(const SILArgument *selfParam) { // Check if the callee is a function representing a defer block. if (SILFunction *callee = apply.getCalleeFunction()) { if (auto *dc = callee->getDeclContext()) { - if (auto *CE = dyn_cast_or_null(dc)) { - if (CE->isDeferBody()) { + if (auto *decl = dyn_cast_or_null(dc->getAsDecl())) { + if (decl->isDeferBody()) { // If we need to analyze the defer first, do so. if (!haveDeferInfo(callee)) { diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 7df6adcbb7b91..13634185ec393 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2580,11 +2580,6 @@ namespace { // stand in for that parameter or return type, allowing it to be inferred // from context. Type resultTy = [&] { - // Defer bodies always return void - if (closure->isDeferBody()) { - return (Type) TupleType::getEmpty(CS.getASTContext()); - } - if (closure->hasExplicitResultType()) { if (auto declaredTy = closure->getExplicitResultType()) { return declaredTy; @@ -2644,11 +2639,6 @@ namespace { extInfo = extInfo.withSendable(); } - // Defer bodies never escape or throw - if (closure->isDeferBody()) { - extInfo = extInfo.withNoEscape().withThrows(false, Type()); - } - auto *fnTy = FunctionType::get(closureParams, resultTy, extInfo); return CS.replaceInferableTypesWithTypeVars( fnTy, CS.getConstraintLocator(closure))->castTo(); diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index b4797d00b2255..8981d5004397f 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -1119,7 +1119,7 @@ class SyntacticElementConstraintGenerator } } - if (context.isSingleExpressionClosure(cs) && !context.getAsClosureExpr().get()->isDeferBody()) { + if (context.isSingleExpressionClosure(cs)) { // Generate constraints for the capture list first. // // TODO: This should be a conjunction connected to diff --git a/lib/Sema/PreCheckTarget.cpp b/lib/Sema/PreCheckTarget.cpp index b1d5e2bde0942..2326a7c621682 100644 --- a/lib/Sema/PreCheckTarget.cpp +++ b/lib/Sema/PreCheckTarget.cpp @@ -412,8 +412,8 @@ static bool isValidForwardReference(ValueDecl *D, DeclContext *DC, // If we're inside of a 'defer' context, walk up to the parent // and check again. We don't want 'defer' bodies to forward // reference bindings in the immediate outer scope. - } while (isa(DC) && - cast(DC)->isDeferBody() && + } while (isa(DC) && + cast(DC)->isDeferBody() && (DC = DC->getParent())); } return true; diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 956cfd96e93e6..af34b013ca4b8 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -5658,6 +5658,9 @@ void TypeChecker::addImplicitDynamicAttribute(Decl *D) { return; if (auto *FD = dyn_cast(D)) { + // Don't add dynamic to defer bodies. + if (FD->isDeferBody()) + return; // Don't add dynamic to functions with a cdecl. if (FD->getAttrs().hasAttribute()) return; diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 23ec52dd7fb66..f46a2c25eb65c 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -147,7 +147,11 @@ static void computeExportContextBits(ASTContext &Ctx, Decl *D, bool *spi, D->isAvailableAsSPI()) *spi = true; - if (D->isImplicit()) + // Defer bodies are desugared to an implicit closure expression. We need to + // dilute the meaning of "implicit" to make sure we're still checking + // availability inside of defer statements. + const auto isDeferBody = isa(D) && cast(D)->isDeferBody(); + if (D->isImplicit() && !isDeferBody) *implicit = true; if (auto *PBD = dyn_cast(D)) { @@ -261,10 +265,12 @@ static bool shouldAllowReferenceToUnavailableInSwiftDeclaration( /// checking. static const DeclContext * getInnermostDeclContextForNoAsync(const DeclContext *DC) { - if (auto *CE = dyn_cast(DC)) { - if (CE->isDeferBody()) - // If this is a defer body, we should delegate to its parent. - return getInnermostDeclContextForNoAsync(DC->getParent()); + if (auto *D = DC->getAsDecl()) { + if (auto *FD = dyn_cast(D)) { + if (FD->isDeferBody()) + // If this is a defer body, we should delegate to its parent. + return getInnermostDeclContextForNoAsync(DC->getParent()); + } } return DC; } diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index 4d9b0af74424d..be0054a709067 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -793,6 +793,14 @@ static bool shouldCaptureIsolationInLocalFunc(AbstractFunctionDecl *AFD, // (in which case it's needed for executor switching) or if we're in the // mode that forces an executor check in all synchronous functions. But // it's a simpler rule if we just do it unconditionally. + + // However, don't do it for the implicit functions that represent defer + // bodies, where it is both unnecessary and likely to lead to bad diagnostics. + // We already suppress the executor check in defer bodies. + if (auto FD = dyn_cast(AFD)) + if (FD->isDeferBody()) + return false; + return true; } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 1500def361b99..93e2ae1ca154a 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -2020,13 +2020,22 @@ static bool checkedByFlowIsolation(DeclContext const *refCxt, // NOTE: once flow-isolation can analyze calls to arbitrary local // functions, we should be using isActorInitOrDeInitContext instead // of this ugly loop. - while (isa(refCxt) && dyn_cast(refCxt)->isDeferBody()) { + AbstractFunctionDecl const* fnDecl = nullptr; + while (true) { + fnDecl = dyn_cast_or_null(refCxt->getAsDecl()); + if (!fnDecl) + break; + // go up one level if this context is a defer. - refCxt = refCxt->getParent(); + if (auto *d = dyn_cast(fnDecl)) { + if (d->isDeferBody()) { + refCxt = refCxt->getParent(); + continue; + } + } + break; } - AbstractFunctionDecl const* fnDecl = dyn_cast_or_null(refCxt->getAsDecl()); - if (memberAccessHasSpecialPermissionInSwift5(refCxt, baseActor, member, memberLoc, useKind)) return true; // then permit it now. @@ -2331,16 +2340,10 @@ static bool safeToDropGlobalActor( static FuncDecl *findAnnotatableFunction(DeclContext *dc) { auto fn = dyn_cast(dc); - if (fn) - return fn; - - // If 'dc' isn't a function, it might be a defer body--in that case pass to - // the parent DC. - auto CE = dyn_cast(dc); - if (!CE) return nullptr; - if (CE->isDeferBody()) - return findAnnotatableFunction(dc->getParent()); - return nullptr; + if (!fn) return nullptr; + if (fn->isDeferBody()) + return findAnnotatableFunction(fn->getDeclContext()); + return fn; } namespace { @@ -3681,8 +3684,8 @@ namespace { // Look through defers. // FIXME: should this be covered automatically by the logic below? - if (auto CE = dyn_cast(dc)) - if (CE->isDeferBody()) + if (auto func = dyn_cast(dc)) + if (func->isDeferBody()) continue; if (auto func = dyn_cast(dc)) { diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 5dbc031cf2f6a..6ecc1023c0354 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2545,6 +2545,12 @@ InterfaceTypeRequest::evaluate(Evaluator &eval, ValueDecl *D) const { infoBuilder = infoBuilder.withSendable(AFD->isSendable()); // 'throws' only applies to the innermost function. infoBuilder = infoBuilder.withThrows(AFD->hasThrows(), thrownTy); + // Defer bodies must not escape. + if (auto fd = dyn_cast(D)) { + infoBuilder = infoBuilder.withNoEscape(fd->isDeferBody()); + if (fd->hasSendingResult()) + infoBuilder = infoBuilder.withSendingResult(); + } // Lifetime dependencies only apply to the outer function type. if (!hasSelf && lifetimeDependenceInfo.has_value()) { diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 6a97d0d351236..c23bd67e24db1 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -2854,6 +2854,9 @@ class Context { /// The guard expression controlling a catch. CatchGuard, + + /// A defer body + DeferBody, }; private: @@ -2970,20 +2973,6 @@ class Context { return isa(closure); } - bool isDeferBody() const { - if (!Function) - return false; - - if (ErrorHandlingIgnoresFunction) - return false; - - auto closure = dyn_cast_or_null(Function->getAbstractClosureExpr()); - if (!closure) - return false; - - return closure->isDeferBody(); - } - static Context forTopLevelCode(TopLevelCodeDecl *D) { // Top-level code implicitly handles errors. return Context(/*handlesErrors=*/true, @@ -3010,6 +2999,10 @@ class Context { return Context(D->hasThrows(), D->isAsyncContext(), AnyFunctionRef(D), D); } + static Context forDeferBody(DeclContext *dc) { + return Context(Kind::DeferBody, dc); + } + static Context forInitializer(Initializer *init) { if (isa(init)) { return Context(Kind::DefaultArgument, init); @@ -3290,12 +3283,6 @@ class Context { return; } - if (isDeferBody()) { - Diags.diagnose(E.getStartLoc(), diag::throwing_op_in_defer, - getEffectSourceName(reason)); - return; - } - if (hasPolymorphicEffect(EffectKind::Throws)) { diagnoseThrowInLegalContext(Diags, E, isTryCovered, reason, diag::throwing_call_in_rethrows_function, @@ -3316,6 +3303,7 @@ class Context { case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: + case Kind::DeferBody: Diags.diagnose(E.getStartLoc(), diag::throwing_op_in_illegal_context, static_cast(getKind()), getEffectSourceName(reason)); return; @@ -3336,11 +3324,6 @@ class Context { return; } - if (isDeferBody()) { - Diags.diagnose(S->getStartLoc(), diag::throw_in_defer_body); - return; - } - if (hasPolymorphicEffect(EffectKind::Throws)) { Diags.diagnose(S->getStartLoc(), diag::throw_in_rethrows_function); return; @@ -3357,6 +3340,7 @@ class Context { case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: + case Kind::DeferBody: Diags.diagnose(S->getStartLoc(), diag::throw_in_illegal_context, static_cast(getKind())); return; @@ -3383,6 +3367,7 @@ class Context { case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: + case Kind::DeferBody: assert(!DiagnoseErrorOnTry); // Diagnosed at the call sites. return; @@ -3492,6 +3477,7 @@ class Context { case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: + case Kind::DeferBody: diagnoseAsyncInIllegalContext(Diags, node); return; } @@ -4653,7 +4639,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker ContextScope scope(*this, std::nullopt); scope.enterUnsafe(S->getDeferLoc()); - S->getBody()->walk(*this); + // Walk the call expression. We don't care about the rest. S->getCallExpr()->walk(*this); return ShouldNotRecurse; @@ -5018,7 +5004,9 @@ void TypeChecker::checkFunctionEffects(AbstractFunctionDecl *fn) { PrettyStackTraceDecl debugStack("checking effects handling for", fn); #endif - auto context = Context::forFunction(fn); + auto isDeferBody = isa(fn) && cast(fn)->isDeferBody(); + auto context = + isDeferBody ? Context::forDeferBody(fn) : Context::forFunction(fn); auto &ctx = fn->getASTContext(); CheckEffectsCoverage checker(ctx, context); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 5debe675b298b..08477dc6a42fa 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -545,8 +545,8 @@ static void tryDiagnoseUnnecessaryCastOverOptionSet(ASTContext &Ctx, /// Whether the given enclosing context is a "defer" body. static bool isDefer(DeclContext *dc) { - if (auto *CE = dyn_cast(dc)) - return CE->isDeferBody(); + if (auto *func = dyn_cast(dc)) + return func->isDeferBody(); return false; } @@ -1365,14 +1365,7 @@ class StmtChecker : public StmtVisitor { Stmt *visitDeferStmt(DeferStmt *DS) { TypeChecker::typeCheckDecl(DS->getTempDecl()); - if (DS->getBody()->getType()->hasError()) { - return nullptr; - } - Expr *theCall = DS->getCallExpr(); - if (DS->getBody()->getType()->getAs()->isAsync()) { - theCall = AwaitExpr::createImplicit(Ctx, SourceLoc(), theCall); - } TypeChecker::typeCheckExpression(theCall, DC); DS->setCallExpr(theCall); @@ -2768,13 +2761,6 @@ static void addImplicitReturnIfNeeded(BraceStmt *body, DeclContext *dc) { if (isa(SemanticExpr)) return; } - - // Don't add implicit return to defer bodies - if (auto *CE = dyn_cast(dc)) { - if (CE->isDeferBody()) { - return; - } - } makeResult(E); } } diff --git a/test/expr/unary/async_await.swift b/test/expr/unary/async_await.swift index 08c4084b25cb0..98bc9ba5ffe8d 100644 --- a/test/expr/unary/async_await.swift +++ b/test/expr/unary/async_await.swift @@ -19,7 +19,7 @@ func test2( defaulted: Int = await getInt() // expected-error{{'async' call cannot occur in a default argument}} ) async { defer { - _ = await getInt() // OK + _ = await getInt() // expected-error{{'async' call cannot occur in a defer body}} } print("foo") } diff --git a/test/stmt/defer.swift b/test/stmt/defer.swift index 709bc248fd44f..ac113a4b1baaa 100644 --- a/test/stmt/defer.swift +++ b/test/stmt/defer.swift @@ -125,7 +125,7 @@ func throwingFunctionCalledInDefer() throws { class SomeDerivedClass: SomeTestClass { override init() { defer { - super.init() // expected-error {{initializer chaining ('super.init') cannot be nested in another statement}} + super.init() // expected-error {{initializer chaining ('super.init') cannot be nested in another expression}} } } } @@ -161,14 +161,3 @@ func badForwardReference() { let z2 = 0 } - -struct MutatingSelf { - var x: Int - mutating func f() { - defer { - _ = x // ok - } - - _ = x - } -} From e7d62c80597a6b14e24139db295e63196a3430b3 Mon Sep 17 00:00:00 2001 From: Freddy Kellison-Linn Date: Sat, 30 Aug 2025 17:31:47 -0400 Subject: [PATCH 4/5] Rework implementation to keep FuncDecl --- include/swift/AST/Decl.h | 2 + include/swift/AST/DiagnosticsSema.def | 6 +++ lib/AST/Stmt.cpp | 9 ++++- lib/Parse/ParseStmt.cpp | 12 ++++++ lib/Sema/TypeCheckEffects.cpp | 57 ++++++++++++++++++++------- test/expr/unary/async_await.swift | 11 ++---- test/stmt/defer.swift | 24 +++++++++++ 7 files changed, 98 insertions(+), 23 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 51376bbceaec4..ba20291156d3a 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -7904,6 +7904,8 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// type of the function will be `async` as well. bool hasAsync() const { return Bits.AbstractFunctionDecl.Async; } + void setHasAsync(bool async) { Bits.AbstractFunctionDecl.Async = async; } + /// Determine whether the given function is concurrent. /// /// A function is concurrent if it has the @Sendable attribute. diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index d7ffcbd2cdd0c..d508ef6e9acd9 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5514,6 +5514,12 @@ ERROR(tryless_throwing_call_in_nonexhaustive_catch,none, "the enclosing catch is not exhaustive", (StringRef)) ERROR(throw_in_nonexhaustive_catch,none, "error is not handled because the enclosing catch is not exhaustive", ()) +ERROR(throw_in_defer_body,none, + "errors cannot be thrown out of a defer body", ()) +ERROR(throwing_op_in_defer_body,none, + "%0 can throw, but errors cannot be thrown out of a defer body", (StringRef)) +ERROR(async_defer_in_non_async_context,none, + "'async' defer must appear within an 'async' context", ()) #define EFFECTS_CONTEXT_KIND \ "%select{<>|" \ diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index f1460e2383ed6..44b824c7a8e4b 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -270,7 +270,8 @@ ASTNode BraceStmt::findAsyncNode() { } PreWalkAction walkToDeclPre(Decl *decl) override { - // Do not walk into function or type declarations. + // Do not walk into function or type declarations (except for defer + // bodies). if (auto *patternBinding = dyn_cast(decl)) { if (patternBinding->isAsyncLet()) AsyncNode = patternBinding; @@ -278,6 +279,12 @@ ASTNode BraceStmt::findAsyncNode() { return Action::Continue(); } + if (auto *fnDecl = dyn_cast(decl)) { + if (fnDecl->isDeferBody()) { + return Action::Continue(); + } + } + return Action::SkipNode(); } diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 35208bdca0393..484b2f0e5bbec 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1097,6 +1097,18 @@ ParserResult Parser::parseStmtDefer() { return nullptr; Status |= Body; + bool isAsync = (bool)Body.get()->findAsyncNode(); + tempDecl->setHasAsync(isAsync); + if (DS->getTempDecl()->isAsync()) { + DS->setCallExpr(AwaitExpr::createImplicit(Context, SourceLoc(), + DS->getCallExpr())); + auto *attr = + new (Context) NonisolatedAttr(SourceLoc(), SourceRange(), + NonIsolatedModifier::NonSending, + /*implicit*/ true); + tempDecl->getAttrs().add(attr); + } + // Clone the current hasher and extract a Fingerprint. StableHasher currentHash{*CurrentTokenHash}; Fingerprint fp(std::move(currentHash)); diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index c23bd67e24db1..61f4c1e8c7d8c 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -2854,9 +2854,6 @@ class Context { /// The guard expression controlling a catch. CatchGuard, - - /// A defer body - DeferBody, }; private: @@ -2973,6 +2970,21 @@ class Context { return isa(closure); } + bool isDeferBody() const { + if (!Function) + return false; + + if (ErrorHandlingIgnoresFunction) + return false; + + auto fnDecl = + dyn_cast_or_null(Function->getAbstractFunctionDecl()); + if (!fnDecl) + return false; + + return fnDecl->isDeferBody(); + } + static Context forTopLevelCode(TopLevelCodeDecl *D) { // Top-level code implicitly handles errors. return Context(/*handlesErrors=*/true, @@ -2999,10 +3011,6 @@ class Context { return Context(D->hasThrows(), D->isAsyncContext(), AnyFunctionRef(D), D); } - static Context forDeferBody(DeclContext *dc) { - return Context(Kind::DeferBody, dc); - } - static Context forInitializer(Initializer *init) { if (isa(init)) { return Context(Kind::DefaultArgument, init); @@ -3283,6 +3291,12 @@ class Context { return; } + if (isDeferBody()) { + Diags.diagnose(E.getStartLoc(), diag::throwing_op_in_defer_body, + getEffectSourceName(reason)); + return; + } + if (hasPolymorphicEffect(EffectKind::Throws)) { diagnoseThrowInLegalContext(Diags, E, isTryCovered, reason, diag::throwing_call_in_rethrows_function, @@ -3303,7 +3317,6 @@ class Context { case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: - case Kind::DeferBody: Diags.diagnose(E.getStartLoc(), diag::throwing_op_in_illegal_context, static_cast(getKind()), getEffectSourceName(reason)); return; @@ -3324,6 +3337,11 @@ class Context { return; } + if (isDeferBody()) { + Diags.diagnose(S->getStartLoc(), diag::throw_in_defer_body); + return; + } + if (hasPolymorphicEffect(EffectKind::Throws)) { Diags.diagnose(S->getStartLoc(), diag::throw_in_rethrows_function); return; @@ -3340,7 +3358,6 @@ class Context { case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: - case Kind::DeferBody: Diags.diagnose(S->getStartLoc(), diag::throw_in_illegal_context, static_cast(getKind())); return; @@ -3367,7 +3384,6 @@ class Context { case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: - case Kind::DeferBody: assert(!DiagnoseErrorOnTry); // Diagnosed at the call sites. return; @@ -3448,6 +3464,21 @@ class Context { void diagnoseUnhandledAsyncSite(DiagnosticEngine &Diags, ASTNode node, std::optional maybeReason, bool forAwait = false) { + + // If this is an apply of a defer body, emit a special diagnostic. We must + // check this before we check `isImplicit` below since these are always + // implicit! + if (auto *applyExpr = dyn_cast_or_null(node.dyn_cast())) { + auto *calledDecl = applyExpr->getCalledValue(); + if (auto *fnDecl = dyn_cast_or_null(calledDecl)) { + if (fnDecl->isDeferBody()) { + Diags.diagnose(fnDecl->getStartLoc(), + diag::async_defer_in_non_async_context); + return; + } + } + } + if (node.isImplicit()) { // The reason we return early on implicit nodes is that sometimes we // inject implicit closures, e.g. in 'async let' and we'd end up @@ -3477,7 +3508,6 @@ class Context { case Kind::PropertyWrapper: case Kind::CatchPattern: case Kind::CatchGuard: - case Kind::DeferBody: diagnoseAsyncInIllegalContext(Diags, node); return; } @@ -4639,7 +4669,6 @@ class CheckEffectsCoverage : public EffectsHandlingWalker ContextScope scope(*this, std::nullopt); scope.enterUnsafe(S->getDeferLoc()); - // Walk the call expression. We don't care about the rest. S->getCallExpr()->walk(*this); return ShouldNotRecurse; @@ -5004,9 +5033,7 @@ void TypeChecker::checkFunctionEffects(AbstractFunctionDecl *fn) { PrettyStackTraceDecl debugStack("checking effects handling for", fn); #endif - auto isDeferBody = isa(fn) && cast(fn)->isDeferBody(); - auto context = - isDeferBody ? Context::forDeferBody(fn) : Context::forFunction(fn); + auto context = Context::forFunction(fn); auto &ctx = fn->getASTContext(); CheckEffectsCoverage checker(ctx, context); diff --git a/test/expr/unary/async_await.swift b/test/expr/unary/async_await.swift index 98bc9ba5ffe8d..571cc4172fa0d 100644 --- a/test/expr/unary/async_await.swift +++ b/test/expr/unary/async_await.swift @@ -18,9 +18,6 @@ func getInt() async -> Int { return 5 } func test2( defaulted: Int = await getInt() // expected-error{{'async' call cannot occur in a default argument}} ) async { - defer { - _ = await getInt() // expected-error{{'async' call cannot occur in a defer body}} - } print("foo") } @@ -182,10 +179,10 @@ func testAsyncLet() async throws { } defer { - async let deferX: Int = await getInt() // expected-error {{'async let' cannot be used on declarations in a defer body}} - _ = await deferX // expected-error {{async let 'deferX' cannot be referenced in a defer body}} - async let _: Int = await getInt() // expected-error {{'async let' cannot be used on declarations in a defer body}} - async let _ = await getInt() // expected-error {{'async let' cannot be used on declarations in a defer body}} + async let deferX: Int = await getInt() + _ = await deferX + async let _: Int = await getInt() + async let _ = await getInt() } async let x1 = getIntUnsafely() // okay, try is implicit here diff --git a/test/stmt/defer.swift b/test/stmt/defer.swift index ac113a4b1baaa..45e08a26c1f67 100644 --- a/test/stmt/defer.swift +++ b/test/stmt/defer.swift @@ -161,3 +161,27 @@ func badForwardReference() { let z2 = 0 } + + +public func basicAsyncDefer() async { + defer { await asyncFunc() } + voidFunc() +} + +func asyncFunc() async {} +func voidFunc() {} + +func testClosure() async { + let f = { + defer { await asyncFunc() } + voidFunc() + } + + await f() +} + +func asyncDeferInSyncFunc() { + defer { await asyncFunc() } // expected-error {{'async' defer must appear within an 'async' context}} + voidFunc() +} + From 1996da87cb7e905891bdd46deeac898fb477f485 Mon Sep 17 00:00:00 2001 From: Freddy Kellison-Linn Date: Mon, 1 Sep 2025 17:59:04 -0400 Subject: [PATCH 5/5] Add AST briding --- include/swift/AST/ASTBridging.h | 7 +++++++ include/swift/AST/Stmt.h | 6 +++++- lib/AST/Bridging/StmtBridging.cpp | 9 +++++++++ lib/AST/Stmt.cpp | 9 +++++++++ lib/ASTGen/Sources/ASTGen/Stmts.swift | 6 +++++- lib/Parse/ParseStmt.cpp | 12 ++---------- lib/Sema/TypeCheckEffects.cpp | 2 +- 7 files changed, 38 insertions(+), 13 deletions(-) diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 99f9d05e0eef3..b5db504efac37 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -2325,6 +2325,9 @@ BridgedBraceStmt BridgedBraceStmt_createImplicit(BridgedASTContext cContext, BridgedASTNode element, swift::SourceLoc rBLoc); +SWIFT_NAME("BridgedBraceStmt.hasAsyncNode(self:)") +bool BridgedBraceStmt_hasAsyncNode(BridgedBraceStmt braceStmt); + SWIFT_NAME("BridgedBreakStmt.createParsed(_:loc:targetName:targetLoc:)") BridgedBreakStmt BridgedBreakStmt_createParsed(BridgedDeclContext cDeclContext, swift::SourceLoc loc, @@ -2356,6 +2359,10 @@ BridgedDeferStmt BridgedDeferStmt_createParsed(BridgedDeclContext cDeclContext, SWIFT_NAME("getter:BridgedDeferStmt.tempDecl(self:)") BridgedFuncDecl BridgedDeferStmt_getTempDecl(BridgedDeferStmt bridged); +SWIFT_NAME("BridgedDeferStmt.makeAsync(self:_:)") +void BridgedDeferStmt_makeAsync(BridgedDeferStmt bridged, + BridgedASTContext ctx); + SWIFT_NAME("BridgedDiscardStmt.createParsed(_:discardLoc:subExpr:)") BridgedDiscardStmt BridgedDiscardStmt_createParsed(BridgedASTContext cContext, swift::SourceLoc discardLoc, diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index c8f9d4bb3e76d..a09e11dfc9efa 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -424,7 +424,11 @@ class DeferStmt : public Stmt { /// Dig the original user's body of the defer out for AST fidelity. BraceStmt *getBodyAsWritten() const; - + + /// Turn this into an async defer by modifying the temp decl and call expr + /// appropriately. + void makeAsync(ASTContext &ctx); + static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Defer; } }; diff --git a/lib/AST/Bridging/StmtBridging.cpp b/lib/AST/Bridging/StmtBridging.cpp index e8c11bb98d0cf..431e6a20eb2ff 100644 --- a/lib/AST/Bridging/StmtBridging.cpp +++ b/lib/AST/Bridging/StmtBridging.cpp @@ -96,6 +96,10 @@ BridgedBraceStmt BridgedBraceStmt_createImplicit(BridgedASTContext cContext, /*Implicit=*/true); } +bool BridgedBraceStmt_hasAsyncNode(BridgedBraceStmt braceStmt) { + return (bool)braceStmt.unbridged()->findAsyncNode(); +} + BridgedBreakStmt BridgedBreakStmt_createParsed(BridgedDeclContext cDeclContext, SourceLoc loc, Identifier targetName, @@ -156,6 +160,11 @@ BridgedFuncDecl BridgedDeferStmt_getTempDecl(BridgedDeferStmt bridged) { return bridged.unbridged()->getTempDecl(); } +void BridgedDeferStmt_makeAsync(BridgedDeferStmt bridged, + BridgedASTContext ctx) { + return bridged.unbridged()->makeAsync(ctx.unbridged()); +} + BridgedDiscardStmt BridgedDiscardStmt_createParsed(BridgedASTContext cContext, SourceLoc discardLoc, BridgedExpr cSubExpr) { diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 44b824c7a8e4b..0535babe7582c 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -401,6 +401,15 @@ BraceStmt *DeferStmt::getBodyAsWritten() const { return tempDecl->getBody(); } +void DeferStmt::makeAsync(ASTContext &ctx) { + tempDecl->setHasAsync(true); + setCallExpr(AwaitExpr::createImplicit(ctx, SourceLoc(), getCallExpr())); + auto *attr = new (ctx) NonisolatedAttr(SourceLoc(), SourceRange(), + NonIsolatedModifier::NonSending, + /*implicit*/ true); + tempDecl->getAttrs().add(attr); +} + bool LabeledStmt::isPossibleContinueTarget() const { switch (getKind()) { #define LABELED_STMT(ID, PARENT) diff --git a/lib/ASTGen/Sources/ASTGen/Stmts.swift b/lib/ASTGen/Sources/ASTGen/Stmts.swift index c97d8d558e3c4..181cacce0e079 100644 --- a/lib/ASTGen/Sources/ASTGen/Stmts.swift +++ b/lib/ASTGen/Sources/ASTGen/Stmts.swift @@ -277,7 +277,11 @@ extension ASTGenVisitor { deferLoc: deferLoc ) self.withDeclContext(stmt.tempDecl.asDeclContext) { - stmt.tempDecl.setParsedBody(self.generate(codeBlock: node.body)) + let body = self.generate(codeBlock: node.body) + stmt.tempDecl.setParsedBody(body) + if body.hasAsyncNode() { + stmt.makeAsync(ctx) + } } return stmt } diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 484b2f0e5bbec..9990491338086 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1097,16 +1097,8 @@ ParserResult Parser::parseStmtDefer() { return nullptr; Status |= Body; - bool isAsync = (bool)Body.get()->findAsyncNode(); - tempDecl->setHasAsync(isAsync); - if (DS->getTempDecl()->isAsync()) { - DS->setCallExpr(AwaitExpr::createImplicit(Context, SourceLoc(), - DS->getCallExpr())); - auto *attr = - new (Context) NonisolatedAttr(SourceLoc(), SourceRange(), - NonIsolatedModifier::NonSending, - /*implicit*/ true); - tempDecl->getAttrs().add(attr); + if (bool(Body.get()->findAsyncNode())) { + DS->makeAsync(Context); } // Clone the current hasher and extract a Fingerprint. diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 61f4c1e8c7d8c..82e798fa00502 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -3469,7 +3469,7 @@ class Context { // check this before we check `isImplicit` below since these are always // implicit! if (auto *applyExpr = dyn_cast_or_null(node.dyn_cast())) { - auto *calledDecl = applyExpr->getCalledValue(); + auto *calledDecl = applyExpr->getCalledValue(/*skipConversions=*/true); if (auto *fnDecl = dyn_cast_or_null(calledDecl)) { if (fnDecl->isDeferBody()) { Diags.diagnose(fnDecl->getStartLoc(),