Skip to content

Commit

Permalink
Bug 1699844 - Add an escape hatch for the refcounted inside lambda ch…
Browse files Browse the repository at this point in the history
…ecker. r=andi

Allow using the MOZ_KnownLive function to get around it.

Use case is the following: I have an std::function member variable, and I want
that member to be able to capture `this`.

Using a strong reference creates a cycle and thus would leak. I know `this` to
always outlive the member, so it is fine to use a weak capture there.

Differential Revision: https://phabricator.services.mozilla.com/D111850
  • Loading branch information
emilio committed Apr 14, 2021
1 parent 85fc3ac commit 64e3675
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 5 deletions.
22 changes: 17 additions & 5 deletions build/clang-plugin/RefCountedInsideLambdaChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ void RefCountedInsideLambdaChecker::emitDiagnostics(SourceLocation Loc,
diag(Loc, "Please consider using a smart pointer", DiagnosticIDs::Note);
}

static bool IsKnownLive(const VarDecl *Var) {
const Stmt *Init = Var->getInit();
if (!Init) {
return false;
}
if (auto *Call = dyn_cast<CallExpr>(Init)) {
const FunctionDecl *Callee = Call->getDirectCallee();
return Callee && Callee->getName() == "MOZ_KnownLive";
}
return false;
}

void RefCountedInsideLambdaChecker::check(
const MatchFinder::MatchResult &Result) {
static DenseSet<const CXXRecordDecl *> CheckedDecls;
Expand Down Expand Up @@ -107,11 +119,11 @@ void RefCountedInsideLambdaChecker::check(
// pointers.
for (const LambdaCapture &Capture : Lambda->captures()) {
if (Capture.capturesVariable()) {
QualType Pointee = Capture.getCapturedVar()->getType()->getPointeeType();

if (!Pointee.isNull() && isClassRefCounted(Pointee)) {
emitDiagnostics(Capture.getLocation(),
Capture.getCapturedVar()->getName(), Pointee);
const VarDecl *Var = Capture.getCapturedVar();
QualType Pointee = Var->getType()->getPointeeType();
if (!Pointee.isNull() && isClassRefCounted(Pointee) &&
!IsKnownLive(Var)) {
emitDiagnostics(Capture.getLocation(), Var->getName(), Pointee);
return;
}
}
Expand Down
5 changes: 5 additions & 0 deletions build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include <mozilla/StaticAnalysisFunctions.h>

#include <functional>
#define MOZ_STRONG_REF
#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
Expand Down Expand Up @@ -667,6 +669,9 @@ R::privateMethod() {
privateMethod();
});

std::function<void()>(
[instance = MOZ_KnownLive(this)]() { instance->privateMethod(); });

// It should be OK to go through `this` if we have captured a reference to it.
std::function<void()>([this, self]() {
this->method();
Expand Down
1 change: 1 addition & 0 deletions mfbt/StaticAnalysisFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# endif
# define MOZ_CONSTEXPR
#else // __cplusplus
# include "mozilla/Attributes.h"
# define MOZ_CONSTEXPR constexpr
#endif
/*
Expand Down

0 comments on commit 64e3675

Please sign in to comment.