@@ -212,8 +212,10 @@ class Fact {
212212 // / A loan expires as its underlying storage is freed (e.g., variable goes
213213 // / out of scope).
214214 Expire,
215+ // / The loan set of an origin is cleared.
216+ KillOrigin,
215217 // / An origin is propagated from a source to a destination (e.g., p = q).
216- AssignOrigin ,
218+ OriginFlow ,
217219 // / An origin escapes the function by flowing into the return value.
218220 ReturnOfOrigin,
219221 // / An origin is used (eg. dereferencing a pointer).
@@ -285,22 +287,24 @@ class ExpireFact : public Fact {
285287 }
286288};
287289
288- class AssignOriginFact : public Fact {
290+ class OriginFlowFact : public Fact {
289291 OriginID OIDDest;
290292 OriginID OIDSrc;
291293
292294public:
293295 static bool classof (const Fact *F) {
294- return F->getKind () == Kind::AssignOrigin ;
296+ return F->getKind () == Kind::OriginFlow ;
295297 }
296298
297- AssignOriginFact (OriginID OIDDest, OriginID OIDSrc)
298- : Fact(Kind::AssignOrigin), OIDDest(OIDDest), OIDSrc(OIDSrc) {}
299+ OriginFlowFact (OriginID OIDDest, OriginID OIDSrc)
300+ : Fact(Kind::OriginFlow), OIDDest(OIDDest), OIDSrc(OIDSrc) {}
301+
299302 OriginID getDestOriginID () const { return OIDDest; }
300303 OriginID getSrcOriginID () const { return OIDSrc; }
304+
301305 void dump (llvm::raw_ostream &OS, const LoanManager &,
302306 const OriginManager &OM) const override {
303- OS << " AssignOrigin (Dest: " ;
307+ OS << " OriginFlow (Dest: " ;
304308 OM.dump (getDestOriginID (), OS);
305309 OS << " , Src: " ;
306310 OM.dump (getSrcOriginID (), OS);
@@ -353,6 +357,23 @@ class UseFact : public Fact {
353357 }
354358};
355359
360+ class KillOriginFact : public Fact {
361+ OriginID OID;
362+
363+ public:
364+ static bool classof (const Fact *F) {
365+ return F->getKind () == Kind::KillOrigin;
366+ }
367+ KillOriginFact (OriginID OID) : Fact(Kind::KillOrigin), OID(OID) {}
368+ OriginID getOriginID () const { return OID; }
369+
370+ void dump (llvm::raw_ostream &OS, const LoanManager &,
371+ const OriginManager &OM) const override {
372+ OS << " KillOrigin (" ;
373+ OM.dump (getOriginID (), OS);
374+ OS << " )\n " ;
375+ }
376+ };
356377// / A dummy-fact used to mark a specific point in the code for testing.
357378// / It is generated by recognizing a `void("__lifetime_test_point_...")` cast.
358379class TestPointFact : public Fact {
@@ -453,8 +474,10 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
453474 for (const Decl *D : DS->decls ())
454475 if (const auto *VD = dyn_cast<VarDecl>(D))
455476 if (hasOrigin (VD))
456- if (const Expr *InitExpr = VD->getInit ())
457- addAssignOriginFact (*VD, *InitExpr);
477+ if (const Expr *InitExpr = VD->getInit ()) {
478+ killOrigin (VD);
479+ addOriginFlowFact (*VD, *InitExpr);
480+ }
458481 }
459482
460483 void VisitDeclRefExpr (const DeclRefExpr *DRE) {
@@ -492,9 +515,23 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
492515 isa<CXXConversionDecl>(MCE->getCalleeDecl ())) {
493516 // The argument is the implicit object itself.
494517 handleFunctionCall (MCE, MCE->getMethodDecl (),
495- {MCE->getImplicitObjectArgument ()});
518+ {MCE->getImplicitObjectArgument ()},
519+ /* IsGslConstruction=*/ true );
496520 }
497- // FIXME: A more general VisitCallExpr could also be used here.
521+ if (const CXXMethodDecl *Method = MCE->getMethodDecl ()) {
522+ // Construct the argument list, with the implicit 'this' object as the
523+ // first argument.
524+ llvm::SmallVector<const Expr *, 4 > Args;
525+ Args.push_back (MCE->getImplicitObjectArgument ());
526+ Args.append (MCE->getArgs (), MCE->getArgs () + MCE->getNumArgs ());
527+
528+ handleFunctionCall (MCE, Method, Args, /* IsGslConstruction=*/ false );
529+ }
530+ }
531+
532+ void VisitCallExpr (const CallExpr *CE) {
533+ handleFunctionCall (CE, CE->getDirectCallee (),
534+ {CE->getArgs (), CE->getNumArgs ()});
498535 }
499536
500537 void VisitCXXNullPtrLiteralExpr (const CXXNullPtrLiteralExpr *N) {
@@ -508,7 +545,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
508545 return ;
509546 // An ImplicitCastExpr node itself gets an origin, which flows from the
510547 // origin of its sub-expression (after stripping its own parens/casts).
511- addAssignOriginFact (*ICE, *ICE->getSubExpr ());
548+ addOriginFlowFact (*ICE, *ICE->getSubExpr ());
512549 }
513550
514551 void VisitUnaryOperator (const UnaryOperator *UO) {
@@ -522,7 +559,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
522559 // its sub-expression (x). This fact will cause the dataflow analysis
523560 // to propagate any loans held by the sub-expression's origin to the
524561 // origin of this UnaryOperator expression.
525- addAssignOriginFact (*UO, *SubExpr);
562+ addOriginFlowFact (*UO, *SubExpr);
526563 }
527564 }
528565
@@ -542,8 +579,15 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
542579 }
543580
544581 void VisitCXXOperatorCallExpr (const CXXOperatorCallExpr *OCE) {
545- if (OCE->isAssignmentOp () && OCE->getNumArgs () == 2 )
582+ // Assignment operators have special "kill-then-propagate" semantics
583+ // and are handled separately.
584+ if (OCE->isAssignmentOp () && OCE->getNumArgs () == 2 ) {
546585 handleAssignment (OCE->getArg (0 ), OCE->getArg (1 ));
586+ return ;
587+ }
588+ handleFunctionCall (OCE, OCE->getDirectCallee (),
589+ {OCE->getArgs (), OCE->getNumArgs ()},
590+ /* IsGslConstruction=*/ false );
547591 }
548592
549593 void VisitCXXFunctionalCastExpr (const CXXFunctionalCastExpr *FCE) {
@@ -552,7 +596,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
552596 if (handleTestPoint (FCE))
553597 return ;
554598 if (isGslPointerType (FCE->getType ()))
555- addAssignOriginFact (*FCE, *FCE->getSubExpr ());
599+ addOriginFlowFact (*FCE, *FCE->getSubExpr ());
556600 }
557601
558602 void VisitInitListExpr (const InitListExpr *ILE) {
@@ -561,15 +605,15 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
561605 // For list initialization with a single element, like `View{...}`, the
562606 // origin of the list itself is the origin of its single element.
563607 if (ILE->getNumInits () == 1 )
564- addAssignOriginFact (*ILE, *ILE->getInit (0 ));
608+ addOriginFlowFact (*ILE, *ILE->getInit (0 ));
565609 }
566610
567611 void VisitMaterializeTemporaryExpr (const MaterializeTemporaryExpr *MTE) {
568612 if (!hasOrigin (MTE))
569613 return ;
570614 // A temporary object's origin is the same as the origin of the
571615 // expression that initializes it.
572- addAssignOriginFact (*MTE, *MTE->getSubExpr ());
616+ addOriginFlowFact (*MTE, *MTE->getSubExpr ());
573617 }
574618
575619 void handleDestructor (const CFGAutomaticObjDtor &DtorOpt) {
@@ -624,34 +668,68 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
624668 if (CCE->getNumArgs () != 1 )
625669 return ;
626670 if (hasOrigin (CCE->getArg (0 )))
627- addAssignOriginFact (*CCE, *CCE->getArg (0 ));
671+ addOriginFlowFact (*CCE, *CCE->getArg (0 ));
628672 else
629673 // This could be a new borrow.
630674 handleFunctionCall (CCE, CCE->getConstructor (),
631- {CCE->getArgs (), CCE->getNumArgs ()});
675+ {CCE->getArgs (), CCE->getNumArgs ()},
676+ /* IsGslConstruction=*/ true );
677+ }
678+ static const FunctionDecl *
679+ getDeclWithMergedLifetimeBoundAttrs (const FunctionDecl *FD) {
680+ return FD != nullptr ? FD->getMostRecentDecl () : nullptr ;
632681 }
633682
683+ static const CXXMethodDecl *
684+ getDeclWithMergedLifetimeBoundAttrs (const CXXMethodDecl *CMD) {
685+ const FunctionDecl *FD = CMD;
686+ return cast_if_present<CXXMethodDecl>(
687+ getDeclWithMergedLifetimeBoundAttrs (FD));
688+ }
689+ static bool implicitObjectParamIsLifetimeBound (const FunctionDecl *FD) {
690+ FD = getDeclWithMergedLifetimeBoundAttrs (FD);
691+ const TypeSourceInfo *TSI = FD->getTypeSourceInfo ();
692+ if (!TSI)
693+ return false ;
694+ // Don't declare this variable in the second operand of the for-statement;
695+ // GCC miscompiles that by ending its lifetime before evaluating the
696+ // third operand. See gcc.gnu.org/PR86769.
697+ AttributedTypeLoc ATL;
698+ for (TypeLoc TL = TSI->getTypeLoc ();
699+ (ATL = TL.getAsAdjusted <AttributedTypeLoc>());
700+ TL = ATL.getModifiedLoc ()) {
701+ if (ATL.getAttrAs <LifetimeBoundAttr>())
702+ return true ;
703+ }
704+ return false ;
705+ }
634706 // / Checks if a call-like expression creates a borrow by passing a value to a
635707 // / reference parameter, creating an IssueFact if it does.
636708 void handleFunctionCall (const Expr *Call, const FunctionDecl *FD,
637- ArrayRef<const Expr *> Args) {
638- if (!FD)
709+ ArrayRef<const Expr *> Args,
710+ bool IsGslConstruction = false ) {
711+ // Ignore functions returning values with no origin.
712+ if (!FD || !hasOrigin (Call))
639713 return ;
640- // TODO: Handle more than one arguments.
641- for (unsigned I = 0 ; I <= 0 /* Args.size()*/ ; ++I) {
642- const Expr *ArgExpr = Args[I];
643-
644- // Propagate origins for CXX this.
645- if (FD->isCXXClassMember () && I == 0 ) {
646- addAssignOriginFact (*Call, *ArgExpr);
647- continue ;
648- }
649- // The parameter is a pointer, reference, or gsl::Pointer.
650- // This is a borrow. We propagate the origin from the argument expression
651- // at the call site to the parameter declaration in the callee.
652- if (hasOrigin (ArgExpr))
653- addAssignOriginFact (*Call, *ArgExpr);
654- }
714+ auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
715+ const ParmVarDecl *PVD = nullptr ;
716+ if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
717+ Method && Method->isInstance ()) {
718+ if (I == 0 )
719+ // For the 'this' argument, the attribute is on the method itself.
720+ return implicitObjectParamIsLifetimeBound (Method);
721+ if ((I - 1 ) < Method->getNumParams ())
722+ // For explicit arguments, find the corresponding parameter
723+ // declaration.
724+ PVD = Method->getParamDecl (I - 1 );
725+ } else if (I < FD->getNumParams ())
726+ // For free functions or static methods.
727+ PVD = FD->getParamDecl (I);
728+ return PVD ? PVD->hasAttr <clang::LifetimeBoundAttr>() : false ;
729+ };
730+ for (unsigned I = 0 ; I < Args.size (); ++I)
731+ if (IsGslConstruction || IsArgLifetimeBound (I))
732+ addOriginFlowFact (*Call, *Args[I]);
655733 }
656734
657735 // / Creates a loan for the storage path of a given declaration reference.
@@ -668,11 +746,16 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
668746 }
669747
670748 template <typename Destination, typename Source>
671- void addAssignOriginFact (const Destination &D, const Source &S) {
749+ void addOriginFlowFact (const Destination &D, const Source &S) {
672750 OriginID DestOID = FactMgr.getOriginMgr ().getOrCreate (D);
673751 OriginID SrcOID = FactMgr.getOriginMgr ().get (S);
674752 CurrentBlockFacts.push_back (
675- FactMgr.createFact <AssignOriginFact>(DestOID, SrcOID));
753+ FactMgr.createFact <OriginFlowFact>(DestOID, SrcOID));
754+ }
755+
756+ void killOrigin (const ValueDecl *D) {
757+ OriginID DestOID = FactMgr.getOriginMgr ().getOrCreate (*D);
758+ CurrentBlockFacts.push_back (FactMgr.createFact <KillOriginFact>(DestOID));
676759 }
677760
678761 // / Checks if the expression is a `void("__lifetime_test_point_...")` cast.
@@ -703,12 +786,12 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
703786 if (const auto *DRE_LHS =
704787 dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts ())) {
705788 markUseAsWrite (DRE_LHS);
706- if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl ()))
707- // We are interested in assignments like `ptr1 = ptr2` or `ptr = &var`.
708- // LHS must be a pointer/reference type that can be an origin. RHS must
709- // also represent an origin (either another pointer/ref or an
710- // address-of).
711- addAssignOriginFact (*VD_LHS, *RHSExpr);
789+ if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl ())) {
790+ // Kill the old loans of the destination origin and flow the new loans
791+ // from the source origin.
792+ killOrigin (VD_LHS);
793+ addOriginFlowFact (*VD_LHS, *RHSExpr);
794+ }
712795 }
713796 }
714797
@@ -882,8 +965,10 @@ class DataflowAnalysis {
882965 return D->transfer (In, *F->getAs <IssueFact>());
883966 case Fact::Kind::Expire:
884967 return D->transfer (In, *F->getAs <ExpireFact>());
885- case Fact::Kind::AssignOrigin:
886- return D->transfer (In, *F->getAs <AssignOriginFact>());
968+ case Fact::Kind::OriginFlow:
969+ return D->transfer (In, *F->getAs <OriginFlowFact>());
970+ case Fact::Kind::KillOrigin:
971+ return D->transfer (In, *F->getAs <KillOriginFact>());
887972 case Fact::Kind::ReturnOfOrigin:
888973 return D->transfer (In, *F->getAs <ReturnOfOriginFact>());
889974 case Fact::Kind::Use:
@@ -897,7 +982,8 @@ class DataflowAnalysis {
897982public:
898983 Lattice transfer (Lattice In, const IssueFact &) { return In; }
899984 Lattice transfer (Lattice In, const ExpireFact &) { return In; }
900- Lattice transfer (Lattice In, const AssignOriginFact &) { return In; }
985+ Lattice transfer (Lattice In, const OriginFlowFact &) { return In; }
986+ Lattice transfer (Lattice In, const KillOriginFact &) { return In; }
901987 Lattice transfer (Lattice In, const ReturnOfOriginFact &) { return In; }
902988 Lattice transfer (Lattice In, const UseFact &) { return In; }
903989 Lattice transfer (Lattice In, const TestPointFact &) { return In; }
@@ -1049,14 +1135,27 @@ class LoanPropagationAnalysis
10491135 LoanSetFactory.add (LoanSetFactory.getEmptySet (), LID)));
10501136 }
10511137
1052- // / The destination origin's loan set is replaced by the source's.
1053- // / This implicitly "resets" the old loans of the destination .
1054- Lattice transfer (Lattice In, const AssignOriginFact &F) {
1138+ // / A flow from source to destination adds the source's loans to the
1139+ // / destination's, without clearing the destination's existing loans .
1140+ Lattice transfer (Lattice In, const OriginFlowFact &F) {
10551141 OriginID DestOID = F.getDestOriginID ();
10561142 OriginID SrcOID = F.getSrcOriginID ();
1143+
1144+ LoanSet DestLoans = getLoans (In, DestOID);
10571145 LoanSet SrcLoans = getLoans (In, SrcOID);
1146+ LoanSet MergedLoans = utils::join (DestLoans, SrcLoans, LoanSetFactory);
1147+
10581148 return LoanPropagationLattice (
1059- OriginLoanMapFactory.add (In.Origins , DestOID, SrcLoans));
1149+ OriginLoanMapFactory.add (In.Origins , DestOID, MergedLoans));
1150+ }
1151+
1152+ // / Clears the loan set of the specified origin. This is used on the
1153+ // / left-hand side of an assignment to invalidate the variable's old lifetime.
1154+ Lattice transfer (Lattice In, const KillOriginFact &F) {
1155+ OriginID OID = F.getOriginID ();
1156+ // Replace the origin's loan set with an empty set.
1157+ return LoanPropagationLattice (OriginLoanMapFactory.add (
1158+ In.Origins , OID, LoanSetFactory.getEmptySet ()));
10601159 }
10611160
10621161 LoanSet getLoans (OriginID OID, ProgramPoint P) {
0 commit comments