[clang] [Clang] Extend lifetime of temporaries in mem-default-init for P2718R0 (PR #86960)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 2 08:49:57 PDT 2024
https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/86960
>From 8bd2742f6bccc0ec7229950e901e99717ba9acb3 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 2 Apr 2024 23:47:33 +0800
Subject: [PATCH] [Clang] Extend lifetime of temporaries in mem-default-init
for P2718R0
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/lib/CodeGen/CGExpr.cpp | 62 +++++++++++++++--------------
clang/lib/CodeGen/CGStmt.cpp | 12 ++++++
clang/lib/CodeGen/CodeGenFunction.h | 31 +++++++++++++++
clang/lib/Sema/SemaExpr.cpp | 9 +++--
clang/lib/Sema/SemaInit.cpp | 7 ++--
5 files changed, 86 insertions(+), 35 deletions(-)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 54432353e7420d..5cd66c43ea88dc 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -268,9 +268,9 @@ void CodeGenFunction::EmitAnyExprToMem(const Expr *E,
llvm_unreachable("bad evaluation kind");
}
-static void
-pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
- const Expr *E, Address ReferenceTemporary) {
+void CodeGenFunction::pushTemporaryCleanup(const MaterializeTemporaryExpr *M,
+ const Expr *E,
+ Address ReferenceTemporary) {
// Objective-C++ ARC:
// If we are binding a reference to a temporary that has ownership, we
// need to perform retain/release operations on the temporary.
@@ -305,9 +305,9 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
CleanupKind CleanupKind;
if (Lifetime == Qualifiers::OCL_Strong) {
const ValueDecl *VD = M->getExtendingDecl();
- bool Precise =
- VD && isa<VarDecl>(VD) && VD->hasAttr<ObjCPreciseLifetimeAttr>();
- CleanupKind = CGF.getARCCleanupKind();
+ bool Precise = isa_and_nonnull<VarDecl>(VD) &&
+ VD->hasAttr<ObjCPreciseLifetimeAttr>();
+ CleanupKind = getARCCleanupKind();
Destroy = Precise ? &CodeGenFunction::destroyARCStrongPrecise
: &CodeGenFunction::destroyARCStrongImprecise;
} else {
@@ -317,13 +317,12 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
Destroy = &CodeGenFunction::destroyARCWeak;
}
if (Duration == SD_FullExpression)
- CGF.pushDestroy(CleanupKind, ReferenceTemporary,
- M->getType(), *Destroy,
- CleanupKind & EHCleanup);
+ pushDestroy(CleanupKind, ReferenceTemporary, M->getType(), *Destroy,
+ CleanupKind & EHCleanup);
else
- CGF.pushLifetimeExtendedDestroy(CleanupKind, ReferenceTemporary,
- M->getType(),
- *Destroy, CleanupKind & EHCleanup);
+ pushLifetimeExtendedDestroy(CleanupKind, ReferenceTemporary,
+ M->getType(), *Destroy,
+ CleanupKind & EHCleanup);
return;
case SD_Dynamic:
@@ -352,32 +351,31 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
llvm::FunctionCallee CleanupFn;
llvm::Constant *CleanupArg;
if (E->getType()->isArrayType()) {
- CleanupFn = CodeGenFunction(CGF.CGM).generateDestroyHelper(
- ReferenceTemporary, E->getType(),
- CodeGenFunction::destroyCXXObject, CGF.getLangOpts().Exceptions,
+ CleanupFn = CodeGenFunction(CGM).generateDestroyHelper(
+ ReferenceTemporary, E->getType(), CodeGenFunction::destroyCXXObject,
+ getLangOpts().Exceptions,
dyn_cast_or_null<VarDecl>(M->getExtendingDecl()));
- CleanupArg = llvm::Constant::getNullValue(CGF.Int8PtrTy);
+ CleanupArg = llvm::Constant::getNullValue(Int8PtrTy);
} else {
- CleanupFn = CGF.CGM.getAddrAndTypeOfCXXStructor(
+ CleanupFn = CGM.getAddrAndTypeOfCXXStructor(
GlobalDecl(ReferenceTemporaryDtor, Dtor_Complete));
- CleanupArg = cast<llvm::Constant>(ReferenceTemporary.emitRawPointer(CGF));
+ CleanupArg =
+ cast<llvm::Constant>(ReferenceTemporary.emitRawPointer(*this));
}
- CGF.CGM.getCXXABI().registerGlobalDtor(
- CGF, *cast<VarDecl>(M->getExtendingDecl()), CleanupFn, CleanupArg);
+ CGM.getCXXABI().registerGlobalDtor(
+ *this, *cast<VarDecl>(M->getExtendingDecl()), CleanupFn, CleanupArg);
break;
}
case SD_FullExpression:
- CGF.pushDestroy(NormalAndEHCleanup, ReferenceTemporary, E->getType(),
- CodeGenFunction::destroyCXXObject,
- CGF.getLangOpts().Exceptions);
+ pushDestroy(NormalAndEHCleanup, ReferenceTemporary, E->getType(),
+ CodeGenFunction::destroyCXXObject, getLangOpts().Exceptions);
break;
case SD_Automatic:
- CGF.pushLifetimeExtendedDestroy(NormalAndEHCleanup,
- ReferenceTemporary, E->getType(),
- CodeGenFunction::destroyCXXObject,
- CGF.getLangOpts().Exceptions);
+ pushLifetimeExtendedDestroy(NormalAndEHCleanup, ReferenceTemporary,
+ E->getType(), CodeGenFunction::destroyCXXObject,
+ getLangOpts().Exceptions);
break;
case SD_Dynamic:
@@ -484,7 +482,7 @@ EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *M) {
}
}
- pushTemporaryCleanup(*this, M, E, Object);
+ pushTemporaryCleanup(M, E, Object);
return RefTempDst;
}
@@ -573,7 +571,13 @@ EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *M) {
}
EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true);
}
- pushTemporaryCleanup(*this, M, E, Object);
+
+ // If this temporary extended by for-range variable, delay to emitting
+ // cleanup.
+ if (!CurLexicalScope->isExtendedByForRangeVar(M))
+ CurLexicalScope->addForRangeInitTemp(M, Object);
+ else
+ pushTemporaryCleanup(M, E, Object);
// Perform derived-to-base casts and/or field accesses, to get from the
// temporary object we created (and, potentially, for which we extended
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 576fe2f7a2d46f..ee30b2b6066889 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -1230,11 +1230,23 @@ CodeGenFunction::EmitCXXForRangeStmt(const CXXForRangeStmt &S,
JumpDest LoopExit = getJumpDestInCurrentScope("for.end");
LexicalScope ForScope(*this, S.getSourceRange());
+ ForScope.setForRangeVar(S.getLoopVariable());
// Evaluate the first pieces before the loop.
if (S.getInit())
EmitStmt(S.getInit());
EmitStmt(S.getRangeStmt());
+
+ // Emit cleanup for tempories in for-range-init expression.
+ {
+ RunCleanupsScope Scope(*this);
+ auto LifetimeExtendTemps = ForScope.getForRangeInitTemps();
+ for (const auto &Temp : LifetimeExtendTemps) {
+ auto [M, Object] = Temp;
+ pushTemporaryCleanup(M, M->getSubExpr(), Object);
+ }
+ }
+
EmitStmt(S.getBeginStmt());
EmitStmt(S.getEndStmt());
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index e2a7e28c8211ea..167ac71fefb722 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -875,6 +875,9 @@ class CodeGenFunction : public CodeGenTypeCache {
new (Buffer + sizeof(Header) + sizeof(T)) RawAddress(ActiveFlag);
}
+ void pushTemporaryCleanup(const MaterializeTemporaryExpr *M, const Expr *E,
+ Address ReferenceTemporary);
+
/// Set up the last cleanup that was pushed as a conditional
/// full-expression cleanup.
void initFullExprCleanup() {
@@ -982,11 +985,24 @@ class CodeGenFunction : public CodeGenTypeCache {
EHScopeStack::stable_iterator CurrentCleanupScopeDepth =
EHScopeStack::stable_end();
+ struct ForRangeInitLifetimeExtendTemporary {
+ const MaterializeTemporaryExpr *M;
+ RawAddress Object;
+ };
+
class LexicalScope : public RunCleanupsScope {
SourceRange Range;
SmallVector<const LabelDecl*, 4> Labels;
+ SmallVector<ForRangeInitLifetimeExtendTemporary, 4> ForRangeInitTemps;
LexicalScope *ParentScope;
+ // This will be set to `__range` variable when we emitting a
+ // CXXForRangeStmt. It was used to check whether we are emitting a
+ // materialized temporary which in for-range-init and lifetime-extended by
+ // __range var. If so, the codegen of cleanup for that temporary object
+ // needs to be delayed.
+ const VarDecl *ForRangeVar = nullptr;
+
LexicalScope(const LexicalScope &) = delete;
void operator=(const LexicalScope &) = delete;
@@ -1004,6 +1020,21 @@ class CodeGenFunction : public CodeGenTypeCache {
Labels.push_back(label);
}
+ void addForRangeInitTemp(const MaterializeTemporaryExpr *M,
+ RawAddress Object) {
+ assert(PerformCleanup && "adding temps to dead scope?");
+ ForRangeInitTemps.push_back({M, Object});
+ }
+
+ ArrayRef<ForRangeInitLifetimeExtendTemporary> getForRangeInitTemps() const {
+ return ForRangeInitTemps;
+ }
+
+ void setForRangeVar(const VarDecl *Var) { ForRangeVar = Var; }
+ bool isExtendedByForRangeVar(const MaterializeTemporaryExpr *M) const {
+ return M && ForRangeVar && M->getExtendingDecl() == ForRangeVar;
+ }
+
/// Exit this cleanup scope, emitting any accumulated
/// cleanups.
~LexicalScope() {
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 80b4257d9d83ed..14c6e4c50a1bec 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6376,7 +6376,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
Expr *Init = nullptr;
bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
-
+ bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
EnterExpressionEvaluationContext EvalContext(
*this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
@@ -6411,12 +6411,15 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
ImmediateCallVisitor V(getASTContext());
if (!NestedDefaultChecking)
V.TraverseDecl(Field);
- if (V.HasImmediateCalls) {
+ if (V.HasImmediateCalls || InLifetimeExtendingContext) {
ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
CurContext};
ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
NestedDefaultChecking;
-
+ // Pass down lifetime extending flag, and collect temporaries in
+ // CreateMaterializeTemporaryExpr when we rewrite the call argument.
+ keepInLifetimeExtendingContext();
+ keepInMaterializeTemporaryObjectContext();
EnsureImmediateInvocationInDefaultArgs Immediate(*this);
ExprResult Res;
runWithSufficientStackSpace(Loc, [&] {
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index e2a1951f1062cb..5ecb23b4a6bac7 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -8208,9 +8208,10 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
// FIXME: Properly handle this situation. Perhaps the easiest approach
// would be to clone the initializer expression on each use that would
// lifetime extend its temporaries.
- Diag(DiagLoc, diag::warn_unsupported_lifetime_extension)
- << RK << DiagRange;
- break;
+ // Diag(DiagLoc, diag::warn_unsupported_lifetime_extension)
+ // << RK << DiagRange;
+ // break;
+ return true;
case PathLifetimeKind::NoExtend:
// If the path goes through the initialization of a variable or field,
More information about the cfe-commits
mailing list