Skip to content

Commit 0e17fcf

Browse files
authored
[LifetimeSafety] Implement support for lifetimebound attribute (#158489)
Add support for `lifetimebound` attributes in the lifetime safety analysis to track loans from function parameters to return values. Implemented support for `lifetimebound` attributes on function parameters This change replaces the single `AssignOriginFact` with two separate operations: `OriginFlowFact` and `KillOriginFact`. The key difference is in semantics: * Old `AssignOriginFact`: Replaced the destination origin's loans entirely with the source origin's loans. * New `OriginFlowFact`: Can now optionally merge the source origin's loans to the destination's existing loans. * New `KillOriginFact`: Clears all loans from an origin. For function calls with `lifetimebound` parameters, we kill the the return value' origin first then use `OriginFlowFact` to accumulate loans from multiple parameters into the return value's origin - enabling tracking multiple lifetimebound arguments. - Added a new `LifetimeAnnotations.h/cpp` to provide helper functions for inspecting and inferring lifetime annotations - Moved several functions from `CheckExprLifetime.cpp` to the new file to make them reusable The `lifetimebound` attribute is a key mechanism for expressing lifetime dependencies between function parameters and return values. This change enables the lifetime safety analysis to properly track these dependencies, allowing it to detect more potential dangling reference issues.
1 parent 3008367 commit 0e17fcf

File tree

12 files changed

+758
-206
lines changed

12 files changed

+758
-206
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===- LifetimeAnnotations.h - -*--------------- C++--------------------*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
// Helper functions to inspect and infer lifetime annotations.
9+
//===----------------------------------------------------------------------===//
10+
#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMEANNOTATIONS_H
11+
#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMEANNOTATIONS_H
12+
13+
#include "clang/AST/DeclCXX.h"
14+
15+
namespace clang {
16+
namespace lifetimes {
17+
18+
/// Returns the most recent declaration of the method to ensure all
19+
/// lifetime-bound attributes from redeclarations are considered.
20+
const FunctionDecl *getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD);
21+
22+
/// Returns the most recent declaration of the method to ensure all
23+
/// lifetime-bound attributes from redeclarations are considered.
24+
const CXXMethodDecl *
25+
getDeclWithMergedLifetimeBoundAttrs(const CXXMethodDecl *CMD);
26+
27+
// Return true if this is an "normal" assignment operator.
28+
// We assume that a normal assignment operator always returns *this, that is,
29+
// an lvalue reference that is the same type as the implicit object parameter
30+
// (or the LHS for a non-member operator==).
31+
bool isNormalAssignmentOperator(const FunctionDecl *FD);
32+
33+
/// Returns true if this is an assignment operator where the parameter
34+
/// has the lifetimebound attribute.
35+
bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD);
36+
37+
/// Returns true if the implicit object parameter (this) should be considered
38+
/// lifetimebound, either due to an explicit lifetimebound attribute on the
39+
/// method or because it's a normal assignment operator.
40+
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD);
41+
} // namespace lifetimes
42+
} // namespace clang
43+
44+
#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMEANNOTATIONS_H

clang/include/clang/Analysis/Analyses/LifetimeSafety.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,14 @@ template <typename Tag> struct ID {
7575
}
7676
};
7777

78-
template <typename Tag>
79-
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
80-
return OS << ID.Value;
81-
}
82-
8378
using LoanID = ID<struct LoanTag>;
8479
using OriginID = ID<struct OriginTag>;
80+
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, LoanID ID) {
81+
return OS << ID.Value;
82+
}
83+
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, OriginID ID) {
84+
return OS << ID.Value;
85+
}
8586

8687
// Using LLVM's immutable collections is efficient for dataflow analysis
8788
// as it avoids deep copies during state transitions.

clang/lib/Analysis/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ add_clang_library(clangAnalysis
2121
FixitUtil.cpp
2222
IntervalPartition.cpp
2323
IssueHash.cpp
24+
LifetimeAnnotations.cpp
2425
LifetimeSafety.cpp
2526
LiveVariables.cpp
2627
MacroExpansionContext.cpp
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//===- LifetimeAnnotations.cpp - -*--------------- C++------------------*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
#include "clang/Analysis/Analyses/LifetimeAnnotations.h"
9+
#include "clang/AST/ASTContext.h"
10+
#include "clang/AST/Attr.h"
11+
#include "clang/AST/Decl.h"
12+
#include "clang/AST/DeclCXX.h"
13+
#include "clang/AST/Type.h"
14+
#include "clang/AST/TypeLoc.h"
15+
16+
namespace clang {
17+
namespace lifetimes {
18+
19+
const FunctionDecl *
20+
getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD) {
21+
return FD != nullptr ? FD->getMostRecentDecl() : nullptr;
22+
}
23+
24+
const CXXMethodDecl *
25+
getDeclWithMergedLifetimeBoundAttrs(const CXXMethodDecl *CMD) {
26+
const FunctionDecl *FD = CMD;
27+
return cast_if_present<CXXMethodDecl>(
28+
getDeclWithMergedLifetimeBoundAttrs(FD));
29+
}
30+
31+
bool isNormalAssignmentOperator(const FunctionDecl *FD) {
32+
OverloadedOperatorKind OO = FD->getDeclName().getCXXOverloadedOperator();
33+
bool IsAssignment = OO == OO_Equal || isCompoundAssignmentOperator(OO);
34+
if (!IsAssignment)
35+
return false;
36+
QualType RetT = FD->getReturnType();
37+
if (!RetT->isLValueReferenceType())
38+
return false;
39+
ASTContext &Ctx = FD->getASTContext();
40+
QualType LHST;
41+
auto *MD = dyn_cast<CXXMethodDecl>(FD);
42+
if (MD && MD->isCXXInstanceMember())
43+
LHST = Ctx.getLValueReferenceType(MD->getFunctionObjectParameterType());
44+
else
45+
LHST = FD->getParamDecl(0)->getType();
46+
return Ctx.hasSameType(RetT, LHST);
47+
}
48+
49+
bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD) {
50+
CMD = getDeclWithMergedLifetimeBoundAttrs(CMD);
51+
return CMD && isNormalAssignmentOperator(CMD) && CMD->param_size() == 1 &&
52+
CMD->getParamDecl(0)->hasAttr<clang::LifetimeBoundAttr>();
53+
}
54+
55+
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
56+
FD = getDeclWithMergedLifetimeBoundAttrs(FD);
57+
const TypeSourceInfo *TSI = FD->getTypeSourceInfo();
58+
if (!TSI)
59+
return false;
60+
// Don't declare this variable in the second operand of the for-statement;
61+
// GCC miscompiles that by ending its lifetime before evaluating the
62+
// third operand. See gcc.gnu.org/PR86769.
63+
AttributedTypeLoc ATL;
64+
for (TypeLoc TL = TSI->getTypeLoc();
65+
(ATL = TL.getAsAdjusted<AttributedTypeLoc>());
66+
TL = ATL.getModifiedLoc()) {
67+
if (ATL.getAttrAs<clang::LifetimeBoundAttr>())
68+
return true;
69+
}
70+
71+
return isNormalAssignmentOperator(FD);
72+
}
73+
74+
} // namespace lifetimes
75+
} // namespace clang

0 commit comments

Comments
 (0)