Skip to content

Commit 8bd2742

Browse files
committed
[Clang] Extend lifetime of temporaries in mem-default-init for P2718R0
Signed-off-by: yronglin <[email protected]>
1 parent e03f16f commit 8bd2742

File tree

5 files changed

+86
-35
lines changed

5 files changed

+86
-35
lines changed

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,9 @@ void CodeGenFunction::EmitAnyExprToMem(const Expr *E,
268268
llvm_unreachable("bad evaluation kind");
269269
}
270270

271-
static void
272-
pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
273-
const Expr *E, Address ReferenceTemporary) {
271+
void CodeGenFunction::pushTemporaryCleanup(const MaterializeTemporaryExpr *M,
272+
const Expr *E,
273+
Address ReferenceTemporary) {
274274
// Objective-C++ ARC:
275275
// If we are binding a reference to a temporary that has ownership, we
276276
// need to perform retain/release operations on the temporary.
@@ -305,9 +305,9 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
305305
CleanupKind CleanupKind;
306306
if (Lifetime == Qualifiers::OCL_Strong) {
307307
const ValueDecl *VD = M->getExtendingDecl();
308-
bool Precise =
309-
VD && isa<VarDecl>(VD) && VD->hasAttr<ObjCPreciseLifetimeAttr>();
310-
CleanupKind = CGF.getARCCleanupKind();
308+
bool Precise = isa_and_nonnull<VarDecl>(VD) &&
309+
VD->hasAttr<ObjCPreciseLifetimeAttr>();
310+
CleanupKind = getARCCleanupKind();
311311
Destroy = Precise ? &CodeGenFunction::destroyARCStrongPrecise
312312
: &CodeGenFunction::destroyARCStrongImprecise;
313313
} else {
@@ -317,13 +317,12 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
317317
Destroy = &CodeGenFunction::destroyARCWeak;
318318
}
319319
if (Duration == SD_FullExpression)
320-
CGF.pushDestroy(CleanupKind, ReferenceTemporary,
321-
M->getType(), *Destroy,
322-
CleanupKind & EHCleanup);
320+
pushDestroy(CleanupKind, ReferenceTemporary, M->getType(), *Destroy,
321+
CleanupKind & EHCleanup);
323322
else
324-
CGF.pushLifetimeExtendedDestroy(CleanupKind, ReferenceTemporary,
325-
M->getType(),
326-
*Destroy, CleanupKind & EHCleanup);
323+
pushLifetimeExtendedDestroy(CleanupKind, ReferenceTemporary,
324+
M->getType(), *Destroy,
325+
CleanupKind & EHCleanup);
327326
return;
328327

329328
case SD_Dynamic:
@@ -352,32 +351,31 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
352351
llvm::FunctionCallee CleanupFn;
353352
llvm::Constant *CleanupArg;
354353
if (E->getType()->isArrayType()) {
355-
CleanupFn = CodeGenFunction(CGF.CGM).generateDestroyHelper(
356-
ReferenceTemporary, E->getType(),
357-
CodeGenFunction::destroyCXXObject, CGF.getLangOpts().Exceptions,
354+
CleanupFn = CodeGenFunction(CGM).generateDestroyHelper(
355+
ReferenceTemporary, E->getType(), CodeGenFunction::destroyCXXObject,
356+
getLangOpts().Exceptions,
358357
dyn_cast_or_null<VarDecl>(M->getExtendingDecl()));
359-
CleanupArg = llvm::Constant::getNullValue(CGF.Int8PtrTy);
358+
CleanupArg = llvm::Constant::getNullValue(Int8PtrTy);
360359
} else {
361-
CleanupFn = CGF.CGM.getAddrAndTypeOfCXXStructor(
360+
CleanupFn = CGM.getAddrAndTypeOfCXXStructor(
362361
GlobalDecl(ReferenceTemporaryDtor, Dtor_Complete));
363-
CleanupArg = cast<llvm::Constant>(ReferenceTemporary.emitRawPointer(CGF));
362+
CleanupArg =
363+
cast<llvm::Constant>(ReferenceTemporary.emitRawPointer(*this));
364364
}
365-
CGF.CGM.getCXXABI().registerGlobalDtor(
366-
CGF, *cast<VarDecl>(M->getExtendingDecl()), CleanupFn, CleanupArg);
365+
CGM.getCXXABI().registerGlobalDtor(
366+
*this, *cast<VarDecl>(M->getExtendingDecl()), CleanupFn, CleanupArg);
367367
break;
368368
}
369369

370370
case SD_FullExpression:
371-
CGF.pushDestroy(NormalAndEHCleanup, ReferenceTemporary, E->getType(),
372-
CodeGenFunction::destroyCXXObject,
373-
CGF.getLangOpts().Exceptions);
371+
pushDestroy(NormalAndEHCleanup, ReferenceTemporary, E->getType(),
372+
CodeGenFunction::destroyCXXObject, getLangOpts().Exceptions);
374373
break;
375374

376375
case SD_Automatic:
377-
CGF.pushLifetimeExtendedDestroy(NormalAndEHCleanup,
378-
ReferenceTemporary, E->getType(),
379-
CodeGenFunction::destroyCXXObject,
380-
CGF.getLangOpts().Exceptions);
376+
pushLifetimeExtendedDestroy(NormalAndEHCleanup, ReferenceTemporary,
377+
E->getType(), CodeGenFunction::destroyCXXObject,
378+
getLangOpts().Exceptions);
381379
break;
382380

383381
case SD_Dynamic:
@@ -484,7 +482,7 @@ EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *M) {
484482
}
485483
}
486484

487-
pushTemporaryCleanup(*this, M, E, Object);
485+
pushTemporaryCleanup(M, E, Object);
488486
return RefTempDst;
489487
}
490488

