@@ -6710,15 +6710,22 @@ struct IndirectLocalPathEntry {
67106710 VarInit,
67116711 LValToRVal,
67126712 LifetimeBoundCall,
6713+ TemporaryCopy,
6714+ LambdaCaptureInit,
67136715 GslReferenceInit,
67146716 GslPointerInit
67156717 } Kind;
67166718 Expr *E;
6717- const Decl *D = nullptr ;
6719+ union {
6720+ const Decl *D = nullptr ;
6721+ const LambdaCapture *Capture;
6722+ };
67186723 IndirectLocalPathEntry () {}
67196724 IndirectLocalPathEntry (EntryKind K, Expr *E) : Kind(K), E(E) {}
67206725 IndirectLocalPathEntry (EntryKind K, Expr *E, const Decl *D)
67216726 : Kind(K), E(E), D(D) {}
6727+ IndirectLocalPathEntry (EntryKind K, Expr *E, const LambdaCapture *Capture)
6728+ : Kind(K), E(E), Capture(Capture) {}
67226729};
67236730
67246731using IndirectLocalPath = llvm::SmallVectorImpl<IndirectLocalPathEntry>;
@@ -6912,6 +6919,26 @@ static bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
69126919 if (ATL.getAttrAs <LifetimeBoundAttr>())
69136920 return true ;
69146921 }
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+
69156942 return false ;
69166943}
69176944
@@ -7257,15 +7284,37 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
72577284 // The lifetime of an init-capture is that of the closure object constructed
72587285 // by a lambda-expression.
72597286 if (auto *LE = dyn_cast<LambdaExpr>(Init)) {
7287+ LambdaExpr::capture_iterator CapI = LE->capture_begin ();
72607288 for (Expr *E : LE->capture_inits ()) {
7289+ assert (CapI != LE->capture_end ());
7290+ const LambdaCapture &Cap = *CapI++;
72617291 if (!E)
72627292 continue ;
7293+ if (Cap.capturesVariable ())
7294+ Path.push_back ({IndirectLocalPathEntry::LambdaCaptureInit, E, &Cap});
72637295 if (E->isGLValue ())
72647296 visitLocalsRetainedByReferenceBinding (Path, E, RK_ReferenceBinding,
72657297 Visit, EnableLifetimeWarnings);
72667298 else
72677299 visitLocalsRetainedByInitializer (Path, E, Visit, true ,
72687300 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+ }
72697318 }
72707319 }
72717320
@@ -7342,14 +7391,31 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
73427391 }
73437392}
73447393
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+
73457407// / 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;
73487412 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;
73517417 }
7352- return true ;
7418+ return Kind ;
73537419}
73547420
73557421// / 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,
73607426 case IndirectLocalPathEntry::AddressOf:
73617427 case IndirectLocalPathEntry::LValToRVal:
73627428 case IndirectLocalPathEntry::LifetimeBoundCall:
7429+ case IndirectLocalPathEntry::TemporaryCopy:
73637430 case IndirectLocalPathEntry::GslReferenceInit:
73647431 case IndirectLocalPathEntry::GslPointerInit:
73657432 // These exist primarily to mark the path as not permitting or
@@ -7372,6 +7439,11 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I,
73727439 LLVM_FALLTHROUGH;
73737440 case IndirectLocalPathEntry::DefaultInit:
73747441 return Path[I].E ->getSourceRange ();
7442+
7443+ case IndirectLocalPathEntry::LambdaCaptureInit:
7444+ if (!Path[I].Capture ->capturesVariable ())
7445+ continue ;
7446+ return Path[I].E ->getSourceRange ();
73757447 }
73767448 }
73777449 return E->getSourceRange ();
@@ -7449,17 +7521,16 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
74497521 return false ;
74507522 }
74517523
7452- // Lifetime-extend the temporary.
7453- if (Path. empty ()) {
7524+ switch ( shouldLifetimeExtendThroughPath (Path)) {
7525+ case PathLifetimeKind::Extend:
74547526 // Update the storage duration of the materialized temporary.
74557527 // FIXME: Rebuild the expression instead of mutating it.
74567528 MTE->setExtendingDecl (ExtendingEntity->getDecl (),
74577529 ExtendingEntity->allocateManglingNumber ());
74587530 // Also visit the temporaries lifetime-extended by this initializer.
74597531 return true ;
7460- }
74617532
7462- if ( shouldLifetimeExtendThroughPath (Path)) {
7533+ case PathLifetimeKind::ShouldExtend:
74637534 // We're supposed to lifetime-extend the temporary along this path (per
74647535 // the resolution of DR1815), but we don't support that yet.
74657536 //
@@ -7468,7 +7539,9 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
74687539 // lifetime extend its temporaries.
74697540 Diag (DiagLoc, diag::warn_unsupported_lifetime_extension)
74707541 << RK << DiagRange;
7471- } else {
7542+ break ;
7543+
7544+ case PathLifetimeKind::NoExtend:
74727545 // If the path goes through the initialization of a variable or field,
74737546 // it can't possibly reach a temporary created in this full-expression.
74747547 // We will have already diagnosed any problems with the initializer.
@@ -7479,6 +7552,7 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
74797552 << RK << !Entity.getParent ()
74807553 << ExtendingEntity->getDecl ()->isImplicit ()
74817554 << ExtendingEntity->getDecl () << Init->isGLValue () << DiagRange;
7555+ break ;
74827556 }
74837557 break ;
74847558 }
@@ -7499,7 +7573,8 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
74997573 return false ;
75007574 }
75017575 bool IsSubobjectMember = ExtendingEntity != &Entity;
7502- Diag (DiagLoc, shouldLifetimeExtendThroughPath (Path)
7576+ Diag (DiagLoc, shouldLifetimeExtendThroughPath (Path) !=
7577+ PathLifetimeKind::NoExtend
75037578 ? diag::err_dangling_member
75047579 : diag::warn_dangling_member)
75057580 << ExtendingDecl << IsSubobjectMember << RK << DiagRange;
@@ -7606,6 +7681,7 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
76067681 break ;
76077682
76087683 case IndirectLocalPathEntry::LifetimeBoundCall:
7684+ case IndirectLocalPathEntry::TemporaryCopy:
76097685 case IndirectLocalPathEntry::GslPointerInit:
76107686 case IndirectLocalPathEntry::GslReferenceInit:
76117687 // FIXME: Consider adding a note for these.
@@ -7618,14 +7694,27 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
76187694 break ;
76197695 }
76207696
7621- case IndirectLocalPathEntry::VarInit:
7697+ case IndirectLocalPathEntry::VarInit: {
76227698 const VarDecl *VD = cast<VarDecl>(Elem.D );
76237699 Diag (VD->getLocation (), diag::note_local_var_initializer)
76247700 << VD->getType ()->isReferenceType ()
76257701 << VD->isImplicit () << VD->getDeclName ()
76267702 << nextPathEntryRange (Path, I + 1 , L);
76277703 break ;
76287704 }
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+ }
76297718 }
76307719
76317720 // We didn't lifetime-extend, so don't go any further; we don't need more
0 commit comments