[PATCH] D152504: [clang][ThreadSafety] Analyze cleanup functions
Aaron Puchert via Phabricator via cfe-commits
cfe-commits at lists.llvm.org
Sun Aug 27 09:42:01 PDT 2023
aaronpuchert added a comment.
Sorry that it took so long, I had to debug it and think a bit. This should do it:
diff --git a/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h b/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
index 9d28325c1ea6..13e37ac2b56b 100644
--- a/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
+++ b/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
@@ -361,7 +361,7 @@ public:
unsigned NumArgs = 0;
// Function arguments
- const Expr *const *FunArgs = nullptr;
+ llvm::PointerUnion<const Expr *const *, til::SExpr *> FunArgs = nullptr;
// is Self referred to with -> or .?
bool SelfArrow = false;
diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp
index b8286cef396c..1eb48c0d5554 100644
--- a/clang/lib/Analysis/ThreadSafetyCommon.cpp
+++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp
@@ -110,7 +110,8 @@ static StringRef ClassifyDiagnostic(QualType VDT) {
/// \param D The declaration to which the attribute is attached.
/// \param DeclExp An expression involving the Decl to which the attribute
/// is attached. E.g. the call to a function.
-/// \param Self S-expression to substitute for a \ref CXXThisExpr.
+/// \param Self S-expression to substitute for a \ref CXXThisExpr in a call,
+/// or argument to a cleanup function.
CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
const NamedDecl *D,
const Expr *DeclExp,
@@ -144,7 +145,11 @@ CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
if (Self) {
assert(!Ctx.SelfArg && "Ambiguous self argument");
- Ctx.SelfArg = Self;
+ assert(isa<FunctionDecl>(D) && "Self argument requires function");
+ if (isa<CXXMethodDecl>(D))
+ Ctx.SelfArg = Self;
+ else
+ Ctx.FunArgs = Self;
// If the attribute has no arguments, then assume the argument is "this".
if (!AttrExp)
@@ -312,8 +317,14 @@ til::SExpr *SExprBuilder::translateDeclRefExpr(const DeclRefExpr *DRE,
? (cast<FunctionDecl>(D)->getCanonicalDecl() == Canonical)
: (cast<ObjCMethodDecl>(D)->getCanonicalDecl() == Canonical)) {
// Substitute call arguments for references to function parameters
- assert(I < Ctx->NumArgs);
- return translate(Ctx->FunArgs[I], Ctx->Prev);
+ if (const Expr *const *FunArgs =
+ Ctx->FunArgs.dyn_cast<const Expr *const *>()) {
+ assert(I < Ctx->NumArgs);
+ return translate(FunArgs[I], Ctx->Prev);
+ } else {
+ assert(I == 0);
+ return Ctx->FunArgs.get<til::SExpr *>();
+ }
}
}
// Map the param back to the param of the original function declaration
The idea (which might be expressed in additional comments) is that until now we only had `Self` for `CXXMethodDecl`, but now we also have it for other `FunctionDecl`, in which case it is to be understood as the sole function argument.
================
Comment at: clang/lib/Analysis/ThreadSafety.cpp:1776
/// \param D The callee declaration.
/// \param Self If \p Exp = nullptr, the implicit this argument.
/// \param Loc If \p Exp = nullptr, the location.
----------------
We should relax this like "the implicit this argument or the argument to an implicitly called cleanup function".
================
Comment at: clang/lib/Analysis/ThreadSafety.cpp:2436
+ CF.getVarDecl()->getLocation());
+ break;
+ }
----------------
tbaeder wrote:
> tbaeder wrote:
> > tbaeder wrote:
> > > aaronpuchert wrote:
> > > > aaronpuchert wrote:
> > > > > tbaeder wrote:
> > > > > > This handles the function call, but without the instance parameter. I was wondering how to best do that.
> > > > > Should you not simply pass `SxBuilder.createVariable(CF.getVarDecl())` as third parameter in analogy with the `AutomaticObjectDtor` case? It might also make sense to copy the attribute check.
> > > > Can you write a test case that relies on passing the variable? Here is an idea:
> > > > ```
> > > > void unlock_scope(Mutex **mu) __attribute__((release_capability(*mu))) {
> > > > mutex_exclusive_unlock(*mu);
> > > > }
> > > >
> > > > Mutex* const CLEANUP(unlock_scope) scope = &mu1;
> > > > mutex_exclusive_lock(*scope);
> > > > // Unlock should happen automatically.
> > > > ```
> > > > I think this is mildly more interesting than the cleanup function with an unused parameter.
> > > >
> > > > Unfortunately this is not quite as powerful as a scoped lock in C++, as we don't track the identity `scope == &mu1`. So `guarded_by` won't work with this. But we can at least see warnings on balanced locking/unlocking.
> > > >
> > > > As for proper scoped locking, we could treat some variable initializations like construction of a C++ scoped lock. But let's discuss this separately.
> > > Yeah, it doesn't find the lock; I assume that's because the first parameter here is not an _actual_ this parameter; I'd have to handle the var decl as a regular parameter.
> > @aaronpuchert Can you explain how that would work? One goal from before was to avoid creating new fake AST nodes, would I'd have to insert the instance parameter as a new `DeclRefExpr` in the CFG, wouldn't I?
> Ping
> I assume that's because the first parameter here is not an _actual_ this parameter; I'd have to handle the var decl as a regular parameter.
Right, in `SExprBuilder::translateAttrExpr` we put `Self` into `Ctx.SelfArg`, but this is only used for substituting a `CXXThisExpr`, not a function parameter.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D152504/new/
https://reviews.llvm.org/D152504
More information about the cfe-commits
mailing list