@@ -573,7 +571,13 @@ EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *M) {
573571
}
574572
EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true);
575573
}
576-
pushTemporaryCleanup(*this, M, E, Object);
574+
575+
// If this temporary extended by for-range variable, delay to emitting
576+
// cleanup.
577+
if (!CurLexicalScope->isExtendedByForRangeVar(M))
578+
CurLexicalScope->addForRangeInitTemp(M, Object);
579+
else
580+
pushTemporaryCleanup(M, E, Object);
577581

578582
// Perform derived-to-base casts and/or field accesses, to get from the
579583
// temporary object we created (and, potentially, for which we extended

clang/lib/CodeGen/CGStmt.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,11 +1230,23 @@ CodeGenFunction::EmitCXXForRangeStmt(const CXXForRangeStmt &S,
12301230
JumpDest LoopExit = getJumpDestInCurrentScope("for.end");
12311231

12321232
LexicalScope ForScope(*this, S.getSourceRange());
1233+
ForScope.setForRangeVar(S.getLoopVariable());
12331234

12341235
// Evaluate the first pieces before the loop.
12351236
if (S.getInit())
12361237
EmitStmt(S.getInit());
12371238
EmitStmt(S.getRangeStmt());
1239+
1240+
// Emit cleanup for tempories in for-range-init expression.
1241+
{
1242+
RunCleanupsScope Scope(*this);
1243+
auto LifetimeExtendTemps = ForScope.getForRangeInitTemps();
1244+
for (const auto &Temp : LifetimeExtendTemps) {
1245+
auto [M, Object] = Temp;
1246+
pushTemporaryCleanup(M, M->getSubExpr(), Object);
1247+
}
1248+
}
1249+
12381250
EmitStmt(S.getBeginStmt());
12391251
EmitStmt(S.getEndStmt());
12401252

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,9 @@ class CodeGenFunction : public CodeGenTypeCache {
875875
new (Buffer + sizeof(Header) + sizeof(T)) RawAddress(ActiveFlag);
876876
}
877877

878+
void pushTemporaryCleanup(const MaterializeTemporaryExpr *M, const Expr *E,
879+
Address ReferenceTemporary);
880+
878881
/// Set up the last cleanup that was pushed as a conditional
879882
/// full-expression cleanup.
880883
void initFullExprCleanup() {
@@ -982,11 +985,24 @@ class CodeGenFunction : public CodeGenTypeCache {
982985
EHScopeStack::stable_iterator CurrentCleanupScopeDepth =
983986
EHScopeStack::stable_end();
984987

988+
struct ForRangeInitLifetimeExtendTemporary {
989+
const MaterializeTemporaryExpr *M;
990+
RawAddress Object;
991+
};
992+
985993
class LexicalScope : public RunCleanupsScope {
986994
SourceRange Range;
987995
SmallVector<const LabelDecl*, 4> Labels;
996+
SmallVector<ForRangeInitLifetimeExtendTemporary, 4> ForRangeInitTemps;
988997
LexicalScope *ParentScope;
989998

999+
// This will be set to `__range` variable when we emitting a
1000+
// CXXForRangeStmt. It was used to check whether we are emitting a
1001+
// materialized temporary which in for-range-init and lifetime-extended by
1002+
// __range var. If so, the codegen of cleanup for that temporary object
1003+
// needs to be delayed.
1004+
const VarDecl *ForRangeVar = nullptr;
1005+
9901006
LexicalScope(const LexicalScope &) = delete;
9911007
void operator=(const LexicalScope &) = delete;
9921008

@@ -1004,6 +1020,21 @@ class CodeGenFunction : public CodeGenTypeCache {
10041020
Labels.push_back(label);
10051021
}
10061022

1023+
void addForRangeInitTemp(const MaterializeTemporaryExpr *M,
1024+
RawAddress Object) {
1025+
assert(PerformCleanup && "adding temps to dead scope?");
1026+
ForRangeInitTemps.push_back({M, Object});
1027+
}
1028+
1029+
ArrayRef<ForRangeInitLifetimeExtendTemporary> getForRangeInitTemps() const {
1030+
return ForRangeInitTemps;
1031+
}
1032+
1033+
void setForRangeVar(const VarDecl *Var) { ForRangeVar = Var; }
1034+
bool isExtendedByForRangeVar(const MaterializeTemporaryExpr *M) const {
1035+
return M && ForRangeVar && M->getExtendingDecl() == ForRangeVar;
1036+
}
1037+
10071038
/// Exit this cleanup scope, emitting any accumulated
10081039
/// cleanups.
10091040
~LexicalScope() {

clang/lib/Sema/SemaExpr.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6376,7 +6376,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
63766376
Expr *Init = nullptr;
63776377

63786378
bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
6379-
6379+
bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
63806380
EnterExpressionEvaluationContext EvalContext(
63816381
*this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
63826382

@@ -6411,12 +6411,15 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
64116411
ImmediateCallVisitor V(getASTContext());
64126412
if (!NestedDefaultChecking)
64136413
V.TraverseDecl(Field);
6414-
if (V.HasImmediateCalls) {
6414+
if (V.HasImmediateCalls || InLifetimeExtendingContext) {
64156415
ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
64166416
CurContext};
64176417
ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
64186418
NestedDefaultChecking;
6419-
6419+
// Pass down lifetime extending flag, and collect temporaries in
6420+
// CreateMaterializeTemporaryExpr when we rewrite the call argument.
6421+
keepInLifetimeExtendingContext();
6422+
keepInMaterializeTemporaryObjectContext();
64206423
EnsureImmediateInvocationInDefaultArgs Immediate(*this);
64216424
ExprResult Res;
64226425
runWithSufficientStackSpace(Loc, [&] {

clang/lib/Sema/SemaInit.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8208,9 +8208,10 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
82088208
// FIXME: Properly handle this situation. Perhaps the easiest approach
82098209
// would be to clone the initializer expression on each use that would
82108210
// lifetime extend its temporaries.
8211-
Diag(DiagLoc, diag::warn_unsupported_lifetime_extension)
8212-
<< RK << DiagRange;
8213-
break;
8211+
// Diag(DiagLoc, diag::warn_unsupported_lifetime_extension)
8212+
// << RK << DiagRange;
8213+
// break;
8214+
return true;
82148215

82158216
case PathLifetimeKind::NoExtend:
82168217
// If the path goes through the initialization of a variable or field,

0 commit comments

Comments
 (0)