diff options
author | Yuxuan Chen <ych@meta.com> | 2023-11-28 19:04:29 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-28 19:04:29 -0800 |
commit | 4a294b5806417aa88c91aa05735b2d557ea5dfe5 (patch) | |
tree | 158bbe34e08c69306dbe8a91e0f4da5f95d36c87 /clang/lib/CodeGen | |
parent | 593358937025e8635f75ba8931130b1ca5212ea1 (diff) | |
download | llvm-4a294b5806417aa88c91aa05735b2d557ea5dfe5.zip llvm-4a294b5806417aa88c91aa05735b2d557ea5dfe5.tar.gz llvm-4a294b5806417aa88c91aa05735b2d557ea5dfe5.tar.bz2 |
[Clang] CGCoroutines skip emitting try block for value returning `noexcept` init `await_resume` calls (#73160)
Previously we were not properly skipping the generation of the `try { }`
block around the `init_suspend.await_resume()` if the `await_resume` is
not returning void. The reason being that the resume expression was
wrapped in a `CXXBindTemporaryExpr` and the first dyn_cast failed,
silently ignoring the noexcept. This only mattered for `init_suspend`
because it had its own try block.
This patch changes to first extract the sub expression when we see a
`CXXBindTemporaryExpr`. Then perform the same logic to check for
`noexcept`.
Another version of this patch also wanted to assert the second step by
`cast<CXXMemberCallExpr>` and as far as I understand it should be a
valid assumption. I can change to that if upstream prefers.
Diffstat (limited to 'clang/lib/CodeGen')
-rw-r--r-- | clang/lib/CodeGen/CGCoroutine.cpp | 52 |
1 files changed, 43 insertions, 9 deletions
diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp index aaf122c..888d30b 100644 --- a/clang/lib/CodeGen/CGCoroutine.cpp +++ b/clang/lib/CodeGen/CGCoroutine.cpp @@ -129,14 +129,48 @@ static SmallString<32> buildSuspendPrefixStr(CGCoroData &Coro, AwaitKind Kind) { return Prefix; } -static bool memberCallExpressionCanThrow(const Expr *E) { - if (const auto *CE = dyn_cast<CXXMemberCallExpr>(E)) - if (const auto *Proto = - CE->getMethodDecl()->getType()->getAs<FunctionProtoType>()) - if (isNoexceptExceptionSpec(Proto->getExceptionSpecType()) && - Proto->canThrow() == CT_Cannot) - return false; - return true; +// Check if function can throw based on prototype noexcept, also works for +// destructors which are implicitly noexcept but can be marked noexcept(false). +static bool FunctionCanThrow(const FunctionDecl *D) { + const auto *Proto = D->getType()->getAs<FunctionProtoType>(); + if (!Proto) { + // Function proto is not found, we conservatively assume throwing. + return true; + } + return !isNoexceptExceptionSpec(Proto->getExceptionSpecType()) || + Proto->canThrow() != CT_Cannot; +} + +static bool ResumeStmtCanThrow(const Stmt *S) { + if (const auto *CE = dyn_cast<CallExpr>(S)) { + const auto *Callee = CE->getDirectCallee(); + if (!Callee) + // We don't have direct callee. Conservatively assume throwing. + return true; + + if (FunctionCanThrow(Callee)) + return true; + + // Fall through to visit the children. + } + + if (const auto *TE = dyn_cast<CXXBindTemporaryExpr>(S)) { + // Special handling of CXXBindTemporaryExpr here as calling of Dtor of the + // temporary is not part of `children()` as covered in the fall through. + // We need to mark entire statement as throwing if the destructor of the + // temporary throws. + const auto *Dtor = TE->getTemporary()->getDestructor(); + if (FunctionCanThrow(Dtor)) + return true; + + // Fall through to visit the children. + } + + for (const auto *child : S->children()) + if (ResumeStmtCanThrow(child)) + return true; + + return false; } // Emit suspend expression which roughly looks like: @@ -233,7 +267,7 @@ static LValueOrRValue emitSuspendExpression(CodeGenFunction &CGF, CGCoroData &Co // is marked as 'noexcept', we avoid generating this additional IR. CXXTryStmt *TryStmt = nullptr; if (Coro.ExceptionHandler && Kind == AwaitKind::Init && - memberCallExpressionCanThrow(S.getResumeExpr())) { + ResumeStmtCanThrow(S.getResumeExpr())) { Coro.ResumeEHVar = CGF.CreateTempAlloca(Builder.getInt1Ty(), Prefix + Twine("resume.eh")); Builder.CreateFlagStore(true, Coro.ResumeEHVar); |