[cfe-commits] r138350 - in /cfe/trunk: include/clang/Basic/Attr.td include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/AnalysisBasedWarnings.h lib/Sema/AnalysisBasedWarnings.cpp lib/Sema/SemaDeclAttr.cpp test/SemaCXX/warn-thread-safety-analysis.cpp test/SemaCXX/warn-thread-safety-parsing.cpp test/SemaCXX/warn-thread-safety.cpp
Caitlin Sadowski
supertri at google.com
Tue Aug 23 11:46:35 PDT 2011
Author: supertri
Date: Tue Aug 23 13:46:34 2011
New Revision: 138350
URL: http://llvm.org/viewvc/llvm-project?rev=138350&view=rev
Log:
Thread-safety analysis: adding in a basic lockset tracking system. This
system flags an error when unlocking a lock which was not held, locking
the same lock twice, having a different lockset on each iteration of a
loop, or going out of scope while still holding a lock. In order to
successfully use the lockset, this patch also makes sure that attribute
arguments are attached correctly for later parsing.
This patch was also worked on by DeLesley Hutchins.
Note: This patch has been reviewed by Chandler Carruth and Jeffrey
Yasskin. Feel free to provide post-commit review comments for a
subsequent patch.
Added:
cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp
cfe/trunk/test/SemaCXX/warn-thread-safety-parsing.cpp
- copied, changed from r138343, cfe/trunk/test/SemaCXX/warn-thread-safety.cpp
Removed:
cfe/trunk/test/SemaCXX/warn-thread-safety.cpp
Modified:
cfe/trunk/include/clang/Basic/Attr.td
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h
cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
cfe/trunk/lib/Sema/SemaDeclAttr.cpp
Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=138350&r1=138349&r2=138350&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Tue Aug 23 13:46:34 2011
@@ -55,6 +55,7 @@
class TypeArgument<string name> : Argument<name>;
class UnsignedArgument<string name> : Argument<name>;
class VariadicUnsignedArgument<string name> : Argument<name>;
+class VariadicExprArgument<string name> : Argument<name>;
// A version of the form major.minor[.subminor].
class VersionArgument<string name> : Argument<name>;
@@ -564,52 +565,65 @@
def GuardedBy : InheritableAttr {
let Spellings = ["guarded_by"];
+ let Args = [ExprArgument<"Arg">];
}
def PtGuardedBy : InheritableAttr {
let Spellings = ["pt_guarded_by"];
+ let Args = [ExprArgument<"Arg">];
}
def AcquiredAfter : InheritableAttr {
let Spellings = ["acquired_after"];
+ let Args = [VariadicExprArgument<"Args">];
}
def AcquiredBefore : InheritableAttr {
let Spellings = ["acquired_before"];
+ let Args = [VariadicExprArgument<"Args">];
}
def ExclusiveLockFunction : InheritableAttr {
let Spellings = ["exclusive_lock_function"];
+ let Args = [VariadicExprArgument<"Args">];
}
def SharedLockFunction : InheritableAttr {
let Spellings = ["shared_lock_function"];
+ let Args = [VariadicExprArgument<"Args">];
}
def ExclusiveTrylockFunction : InheritableAttr {
let Spellings = ["exclusive_trylock_function"];
+ let Args = [VariadicExprArgument<"Args">];
}
def SharedTrylockFunction : InheritableAttr {
let Spellings = ["shared_trylock_function"];
+ let Args = [VariadicExprArgument<"Args">];
}
def UnlockFunction : InheritableAttr {
let Spellings = ["unlock_function"];
+ let Args = [VariadicExprArgument<"Args">];
}
def LockReturned : InheritableAttr {
let Spellings = ["lock_returned"];
+ let Args = [ExprArgument<"Arg">];
}
def LocksExcluded : InheritableAttr {
let Spellings = ["locks_excluded"];
+ let Args = [VariadicExprArgument<"Args">];
}
def ExclusiveLocksRequired : InheritableAttr {
let Spellings = ["exclusive_locks_required"];
+ let Args = [VariadicExprArgument<"Args">];
}
def SharedLocksRequired : InheritableAttr {
let Spellings = ["shared_locks_required"];
+ let Args = [VariadicExprArgument<"Args">];
}
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=138350&r1=138349&r2=138350&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Aug 23 13:46:34 2011
@@ -1383,7 +1383,24 @@
def err_attribute_decl_not_lockable : Error<
"%0 attribute can only be applied in a context annotated "
"with 'lockable' attribute">;
-
+def warn_unlock_but_no_acquire : Warning<
+ "unlocking '%0' that was not acquired">,
+ InGroup<DiagGroup<"thread-safety">>, DefaultIgnore;
+def warn_double_lock : Warning<
+ "locking '%0' that is already acquired">,
+ InGroup<DiagGroup<"thread-safety">>, DefaultIgnore;
+def warn_function_requires_lock : Warning<
+ "calling function '%0' requires lock '%0'">,
+ InGroup<DiagGroup<"thread-safety">>, DefaultIgnore;
+def warn_locks_not_released : Warning<
+ "lock '%0' is not released at the end of function '%1'">,
+ InGroup<DiagGroup<"thread-safety">>, DefaultIgnore;
+def warn_lock_not_released_in_scope : Warning<
+ "lock '%0' is not released at the end of its scope">,
+ InGroup<DiagGroup<"thread-safety">>, DefaultIgnore;
+def warn_expecting_lock_held_on_loop : Warning<
+ "expecting lock '%0' to be held at start of each loop">,
+ InGroup<DiagGroup<"thread-safety">>, DefaultIgnore;
def warn_impcast_vector_scalar : Warning<
"implicit conversion turns vector to scalar: %0 to %1">,
Modified: cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h?rev=138350&r1=138349&r2=138350&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h (original)
+++ cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h Tue Aug 23 13:46:34 2011
@@ -37,6 +37,7 @@
// The warnings to run.
unsigned enableCheckFallThrough : 1;
unsigned enableCheckUnreachable : 1;
+ unsigned enableThreadSafetyAnalysis : 1;
public:
Policy();
void disableCheckFallThrough() { enableCheckFallThrough = 0; }
Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=138350&r1=138349&r2=138350&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Tue Aug 23 13:46:34 2011
@@ -25,6 +25,7 @@
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/EvaluatedExprVisitor.h"
+#include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/Analyses/ReachableCode.h"
@@ -32,7 +33,13 @@
#include "clang/Analysis/CFGStmtMap.h"
#include "clang/Analysis/Analyses/UninitializedValues.h"
#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
+#include <algorithm>
+#include <vector>
using namespace clang;
@@ -585,6 +592,551 @@
};
}
+
+//===----------------------------------------------------------------------===//
+// -Wthread-safety
+//===----------------------------------------------------------------------===//
+
+namespace {
+/// \brief Implements a set of CFGBlocks using a BitVector.
+///
+/// This class contains a minimal interface, primarily dictated by the SetType
+/// template parameter of the llvm::po_iterator template, as used with external
+/// storage. We also use this set to keep track of which CFGBlocks we visit
+/// during the analysis.
+class CFGBlockSet {
+ llvm::BitVector VisitedBlockIDs;
+
+public:
+ // po_iterator requires this iterator, but the only interface needed is the
+ // value_type typedef.
+ struct iterator {
+ typedef const CFGBlock *value_type;
+ };
+
+ CFGBlockSet() {}
+ CFGBlockSet(const CFG *G) : VisitedBlockIDs(G->getNumBlockIDs(), false) {}
+
+ /// \brief Set the bit associated with a particular CFGBlock.
+ /// This is the important method for the SetType template parameter.
+ bool insert(const CFGBlock *Block) {
+ if (VisitedBlockIDs.test(Block->getBlockID()))
+ return false;
+ VisitedBlockIDs.set(Block->getBlockID());
+ return true;
+ }
+
+ /// \brief Check if the bit for a CFGBlock has been already set.
+ /// This mehtod is for tracking visited blocks in the main threadsafety loop.
+ bool alreadySet(const CFGBlock *Block) {
+ return VisitedBlockIDs.test(Block->getBlockID());
+ }
+};
+
+/// \brief We create a helper class which we use to iterate through CFGBlocks in
+/// the topological order.
+class TopologicallySortedCFG {
+ typedef llvm::po_iterator<const CFG*, CFGBlockSet, true> po_iterator;
+
+ std::vector<const CFGBlock*> Blocks;
+
+public:
+ typedef std::vector<const CFGBlock*>::reverse_iterator iterator;
+
+ TopologicallySortedCFG(const CFG *CFGraph) {
+ Blocks.reserve(CFGraph->getNumBlockIDs());
+ CFGBlockSet BSet(CFGraph);
+
+ for (po_iterator I = po_iterator::begin(CFGraph, BSet),
+ E = po_iterator::end(CFGraph, BSet); I != E; ++I) {
+ Blocks.push_back(*I);
+ }
+ }
+
+ iterator begin() {
+ return Blocks.rbegin();
+ }
+
+ iterator end() {
+ return Blocks.rend();
+ }
+};
+
+/// \brief A Lock object uniquely identifies a particular lock acquired, and is
+/// built from an Expr* (i.e. calling a lock function).
+///
+/// Thread-safety analysis works by comparing lock expressions. Within the
+/// body of a function, an expression such as "x->foo->bar.mu" will resolve to
+/// a particular lock object at run-time. Subsequent occurrences of the same
+/// expression (where "same" means syntactic equality) will refer to the same
+/// run-time object if three conditions hold:
+/// (1) Local variables in the expression, such as "x" have not changed.
+/// (2) Values on the heap that affect the expression have not changed.
+/// (3) The expression involves only pure function calls.
+/// The current implementation assumes, but does not verify, that multiple uses
+/// of the same lock expression satisfies these criteria.
+///
+/// Clang introduces an additional wrinkle, which is that it is difficult to
+/// derive canonical expressions, or compare expressions directly for equality.
+/// Thus, we identify a lock not by an Expr, but by the set of named
+/// declarations that are referenced by the Expr. In other words,
+/// x->foo->bar.mu will be a four element vector with the Decls for
+/// mu, bar, and foo, and x. The vector will uniquely identify the expression
+/// for all practical purposes.
+///
+/// Note we will need to perform substitution on "this" and function parameter
+/// names when constructing a lock expression.
+///
+/// For example:
+/// class C { Mutex Mu; void lock() EXCLUSIVE_LOCK_FUNCTION(this->Mu); };
+/// void myFunc(C *X) { ... X->lock() ... }
+/// The original expression for the lock acquired by myFunc is "this->Mu", but
+/// "X" is substituted for "this" so we get X->Mu();
+///
+/// For another example:
+/// foo(MyList *L) EXCLUSIVE_LOCKS_REQUIRED(L->Mu) { ... }
+/// MyList *MyL;
+/// foo(MyL); // requires lock MyL->Mu to be held
+///
+/// FIXME: In C++0x Mutexes are the objects that control access to shared
+/// variables, while Locks are the objects that acquire and release Mutexes. We
+/// may want to switch to this new terminology soon, in which case we should
+/// rename this class "Mutex" and rename "LockId" to "MutexId", as well as
+/// making sure that the terms Lock and Mutex throughout this code are
+/// consistent with C++0x
+///
+/// FIXME: We should also pick one and canonicalize all usage of lock vs acquire
+/// and unlock vs release as verbs.
+class LockID {
+ SmallVector<NamedDecl*, 2> DeclSeq;
+
+ /// Build a Decl sequence representing the lock from the given expression.
+ /// Recursive function that bottoms out when the final DeclRefExpr is reached.
+ void buildLock(Expr *Exp) {
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) {
+ NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl());
+ DeclSeq.push_back(ND);
+ } else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
+ NamedDecl *ND = ME->getMemberDecl();
+ DeclSeq.push_back(ND);
+ buildLock(ME->getBase());
+ } else {
+ // FIXME: add diagnostic
+ llvm::report_fatal_error("Expected lock expression!");
+ }
+ }
+
+public:
+ LockID(Expr *LExpr) {
+ buildLock(LExpr);
+ assert(!DeclSeq.empty());
+ }
+
+ bool operator==(const LockID &other) const {
+ return DeclSeq == other.DeclSeq;
+ }
+
+ bool operator!=(const LockID &other) const {
+ return !(*this == other);
+ }
+
+ // SmallVector overloads Operator< to do lexicographic ordering. Note that
+ // we use pointer equality (and <) to compare NamedDecls. This means the order
+ // of locks in a lockset is nondeterministic. In order to output
+ // diagnostics in a deterministic ordering, we must order all diagnostics to
+ // output by SourceLocation when iterating through this lockset.
+ bool operator<(const LockID &other) const {
+ return DeclSeq < other.DeclSeq;
+ }
+
+ /// \brief Returns the name of the first Decl in the list for a given LockId;
+ /// e.g. the lock expression foo.bar() has name "bar".
+ /// The caret will point unambiguously to the lock expression, so using this
+ /// name in diagnostics is a way to get simple, and consistent, lock names.
+ /// We do not want to output the entire expression text for security reasons.
+ StringRef getName() const {
+ return DeclSeq.front()->getName();
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ for (SmallVectorImpl<NamedDecl*>::const_iterator I = DeclSeq.begin(),
+ E = DeclSeq.end(); I != E; ++I) {
+ ID.AddPointer(*I);
+ }
+ }
+};
+
+/// \brief This is a helper class that stores info about the most recent
+/// accquire of a Lock.
+///
+/// The main body of the analysis maps Locks to LockDatas.
+struct LockData {
+ SourceLocation AcquireLoc;
+
+ LockData(SourceLocation Loc) : AcquireLoc(Loc) {}
+
+ bool operator==(const LockData &other) const {
+ return AcquireLoc == other.AcquireLoc;
+ }
+
+ bool operator!=(const LockData &other) const {
+ return !(*this == other);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(AcquireLoc.getRawEncoding());
+ }
+};
+
+ /// A Lockset maps each lock (defined above) to information about how it has
+/// been locked.
+typedef llvm::ImmutableMap<LockID, LockData> Lockset;
+
+/// \brief We use this class to visit different types of expressions in
+/// CFGBlocks, and build up the lockset.
+/// An expression may cause us to add or remove locks from the lockset, or else
+/// output error messages related to missing locks.
+/// FIXME: In future, we may be able to not inherit from a visitor.
+class BuildLockset : public StmtVisitor<BuildLockset> {
+ Sema &S;
+ Lockset LSet;
+ Lockset::Factory &LocksetFactory;
+
+ // Helper functions
+ void RemoveLock(SourceLocation UnlockLoc, Expr *LockExp);
+ void AddLock(SourceLocation LockLoc, Expr *LockExp);
+
+public:
+ BuildLockset(Sema &S, Lockset LS, Lockset::Factory &F)
+ : StmtVisitor<BuildLockset>(), S(S), LSet(LS),
+ LocksetFactory(F) {}
+
+ Lockset getLockset() {
+ return LSet;
+ }
+
+ void VisitDeclRefExpr(DeclRefExpr *Exp);
+ void VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp);
+};
+
+/// \brief Add a new lock to the lockset, warning if the lock is already there.
+/// \param LockExp The lock expression corresponding to the lock to be added
+/// \param LockLoc The source location of the acquire
+void BuildLockset::AddLock(SourceLocation LockLoc, Expr *LockExp) {
+ LockID Lock(LockExp);
+ LockData NewLockData(LockLoc);
+
+ if (LSet.contains(Lock))
+ S.Diag(LockLoc, diag::warn_double_lock) << Lock.getName();
+
+ LSet = LocksetFactory.add(LSet, Lock, NewLockData);
+}
+
+/// \brief Remove a lock from the lockset, warning if the lock is not there.
+/// \param LockExp The lock expression corresponding to the lock to be removed
+/// \param UnlockLoc The source location of the unlock (only used in error msg)
+void BuildLockset::RemoveLock(SourceLocation UnlockLoc, Expr *LockExp) {
+ LockID Lock(LockExp);
+
+ Lockset NewLSet = LocksetFactory.remove(LSet, Lock);
+ if(NewLSet == LSet)
+ S.Diag(UnlockLoc, diag::warn_unlock_but_no_acquire) << Lock.getName();
+
+ LSet = NewLSet;
+}
+
+void BuildLockset::VisitDeclRefExpr(DeclRefExpr *Exp) {
+ // FIXME: checking for guarded_by/var and pt_guarded_by/var
+}
+
+/// \brief When visiting CXXMemberCallExprs we need to examine the attributes on
+/// the method that is being called and add, remove or check locks in the
+/// lockset accordingly.
+void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
+ NamedDecl *D = dyn_cast<NamedDecl>(Exp->getCalleeDecl());
+ SourceLocation ExpLocation = Exp->getExprLoc();
+ Expr *Parent = Exp->getImplicitObjectArgument();
+
+ if(!D || !D->hasAttrs())
+ return;
+
+ AttrVec &ArgAttrs = D->getAttrs();
+ for(unsigned i = 0; i < ArgAttrs.size(); ++i) {
+ Attr *Attr = ArgAttrs[i];
+ switch (Attr->getKind()) {
+ // When we encounter an exclusive lock function, we need to add the lock
+ // to our lockset.
+ case attr::ExclusiveLockFunction: {
+ ExclusiveLockFunctionAttr *ELFAttr =
+ cast<ExclusiveLockFunctionAttr>(Attr);
+
+ if (ELFAttr->args_size() == 0) {// The lock held is the "this" object.
+ AddLock(ExpLocation, Parent);
+ break;
+ }
+
+ for (ExclusiveLockFunctionAttr::args_iterator I = ELFAttr->args_begin(),
+ E = ELFAttr->args_end(); I != E; ++I)
+ AddLock(ExpLocation, *I);
+ // FIXME: acquired_after/acquired_before annotations
+ break;
+ }
+
+ // When we encounter an unlock function, we need to remove unlocked locks
+ // from the lockset, and flag a warning if they are not there.
+ case attr::UnlockFunction: {
+ UnlockFunctionAttr *UFAttr = cast<UnlockFunctionAttr>(Attr);
+
+ if (UFAttr->args_size() == 0) { // The lock held is the "this" object.
+ RemoveLock(ExpLocation, Parent);
+ break;
+ }
+
+ for (UnlockFunctionAttr::args_iterator I = UFAttr->args_begin(),
+ E = UFAttr->args_end(); I != E; ++I)
+ RemoveLock(ExpLocation, *I);
+ break;
+ }
+
+ // Ignore other (non thread-safety) attributes
+ default:
+ break;
+ }
+ }
+}
+
+typedef std::pair<SourceLocation, PartialDiagnostic> DelayedDiag;
+typedef llvm::SmallVector<DelayedDiag, 4> DiagList;
+
+struct SortDiagBySourceLocation {
+ Sema &S;
+
+ SortDiagBySourceLocation(Sema &S) : S(S) {}
+
+ bool operator()(const DelayedDiag &left, const DelayedDiag &right) {
+ // Although this call will be slow, this is only called when outputting
+ // multiple warnings.
+ return S.getSourceManager().isBeforeInTranslationUnit(left.first,
+ right.first);
+ }
+};
+} // end anonymous namespace
+
+/// \brief Emit all buffered diagnostics in order of sourcelocation.
+/// We need to output diagnostics produced while iterating through
+/// the lockset in deterministic order, so this function orders diagnostics
+/// and outputs them.
+static void EmitDiagnostics(Sema &S, DiagList &D) {
+ SortDiagBySourceLocation SortDiagBySL(S);
+ sort(D.begin(), D.end(), SortDiagBySL);
+ for (DiagList::iterator I = D.begin(), E = D.end(); I != E; ++I)
+ S.Diag(I->first, I->second);
+}
+
+/// \brief Compute the intersection of two locksets and issue warnings for any
+/// locks in the symmetric difference.
+///
+/// This function is used at a merge point in the CFG when comparing the lockset
+/// of each branch being merged. For example, given the following sequence:
+/// A; if () then B; else C; D; we need to check that the lockset after B and C
+/// are the same. In the event of a difference, we use the intersection of these
+/// two locksets at the start of D.
+static Lockset intersectAndWarn(Sema &S, Lockset LSet1, Lockset LSet2,
+ Lockset::Factory &Fact) {
+ Lockset Intersection = LSet1;
+ DiagList Warnings;
+
+ for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I) {
+ if (!LSet1.contains(I.getKey())) {
+ const LockID &MissingLock = I.getKey();
+ const LockData &MissingLockData = I.getData();
+ PartialDiagnostic Warning =
+ S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName();
+ Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
+ }
+ }
+
+ for (Lockset::iterator I = LSet1.begin(), E = LSet1.end(); I != E; ++I) {
+ if (!LSet2.contains(I.getKey())) {
+ const LockID &MissingLock = I.getKey();
+ const LockData &MissingLockData = I.getData();
+ PartialDiagnostic Warning =
+ S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName();
+ Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
+ Intersection = Fact.remove(Intersection, MissingLock);
+ }
+ }
+
+ EmitDiagnostics(S, Warnings);
+ return Intersection;
+}
+
+/// \brief Returns the location of the first Stmt in a Block.
+static SourceLocation getFirstStmtLocation(CFGBlock *Block) {
+ for (CFGBlock::const_iterator BI = Block->begin(), BE = Block->end();
+ BI != BE; ++BI) {
+ if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&(*BI)))
+ return CfgStmt->getStmt()->getLocStart();
+ }
+ return SourceLocation();
+}
+
+/// \brief Warn about different locksets along backedges of loops.
+/// This function is called when we encounter a back edge. At that point,
+/// we need to verify that the lockset before taking the backedge is the
+/// same as the lockset before entering the loop.
+///
+/// \param LoopEntrySet Locks held before starting the loop
+/// \param LoopReentrySet Locks held in the last CFG block of the loop
+static void warnBackEdgeUnequalLocksets(Sema &S, const Lockset LoopReentrySet,
+ const Lockset LoopEntrySet,
+ SourceLocation FirstLocInLoop) {
+ assert(FirstLocInLoop.isValid());
+ DiagList Warnings;
+
+ // Warn for locks held at the start of the loop, but not the end.
+ for (Lockset::iterator I = LoopEntrySet.begin(), E = LoopEntrySet.end();
+ I != E; ++I) {
+ if (!LoopReentrySet.contains(I.getKey())) {
+ const LockID &MissingLock = I.getKey();
+ // We report this error at the location of the first statement in a loop
+ PartialDiagnostic Warning =
+ S.PDiag(diag::warn_expecting_lock_held_on_loop)
+ << MissingLock.getName();
+ Warnings.push_back(DelayedDiag(FirstLocInLoop, Warning));
+ }
+ }
+
+ // Warn for locks held at the end of the loop, but not at the start.
+ for (Lockset::iterator I = LoopReentrySet.begin(), E = LoopReentrySet.end();
+ I != E; ++I) {
+ if (!LoopEntrySet.contains(I.getKey())) {
+ const LockID &MissingLock = I.getKey();
+ const LockData &MissingLockData = I.getData();
+ PartialDiagnostic Warning =
+ S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName();
+ Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
+ }
+ }
+
+ EmitDiagnostics(S, Warnings);
+}
+
+/// \brief Check a function's CFG for thread-safety violations.
+///
+/// We traverse the blocks in the CFG, compute the set of locks that are held
+/// at the end of each block, and issue warnings for thread safety violations.
+/// Each block in the CFG is traversed exactly once.
+static void checkThreadSafety(Sema &S, AnalysisContext &AC) {
+ CFG *CFGraph = AC.getCFG();
+ if (!CFGraph) return;
+
+ StringRef FunName;
+ if (const NamedDecl *ContextDecl = dyn_cast<NamedDecl>(AC.getDecl()))
+ FunName = ContextDecl->getName();
+
+ Lockset::Factory LocksetFactory;
+
+ // FIXME: Swith to SmallVector? Otherwise improve performance impact?
+ std::vector<Lockset> EntryLocksets(CFGraph->getNumBlockIDs(),
+ LocksetFactory.getEmptyMap());
+ std::vector<Lockset> ExitLocksets(CFGraph->getNumBlockIDs(),
+ LocksetFactory.getEmptyMap());
+
+ // We need to explore the CFG via a "topological" ordering.
+ // That way, we will be guaranteed to have information about required
+ // predecessor locksets when exploring a new block.
+ TopologicallySortedCFG SortedGraph(CFGraph);
+ CFGBlockSet VisitedBlocks(CFGraph);
+
+ for (TopologicallySortedCFG::iterator I = SortedGraph.begin(),
+ E = SortedGraph.end(); I!= E; ++I) {
+ const CFGBlock *CurrBlock = *I;
+ int CurrBlockID = CurrBlock->getBlockID();
+
+ VisitedBlocks.insert(CurrBlock);
+
+ // Use the default initial lockset in case there are no predecessors.
+ Lockset &Entryset = EntryLocksets[CurrBlockID];
+ Lockset &Exitset = ExitLocksets[CurrBlockID];
+
+ // Iterate through the predecessor blocks and warn if the lockset for all
+ // predecessors is not the same. We take the entry lockset of the current
+ // block to be the intersection of all previous locksets.
+ // FIXME: By keeping the intersection, we may output more errors in future
+ // for a lock which is not in the intersection, but was in the union. We
+ // may want to also keep the union in future. As an example, let's say
+ // the intersection contains Lock L, and the union contains L and M.
+ // Later we unlock M. At this point, we would output an error because we
+ // never locked M; although the real error is probably that we forgot to
+ // lock M on all code paths. Conversely, let's say that later we lock M.
+ // In this case, we should compare against the intersection instead of the
+ // union because the real error is probably that we forgot to unlock M on
+ // all code paths.
+ bool LocksetInitialized = false;
+ for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
+ PE = CurrBlock->pred_end(); PI != PE; ++PI) {
+
+ // if *PI -> CurrBlock is a back edge
+ if (!VisitedBlocks.alreadySet(*PI))
+ continue;
+
+ int PrevBlockID = (*PI)->getBlockID();
+ if (!LocksetInitialized) {
+ Entryset = ExitLocksets[PrevBlockID];
+ LocksetInitialized = true;
+ } else {
+ Entryset = intersectAndWarn(S, Entryset, ExitLocksets[PrevBlockID],
+ LocksetFactory);
+ }
+ }
+
+ BuildLockset LocksetBuilder(S, Entryset, LocksetFactory);
+ for (CFGBlock::const_iterator BI = CurrBlock->begin(),
+ BE = CurrBlock->end(); BI != BE; ++BI) {
+ if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&*BI)) {
+ LocksetBuilder.Visit(CfgStmt->getStmt());
+ }
+ }
+ Exitset = LocksetBuilder.getLockset();
+
+ // For every back edge from CurrBlock (the end of the loop) to another block
+ // (FirstLoopBlock) we need to check that the Lockset of Block is equal to
+ // the one held at the beginning of FirstLoopBlock. We can look up the
+ // Lockset held at the beginning of FirstLoopBlock in the EntryLockSets map.
+ for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
+ SE = CurrBlock->succ_end(); SI != SE; ++SI) {
+
+ // if CurrBlock -> *SI is *not* a back edge
+ if (!VisitedBlocks.alreadySet(*SI))
+ continue;
+
+ CFGBlock *FirstLoopBlock = *SI;
+ SourceLocation FirstLoopLocation = getFirstStmtLocation(FirstLoopBlock);
+
+ Lockset PreLoop = EntryLocksets[FirstLoopBlock->getBlockID()];
+ Lockset LoopEnd = ExitLocksets[CurrBlockID];
+ warnBackEdgeUnequalLocksets(S, LoopEnd, PreLoop, FirstLoopLocation);
+ }
+ }
+
+ Lockset FinalLockset = ExitLocksets[CFGraph->getExit().getBlockID()];
+ if (!FinalLockset.isEmpty()) {
+ DiagList Warnings;
+ for (Lockset::iterator I=FinalLockset.begin(), E=FinalLockset.end();
+ I != E; ++I) {
+ const LockID &MissingLock = I.getKey();
+ const LockData &MissingLockData = I.getData();
+ PartialDiagnostic Warning =
+ S.PDiag(diag::warn_locks_not_released)
+ << MissingLock.getName() << FunName;
+ Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
+ }
+ EmitDiagnostics(S, Warnings);
+ }
+}
+
+
//===----------------------------------------------------------------------===//
// AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based
// warnings on a function, method, or block.
@@ -593,6 +1145,7 @@
clang::sema::AnalysisBasedWarnings::Policy::Policy() {
enableCheckFallThrough = 1;
enableCheckUnreachable = 0;
+ enableThreadSafetyAnalysis = 0;
}
clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
@@ -610,6 +1163,10 @@
DefaultPolicy.enableCheckUnreachable = (unsigned)
(D.getDiagnosticLevel(diag::warn_unreachable, SourceLocation()) !=
Diagnostic::Ignored);
+ DefaultPolicy.enableThreadSafetyAnalysis = (unsigned)
+ (D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
+ Diagnostic::Ignored);
+
}
static void flushDiagnostics(Sema &S, sema::FunctionScopeInfo *fscope) {
@@ -735,6 +1292,10 @@
if (P.enableCheckUnreachable)
CheckUnreachable(S, AC);
+ // Check for thread safety violations
+ if (P.enableThreadSafetyAnalysis)
+ checkThreadSafety(S, AC);
+
if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart())
!= Diagnostic::Ignored ||
Diags.getDiagnosticLevel(diag::warn_maybe_uninit_var, D->getLocStart())
Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=138350&r1=138349&r2=138350&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Tue Aug 23 13:46:34 2011
@@ -272,55 +272,69 @@
return 0;
}
+/// \brief Thread Safety Analysis: Checks that the passed in RecordType
+/// resolves to a lockable object. May flag an error.
+bool checkForLockableRecord(Sema &S, Decl *D, const AttributeList &Attr,
+ const RecordType *RT) {
+ // Flag error if could not get record type for this argument.
+ if(!RT) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_class)
+ << Attr.getName();
+ return false;
+ }
+ // Flag error if the type is not lockable.
+ if(!RT->getDecl()->getAttr<LockableAttr>()) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_lockable)
+ << Attr.getName();
+ return false;
+ }
+ return true;
+}
+
/// \brief Thread Safety Analysis: Checks that all attribute arguments, starting
/// from Sidx, resolve to a lockable object. May flag an error.
-static bool checkAttrArgsAreLockableObjs(Sema & S, Decl *D,
- const AttributeList & Attr,
+/// \param Sidx The attribute argument index to start checking with.
+/// \param ParamIdxOk Whether an argument can be indexing into a function
+/// parameter list.
+static bool checkAttrArgsAreLockableObjs(Sema &S, Decl *D,
+ const AttributeList &Attr,
+ SmallVectorImpl<Expr*> &Args,
int Sidx = 0,
bool ParamIdxOk = false) {
- for(unsigned int Idx = Sidx; Idx < Attr.getNumArgs(); ++Idx) {
+ for(unsigned Idx = Sidx; Idx < Attr.getNumArgs(); ++Idx) {
Expr *ArgExp = Attr.getArg(Idx);
+
if (ArgExp->isTypeDependent())
- continue;
+ continue;
- QualType Arg_QT = ArgExp->getType();
+ QualType ArgTy = ArgExp->getType();
- // Get record type.
- // first see if we can just cast to record type, or point to record type
- const RecordType *RT = getRecordType(Arg_QT);
-
- // now check if we idx into a record type function param
- if (!RT && ParamIdxOk) {
- FunctionDecl *FD = dyn_cast <FunctionDecl>(D);
+ // First see if we can just cast to record type, or point to record type.
+ const RecordType *RT = getRecordType(ArgTy);
+
+ // Now check if we index into a record type function param.
+ if(!RT && ParamIdxOk) {
+ FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
IntegerLiteral *IL = dyn_cast<IntegerLiteral>(ArgExp);
if(FD && IL) {
unsigned int NumParams = FD->getNumParams();
llvm::APInt ArgValue = IL->getValue();
- uint64_t ParamIdx_from1 = ArgValue.getZExtValue();
- uint64_t ParamIdx_from0 = ParamIdx_from1 - 1;
- if(!ArgValue.isStrictlyPositive() || ParamIdx_from1 > NumParams) {
+ uint64_t ParamIdxFromOne = ArgValue.getZExtValue();
+ uint64_t ParamIdxFromZero = ParamIdxFromOne - 1;
+ if(!ArgValue.isStrictlyPositive() || ParamIdxFromOne > NumParams) {
S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_range)
<< Attr.getName() << Idx + 1 << NumParams;
return false;
}
- Arg_QT = FD->getParamDecl(ParamIdx_from0)->getType();
- RT = getRecordType(Arg_QT);
+ ArgTy = FD->getParamDecl(ParamIdxFromZero)->getType();
+ RT = getRecordType(ArgTy);
}
}
- // Flag error if could not get record type for this argument
- if (!RT) {
- S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_class)
- << Attr.getName();
+ if (!checkForLockableRecord(S, D, Attr, RT))
return false;
- }
- // Flag error if the type is not lockable
- if (!RT->getDecl()->getAttr<LockableAttr>()) {
- S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_lockable)
- << Attr.getName();
- return false;
- }
+ Args.push_back(ArgExp);
}
return true;
}
@@ -363,6 +377,8 @@
if (!checkAttributeNumArgs(S, Attr, 1))
return;
+ Expr *Arg = Attr.getArg(0);
+
// D must be either a member field or global (potentially shared) variable.
if (!mayBeSharedVariable(D)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
@@ -373,14 +389,18 @@
if (pointer && !checkIsPointer(S, D, Attr))
return;
- // check that all arguments are lockable objects
- if (!checkAttrArgsAreLockableObjs(S, D, Attr))
+ if (Arg->isTypeDependent())
+ return;
+
+ // check that the argument is lockable object
+ if (!checkForLockableRecord(S, D, Attr, getRecordType(Arg->getType())))
return;
if (pointer)
- D->addAttr(::new (S.Context) PtGuardedByAttr(Attr.getLoc(), S.Context));
+ D->addAttr(::new (S.Context) PtGuardedByAttr(Attr.getLoc(),
+ S.Context, Arg));
else
- D->addAttr(::new (S.Context) GuardedByAttr(Attr.getLoc(), S.Context));
+ D->addAttr(::new (S.Context) GuardedByAttr(Attr.getLoc(), S.Context, Arg));
}
@@ -446,14 +466,21 @@
}
}
+ SmallVector<Expr*, 1> Args;
// check that all arguments are lockable objects
- if (!checkAttrArgsAreLockableObjs(S, D, Attr))
+ if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args))
return;
+ unsigned Size = Args.size();
+ assert(Size == Attr.getNumArgs());
+ Expr **StartArg = Size == 0 ? 0 : &Args[0];
+
if (before)
- D->addAttr(::new (S.Context) AcquiredBeforeAttr(Attr.getLoc(), S.Context));
+ D->addAttr(::new (S.Context) AcquiredBeforeAttr(Attr.getLoc(), S.Context,
+ StartArg, Size));
else
- D->addAttr(::new (S.Context) AcquiredAfterAttr(Attr.getLoc(), S.Context));
+ D->addAttr(::new (S.Context) AcquiredAfterAttr(Attr.getLoc(), S.Context,
+ StartArg, Size));
}
static void handleLockFunAttr(Sema &S, Decl *D, const AttributeList &Attr,
@@ -470,15 +497,22 @@
}
// check that all arguments are lockable objects
- if (!checkAttrArgsAreLockableObjs(S, D, Attr, 0, /*ParamIdxOk=*/true))
+ SmallVector<Expr*, 1> Args;
+ if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args, 0, /*ParamIdxOk=*/true))
return;
+ unsigned Size = Args.size();
+ assert(Size == Attr.getNumArgs());
+ Expr **StartArg = Size == 0 ? 0 : &Args[0];
+
if (exclusive)
D->addAttr(::new (S.Context) ExclusiveLockFunctionAttr(Attr.getLoc(),
- S.Context));
+ S.Context, StartArg,
+ Size));
else
D->addAttr(::new (S.Context) SharedLockFunctionAttr(Attr.getLoc(),
- S.Context));
+ S.Context, StartArg,
+ Size));
}
static void handleTrylockFunAttr(Sema &S, Decl *D, const AttributeList &Attr,
@@ -501,16 +535,24 @@
return;
}
+ SmallVector<Expr*, 2> Args;
+ Args.push_back(Attr.getArg(0)); //FIXME
// check that all arguments are lockable objects
- if (!checkAttrArgsAreLockableObjs(S, D, Attr, 1))
+ if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args, 1))
return;
+ unsigned Size = Args.size();
+ assert(Size == Attr.getNumArgs());
+ Expr **StartArg = Size == 0 ? 0 : &Args[0];
+
if (exclusive)
D->addAttr(::new (S.Context) ExclusiveTrylockFunctionAttr(Attr.getLoc(),
- S.Context));
+ S.Context,
+ StartArg, Size));
else
D->addAttr(::new (S.Context) SharedTrylockFunctionAttr(Attr.getLoc(),
- S.Context));
+ S.Context, StartArg,
+ Size));
}
static void handleLocksRequiredAttr(Sema &S, Decl *D, const AttributeList &Attr,
@@ -527,15 +569,22 @@
}
// check that all arguments are lockable objects
- if (!checkAttrArgsAreLockableObjs(S, D, Attr))
+ SmallVector<Expr*, 1> Args;
+ if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args))
return;
+ unsigned Size = Args.size();
+ assert(Size == Attr.getNumArgs());
+ Expr **StartArg = Size == 0 ? 0 : &Args[0];
+
if (exclusive)
D->addAttr(::new (S.Context) ExclusiveLocksRequiredAttr(Attr.getLoc(),
- S.Context));
+ S.Context, StartArg,
+ Size));
else
D->addAttr(::new (S.Context) SharedLocksRequiredAttr(Attr.getLoc(),
- S.Context));
+ S.Context, StartArg,
+ Size));
}
static void handleUnlockFunAttr(Sema &S, Decl *D,
@@ -551,10 +600,16 @@
}
// check that all arguments are lockable objects
- if (!checkAttrArgsAreLockableObjs(S, D, Attr, 0, /*ParamIdxOk=*/true))
+ SmallVector<Expr*, 1> Args;
+ if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args, 0, /*ParamIdxOk=*/true))
return;
- D->addAttr(::new (S.Context) UnlockFunctionAttr(Attr.getLoc(), S.Context));
+ unsigned Size = Args.size();
+ assert(Size == Attr.getNumArgs());
+ Expr **StartArg = Size == 0 ? 0 : &Args[0];
+
+ D->addAttr(::new (S.Context) UnlockFunctionAttr(Attr.getLoc(), S.Context,
+ StartArg, Size));
}
static void handleLockReturnedAttr(Sema &S, Decl *D,
@@ -563,6 +618,7 @@
if (!checkAttributeNumArgs(S, Attr, 1))
return;
+ Expr *Arg = Attr.getArg(0);
if (!isa<FunctionDecl>(D) && !isa<FunctionTemplateDecl>(D)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
@@ -570,11 +626,14 @@
return;
}
- // check that all arguments are lockable objects
- if (!checkAttrArgsAreLockableObjs(S, D, Attr))
+ if (Arg->isTypeDependent())
+ return;
+
+ // check that the argument is lockable object
+ if (!checkForLockableRecord(S, D, Attr, getRecordType(Arg->getType())))
return;
- D->addAttr(::new (S.Context) LockReturnedAttr(Attr.getLoc(), S.Context));
+ D->addAttr(::new (S.Context) LockReturnedAttr(Attr.getLoc(), S.Context, Arg));
}
static void handleLocksExcludedAttr(Sema &S, Decl *D,
@@ -591,10 +650,16 @@
}
// check that all arguments are lockable objects
- if (!checkAttrArgsAreLockableObjs(S, D, Attr))
+ SmallVector<Expr*, 1> Args;
+ if (!checkAttrArgsAreLockableObjs(S, D, Attr, Args))
return;
- D->addAttr(::new (S.Context) LocksExcludedAttr(Attr.getLoc(), S.Context));
+ unsigned Size = Args.size();
+ assert(Size == Attr.getNumArgs());
+ Expr **StartArg = Size == 0 ? 0 : &Args[0];
+
+ D->addAttr(::new (S.Context) LocksExcludedAttr(Attr.getLoc(), S.Context,
+ StartArg, Size));
}
Added: cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp?rev=138350&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp (added)
+++ cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp Tue Aug 23 13:46:34 2011
@@ -0,0 +1,267 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety %s
+
+
+//-----------------------------------------//
+// Helper fields
+//-----------------------------------------//
+
+
+class __attribute__((lockable)) Mutex {
+ public:
+ void Lock() __attribute__((exclusive_lock_function));
+ void ReaderLock() __attribute__((shared_lock_function));
+ void Unlock() __attribute__((unlock_function));
+ bool TryLock() __attribute__((exclusive_trylock_function(true)));
+ bool ReaderTryLock() __attribute__((shared_trylock_function(true)));
+ void LockWhen(const int &cond) __attribute__((exclusive_lock_function));
+};
+
+
+Mutex sls_mu;
+
+Mutex sls_mu2 __attribute__((acquired_after(sls_mu)));
+int sls_guard_var __attribute__((guarded_var)) = 0;
+int sls_guardby_var __attribute__((guarded_by(sls_mu))) = 0;
+
+bool getBool();
+
+class MutexWrapper {
+public:
+ Mutex mu;
+ // int x __attribute__((guarded_by(mu))); // FIXME: scoping error
+};
+
+MutexWrapper sls_mw;
+
+void sls_fun_0() {
+ sls_mw.mu.Lock();
+ // sls_mw.x = 5; // FIXME: turn mu into sls_mw.mu
+ sls_mw.mu.Unlock();
+}
+
+
+void sls_fun_2() {
+ sls_mu.Lock();
+ int x = sls_guard_var;
+ sls_mu.Unlock();
+}
+
+void sls_fun_3() {
+ sls_mu.Lock();
+ sls_guard_var = 2;
+ sls_mu.Unlock();
+}
+
+void sls_fun_4() {
+ sls_mu2.Lock();
+ sls_guard_var = 2;
+ sls_mu2.Unlock();
+}
+
+void sls_fun_5() {
+ sls_mu.Lock();
+ int x = sls_guardby_var;
+ sls_mu.Unlock();
+}
+
+void sls_fun_6() {
+ sls_mu.Lock();
+ sls_guardby_var = 2;
+ sls_mu.Unlock();
+}
+
+void sls_fun_7() {
+ sls_mu.Lock();
+ sls_mu2.Lock();
+ sls_mu2.Unlock();
+ sls_mu.Unlock();
+}
+
+void sls_fun_8() {
+ sls_mu.Lock();
+ if (getBool())
+ sls_mu.Unlock();
+ else
+ sls_mu.Unlock();
+}
+
+void sls_fun_9() {
+ if (getBool())
+ sls_mu.Lock();
+ else
+ sls_mu.Lock();
+ sls_mu.Unlock();
+}
+
+void sls_fun_good_6() {
+ if (getBool()) {
+ sls_mu.Lock();
+ } else {
+ if (getBool()) {
+ getBool(); // EMPTY
+ } else {
+ getBool(); // EMPTY
+ }
+ sls_mu.Lock();
+ }
+ sls_mu.Unlock();
+}
+
+void sls_fun_good_7() {
+ sls_mu.Lock();
+ while (getBool()) {
+ sls_mu.Unlock();
+ if (getBool()) {
+ if (getBool()) {
+ sls_mu.Lock();
+ continue;
+ }
+ }
+ sls_mu.Lock();
+ }
+ sls_mu.Unlock();
+}
+
+void sls_fun_bad_1() {
+ sls_mu.Unlock(); // \
+ expected-warning {{unlocking 'sls_mu' that was not acquired}}
+}
+
+void sls_fun_bad_2() {
+ sls_mu.Lock();
+ sls_mu.Lock(); // \
+ expected-warning {{locking 'sls_mu' that is already acquired}}
+ sls_mu.Unlock();
+}
+
+void sls_fun_bad_3() {
+ sls_mu.Lock(); // \
+ expected-warning {{lock 'sls_mu' is not released at the end of function 'sls_fun_bad_3'}}
+}
+
+void sls_fun_bad_4() {
+ if (getBool())
+ sls_mu.Lock(); // \
+ expected-warning {{lock 'sls_mu' is not released at the end of its scope}}
+ else
+ sls_mu2.Lock(); // \
+ expected-warning {{lock 'sls_mu2' is not released at the end of its scope}}
+}
+
+void sls_fun_bad_5() {
+ sls_mu.Lock(); // \
+ expected-warning {{lock 'sls_mu' is not released at the end of its scope}}
+ if (getBool())
+ sls_mu.Unlock();
+}
+
+void sls_fun_bad_6() {
+ if (getBool()) {
+ sls_mu.Lock(); // \
+ expected-warning {{lock 'sls_mu' is not released at the end of its scope}}
+ } else {
+ if (getBool()) {
+ getBool(); // EMPTY
+ } else {
+ getBool(); // EMPTY
+ }
+ }
+ sls_mu.Unlock(); // \
+ expected-warning {{unlocking 'sls_mu' that was not acquired}}
+}
+
+void sls_fun_bad_7() {
+ sls_mu.Lock();
+ while (getBool()) { // \
+ expected-warning {{expecting lock 'sls_mu' to be held at start of each loop}}
+ sls_mu.Unlock();
+ if (getBool()) {
+ if (getBool()) {
+ continue;
+ }
+ }
+ sls_mu.Lock(); // \
+ expected-warning {{lock 'sls_mu' is not released at the end of its scope}}
+ }
+ sls_mu.Unlock();
+}
+
+void sls_fun_bad_8() {
+ sls_mu.Lock();
+ do {
+ sls_mu.Unlock(); // \
+ expected-warning {{expecting lock 'sls_mu' to be held at start of each loop}}
+ } while (getBool());
+}
+
+void sls_fun_bad_9() {
+ do {
+ sls_mu.Lock(); // \
+ expected-warning {{lock 'sls_mu' is not released at the end of its scope}}
+ } while (getBool());
+ sls_mu.Unlock();
+}
+
+void sls_fun_bad_10() {
+ sls_mu.Lock(); // \
+ expected-warning {{lock 'sls_mu' is not released at the end of function 'sls_fun_bad_10'}}
+ while(getBool()) { // \
+ expected-warning {{expecting lock 'sls_mu' to be held at start of each loop}}
+ sls_mu.Unlock();
+ }
+}
+
+void sls_fun_bad_11() {
+ while (getBool()) {
+ sls_mu.Lock(); // \
+ expected-warning {{lock 'sls_mu' is not released at the end of its scope}}
+ }
+ sls_mu.Unlock(); // \
+ expected-warning {{unlocking 'sls_mu' that was not acquired}}
+}
+
+
+//-----------------------------------------//
+// Handling lock expressions in attribute args
+// -------------------------------------------//
+
+Mutex aa_mu;
+
+class GlobalLocker {
+public:
+ void globalLock() __attribute__((exclusive_lock_function(aa_mu)));
+ void globalUnlock() __attribute__((unlock_function(aa_mu)));
+};
+
+GlobalLocker glock;
+
+void aa_elr_fun() __attribute__((exclusive_locks_required(aa_mu)));
+void aa_elr_fun() { }
+
+void aa_fun_1() {
+ glock.globalLock();
+ glock.globalUnlock();
+}
+
+void aa_fun_2() {
+ aa_mu.Lock();
+ aa_elr_fun();
+ aa_mu.Unlock();
+}
+
+void aa_fun_bad_1() {
+ glock.globalUnlock(); // \
+ expected-warning {{unlocking 'aa_mu' that was not acquired}}
+}
+
+void aa_fun_bad_2() {
+ glock.globalLock();
+ glock.globalLock(); // \
+ expected-warning {{locking 'aa_mu' that is already acquired}}
+ glock.globalUnlock();
+}
+
+void aa_fun_bad_3() {
+ glock.globalLock(); // \
+ expected-warning {{lock 'aa_mu' is not released at the end of function 'aa_fun_bad_3'}}
+}
Copied: cfe/trunk/test/SemaCXX/warn-thread-safety-parsing.cpp (from r138343, cfe/trunk/test/SemaCXX/warn-thread-safety.cpp)
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-thread-safety-parsing.cpp?p2=cfe/trunk/test/SemaCXX/warn-thread-safety-parsing.cpp&p1=cfe/trunk/test/SemaCXX/warn-thread-safety.cpp&r1=138343&r2=138350&rev=138350&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-thread-safety.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-thread-safety-parsing.cpp Tue Aug 23 13:46:34 2011
@@ -51,9 +51,9 @@
#error "Should support no_thread_safety_analysis attribute"
#endif
-void noanal_function() __attribute__((no_thread_safety_analysis));
+void noanal_fun() __attribute__((no_thread_safety_analysis));
-void noanal_function() __attribute__((no_thread_safety_analysis(1))); // \
+void noanal_fun_args() __attribute__((no_thread_safety_analysis(1))); // \
expected-error {{attribute takes no arguments}}
int noanal_testfn(int y) __attribute__((no_thread_safety_analysis));
Removed: cfe/trunk/test/SemaCXX/warn-thread-safety.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-thread-safety.cpp?rev=138349&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-thread-safety.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-thread-safety.cpp (removed)
@@ -1,1141 +0,0 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety %s
-
-
-//-----------------------------------------//
-// Helper fields
-//-----------------------------------------//
-
-class __attribute__((lockable)) Mu {
-};
-
-class UnlockableMu{
-};
-
-class MuWrapper {
- public:
- Mu mu;
- Mu getMu() {
- return mu;
- }
- Mu * getMuPointer() {
- return μ
- }
-};
-
-
-class MuDoubleWrapper {
- public:
- MuWrapper* muWrapper;
- MuWrapper* getWrapper() {
- return muWrapper;
- }
-};
-
-Mu mu1;
-UnlockableMu umu;
-Mu mu2;
-MuWrapper muWrapper;
-MuDoubleWrapper muDoubleWrapper;
-Mu* muPointer;
-Mu ** muDoublePointer = & muPointer;
-Mu& muRef = mu1;
-
-//-----------------------------------------//
-// No Thread Safety Analysis (noanal) //
-//-----------------------------------------//
-
-// FIXME: Right now we cannot parse attributes put on function definitions
-// We would like to patch this at some point.
-
-#if !__has_attribute(no_thread_safety_analysis)
-#error "Should support no_thread_safety_analysis attribute"
-#endif
-
-void noanal_function() __attribute__((no_thread_safety_analysis));
-
-void noanal_function() __attribute__((no_thread_safety_analysis(1))); // \
- expected-error {{attribute takes no arguments}}
-
-int noanal_testfn(int y) __attribute__((no_thread_safety_analysis));
-
-int noanal_testfn(int y) {
- int x __attribute__((no_thread_safety_analysis)) = y; // \
- expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
- return x;
-};
-
-int noanal_test_var __attribute__((no_thread_safety_analysis)); // \
- expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
-
-class NoanalFoo {
- private:
- int test_field __attribute__((no_thread_safety_analysis)); // \
- expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
- void test_method() __attribute__((no_thread_safety_analysis));
-};
-
-class __attribute__((no_thread_safety_analysis)) NoanalTestClass { // \
- expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
-};
-
-void noanal_fun_params(int lvar __attribute__((no_thread_safety_analysis))); // \
- expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
-
-
-//-----------------------------------------//
-// Guarded Var Attribute (gv)
-//-----------------------------------------//
-
-#if !__has_attribute(guarded_var)
-#error "Should support guarded_var attribute"
-#endif
-
-int gv_var_noargs __attribute__((guarded_var));
-
-int gv_var_args __attribute__((guarded_var(1))); // \
- expected-error {{attribute takes no arguments}}
-
-class GVFoo {
- private:
- int gv_field_noargs __attribute__((guarded_var));
- int gv_field_args __attribute__((guarded_var(1))); // \
- expected-error {{attribute takes no arguments}}
-};
-
-class __attribute__((guarded_var)) GV { // \
- expected-warning {{'guarded_var' attribute only applies to fields and global variables}}
-};
-
-void gv_function() __attribute__((guarded_var)); // \
- expected-warning {{'guarded_var' attribute only applies to fields and global variables}}
-
-void gv_function_params(int gv_lvar __attribute__((guarded_var))); // \
- expected-warning {{'guarded_var' attribute only applies to fields and global variables}}
-
-int gv_testfn(int y){
- int x __attribute__((guarded_var)) = y; // \
- expected-warning {{'guarded_var' attribute only applies to fields and global variables}}
- return x;
-}
-
-//-----------------------------------------//
-// Pt Guarded Var Attribute (pgv)
-//-----------------------------------------//
-
-//FIXME: add support for boost::scoped_ptr<int> fancyptr and references
-
-#if !__has_attribute(pt_guarded_var)
-#error "Should support pt_guarded_var attribute"
-#endif
-
-int *pgv_pt_var_noargs __attribute__((pt_guarded_var));
-
-int pgv_var_noargs __attribute__((pt_guarded_var)); // \
- expected-warning {{'pt_guarded_var' only applies to pointer types; type here is 'int'}}
-
-class PGVFoo {
- private:
- int *pt_field_noargs __attribute__((pt_guarded_var));
- int field_noargs __attribute__((pt_guarded_var)); // \
- expected-warning {{'pt_guarded_var' only applies to pointer types; type here is 'int'}}
- int *gv_field_args __attribute__((pt_guarded_var(1))); // \
- expected-error {{attribute takes no arguments}}
-};
-
-class __attribute__((pt_guarded_var)) PGV { // \
- expected-warning {{'pt_guarded_var' attribute only applies to fields and global variables}}
-};
-
-int *pgv_var_args __attribute__((pt_guarded_var(1))); // \
- expected-error {{attribute takes no arguments}}
-
-
-void pgv_function() __attribute__((pt_guarded_var)); // \
- expected-warning {{'pt_guarded_var' attribute only applies to fields and global variables}}
-
-void pgv_function_params(int *gv_lvar __attribute__((pt_guarded_var))); // \
- expected-warning {{'pt_guarded_var' attribute only applies to fields and global variables}}
-
-void pgv_testfn(int y){
- int *x __attribute__((pt_guarded_var)) = new int(0); // \
- expected-warning {{'pt_guarded_var' attribute only applies to fields and global variables}}
- delete x;
-}
-
-//-----------------------------------------//
-// Lockable Attribute (l)
-//-----------------------------------------//
-
-//FIXME: In future we may want to add support for structs, ObjC classes, etc.
-
-#if !__has_attribute(lockable)
-#error "Should support lockable attribute"
-#endif
-
-class __attribute__((lockable)) LTestClass {
-};
-
-class __attribute__((lockable (1))) LTestClass_args { // \
- expected-error {{attribute takes no arguments}}
-};
-
-void l_test_function() __attribute__((lockable)); // \
- expected-warning {{'lockable' attribute only applies to classes}}
-
-int l_testfn(int y) {
- int x __attribute__((lockable)) = y; // \
- expected-warning {{'lockable' attribute only applies to classes}}
- return x;
-}
-
-int l_test_var __attribute__((lockable)); // \
- expected-warning {{'lockable' attribute only applies to classes}}
-
-class LFoo {
- private:
- int test_field __attribute__((lockable)); // \
- expected-warning {{'lockable' attribute only applies to classes}}
- void test_method() __attribute__((lockable)); // \
- expected-warning {{'lockable' attribute only applies to classes}}
-};
-
-
-void l_function_params(int lvar __attribute__((lockable))); // \
- expected-warning {{'lockable' attribute only applies to classes}}
-
-
-//-----------------------------------------//
-// Scoped Lockable Attribute (sl)
-//-----------------------------------------//
-
-#if !__has_attribute(scoped_lockable)
-#error "Should support scoped_lockable attribute"
-#endif
-
-class __attribute__((scoped_lockable)) SLTestClass {
-};
-
-class __attribute__((scoped_lockable (1))) SLTestClass_args { // \
- expected-error {{attribute takes no arguments}}
-};
-
-void sl_test_function() __attribute__((scoped_lockable)); // \
- expected-warning {{'scoped_lockable' attribute only applies to classes}}
-
-int sl_testfn(int y) {
- int x __attribute__((scoped_lockable)) = y; // \
- expected-warning {{'scoped_lockable' attribute only applies to classes}}
- return x;
-}
-
-int sl_test_var __attribute__((scoped_lockable)); // \
- expected-warning {{'scoped_lockable' attribute only applies to classes}}
-
-class SLFoo {
- private:
- int test_field __attribute__((scoped_lockable)); // \
- expected-warning {{'scoped_lockable' attribute only applies to classes}}
- void test_method() __attribute__((scoped_lockable)); // \
- expected-warning {{'scoped_lockable' attribute only applies to classes}}
-};
-
-
-void sl_function_params(int lvar __attribute__((scoped_lockable))); // \
- expected-warning {{'scoped_lockable' attribute only applies to classes}}
-
-
-//-----------------------------------------//
-// Guarded By Attribute (gb)
-//-----------------------------------------//
-
-// FIXME: Eventually, would we like this attribute to take more than 1 arg?
-
-#if !__has_attribute(guarded_by)
-#error "Should support guarded_by attribute"
-#endif
-
-//1. Check applied to the right types & argument number
-
-int gb_var_arg __attribute__((guarded_by(mu1)));
-
-int gb_var_args __attribute__((guarded_by(mu1, mu2))); // \
- expected-error {{attribute takes one argument}}
-
-int gb_var_noargs __attribute__((guarded_by)); // \
- expected-error {{attribute takes one argument}}
-
-class GBFoo {
- private:
- int gb_field_noargs __attribute__((guarded_by)); // \
- expected-error {{attribute takes one argument}}
- int gb_field_args __attribute__((guarded_by(mu1)));
-};
-
-class __attribute__((guarded_by(mu1))) GB { // \
- expected-warning {{'guarded_by' attribute only applies to fields and global variables}}
-};
-
-void gb_function() __attribute__((guarded_by(mu1))); // \
- expected-warning {{'guarded_by' attribute only applies to fields and global variables}}
-
-void gb_function_params(int gv_lvar __attribute__((guarded_by(mu1)))); // \
- expected-warning {{'guarded_by' attribute only applies to fields and global variables}}
-
-int gb_testfn(int y){
- int x __attribute__((guarded_by(mu1))) = y; // \
- expected-warning {{'guarded_by' attribute only applies to fields and global variables}}
- return x;
-}
-
-//2. Check argument parsing.
-
-// legal attribute arguments
-int gb_var_arg_1 __attribute__((guarded_by(muWrapper.mu)));
-int gb_var_arg_2 __attribute__((guarded_by(muDoubleWrapper.muWrapper->mu)));
-int gb_var_arg_3 __attribute__((guarded_by(muWrapper.getMu())));
-int gb_var_arg_4 __attribute__((guarded_by(*muWrapper.getMuPointer())));
-int gb_var_arg_5 __attribute__((guarded_by(&mu1)));
-int gb_var_arg_6 __attribute__((guarded_by(muRef)));
-int gb_var_arg_7 __attribute__((guarded_by(muDoubleWrapper.getWrapper()->getMu())));
-int gb_var_arg_8 __attribute__((guarded_by(muPointer)));
-
-
-// illegal attribute arguments
-int gb_var_arg_bad_1 __attribute__((guarded_by(1))); // \
- expected-error {{'guarded_by' attribute requires arguments that are class type or point to class type}}
-int gb_var_arg_bad_2 __attribute__((guarded_by("mu"))); // \
- expected-error {{'guarded_by' attribute requires arguments that are class type or point to class type}}
-int gb_var_arg_bad_3 __attribute__((guarded_by(muDoublePointer))); // \
- expected-error {{'guarded_by' attribute requires arguments that are class type or point to class type}}
-int gb_var_arg_bad_4 __attribute__((guarded_by(umu))); // \
- expected-error {{'guarded_by' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-//3.
-// Thread Safety analysis tests
-
-
-//-----------------------------------------//
-// Pt Guarded By Attribute (pgb)
-//-----------------------------------------//
-
-#if !__has_attribute(pt_guarded_by)
-#error "Should support pt_guarded_by attribute"
-#endif
-
-//1. Check applied to the right types & argument number
-
-int *pgb_var_noargs __attribute__((pt_guarded_by)); // \
- expected-error {{attribute takes one argument}}
-
-int *pgb_ptr_var_arg __attribute__((pt_guarded_by(mu1)));
-
-int *pgb_ptr_var_args __attribute__((guarded_by(mu1, mu2))); // \
- expected-error {{attribute takes one argument}}
-
-int pgb_var_args __attribute__((pt_guarded_by(mu1))); // \
- expected-warning {{'pt_guarded_by' only applies to pointer types; type here is 'int'}}
-
-class PGBFoo {
- private:
- int *pgb_field_noargs __attribute__((pt_guarded_by)); // \
- expected-error {{attribute takes one argument}}
- int *pgb_field_args __attribute__((pt_guarded_by(mu1)));
-};
-
-class __attribute__((pt_guarded_by(mu1))) PGB { // \
- expected-warning {{'pt_guarded_by' attribute only applies to fields and global variables}}
-};
-
-void pgb_function() __attribute__((pt_guarded_by(mu1))); // \
- expected-warning {{'pt_guarded_by' attribute only applies to fields and global variables}}
-
-void pgb_function_params(int gv_lvar __attribute__((pt_guarded_by(mu1)))); // \
- expected-warning {{'pt_guarded_by' attribute only applies to fields and global variables}}
-
-void pgb_testfn(int y){
- int *x __attribute__((pt_guarded_by(mu1))) = new int(0); // \
- expected-warning {{'pt_guarded_by' attribute only applies to fields and global variables}}
- delete x;
-}
-
-//2. Check argument parsing.
-
-// legal attribute arguments
-int * pgb_var_arg_1 __attribute__((pt_guarded_by(muWrapper.mu)));
-int * pgb_var_arg_2 __attribute__((pt_guarded_by(muDoubleWrapper.muWrapper->mu)));
-int * pgb_var_arg_3 __attribute__((pt_guarded_by(muWrapper.getMu())));
-int * pgb_var_arg_4 __attribute__((pt_guarded_by(*muWrapper.getMuPointer())));
-int * pgb_var_arg_5 __attribute__((pt_guarded_by(&mu1)));
-int * pgb_var_arg_6 __attribute__((pt_guarded_by(muRef)));
-int * pgb_var_arg_7 __attribute__((pt_guarded_by(muDoubleWrapper.getWrapper()->getMu())));
-int * pgb_var_arg_8 __attribute__((pt_guarded_by(muPointer)));
-
-
-// illegal attribute arguments
-int * pgb_var_arg_bad_1 __attribute__((pt_guarded_by(1))); // \
- expected-error {{'pt_guarded_by' attribute requires arguments that are class type or point to class type}}
-int * pgb_var_arg_bad_2 __attribute__((pt_guarded_by("mu"))); // \
- expected-error {{'pt_guarded_by' attribute requires arguments that are class type or point to class type}}
-int * pgb_var_arg_bad_3 __attribute__((pt_guarded_by(muDoublePointer))); // \
- expected-error {{'pt_guarded_by' attribute requires arguments that are class type or point to class type}}
-int * pgb_var_arg_bad_4 __attribute__((pt_guarded_by(umu))); // \
- expected-error {{'pt_guarded_by' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-
-//-----------------------------------------//
-// Acquired After (aa)
-//-----------------------------------------//
-
-// FIXME: Would we like this attribute to take more than 1 arg?
-
-#if !__has_attribute(acquired_after)
-#error "Should support acquired_after attribute"
-#endif
-
-Mu mu_aa __attribute__((acquired_after(mu1)));
-
-Mu aa_var_noargs __attribute__((acquired_after)); // \
- expected-error {{attribute takes at least 1 argument}}
-
-class AAFoo {
- private:
- Mu aa_field_noargs __attribute__((acquired_after)); // \
- expected-error {{attribute takes at least 1 argument}}
- Mu aa_field_args __attribute__((acquired_after(mu1)));
-};
-
-class __attribute__((acquired_after(mu1))) AA { // \
- expected-warning {{'acquired_after' attribute only applies to fields and global variables}}
-};
-
-void aa_function() __attribute__((acquired_after(mu1))); // \
- expected-warning {{'acquired_after' attribute only applies to fields and global variables}}
-
-void aa_function_params(int gv_lvar __attribute__((acquired_after(mu1)))); // \
- expected-warning {{'acquired_after' attribute only applies to fields and global variables}}
-
-void aa_testfn(int y){
- Mu x __attribute__((acquired_after(mu1))) = Mu(); // \
- expected-warning {{'acquired_after' attribute only applies to fields and global variables}}
-}
-
-//Check argument parsing.
-
-// legal attribute arguments
-Mu aa_var_arg_1 __attribute__((acquired_after(muWrapper.mu)));
-Mu aa_var_arg_2 __attribute__((acquired_after(muDoubleWrapper.muWrapper->mu)));
-Mu aa_var_arg_3 __attribute__((acquired_after(muWrapper.getMu())));
-Mu aa_var_arg_4 __attribute__((acquired_after(*muWrapper.getMuPointer())));
-Mu aa_var_arg_5 __attribute__((acquired_after(&mu1)));
-Mu aa_var_arg_6 __attribute__((acquired_after(muRef)));
-Mu aa_var_arg_7 __attribute__((acquired_after(muDoubleWrapper.getWrapper()->getMu())));
-Mu aa_var_arg_8 __attribute__((acquired_after(muPointer)));
-
-
-// illegal attribute arguments
-Mu aa_var_arg_bad_1 __attribute__((acquired_after(1))); // \
- expected-error {{'acquired_after' attribute requires arguments that are class type or point to class type}}
-Mu aa_var_arg_bad_2 __attribute__((acquired_after("mu"))); // \
- expected-error {{'acquired_after' attribute requires arguments that are class type or point to class type}}
-Mu aa_var_arg_bad_3 __attribute__((acquired_after(muDoublePointer))); // \
- expected-error {{'acquired_after' attribute requires arguments that are class type or point to class type}}
-Mu aa_var_arg_bad_4 __attribute__((acquired_after(umu))); // \
- expected-error {{'acquired_after' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-UnlockableMu aa_var_arg_bad_5 __attribute__((acquired_after(mu_aa))); // \
- expected-error {{'acquired_after' attribute can only be applied in a context annotated with 'lockable' attribute}}
-
-//-----------------------------------------//
-// Acquired Before (ab)
-//-----------------------------------------//
-
-#if !__has_attribute(acquired_before)
-#error "Should support acquired_before attribute"
-#endif
-
-Mu mu_ab __attribute__((acquired_before(mu1)));
-
-Mu ab_var_noargs __attribute__((acquired_before)); // \
- expected-error {{attribute takes at least 1 argument}}
-
-class ABFoo {
- private:
- Mu ab_field_noargs __attribute__((acquired_before)); // \
- expected-error {{attribute takes at least 1 argument}}
- Mu ab_field_args __attribute__((acquired_before(mu1)));
-};
-
-class __attribute__((acquired_before(mu1))) AB { // \
- expected-warning {{'acquired_before' attribute only applies to fields and global variables}}
-};
-
-void ab_function() __attribute__((acquired_before(mu1))); // \
- expected-warning {{'acquired_before' attribute only applies to fields and global variables}}
-
-void ab_function_params(int gv_lvar __attribute__((acquired_before(mu1)))); // \
- expected-warning {{'acquired_before' attribute only applies to fields and global variables}}
-
-void ab_testfn(int y){
- Mu x __attribute__((acquired_before(mu1))) = Mu(); // \
- expected-warning {{'acquired_before' attribute only applies to fields and global variables}}
-}
-
-// Note: illegal int ab_int __attribute__((acquired_before(mu1))) will
-// be taken care of by warnings that ab__int is not lockable.
-
-//Check argument parsing.
-
-// legal attribute arguments
-Mu ab_var_arg_1 __attribute__((acquired_before(muWrapper.mu)));
-Mu ab_var_arg_2 __attribute__((acquired_before(muDoubleWrapper.muWrapper->mu)));
-Mu ab_var_arg_3 __attribute__((acquired_before(muWrapper.getMu())));
-Mu ab_var_arg_4 __attribute__((acquired_before(*muWrapper.getMuPointer())));
-Mu ab_var_arg_5 __attribute__((acquired_before(&mu1)));
-Mu ab_var_arg_6 __attribute__((acquired_before(muRef)));
-Mu ab_var_arg_7 __attribute__((acquired_before(muDoubleWrapper.getWrapper()->getMu())));
-Mu ab_var_arg_8 __attribute__((acquired_before(muPointer)));
-
-
-// illegal attribute arguments
-Mu ab_var_arg_bad_1 __attribute__((acquired_before(1))); // \
- expected-error {{'acquired_before' attribute requires arguments that are class type or point to class type}}
-Mu ab_var_arg_bad_2 __attribute__((acquired_before("mu"))); // \
- expected-error {{'acquired_before' attribute requires arguments that are class type or point to class type}}
-Mu ab_var_arg_bad_3 __attribute__((acquired_before(muDoublePointer))); // \
- expected-error {{'acquired_before' attribute requires arguments that are class type or point to class type}}
-Mu ab_var_arg_bad_4 __attribute__((acquired_before(umu))); // \
- expected-error {{'acquired_before' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-UnlockableMu ab_var_arg_bad_5 __attribute__((acquired_before(mu_ab))); // \
- expected-error {{'acquired_before' attribute can only be applied in a context annotated with 'lockable' attribute}}
-
-
-//-----------------------------------------//
-// Exclusive Lock Function (elf)
-//-----------------------------------------//
-
-#if !__has_attribute(exclusive_lock_function)
-#error "Should support exclusive_lock_function attribute"
-#endif
-
-// takes zero or more arguments, all locks (vars/fields)
-
-void elf_function() __attribute__((exclusive_lock_function));
-
-void elf_function_args() __attribute__((exclusive_lock_function(mu1, mu2)));
-
-int elf_testfn(int y) __attribute__((exclusive_lock_function));
-
-int elf_testfn(int y) {
- int x __attribute__((exclusive_lock_function)) = y; // \
- expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
- return x;
-};
-
-int elf_test_var __attribute__((exclusive_lock_function)); // \
- expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
-
-class ElfFoo {
- private:
- int test_field __attribute__((exclusive_lock_function)); // \
- expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
- void test_method() __attribute__((exclusive_lock_function));
-};
-
-class __attribute__((exclusive_lock_function)) ElfTestClass { // \
- expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
-};
-
-void elf_fun_params(int lvar __attribute__((exclusive_lock_function))); // \
- expected-warning {{'exclusive_lock_function' attribute only applies to functions and methods}}
-
-// Check argument parsing.
-
-// legal attribute arguments
-int elf_function_1() __attribute__((exclusive_lock_function(muWrapper.mu)));
-int elf_function_2() __attribute__((exclusive_lock_function(muDoubleWrapper.muWrapper->mu)));
-int elf_function_3() __attribute__((exclusive_lock_function(muWrapper.getMu())));
-int elf_function_4() __attribute__((exclusive_lock_function(*muWrapper.getMuPointer())));
-int elf_function_5() __attribute__((exclusive_lock_function(&mu1)));
-int elf_function_6() __attribute__((exclusive_lock_function(muRef)));
-int elf_function_7() __attribute__((exclusive_lock_function(muDoubleWrapper.getWrapper()->getMu())));
-int elf_function_8() __attribute__((exclusive_lock_function(muPointer)));
-int elf_function_9(Mu x) __attribute__((exclusive_lock_function(1)));
-int elf_function_9(Mu x, Mu y) __attribute__((exclusive_lock_function(1,2)));
-
-
-// illegal attribute arguments
-int elf_function_bad_2() __attribute__((exclusive_lock_function("mu"))); // \
- expected-error {{'exclusive_lock_function' attribute requires arguments that are class type or point to class type}}
-int elf_function_bad_3() __attribute__((exclusive_lock_function(muDoublePointer))); // \
- expected-error {{'exclusive_lock_function' attribute requires arguments that are class type or point to class type}}
-int elf_function_bad_4() __attribute__((exclusive_lock_function(umu))); // \
- expected-error {{'exclusive_lock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-int elf_function_bad_1() __attribute__((exclusive_lock_function(1))); // \
- expected-error {{'exclusive_lock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-int elf_function_bad_5(Mu x) __attribute__((exclusive_lock_function(0))); // \
- expected-error {{'exclusive_lock_function' attribute parameter 1 is out of bounds: can only be 1, since there is one parameter}}
-int elf_function_bad_6(Mu x, Mu y) __attribute__((exclusive_lock_function(0))); // \
- expected-error {{'exclusive_lock_function' attribute parameter 1 is out of bounds: must be between 1 and 2}}
-int elf_function_bad_7() __attribute__((exclusive_lock_function(0))); // \
- expected-error {{'exclusive_lock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-
-
-//-----------------------------------------//
-// Shared Lock Function (slf)
-//-----------------------------------------//
-
-#if !__has_attribute(shared_lock_function)
-#error "Should support shared_lock_function attribute"
-#endif
-
-// takes zero or more arguments, all locks (vars/fields)
-
-void slf_function() __attribute__((shared_lock_function));
-
-void slf_function_args() __attribute__((shared_lock_function(mu1, mu2)));
-
-int slf_testfn(int y) __attribute__((shared_lock_function));
-
-int slf_testfn(int y) {
- int x __attribute__((shared_lock_function)) = y; // \
- expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
- return x;
-};
-
-int slf_test_var __attribute__((shared_lock_function)); // \
- expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
-
-void slf_fun_params(int lvar __attribute__((shared_lock_function))); // \
- expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
-
-class SlfFoo {
- private:
- int test_field __attribute__((shared_lock_function)); // \
- expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
- void test_method() __attribute__((shared_lock_function));
-};
-
-class __attribute__((shared_lock_function)) SlfTestClass { // \
- expected-warning {{'shared_lock_function' attribute only applies to functions and methods}}
-};
-
-// Check argument parsing.
-
-// legal attribute arguments
-int slf_function_1() __attribute__((shared_lock_function(muWrapper.mu)));
-int slf_function_2() __attribute__((shared_lock_function(muDoubleWrapper.muWrapper->mu)));
-int slf_function_3() __attribute__((shared_lock_function(muWrapper.getMu())));
-int slf_function_4() __attribute__((shared_lock_function(*muWrapper.getMuPointer())));
-int slf_function_5() __attribute__((shared_lock_function(&mu1)));
-int slf_function_6() __attribute__((shared_lock_function(muRef)));
-int slf_function_7() __attribute__((shared_lock_function(muDoubleWrapper.getWrapper()->getMu())));
-int slf_function_8() __attribute__((shared_lock_function(muPointer)));
-int slf_function_9(Mu x) __attribute__((shared_lock_function(1)));
-int slf_function_9(Mu x, Mu y) __attribute__((shared_lock_function(1,2)));
-
-
-// illegal attribute arguments
-int slf_function_bad_2() __attribute__((shared_lock_function("mu"))); // \
- expected-error {{'shared_lock_function' attribute requires arguments that are class type or point to class type}}
-int slf_function_bad_3() __attribute__((shared_lock_function(muDoublePointer))); // \
- expected-error {{'shared_lock_function' attribute requires arguments that are class type or point to class type}}
-int slf_function_bad_4() __attribute__((shared_lock_function(umu))); // \
- expected-error {{'shared_lock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-int slf_function_bad_1() __attribute__((shared_lock_function(1))); // \
- expected-error {{'shared_lock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-int slf_function_bad_5(Mu x) __attribute__((shared_lock_function(0))); // \
- expected-error {{'shared_lock_function' attribute parameter 1 is out of bounds: can only be 1, since there is one parameter}}
-int slf_function_bad_6(Mu x, Mu y) __attribute__((shared_lock_function(0))); // \
- expected-error {{'shared_lock_function' attribute parameter 1 is out of bounds: must be between 1 and 2}}
-int slf_function_bad_7() __attribute__((shared_lock_function(0))); // \
- expected-error {{'shared_lock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-
-
-//-----------------------------------------//
-// Exclusive TryLock Function (etf)
-//-----------------------------------------//
-
-#if !__has_attribute(exclusive_trylock_function)
-#error "Should support exclusive_trylock_function attribute"
-#endif
-
-// takes a mandatory boolean or integer argument specifying the retval
-// plus an optional list of locks (vars/fields)
-
-void etf_function() __attribute__((exclusive_trylock_function)); // \
- expected-error {{attribute takes attribute takes at least 1 argument arguments}}
-
-void etf_function_args() __attribute__((exclusive_trylock_function(1, mu2)));
-
-void etf_function_arg() __attribute__((exclusive_trylock_function(1)));
-
-int etf_testfn(int y) __attribute__((exclusive_trylock_function(1)));
-
-int etf_testfn(int y) {
- int x __attribute__((exclusive_trylock_function(1))) = y; // \
- expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
- return x;
-};
-
-int etf_test_var __attribute__((exclusive_trylock_function(1))); // \
- expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
-
-class EtfFoo {
- private:
- int test_field __attribute__((exclusive_trylock_function(1))); // \
- expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
- void test_method() __attribute__((exclusive_trylock_function(1)));
-};
-
-class __attribute__((exclusive_trylock_function(1))) EtfTestClass { // \
- expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
-};
-
-void etf_fun_params(int lvar __attribute__((exclusive_trylock_function(1)))); // \
- expected-warning {{'exclusive_trylock_function' attribute only applies to functions and methods}}
-
-// Check argument parsing.
-
-// legal attribute arguments
-int etf_function_1() __attribute__((exclusive_trylock_function(1, muWrapper.mu)));
-int etf_function_2() __attribute__((exclusive_trylock_function(1, muDoubleWrapper.muWrapper->mu)));
-int etf_function_3() __attribute__((exclusive_trylock_function(1, muWrapper.getMu())));
-int etf_function_4() __attribute__((exclusive_trylock_function(1, *muWrapper.getMuPointer())));
-int etf_function_5() __attribute__((exclusive_trylock_function(1, &mu1)));
-int etf_function_6() __attribute__((exclusive_trylock_function(1, muRef)));
-int etf_function_7() __attribute__((exclusive_trylock_function(1, muDoubleWrapper.getWrapper()->getMu())));
-int etf_functetfn_8() __attribute__((exclusive_trylock_function(1, muPointer)));
-int etf_function_9() __attribute__((exclusive_trylock_function(true)));
-
-
-// illegal attribute arguments
-int etf_function_bad_1() __attribute__((exclusive_trylock_function(mu1))); // \
- expected-error {{'exclusive_trylock_function' attribute first argument must be of int or bool type}}
-int etf_function_bad_2() __attribute__((exclusive_trylock_function("mu"))); // \
- expected-error {{'exclusive_trylock_function' attribute first argument must be of int or bool type}}
-int etf_function_bad_3() __attribute__((exclusive_trylock_function(muDoublePointer))); // \
- expected-error {{'exclusive_trylock_function' attribute first argument must be of int or bool type}}
-
-int etf_function_bad_4() __attribute__((exclusive_trylock_function(1, "mu"))); // \
- expected-error {{'exclusive_trylock_function' attribute requires arguments that are class type or point to class type}}
-int etf_function_bad_5() __attribute__((exclusive_trylock_function(1, muDoublePointer))); // \
- expected-error {{'exclusive_trylock_function' attribute requires arguments that are class type or point to class type}}
-int etf_function_bad_6() __attribute__((exclusive_trylock_function(1, umu))); // \
- expected-error {{'exclusive_trylock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-
-//-----------------------------------------//
-// Shared TryLock Function (stf)
-//-----------------------------------------//
-
-#if !__has_attribute(shared_trylock_function)
-#error "Should support shared_trylock_function attribute"
-#endif
-
-// takes a mandatory boolean or integer argument specifying the retval
-// plus an optional list of locks (vars/fields)
-
-void stf_function() __attribute__((shared_trylock_function)); // \
- expected-error {{attribute takes at least 1 argument}}
-
-void stf_function_args() __attribute__((shared_trylock_function(1, mu2)));
-
-void stf_function_arg() __attribute__((shared_trylock_function(1)));
-
-int stf_testfn(int y) __attribute__((shared_trylock_function(1)));
-
-int stf_testfn(int y) {
- int x __attribute__((shared_trylock_function(1))) = y; // \
- expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
- return x;
-};
-
-int stf_test_var __attribute__((shared_trylock_function(1))); // \
- expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
-
-void stf_fun_params(int lvar __attribute__((shared_trylock_function(1)))); // \
- expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
-
-
-class StfFoo {
- private:
- int test_field __attribute__((shared_trylock_function(1))); // \
- expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
- void test_method() __attribute__((shared_trylock_function(1)));
-};
-
-class __attribute__((shared_trylock_function(1))) StfTestClass { // \
- expected-warning {{'shared_trylock_function' attribute only applies to functions and methods}}
-};
-
-// Check argument parsing.
-
-// legal attribute arguments
-int stf_function_1() __attribute__((shared_trylock_function(1, muWrapper.mu)));
-int stf_function_2() __attribute__((shared_trylock_function(1, muDoubleWrapper.muWrapper->mu)));
-int stf_function_3() __attribute__((shared_trylock_function(1, muWrapper.getMu())));
-int stf_function_4() __attribute__((shared_trylock_function(1, *muWrapper.getMuPointer())));
-int stf_function_5() __attribute__((shared_trylock_function(1, &mu1)));
-int stf_function_6() __attribute__((shared_trylock_function(1, muRef)));
-int stf_function_7() __attribute__((shared_trylock_function(1, muDoubleWrapper.getWrapper()->getMu())));
-int stf_function_8() __attribute__((shared_trylock_function(1, muPointer)));
-int stf_function_9() __attribute__((shared_trylock_function(true)));
-
-
-// illegal attribute arguments
-int stf_function_bad_1() __attribute__((shared_trylock_function(mu1))); // \
- expected-error {{'shared_trylock_function' attribute first argument must be of int or bool type}}
-int stf_function_bad_2() __attribute__((shared_trylock_function("mu"))); // \
- expected-error {{'shared_trylock_function' attribute first argument must be of int or bool type}}
-int stf_function_bad_3() __attribute__((shared_trylock_function(muDoublePointer))); // \
- expected-error {{'shared_trylock_function' attribute first argument must be of int or bool type}}
-
-int stf_function_bad_4() __attribute__((shared_trylock_function(1, "mu"))); // \
- expected-error {{'shared_trylock_function' attribute requires arguments that are class type or point to class type}}
-int stf_function_bad_5() __attribute__((shared_trylock_function(1, muDoublePointer))); // \
- expected-error {{'shared_trylock_function' attribute requires arguments that are class type or point to class type}}
-int stf_function_bad_6() __attribute__((shared_trylock_function(1, umu))); // \
- expected-error {{'shared_trylock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-
-//-----------------------------------------//
-// Unlock Function (uf)
-//-----------------------------------------//
-
-#if !__has_attribute(unlock_function)
-#error "Should support unlock_function attribute"
-#endif
-
-// takes zero or more arguments, all locks (vars/fields)
-
-void uf_function() __attribute__((unlock_function));
-
-void uf_function_args() __attribute__((unlock_function(mu1, mu2)));
-
-int uf_testfn(int y) __attribute__((unlock_function));
-
-int uf_testfn(int y) {
- int x __attribute__((unlock_function)) = y; // \
- expected-warning {{'unlock_function' attribute only applies to functions and methods}}
- return x;
-};
-
-int uf_test_var __attribute__((unlock_function)); // \
- expected-warning {{'unlock_function' attribute only applies to functions and methods}}
-
-class UfFoo {
- private:
- int test_field __attribute__((unlock_function)); // \
- expected-warning {{'unlock_function' attribute only applies to functions and methods}}
- void test_method() __attribute__((unlock_function));
-};
-
-class __attribute__((no_thread_safety_analysis)) UfTestClass { // \
- expected-warning {{'no_thread_safety_analysis' attribute only applies to functions and methods}}
-};
-
-void uf_fun_params(int lvar __attribute__((unlock_function))); // \
- expected-warning {{'unlock_function' attribute only applies to functions and methods}}
-
-// Check argument parsing.
-
-// legal attribute arguments
-int uf_function_1() __attribute__((unlock_function(muWrapper.mu)));
-int uf_function_2() __attribute__((unlock_function(muDoubleWrapper.muWrapper->mu)));
-int uf_function_3() __attribute__((unlock_function(muWrapper.getMu())));
-int uf_function_4() __attribute__((unlock_function(*muWrapper.getMuPointer())));
-int uf_function_5() __attribute__((unlock_function(&mu1)));
-int uf_function_6() __attribute__((unlock_function(muRef)));
-int uf_function_7() __attribute__((unlock_function(muDoubleWrapper.getWrapper()->getMu())));
-int uf_function_8() __attribute__((unlock_function(muPointer)));
-int uf_function_9(Mu x) __attribute__((unlock_function(1)));
-int uf_function_9(Mu x, Mu y) __attribute__((unlock_function(1,2)));
-
-
-// illegal attribute arguments
-int uf_function_bad_2() __attribute__((unlock_function("mu"))); // \
- expected-error {{'unlock_function' attribute requires arguments that are class type or point to class type}}
-int uf_function_bad_3() __attribute__((unlock_function(muDoublePointer))); // \
-expected-error {{'unlock_function' attribute requires arguments that are class type or point to class type}}
-int uf_function_bad_4() __attribute__((unlock_function(umu))); // \
- expected-error {{'unlock_function' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-int uf_function_bad_1() __attribute__((unlock_function(1))); // \
- expected-error {{'unlock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-int uf_function_bad_5(Mu x) __attribute__((unlock_function(0))); // \
- expected-error {{'unlock_function' attribute parameter 1 is out of bounds: can only be 1, since there is one parameter}}
-int uf_function_bad_6(Mu x, Mu y) __attribute__((unlock_function(0))); // \
- expected-error {{'unlock_function' attribute parameter 1 is out of bounds: must be between 1 and 2}}
-int uf_function_bad_7() __attribute__((unlock_function(0))); // \
- expected-error {{'unlock_function' attribute parameter 1 is out of bounds: no parameters to index into}}
-
-
-//-----------------------------------------//
-// Lock Returned (lr)
-//-----------------------------------------//
-
-#if !__has_attribute(lock_returned)
-#error "Should support lock_returned attribute"
-#endif
-
-// Takes exactly one argument, a var/field
-
-void lr_function() __attribute__((lock_returned)); // \
- expected-error {{attribute takes one argument}}
-
-void lr_function_arg() __attribute__((lock_returned(mu1)));
-
-void lr_function_args() __attribute__((lock_returned(mu1, mu2))); // \
- expected-error {{attribute takes one argument}}
-
-int lr_testfn(int y) __attribute__((lock_returned(mu1)));
-
-int lr_testfn(int y) {
- int x __attribute__((lock_returned(mu1))) = y; // \
- expected-warning {{'lock_returned' attribute only applies to functions and methods}}
- return x;
-};
-
-int lr_test_var __attribute__((lock_returned(mu1))); // \
- expected-warning {{'lock_returned' attribute only applies to functions and methods}}
-
-void lr_fun_params(int lvar __attribute__((lock_returned(mu1)))); // \
- expected-warning {{'lock_returned' attribute only applies to functions and methods}}
-
-class LrFoo {
- private:
- int test_field __attribute__((lock_returned(mu1))); // \
- expected-warning {{'lock_returned' attribute only applies to functions and methods}}
- void test_method() __attribute__((lock_returned(mu1)));
-};
-
-class __attribute__((lock_returned(mu1))) LrTestClass { // \
- expected-warning {{'lock_returned' attribute only applies to functions and methods}}
-};
-
-// Check argument parsing.
-
-// legal attribute arguments
-int lr_function_1() __attribute__((lock_returned(muWrapper.mu)));
-int lr_function_2() __attribute__((lock_returned(muDoubleWrapper.muWrapper->mu)));
-int lr_function_3() __attribute__((lock_returned(muWrapper.getMu())));
-int lr_function_4() __attribute__((lock_returned(*muWrapper.getMuPointer())));
-int lr_function_5() __attribute__((lock_returned(&mu1)));
-int lr_function_6() __attribute__((lock_returned(muRef)));
-int lr_function_7() __attribute__((lock_returned(muDoubleWrapper.getWrapper()->getMu())));
-int lr_function_8() __attribute__((lock_returned(muPointer)));
-
-
-// illegal attribute arguments
-int lr_function_bad_1() __attribute__((lock_returned(1))); // \
- expected-error {{'lock_returned' attribute requires arguments that are class type or point to class type}}
-int lr_function_bad_2() __attribute__((lock_returned("mu"))); // \
- expected-error {{'lock_returned' attribute requires arguments that are class type or point to class type}}
-int lr_function_bad_3() __attribute__((lock_returned(muDoublePointer))); // \
- expected-error {{'lock_returned' attribute requires arguments that are class type or point to class type}}
-int lr_function_bad_4() __attribute__((lock_returned(umu))); // \
- expected-error {{'lock_returned' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-
-
-//-----------------------------------------//
-// Locks Excluded (le)
-//-----------------------------------------//
-
-#if !__has_attribute(locks_excluded)
-#error "Should support locks_excluded attribute"
-#endif
-
-// takes one or more arguments, all locks (vars/fields)
-
-void le_function() __attribute__((locks_excluded)); // \
- expected-error {{attribute takes at least 1 argument}}
-
-void le_function_arg() __attribute__((locks_excluded(mu1)));
-
-void le_function_args() __attribute__((locks_excluded(mu1, mu2)));
-
-int le_testfn(int y) __attribute__((locks_excluded(mu1)));
-
-int le_testfn(int y) {
- int x __attribute__((locks_excluded(mu1))) = y; // \
- expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
- return x;
-};
-
-int le_test_var __attribute__((locks_excluded(mu1))); // \
- expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
-
-void le_fun_params(int lvar __attribute__((locks_excluded(mu1)))); // \
- expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
-
-class LeFoo {
- private:
- int test_field __attribute__((locks_excluded(mu1))); // \
- expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
- void test_method() __attribute__((locks_excluded(mu1)));
-};
-
-class __attribute__((locks_excluded(mu1))) LeTestClass { // \
- expected-warning {{'locks_excluded' attribute only applies to functions and methods}}
-};
-
-// Check argument parsing.
-
-// legal attribute arguments
-int le_function_1() __attribute__((locks_excluded(muWrapper.mu)));
-int le_function_2() __attribute__((locks_excluded(muDoubleWrapper.muWrapper->mu)));
-int le_function_3() __attribute__((locks_excluded(muWrapper.getMu())));
-int le_function_4() __attribute__((locks_excluded(*muWrapper.getMuPointer())));
-int le_function_5() __attribute__((locks_excluded(&mu1)));
-int le_function_6() __attribute__((locks_excluded(muRef)));
-int le_function_7() __attribute__((locks_excluded(muDoubleWrapper.getWrapper()->getMu())));
-int le_function_8() __attribute__((locks_excluded(muPointer)));
-
-
-// illegal attribute arguments
-int le_function_bad_1() __attribute__((locks_excluded(1))); // \
- expected-error {{'locks_excluded' attribute requires arguments that are class type or point to class type}}
-int le_function_bad_2() __attribute__((locks_excluded("mu"))); // \
- expected-error {{'locks_excluded' attribute requires arguments that are class type or point to class type}}
-int le_function_bad_3() __attribute__((locks_excluded(muDoublePointer))); // \
- expected-error {{'locks_excluded' attribute requires arguments that are class type or point to class type}}
-int le_function_bad_4() __attribute__((locks_excluded(umu))); // \
- expected-error {{'locks_excluded' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-
-
-//-----------------------------------------//
-// Exclusive Locks Required (elr)
-//-----------------------------------------//
-
-#if !__has_attribute(exclusive_locks_required)
-#error "Should support exclusive_locks_required attribute"
-#endif
-
-// takes one or more arguments, all locks (vars/fields)
-
-void elr_function() __attribute__((exclusive_locks_required)); // \
- expected-error {{attribute takes at least 1 argument}}
-
-void elr_function_arg() __attribute__((exclusive_locks_required(mu1)));
-
-void elr_function_args() __attribute__((exclusive_locks_required(mu1, mu2)));
-
-int elr_testfn(int y) __attribute__((exclusive_locks_required(mu1)));
-
-int elr_testfn(int y) {
- int x __attribute__((exclusive_locks_required(mu1))) = y; // \
- expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
- return x;
-};
-
-int elr_test_var __attribute__((exclusive_locks_required(mu1))); // \
- expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
-
-void elr_fun_params(int lvar __attribute__((exclusive_locks_required(mu1)))); // \
- expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
-
-class ElrFoo {
- private:
- int test_field __attribute__((exclusive_locks_required(mu1))); // \
- expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
- void test_method() __attribute__((exclusive_locks_required(mu1)));
-};
-
-class __attribute__((exclusive_locks_required(mu1))) ElrTestClass { // \
- expected-warning {{'exclusive_locks_required' attribute only applies to functions and methods}}
-};
-
-// Check argument parsing.
-
-// legal attribute arguments
-int elr_function_1() __attribute__((exclusive_locks_required(muWrapper.mu)));
-int elr_function_2() __attribute__((exclusive_locks_required(muDoubleWrapper.muWrapper->mu)));
-int elr_function_3() __attribute__((exclusive_locks_required(muWrapper.getMu())));
-int elr_function_4() __attribute__((exclusive_locks_required(*muWrapper.getMuPointer())));
-int elr_function_5() __attribute__((exclusive_locks_required(&mu1)));
-int elr_function_6() __attribute__((exclusive_locks_required(muRef)));
-int elr_function_7() __attribute__((exclusive_locks_required(muDoubleWrapper.getWrapper()->getMu())));
-int elr_function_8() __attribute__((exclusive_locks_required(muPointer)));
-
-
-// illegal attribute arguments
-int elr_function_bad_1() __attribute__((exclusive_locks_required(1))); // \
- expected-error {{'exclusive_locks_required' attribute requires arguments that are class type or point to class type}}
-int elr_function_bad_2() __attribute__((exclusive_locks_required("mu"))); // \
- expected-error {{'exclusive_locks_required' attribute requires arguments that are class type or point to class type}}
-int elr_function_bad_3() __attribute__((exclusive_locks_required(muDoublePointer))); // \
- expected-error {{'exclusive_locks_required' attribute requires arguments that are class type or point to class type}}
-int elr_function_bad_4() __attribute__((exclusive_locks_required(umu))); // \
- expected-error {{'exclusive_locks_required' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
-
-
-
-//-----------------------------------------//
-// Shared Locks Required (slr)
-//-----------------------------------------//
-
-#if !__has_attribute(shared_locks_required)
-#error "Should support shared_locks_required attribute"
-#endif
-
-// takes one or more arguments, all locks (vars/fields)
-
-void slr_function() __attribute__((shared_locks_required)); // \
- expected-error {{attribute takes at least 1 argument}}
-
-void slr_function_arg() __attribute__((shared_locks_required(mu1)));
-
-void slr_function_args() __attribute__((shared_locks_required(mu1, mu2)));
-
-int slr_testfn(int y) __attribute__((shared_locks_required(mu1)));
-
-int slr_testfn(int y) {
- int x __attribute__((shared_locks_required(mu1))) = y; // \
- expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
- return x;
-};
-
-int slr_test_var __attribute__((shared_locks_required(mu1))); // \
- expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
-
-void slr_fun_params(int lvar __attribute__((shared_locks_required(mu1)))); // \
- expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
-
-class SlrFoo {
- private:
- int test_field __attribute__((shared_locks_required(mu1))); // \
- expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
- void test_method() __attribute__((shared_locks_required(mu1)));
-};
-
-class __attribute__((shared_locks_required(mu1))) SlrTestClass { // \
- expected-warning {{'shared_locks_required' attribute only applies to functions and methods}}
-};
-
-// Check argument parsing.
-
-// legal attribute arguments
-int slr_function_1() __attribute__((shared_locks_required(muWrapper.mu)));
-int slr_function_2() __attribute__((shared_locks_required(muDoubleWrapper.muWrapper->mu)));
-int slr_function_3() __attribute__((shared_locks_required(muWrapper.getMu())));
-int slr_function_4() __attribute__((shared_locks_required(*muWrapper.getMuPointer())));
-int slr_function_5() __attribute__((shared_locks_required(&mu1)));
-int slr_function_6() __attribute__((shared_locks_required(muRef)));
-int slr_function_7() __attribute__((shared_locks_required(muDoubleWrapper.getWrapper()->getMu())));
-int slr_function_8() __attribute__((shared_locks_required(muPointer)));
-
-
-// illegal attribute arguments
-int slr_function_bad_1() __attribute__((shared_locks_required(1))); // \
- expected-error {{'shared_locks_required' attribute requires arguments that are class type or point to class type}}
-int slr_function_bad_2() __attribute__((shared_locks_required("mu"))); // \
- expected-error {{'shared_locks_required' attribute requires arguments that are class type or point to class type}}
-int slr_function_bad_3() __attribute__((shared_locks_required(muDoublePointer))); // \
- expected-error {{'shared_locks_required' attribute requires arguments that are class type or point to class type}}
-int slr_function_bad_4() __attribute__((shared_locks_required(umu))); // \
- expected-error {{'shared_locks_required' attribute requires arguments whose type is annotated with 'lockable' attribute}}
-
More information about the cfe-commits
mailing list