Skip to content

Commit 271e174

Browse files
committed
lifetime-analysis-lifetimebound
1 parent 53adfd8 commit 271e174

File tree

12 files changed

+736
-206
lines changed

12 files changed

+736
-206
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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_AST_LIFETIMEANNOTATIONS_H
11+
#define LLVM_CLANG_AST_LIFETIMEANNOTATIONS_H
12+
13+
#include "clang/AST/DeclCXX.h"
14+
15+
namespace clang {
16+
namespace lifetimes {
17+
18+
const FunctionDecl *getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD);
19+
20+
const CXXMethodDecl *
21+
getDeclWithMergedLifetimeBoundAttrs(const CXXMethodDecl *CMD);
22+
23+
// Return true if this is an "normal" assignment operator.
24+
// We assume that a normal assignment operator always returns *this, that is,
25+
// an lvalue reference that is the same type as the implicit object parameter
26+
// (or the LHS for a non-member operator$=).
27+
bool isNormalAssignmentOperator(const FunctionDecl *FD);
28+
29+
bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD);
30+
31+
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD);
32+
} // namespace lifetimes
33+
} // namespace clang
34+
35+
#endif // LLVM_CLANG_AST_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: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#include "clang/Analysis/Analyses/LifetimeAnnotations.h"
2+
#include "clang/AST/ASTContext.h"
3+
#include "clang/AST/Attr.h"
4+
#include "clang/AST/Decl.h"
5+
#include "clang/AST/DeclCXX.h"
6+
#include "clang/AST/Type.h"
7+
#include "clang/AST/TypeLoc.h"
8+
9+
namespace clang {
10+
namespace lifetimes {
11+
12+
const FunctionDecl *
13+
getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD) {
14+
return FD != nullptr ? FD->getMostRecentDecl() : nullptr;
15+
}
16+
17+
const CXXMethodDecl *
18+
getDeclWithMergedLifetimeBoundAttrs(const CXXMethodDecl *CMD) {
19+
const FunctionDecl *FD = CMD;
20+
return cast_if_present<CXXMethodDecl>(
21+
getDeclWithMergedLifetimeBoundAttrs(FD));
22+
}
23+
24+
// Return true if this is an "normal" assignment operator.
25+
// We assume that a normal assignment operator always returns *this, that is,
26+
// an lvalue reference that is the same type as the implicit object parameter
27+
// (or the LHS for a non-member operator$=).
28+
bool isNormalAssignmentOperator(const FunctionDecl *FD) {
29+
OverloadedOperatorKind OO = FD->getDeclName().getCXXOverloadedOperator();
30+
if (OO == OO_Equal || isCompoundAssignmentOperator(OO)) {
31+
QualType RetT = FD->getReturnType();
32+
if (RetT->isLValueReferenceType()) {
33+
ASTContext &Ctx = FD->getASTContext();
34+
QualType LHST;
35+
auto *MD = dyn_cast<CXXMethodDecl>(FD);
36+
if (MD && MD->isCXXInstanceMember())
37+
LHST = Ctx.getLValueReferenceType(MD->getFunctionObjectParameterType());
38+
else
39+
LHST = FD->getParamDecl(0)->getType();
40+
if (Ctx.hasSameType(RetT, LHST))
41+
return true;
42+
}
43+
}
44+
return false;
45+
}
46+
47+
bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD) {
48+
CMD = lifetimes::getDeclWithMergedLifetimeBoundAttrs(CMD);
49+
return CMD && isNormalAssignmentOperator(CMD) && CMD->param_size() == 1 &&
50+
CMD->getParamDecl(0)->hasAttr<clang::LifetimeBoundAttr>();
51+
}
52+
53+
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
54+
FD = getDeclWithMergedLifetimeBoundAttrs(FD);
55+
const TypeSourceInfo *TSI = FD->getTypeSourceInfo();
56+
if (!TSI)
57+
return false;
58+
// Don't declare this variable in the second operand of the for-statement;
59+
// GCC miscompiles that by ending its lifetime before evaluating the
60+
// third operand. See gcc.gnu.org/PR86769.
61+
AttributedTypeLoc ATL;
62+
for (TypeLoc TL = TSI->getTypeLoc();
63+
(ATL = TL.getAsAdjusted<AttributedTypeLoc>());
64+
TL = ATL.getModifiedLoc()) {
65+
if (ATL.getAttrAs<clang::LifetimeBoundAttr>())
66+
return true;
67+
}
68+
69+
return isNormalAssignmentOperator(FD);
70+
}
71+
72+
} // namespace lifetimes
73+
} // namespace clang

0 commit comments

Comments
 (0)