diff options
-rw-r--r-- | docs/LanguageExtensions.html | 149 | ||||
-rw-r--r-- | include/clang/Basic/Attr.td | 52 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 2 | ||||
-rw-r--r-- | include/clang/Sema/AttributeList.h | 13 | ||||
-rw-r--r-- | lib/Sema/AttributeList.cpp | 13 | ||||
-rw-r--r-- | lib/Sema/SemaDeclAttr.cpp | 230 | ||||
-rw-r--r-- | test/SemaCXX/warn-thread-safety.cpp | 575 |
7 files changed, 1015 insertions, 19 deletions
diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index 37ea0396e0..804080d326 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -89,11 +89,24 @@ <li><a href="#analyzerspecific">Static Analysis-Specific Extensions</a></li> <li><a href="#threadsafety">Thread Safety Annotation Checking</a></li> <ul> + <li><a href="#ts_noanal"><tt>no_thread_safety_analysis</tt></a></li> + <li><a href="#ts_lockable"><tt>lockable</tt></a></li> + <li><a href="#ts_scopedlockable"><tt>scoped_lockable</tt></a></li> <li><a href="#ts_guardedvar"><tt>guarded_var</tt></a></li> <li><a href="#ts_ptguardedvar"><tt>pt_guarded_var</tt></a></li> - <li><a href="#ts_lockable"><tt>lockable</tt></a></li> - <li><a href="#ts_scopedlockable"><tt>scoped_lockable</tt></a></li> - <li><a href="#ts_noanal"><tt>no_thread_safety_analysis</tt></a></li> + <li><a href="#ts_guardedby"><tt>guarded_by(l)</tt></a></li> + <li><a href="#ts_ptguardedby"><tt>pt_guarded_by(l)</tt></a></li> + <li><a href="#ts_acquiredbefore"><tt>acquired_before(...)</tt></a></li> + <li><a href="#ts_acquiredafter"><tt>acquired_after(...)</tt></a></li> + <li><a href="#ts_elf"><tt>exclusive_lock_function(...)</tt></a></li> + <li><a href="#ts_slf"><tt>shared_lock_function(...)</tt></a></li> + <li><a href="#ts_etf"><tt>exclusive_trylock_function(...)</tt></a></li> + <li><a href="#ts_stf"><tt>shared_trylock_function(...)</tt></a></li> + <li><a href="#ts_uf"><tt>unlock_function(...)</tt></a></li> + <li><a href="#ts_lr"><tt>lock_returned(l)</tt></a></li> + <li><a href="#ts_le"><tt>locks_excluded(...)</tt></a></li> + <li><a href="#ts_elr"><tt>exclusive_locks_required(...)</tt></a></li> + <li><a href="#ts_slr"><tt>shared_locks_required(...)</tt></a></li> </ul> </ul> @@ -1109,15 +1122,12 @@ For more details, see the <a href="http://gcc.gnu.org/wiki/ThreadSafetyAnnotation">GCC implementation</a>. </p> -<h4 id="ts_guardedvar">guarded_var</h4> - -<p>Use <tt>__attribute__((guarded_var))</tt> on a variable declaration to -specify that the variable must be accessed while holding some lock.</p> - -<h4 id="ts_ptguardedvar">pt_guarded_var</h4> +<h4 id="ts_noanal">no_thread_safety_analysis</h4> -<p>Use <tt>__attribute__((pt_guarded_var))</tt> on a pointer declaration to -specify that the pointer must be dereferenced while holding some lock.</p> +<p>Use <tt>__attribute__((no_thread_safety_analysis))</tt> on a function +declaration to specify that the thread safety analysis should not be run on that +function. This attribute provides an escape hatch (e.g. for situations when it +is difficult to annotate the locking policy). </p> <h4 id="ts_lockable">lockable</h4> @@ -1133,12 +1143,119 @@ the lock upon construction and release it upon going out of scope. This annotation is primarily used to check consistency.</p> -<h4 id="ts_noanal">no_thread_safety_analysis</h4> +<h4 id="ts_guardedvar">guarded_var</h4> -<p>Use <tt>__attribute__((no_thread_safety_analysis))</tt> on a function -declaration to specify that the thread safety analysis should not be run on that -function. This attribute provides an escape hatch (e.g. for situations when it -is difficult to annotate the locking policy). </p> +<p>Use <tt>__attribute__((guarded_var))</tt> on a variable declaration to +specify that the variable must be accessed while holding some lock.</p> + +<h4 id="ts_ptguardedvar">pt_guarded_var</h4> + +<p>Use <tt>__attribute__((pt_guarded_var))</tt> on a pointer declaration to +specify that the pointer must be dereferenced while holding some lock.</p> + +<h4 id="ts_guardedby">guarded_by(l)</h4> + +<p>Use <tt>__attribute__((guarded_by(l)))</tt> on a variable declaration to +specify that the variable must be accessed while holding lock l.</p> + +<h4 id="ts_ptguardedby">pt_guarded_by(l)</h4> + +<p>Use <tt>__attribute__((pt_guarded_by(l)))</tt> on a pointer declaration to +specify that the pointer must be dereferenced while holding lock l.</p> + +<h4 id="ts_acquiredbefore">acquired_before(...)</h4> + +<p>Use <tt>__attribute__((acquired_before(...)))</tt> on a declaration +of a lockable variable to specify that the lock must be acquired before all +attribute arguments. Arguments must be lockable type, and there must be at +least one argument.</p> + +<h4 id="ts_acquiredafter">acquired_after(...)</h4> + +<p>Use <tt>__attribute__((acquired_after(...)))</tt> on a declaration +of a lockable variable to specify that the lock must be acquired after all +attribute arguments. Arguments must be lockable type, and there must be at +least one argument.</p> + +<h4 id="ts_elf">exclusive_lock_function(...)</h4> + +<p>Use <tt>__attribute__((exclusive_lock_function(...)))</tt> on a function +declaration to specify that the function acquires all listed locks +exclusively. This attribute takes zero or more +arguments: either of lockable type or integers indexing into +function parameters of lockable type. If no arguments are given, the acquired +lock is implicitly <tt>this</tt> of the enclosing object.</p> + +<h4 id="ts_slf">shared_lock_function(...)</h4> + +<p>Use <tt>__attribute__((shared_lock_function(...)))</tt> on a function +declaration to specify that the function acquires all listed locks, although + the locks may be shared (e.g. read locks). +This attribute takes zero or more +arguments: either of lockable type or integers indexing into +function parameters of lockable type. If no arguments are given, the acquired +lock is implicitly <tt>this</tt> of the enclosing object.</p> + +<h4 id="ts_etf">exclusive_trylock_function(...)</h4> + +<p>Use <tt>__attribute__((exclusive_lock_function(...)))</tt> on a function +declaration to specify that the function will try (without blocking) to acquire +all listed locks exclusively. This attribute takes one or more +arguments. The first argument is an integer or boolean value specifying the +return value of a successful lock acquisition. The remaining arugments are +either of lockable type or integers indexing into +function parameters of lockable type. If only one argument is given, the +acquired lock is implicitly <tt>this</tt> of the enclosing object.</p> + +<h4 id="ts_stf">shared_trylock_function(...)</h4> + +<p>Use <tt>__attribute__((shared_lock_function(...)))</tt> on a function +declaration to specify that the function will try (without blocking) to acquire +all listed locks, although + the locks may be shared (e.g. read locks). +This attribute takes one or more +arguments. The first argument is an integer or boolean value specifying the +return value of a successful lock acquisition. The remaining arugments are +either of lockable type or integers indexing into +function parameters of lockable type. If only one argument is given, the +acquired lock is implicitly <tt>this</tt> of the enclosing object.</p> + +<h4 id="ts_uf">unlock_function(...)</h4> + +<p>Use <tt>__attribute__((unlock_function(...)))</tt> on a function +declaration to specify that the function release all listed locks. + This attribute takes zero or more +arguments: either of lockable type or integers indexing into +function parameters of lockable type. If no arguments are given, the acquired +lock is implicitly <tt>this</tt> of the enclosing object.</p> + +<h4 id="ts_lr">lock_returned(l)</h4> + +<p>Use <tt>__attribute__((lock_returned(l)))</tt> on a function +declaration to specify that the function returns lock l (l must be of lockable +type). This annotation is used +to aid in resolving lock expressions.</p> + +<h4 id="ts_le">locks_excluded(...)</h4> + +<p>Use <tt>__attribute__((locks_excluded(...)))</tt> on a function declaration +to specify that the function must not be called with the listed locks. +Arguments must be lockable type, and there must be at +least one argument.</p> + +<h4 id="ts_elr">exclusive_locks_required(...)</h4> + +<p>Use <tt>__attribute__((exclusive_locks_required(...)))</tt> on a function +declaration to specify that the function must be called while holding the listed +exclusive locks. Arguments must be lockable type, and there must be at +least one argument.</p> + +<h4 id="ts_slr">shared_locks_required(...)</h4> + +<p>Use <tt>__attribute__((shared_locks_required(...)))</tt> on a function +declaration to specify that the function must be called while holding the listed +shared locks. Arguments must be lockable type, and there must be at +least one argument.</p> </div> </body> diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 8f52a5b246..2b0ed3d669 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -561,3 +561,55 @@ def ScopedLockable : InheritableAttr { def NoThreadSafetyAnalysis : InheritableAttr { let Spellings = ["no_thread_safety_analysis"]; } + +def GuardedBy : InheritableAttr { + let Spellings = ["guarded_by"]; +} + +def PtGuardedBy : InheritableAttr { + let Spellings = ["pt_guarded_by"]; +} + +def AcquiredAfter : InheritableAttr { + let Spellings = ["acquired_after"]; +} + +def AcquiredBefore : InheritableAttr { + let Spellings = ["acquired_before"]; +} + +def ExclusiveLockFunction : InheritableAttr { + let Spellings = ["exclusive_lock_function"]; +} + +def SharedLockFunction : InheritableAttr { + let Spellings = ["shared_lock_function"]; +} + +def ExclusiveTrylockFunction : InheritableAttr { + let Spellings = ["exclusive_trylock_function"]; +} + +def SharedTrylockFunction : InheritableAttr { + let Spellings = ["shared_trylock_function"]; +} + +def UnlockFunction : InheritableAttr { + let Spellings = ["unlock_function"]; +} + +def LockReturned : InheritableAttr { + let Spellings = ["lock_returned"]; +} + +def LocksExcluded : InheritableAttr { + let Spellings = ["locks_excluded"]; +} + +def ExclusiveLocksRequired : InheritableAttr { + let Spellings = ["exclusive_locks_required"]; +} + +def SharedLocksRequired : InheritableAttr { + let Spellings = ["shared_locks_required"]; +} diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 1bb24a46fc..660528a2c1 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1128,6 +1128,8 @@ def err_attribute_wrong_number_arguments : Error< ":requires exactly %0 arguments}0">; def err_attribute_too_many_arguments : Error< "attribute takes no more than %0 argument%s0">; +def err_attribute_too_few_arguments : Error< + "attribute takes at least %0 argument%s0">; def err_iboutletcollection_type : Error< "invalid type %0 as argument of iboutletcollection attribute">; def err_iboutletcollection_object_type : Error< diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index d741e89bc8..674e13a3df 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -152,6 +152,8 @@ private: public: enum Kind { // Please keep this list alphabetized. + AT_acquired_after, + AT_acquired_before, AT_address_space, AT_alias, AT_aligned, @@ -178,12 +180,16 @@ public: AT_device, AT_dllexport, AT_dllimport, + AT_exclusive_lock_function, + AT_exclusive_locks_required, + AT_exclusive_trylock_function, AT_ext_vector_type, AT_fastcall, AT_format, AT_format_arg, AT_global, AT_gnu_inline, + AT_guarded_by, AT_guarded_var, AT_host, AT_IBAction, // Clang-specific. @@ -191,7 +197,9 @@ public: AT_IBOutletCollection, // Clang-specific. AT_init_priority, AT_launch_bounds, + AT_lock_returned, AT_lockable, + AT_locks_excluded, AT_malloc, AT_may_alias, AT_mode, @@ -228,6 +236,7 @@ public: AT_packed, AT_pascal, AT_pcs, // ARM specific + AT_pt_guarded_by, AT_pt_guarded_var, AT_pure, AT_regparm, @@ -236,10 +245,14 @@ public: AT_section, AT_sentinel, AT_shared, + AT_shared_lock_function, + AT_shared_locks_required, + AT_shared_trylock_function, AT_stdcall, AT_thiscall, AT_transparent_union, AT_unavailable, + AT_unlock_function, AT_unused, AT_used, AT_uuid, diff --git a/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp index babf491b94..d9f17b42da 100644 --- a/lib/Sema/AttributeList.cpp +++ b/lib/Sema/AttributeList.cpp @@ -215,5 +215,18 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { .Case("scoped_lockable", AT_scoped_lockable) .Case("lockable", AT_lockable) .Case("no_thread_safety_analysis", AT_no_thread_safety_analysis) + .Case("guarded_by", AT_guarded_by) + .Case("pt_guarded_by", AT_pt_guarded_by) + .Case("acquired_after", AT_acquired_after) + .Case("acquired_before", AT_acquired_before) + .Case("exclusive_lock_function", AT_exclusive_lock_function) + .Case("exclusive_locks_required", AT_exclusive_locks_required) + .Case("exclusive_trylock_function", AT_exclusive_trylock_function) + .Case("lock_returned", AT_lock_returned) + .Case("locks_excluded", AT_locks_excluded) + .Case("shared_lock_function", AT_shared_lock_function) + .Case("shared_locks_required", AT_shared_locks_required) + .Case("shared_trylock_function", AT_shared_trylock_function) + .Case("unlock_function", AT_unlock_function) .Default(UnknownAttribute); } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 694d38b4a1..0f02ed31df 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -27,7 +27,7 @@ using namespace sema; /// These constants match the enumerated choices of /// warn_attribute_wrong_decl_type and err_attribute_wrong_decl_type. -enum { +enum AttributeDeclType { ExpectedFunction, ExpectedUnion, ExpectedVariableOrFunction, @@ -42,7 +42,8 @@ enum { ExpectedClassMember, ExpectedVariable, ExpectedMethod, - ExpectedVariableFunctionOrLabel + ExpectedVariableFunctionOrLabel, + ExpectedFieldOrGlobalVar }; //===----------------------------------------------------------------------===// @@ -205,6 +206,34 @@ static bool checkAttributeNumArgs(Sema &S, const AttributeList &Attr, } /// +/// \brief Check the total number of argumenation, whether parsed by clang +/// as arguments or parameters. Outputs a warning. +/// \return false if the number of argumenation units does not match expectation +/// +static bool checkAttributeNumArgsPlusParams(Sema &S, const AttributeList &Attr, + unsigned int Num, + bool moreok = false) { + unsigned int numArgsPlusParams = 0; + + if (Attr.getParameterName()) + numArgsPlusParams++; + + numArgsPlusParams += Attr.getNumArgs(); + + if (moreok && numArgsPlusParams < Num) { + S.Diag(Attr.getLoc(), diag::err_attribute_too_few_arguments) << Num; + return false; + } + + if (!moreok && numArgsPlusParams != Num) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << Num; + return false; + } + + return true; +} + +/// /// \brief Check if passed in Decl is a field or potentially shared global var /// \return true if the Decl is a field or potentially shared global variable /// @@ -225,7 +254,7 @@ static bool mayBeSharedVariable(Decl *D) { bool checkIsPointer(Sema & S, Decl * D, const AttributeList & Attr) { if(ValueDecl * vd = dyn_cast <ValueDecl>(D)) { QualType QT = vd->getType(); - if(QT->isAnyPointerType()){ + if(QT->isAnyPointerType()) { return true; } S.Diag(Attr.getLoc(), diag::warn_pointer_attribute_wrong_type) @@ -268,6 +297,30 @@ static void handleGuardedVarAttr(Sema &S, Decl *D, const AttributeList &Attr, D->addAttr(::new (S.Context) GuardedVarAttr(Attr.getLoc(), S.Context)); } +static void handleGuardedByAttr(Sema &S, Decl *D, const AttributeList &Attr, + bool pointer = false) { + assert(!Attr.isInvalid()); + + if (!checkAttributeNumArgsPlusParams(S, Attr, 1)) + return; + + // 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) + << Attr.getName() << 15; /*fields and global vars*/; + return; + } + + if (pointer && !checkIsPointer(S, D, Attr)) + return; + + if (pointer) + D->addAttr(::new (S.Context) PtGuardedByAttr(Attr.getLoc(), S.Context)); + else + D->addAttr(::new (S.Context) GuardedByAttr(Attr.getLoc(), S.Context)); +} + + static void handleLockableAttr(Sema &S, Decl *D, const AttributeList &Attr, bool scoped = false) { assert(!Attr.isInvalid()); @@ -304,6 +357,138 @@ static void handleNoThreadSafetyAttr(Sema &S, Decl *D, S.Context)); } +static void handleAcquireOrderAttr(Sema &S, Decl *D, const AttributeList &Attr, + bool before) { + assert(!Attr.isInvalid()); + + if (!checkAttributeNumArgsPlusParams(S, Attr, 1, /*moreok=*/true)) + return; + + // 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) + << Attr.getName() << 15; /*fields and global vars*/; + return; + } + + if (before) + D->addAttr(::new (S.Context) AcquiredBeforeAttr(Attr.getLoc(), S.Context)); + else + D->addAttr(::new (S.Context) AcquiredAfterAttr(Attr.getLoc(), S.Context)); +} + +static void handleLockFunAttr(Sema &S, Decl *D, const AttributeList &Attr, + bool exclusive = false) { + assert(!Attr.isInvalid()); + + // zero or more arguments ok + + if (!isFunction(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunctionOrMethod; + return; + } + + if (exclusive) + D->addAttr(::new (S.Context) ExclusiveLockFunctionAttr(Attr.getLoc(), + S.Context)); + else + D->addAttr(::new (S.Context) SharedLockFunctionAttr(Attr.getLoc(), + S.Context)); +} + +static void handleTrylockFunAttr(Sema &S, Decl *D, const AttributeList &Attr, + bool exclusive = false) { + assert(!Attr.isInvalid()); + + if (!checkAttributeNumArgsPlusParams(S, Attr, 1, /*moreok=*/true)) + return; + + if (!isFunction(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunctionOrMethod; + return; + } + + if (exclusive) + D->addAttr(::new (S.Context) ExclusiveTrylockFunctionAttr(Attr.getLoc(), + S.Context)); + else + D->addAttr(::new (S.Context) SharedTrylockFunctionAttr(Attr.getLoc(), + S.Context)); + +} + +static void handleLocksRequiredAttr(Sema &S, Decl *D, const AttributeList &Attr, + bool exclusive = false) { + assert(!Attr.isInvalid()); + + if (!checkAttributeNumArgsPlusParams(S, Attr, 1, /*moreok=*/true)) + return; + + if (!isFunction(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunctionOrMethod; + return; + } + + if (exclusive) + D->addAttr(::new (S.Context) ExclusiveLocksRequiredAttr(Attr.getLoc(), + S.Context)); + else + D->addAttr(::new (S.Context) SharedLocksRequiredAttr(Attr.getLoc(), + S.Context)); +} + + +static void handleUnlockFunAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + assert(!Attr.isInvalid()); + + // zero or more arguments ok + + if (!isFunction(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunctionOrMethod; + return; + } + + D->addAttr(::new (S.Context) UnlockFunctionAttr(Attr.getLoc(), S.Context)); +} + +static void handleLockReturnedAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + assert(!Attr.isInvalid()); + + if (!checkAttributeNumArgsPlusParams(S, Attr, 1)) + return; + + if (!isFunction(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunctionOrMethod; + return; + } + + D->addAttr(::new (S.Context) LockReturnedAttr(Attr.getLoc(), S.Context)); +} + +static void handleLocksExcludedAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + assert(!Attr.isInvalid()); + + if (!checkAttributeNumArgsPlusParams(S, Attr, 1, /*moreok=*/true)) + return; + + if (!isFunction(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunctionOrMethod; + return; + } + + D->addAttr(::new (S.Context) LocksExcludedAttr(Attr.getLoc(), S.Context)); +} + + static void handleExtVectorTypeAttr(Sema &S, Scope *scope, Decl *D, const AttributeList &Attr) { TypedefNameDecl *tDecl = dyn_cast<TypedefNameDecl>(D); @@ -3180,6 +3365,45 @@ static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_lockable: handleLockableAttr(S, D, Attr); break; + case AttributeList::AT_guarded_by: + handleGuardedByAttr(S, D, Attr); + break; + case AttributeList::AT_pt_guarded_by: + handleGuardedByAttr(S, D, Attr, /*pointer = */true); + break; + case AttributeList::AT_exclusive_lock_function: + handleLockFunAttr(S, D, Attr, /*exclusive = */true); + break; + case AttributeList::AT_exclusive_locks_required: + handleLocksRequiredAttr(S, D, Attr, /*exclusive = */true); + break; + case AttributeList::AT_exclusive_trylock_function: + handleTrylockFunAttr(S, D, Attr, /*exclusive = */true); + break; + case AttributeList::AT_lock_returned: + handleLockReturnedAttr(S, D, Attr); + break; + case AttributeList::AT_locks_excluded: + handleLocksExcludedAttr(S, D, Attr); + break; + case AttributeList::AT_shared_lock_function: + handleLockFunAttr(S, D, Attr); + break; + case AttributeList::AT_shared_locks_required: + handleLocksRequiredAttr(S, D, Attr); + break; + case AttributeList::AT_shared_trylock_function: + handleTrylockFunAttr(S, D, Attr); + break; + case AttributeList::AT_unlock_function: + handleUnlockFunAttr(S, D, Attr); + break; + case AttributeList::AT_acquired_before: + handleAcquireOrderAttr(S, D, Attr, /*before = */true); + break; + case AttributeList::AT_acquired_after: + handleAcquireOrderAttr(S, D, Attr, /*before = */false); + break; default: // Ask target about the attribute. diff --git a/test/SemaCXX/warn-thread-safety.cpp b/test/SemaCXX/warn-thread-safety.cpp index 39a3fa2e4d..4958d2ed0f 100644 --- a/test/SemaCXX/warn-thread-safety.cpp +++ b/test/SemaCXX/warn-thread-safety.cpp @@ -1,5 +1,16 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s + +/** + * Helper fields + */ + +class __attribute__((lockable)) Mu { +}; + +Mu mu1; +Mu mu2; + /*********************************** * No Thread Safety Analysis (noanal) ***********************************/ @@ -202,3 +213,567 @@ class SLFoo { void sl_function_params(int lvar __attribute__((scoped_lockable))); // \ expected-warning {{'scoped_lockable' attribute only applies to classes}} + + +/*********************************** + * Guarded By Attribute (gb) + ***********************************/ + +// FIXME: 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.Deal with argument parsing: +// grab token stream parsing from C++0x branch +// possibly create new, more permissive category for gcc attributes + +//foo +//foo.bar +//foo.bar->baz +//foo.bar()->baz()->a +//&foo +//*foo + +//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; +} + +/*********************************** + * Acquired After (aa) + ***********************************/ + +// FIXME: Would we like this attribute to take more than 1 arg? +// FIXME: What about pointers to locks? + +#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}} +} + +// Note: illegal int aa_int __attribute__((acquired_after(mu1))) will +// be taken care of by warnings that aa__int is not lockable. + + +/*********************************** + * 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. + +/*********************************** + * 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}} + + + +/*********************************** + * 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}} +}; + + +/*********************************** + * 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_f |