@@ -6710,15 +6710,22 @@ struct IndirectLocalPathEntry {
6710
6710
VarInit,
6711
6711
LValToRVal,
6712
6712
LifetimeBoundCall,
6713
+ TemporaryCopy,
6714
+ LambdaCaptureInit,
6713
6715
GslReferenceInit,
6714
6716
GslPointerInit
6715
6717
} Kind;
6716
6718
Expr *E;
6717
- const Decl *D = nullptr ;
6719
+ union {
6720
+ const Decl *D = nullptr ;
6721
+ const LambdaCapture *Capture;
6722
+ };
6718
6723
IndirectLocalPathEntry () {}
6719
6724
IndirectLocalPathEntry (EntryKind K, Expr *E) : Kind(K), E(E) {}
6720
6725
IndirectLocalPathEntry (EntryKind K, Expr *E, const Decl *D)
6721
6726
: Kind(K), E(E), D(D) {}
6727
+ IndirectLocalPathEntry (EntryKind K, Expr *E, const LambdaCapture *Capture)
6728
+ : Kind(K), E(E), Capture(Capture) {}
6722
6729
};
6723
6730
6724
6731
using IndirectLocalPath = llvm::SmallVectorImpl<IndirectLocalPathEntry>;
@@ -6912,6 +6919,26 @@ static bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
6912
6919
if (ATL.getAttrAs <LifetimeBoundAttr>())
6913
6920
return true ;
6914
6921
}
6922
+
6923
+ // Assume that all assignment operators with a "normal" return type return
6924
+ // *this, that is, an lvalue reference that is the same type as the implicit
6925
+ // object parameter (or the LHS for a non-member operator$=).
6926
+ OverloadedOperatorKind OO = FD->getDeclName ().getCXXOverloadedOperator ();
6927
+ if (OO == OO_Equal || isCompoundAssignmentOperator (OO)) {
6928
+ QualType RetT = FD->getReturnType ();
6929
+ if (RetT->isLValueReferenceType ()) {
6930
+ ASTContext &Ctx = FD->getASTContext ();
6931
+ QualType LHST;
6932
+ auto *MD = dyn_cast<CXXMethodDecl>(FD);
6933
+ if (MD && MD->isCXXInstanceMember ())
6934
+ LHST = Ctx.getLValueReferenceType (MD->getThisObjectType ());
6935
+ else
6936
+ LHST = MD->getParamDecl (0 )->getType ();
6937
+ if (Ctx.hasSameType (RetT, LHST))
6938
+ return true ;
6939
+ }
6940
+ }
6941
+
6915
6942
return false ;
6916
6943
}
6917
6944
@@ -7257,15 +7284,37 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
7257
7284
// The lifetime of an init-capture is that of the closure object constructed
7258
7285
// by a lambda-expression.
7259
7286
if (auto *LE = dyn_cast<LambdaExpr>(Init)) {
7287
+ LambdaExpr::capture_iterator CapI = LE->capture_begin ();
7260
7288
for (Expr *E : LE->capture_inits ()) {
7289
+ assert (CapI != LE->capture_end ());
7290
+ const LambdaCapture &Cap = *CapI++;
7261
7291
if (!E)
7262
7292
continue ;
7293
+ if (Cap.capturesVariable ())
7294
+ Path.push_back ({IndirectLocalPathEntry::LambdaCaptureInit, E, &Cap});
7263
7295
if (E->isGLValue ())
7264
7296
visitLocalsRetainedByReferenceBinding (Path, E, RK_ReferenceBinding,
7265
7297
Visit, EnableLifetimeWarnings);
7266
7298
else
7267
7299
visitLocalsRetainedByInitializer (Path, E, Visit, true ,
7268
7300
EnableLifetimeWarnings);
7301
+ if (Cap.capturesVariable ())
7302
+ Path.pop_back ();
7303
+ }
7304
+ }
7305
+
7306
+ // Assume that a copy or move from a temporary references the same objects
7307
+ // that the temporary does.
7308
+ if (auto *CCE = dyn_cast<CXXConstructExpr>(Init)) {
7309
+ if (CCE->getConstructor ()->isCopyOrMoveConstructor ()) {
7310
+ if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(CCE->getArg (0 ))) {
7311
+ Expr *Arg = MTE->getSubExpr ();
7312
+ Path.push_back ({IndirectLocalPathEntry::TemporaryCopy, Arg,
7313
+ CCE->getConstructor ()});
7314
+ visitLocalsRetainedByInitializer (Path, Arg, Visit, true ,
7315
+ /* EnableLifetimeWarnings*/ false );
7316
+ Path.pop_back ();
7317
+ }
7269
7318
}
7270
7319
}
7271
7320
@@ -7342,14 +7391,31 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
7342
7391
}
7343
7392
}
7344
7393
7394
+ // / Whether a path to an object supports lifetime extension.
7395
+ enum PathLifetimeKind {
7396
+ // / Lifetime-extend along this path.
7397
+ Extend,
7398
+ // / We should lifetime-extend, but we don't because (due to technical
7399
+ // / limitations) we can't. This happens for default member initializers,
7400
+ // / which we don't clone for every use, so we don't have a unique
7401
+ // / MaterializeTemporaryExpr to update.
7402
+ ShouldExtend,
7403
+ // / Do not lifetime extend along this path.
7404
+ NoExtend
7405
+ };
7406
+
7345
7407
// / Determine whether this is an indirect path to a temporary that we are
7346
- // / supposed to lifetime-extend along (but don't).
7347
- static bool shouldLifetimeExtendThroughPath (const IndirectLocalPath &Path) {
7408
+ // / supposed to lifetime-extend along.
7409
+ static PathLifetimeKind
7410
+ shouldLifetimeExtendThroughPath (const IndirectLocalPath &Path) {
7411
+ PathLifetimeKind Kind = PathLifetimeKind::Extend;
7348
7412
for (auto Elem : Path) {
7349
- if (Elem.Kind != IndirectLocalPathEntry::DefaultInit)
7350
- return false ;
7413
+ if (Elem.Kind == IndirectLocalPathEntry::DefaultInit)
7414
+ Kind = PathLifetimeKind::ShouldExtend;
7415
+ else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit)
7416
+ return PathLifetimeKind::NoExtend;
7351
7417
}
7352
- return true ;
7418
+ return Kind ;
7353
7419
}
7354
7420
7355
7421
// / Find the range for the first interesting entry in the path at or after I.
@@ -7360,6 +7426,7 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I,
7360
7426
case IndirectLocalPathEntry::AddressOf:
7361
7427
case IndirectLocalPathEntry::LValToRVal:
7362
7428
case IndirectLocalPathEntry::LifetimeBoundCall:
7429
+ case IndirectLocalPathEntry::TemporaryCopy:
7363
7430
case IndirectLocalPathEntry::GslReferenceInit:
7364
7431
case IndirectLocalPathEntry::GslPointerInit:
7365
7432
// These exist primarily to mark the path as not permitting or
@@ -7372,6 +7439,11 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I,
7372
7439
LLVM_FALLTHROUGH;
7373
7440
case IndirectLocalPathEntry::DefaultInit:
7374
7441
return Path[I].E ->getSourceRange ();
7442
+
7443
+ case IndirectLocalPathEntry::LambdaCaptureInit:
7444
+ if (!Path[I].Capture ->capturesVariable ())
7445
+ continue ;
7446
+ return Path[I].E ->getSourceRange ();
7375
7447
}
7376
7448
}
7377
7449
return E->getSourceRange ();
@@ -7449,17 +7521,16 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
7449
7521
return false ;
7450
7522
}
7451
7523
7452
- // Lifetime-extend the temporary.
7453
- if (Path. empty ()) {
7524
+ switch ( shouldLifetimeExtendThroughPath (Path)) {
7525
+ case PathLifetimeKind::Extend:
7454
7526
// Update the storage duration of the materialized temporary.
7455
7527
// FIXME: Rebuild the expression instead of mutating it.
7456
7528
MTE->setExtendingDecl (ExtendingEntity->getDecl (),
7457
7529
ExtendingEntity->allocateManglingNumber ());
7458
7530
// Also visit the temporaries lifetime-extended by this initializer.
7459
7531
return true ;
7460
- }
7461
7532
7462
- if ( shouldLifetimeExtendThroughPath (Path)) {
7533
+ case PathLifetimeKind::ShouldExtend:
7463
7534
// We're supposed to lifetime-extend the temporary along this path (per
7464
7535
// the resolution of DR1815), but we don't support that yet.
7465
7536
//
@@ -7468,7 +7539,9 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
7468
7539
// lifetime extend its temporaries.
7469
7540
Diag (DiagLoc, diag::warn_unsupported_lifetime_extension)
7470
7541
<< RK << DiagRange;
7471
- } else {
7542
+ break ;
7543
+
7544
+ case PathLifetimeKind::NoExtend:
7472
7545
// If the path goes through the initialization of a variable or field,
7473
7546
// it can't possibly reach a temporary created in this full-expression.
7474
7547
// We will have already diagnosed any problems with the initializer.
@@ -7479,6 +7552,7 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
7479
7552
<< RK << !Entity.getParent ()
7480
7553
<< ExtendingEntity->getDecl ()->isImplicit ()
7481
7554
<< ExtendingEntity->getDecl () << Init->isGLValue () << DiagRange;
7555
+ break ;
7482
7556
}
7483
7557
break ;
7484
7558
}
@@ -7499,7 +7573,8 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
7499
7573
return false ;
7500
7574
}
7501
7575
bool IsSubobjectMember = ExtendingEntity != &Entity;
7502
- Diag (DiagLoc, shouldLifetimeExtendThroughPath (Path)
7576
+ Diag (DiagLoc, shouldLifetimeExtendThroughPath (Path) !=
7577
+ PathLifetimeKind::NoExtend
7503
7578
? diag::err_dangling_member
7504
7579
: diag::warn_dangling_member)
7505
7580
<< ExtendingDecl << IsSubobjectMember << RK << DiagRange;
@@ -7606,6 +7681,7 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
7606
7681
break ;
7607
7682
7608
7683
case IndirectLocalPathEntry::LifetimeBoundCall:
7684
+ case IndirectLocalPathEntry::TemporaryCopy:
7609
7685
case IndirectLocalPathEntry::GslPointerInit:
7610
7686
case IndirectLocalPathEntry::GslReferenceInit:
7611
7687
// FIXME: Consider adding a note for these.
@@ -7618,14 +7694,27 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
7618
7694
break ;
7619
7695
}
7620
7696
7621
- case IndirectLocalPathEntry::VarInit:
7697
+ case IndirectLocalPathEntry::VarInit: {
7622
7698
const VarDecl *VD = cast<VarDecl>(Elem.D );
7623
7699
Diag (VD->getLocation (), diag::note_local_var_initializer)
7624
7700
<< VD->getType ()->isReferenceType ()
7625
7701
<< VD->isImplicit () << VD->getDeclName ()
7626
7702
<< nextPathEntryRange (Path, I + 1 , L);
7627
7703
break ;
7628
7704
}
7705
+
7706
+ case IndirectLocalPathEntry::LambdaCaptureInit:
7707
+ if (!Elem.Capture ->capturesVariable ())
7708
+ break ;
7709
+ // FIXME: We can't easily tell apart an init-capture from a nested
7710
+ // capture of an init-capture.
7711
+ const VarDecl *VD = Elem.Capture ->getCapturedVar ();
7712
+ Diag (Elem.Capture ->getLocation (), diag::note_lambda_capture_initializer)
7713
+ << VD << VD->isInitCapture () << Elem.Capture ->isExplicit ()
7714
+ << (Elem.Capture ->getCaptureKind () == LCK_ByRef) << VD
7715
+ << nextPathEntryRange (Path, I + 1 , L);
7716
+ break ;
7717
+ }
7629
7718
}
7630
7719
7631
7720
// We didn't lifetime-extend, so don't go any further; we don't need more
0 commit comments