diff options
author | Caitlin Sadowski <supertri@google.com> | 2011-07-28 17:21:07 +0000 |
---|---|---|
committer | Caitlin Sadowski <supertri@google.com> | 2011-07-28 17:21:07 +0000 |
commit | fdde9e719ad75e656a1475a36b06c2f88f0957cc (patch) | |
tree | 7b91e22376a0bc6f1d03b67933e7f13bcfa7337a | |
parent | 1afb661bc5444462a246cefa0effa61ef25fab29 (diff) |
Added parsing for guarded_var, pt_guarded_var, lockable,
scoped_lockable, and no_thread_safety_analysis attributes, all for thread safety analysis
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@136364 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | docs/LanguageExtensions.html | 52 | ||||
-rw-r--r-- | include/clang/Basic/Attr.td | 23 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 5 | ||||
-rw-r--r-- | include/clang/Sema/AttributeList.h | 5 | ||||
-rw-r--r-- | lib/Sema/AttributeList.cpp | 5 | ||||
-rw-r--r-- | lib/Sema/SemaDeclAttr.cpp | 110 | ||||
-rw-r--r-- | test/SemaCXX/warn-thread-safety.cpp | 204 |
7 files changed, 403 insertions, 1 deletions
diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index 528315b820..877217f9aa 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -87,6 +87,14 @@ </ul> </li> <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_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> + </ul> </ul> <!-- ======================================================================= --> @@ -1088,6 +1096,50 @@ balance in some way.</p> <p>Query for these features with <tt>__has_attribute(ns_consumed)</tt>, <tt>__has_attribute(ns_returns_retained)</tt>, etc.</p> + +<!-- ======================================================================= --> +<h2 id="analyzerspecific">Thread-Safety Annotation Checking</h2> +<!-- ======================================================================= --> + +<p>Clang supports additional attributes for checking basic locking policies in +multithreaded programs. +Clang currently parses the following list of attributes, although +<b>the implementation for these annotations is currently in development.</b> +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> + +<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_lockable">lockable</h4> + +<p>Use <tt>__attribute__((lockable))</tt> on a class definition to specify +that it has a lockable type (e.g. a Mutex class). This annotation is primarily +used to check consistency.</p> + +<h4 id="ts_scopedlockable">scoped_lockable</h4> + +<p>Use <tt>__attribute__((scoped_lockable))</tt> on a class definition to +specify that it has a "scoped" lockable type. Objects of this type will acquire +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> + +<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> + </div> </body> </html> diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 094ccb1000..8f52a5b246 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -538,3 +538,26 @@ def WeakRef : InheritableAttr { def X86ForceAlignArgPointer : InheritableAttr { let Spellings = []; } + + +// C/C++ Thread safety attributes (e.g. for deadlock, data race checking) + +def GuardedVar : InheritableAttr { + let Spellings = ["guarded_var"]; +} + +def PtGuardedVar : InheritableAttr { + let Spellings = ["pt_guarded_var"]; +} + +def Lockable : InheritableAttr { + let Spellings = ["lockable"]; +} + +def ScopedLockable : InheritableAttr { + let Spellings = ["scoped_lockable"]; +} + +def NoThreadSafetyAnalysis : InheritableAttr { + let Spellings = ["no_thread_safety_analysis"]; +} diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 239bd45d22..98f758332f 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1249,7 +1249,7 @@ def warn_attribute_wrong_decl_type : Warning< "parameters and methods|functions, methods and blocks|" "classes and virtual methods|functions, methods, and parameters|" "classes|virtual methods|class members|variables|methods|" - "variables, functions and labels}1">; + "variables, functions and labels|fields and global variables}1">; def err_attribute_wrong_decl_type : Error< "%0 attribute only applies to %select{functions|unions|" "variables and functions|functions and methods|parameters|" @@ -1287,6 +1287,9 @@ def warn_objc_precise_lifetime_meaningless : Error< def warn_label_attribute_not_unused : Warning< "The only valid attribute for labels is 'unused'">; def err_invalid_pcs : Error<"Invalid PCS type">; +def err_attribute_can_be_applied_only_to_value_decl : Error< + "%0 attribute can only be applied to value declarations">; + // Availability attribute def warn_availability_unknown_platform : Warning< diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 5b4c7d4519..d741e89bc8 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -184,12 +184,14 @@ public: AT_format_arg, AT_global, AT_gnu_inline, + AT_guarded_var, AT_host, AT_IBAction, // Clang-specific. AT_IBOutlet, // Clang-specific. AT_IBOutletCollection, // Clang-specific. AT_init_priority, AT_launch_bounds, + AT_lockable, AT_malloc, AT_may_alias, AT_mode, @@ -198,6 +200,7 @@ public: AT_neon_polyvector_type, // Clang-specific. AT_neon_vector_type, // Clang-specific. AT_no_instrument_function, + AT_no_thread_safety_analysis, AT_nocommon, AT_nodebug, AT_noinline, @@ -225,9 +228,11 @@ public: AT_packed, AT_pascal, AT_pcs, // ARM specific + AT_pt_guarded_var, AT_pure, AT_regparm, AT_reqd_wg_size, + AT_scoped_lockable, AT_section, AT_sentinel, AT_shared, diff --git a/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp index f90c6dc0ab..babf491b94 100644 --- a/lib/Sema/AttributeList.cpp +++ b/lib/Sema/AttributeList.cpp @@ -210,5 +210,10 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { .Case("uuid", AT_uuid) .Case("pcs", AT_pcs) .Case("ms_struct", AT_MsStruct) + .Case("guarded_var", AT_guarded_var) + .Case("pt_guarded_var", AT_pt_guarded_var) + .Case("scoped_lockable", AT_scoped_lockable) + .Case("lockable", AT_lockable) + .Case("no_thread_safety_analysis", AT_no_thread_safety_analysis) .Default(UnknownAttribute); } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 2cbd83a8fd..2294c0e784 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -204,6 +204,39 @@ static bool checkAttributeNumArgs(Sema &S, const AttributeList &Attr, 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 +/// +static bool mayBeSharedVariable(Decl *D) { + if (isa<FieldDecl>(D)) + return true; + if(VarDecl *vd = dyn_cast<VarDecl>(D)) + return (vd->hasGlobalStorage() && !(vd->isThreadSpecified())); + + return false; +} + +/// +/// \brief Check if passed in Decl is a pointer type. +/// Note that this function may produce an error message. +/// \return true if the Decl is a pointer type; false otherwise +/// +bool checkIsPointer(Sema & S, Decl * D, const AttributeList & Attr) { + if(ValueDecl * vd = dyn_cast <ValueDecl>(D)) { + QualType QT = vd->getType(); + if(QT->isAnyPointerType()){ + return true; + } + S.Diag(Attr.getLoc(), diag::warn_pointer_attribute_wrong_type) + << Attr.getName()->getName() << QT; + } else { + S.Diag(Attr.getLoc(), diag::err_attribute_can_be_applied_only_to_value_decl) + << Attr.getName(); + } + return false; +} + //===----------------------------------------------------------------------===// // Attribute Implementations //===----------------------------------------------------------------------===// @@ -212,6 +245,65 @@ static bool checkAttributeNumArgs(Sema &S, const AttributeList &Attr, // least add some helper functions to check most argument patterns (# // and types of args). +static void handleGuardedVarAttr(Sema &S, Decl *D, const AttributeList &Attr, + bool pointer = false) { + assert(!Attr.isInvalid()); + + if (!checkAttributeNumArgs(S, Attr, 0)) + 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) PtGuardedVarAttr(Attr.getLoc(), S.Context)); + else + D->addAttr(::new (S.Context) GuardedVarAttr(Attr.getLoc(), S.Context)); +} + +static void handleLockableAttr(Sema &S, Decl *D, const AttributeList &Attr, + bool scoped = false) { + assert(!Attr.isInvalid()); + + if (!checkAttributeNumArgs(S, Attr, 0)) + return; + + if (!isa<CXXRecordDecl>(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedClass; + return; + } + + if (scoped) + D->addAttr(::new (S.Context) ScopedLockableAttr(Attr.getLoc(), S.Context)); + else + D->addAttr(::new (S.Context) LockableAttr(Attr.getLoc(), S.Context)); +} + +static void handleNoThreadSafetyAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + assert(!Attr.isInvalid()); + + if (!checkAttributeNumArgs(S, Attr, 0)) + return; + + if (!isFunction(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunctionOrMethod; + return; + } + + D->addAttr(::new (S.Context) NoThreadSafetyAnalysisAttr(Attr.getLoc(), + S.Context)); +} + static void handleExtVectorTypeAttr(Sema &S, Scope *scope, Decl *D, const AttributeList &Attr) { TypedefNameDecl *tDecl = dyn_cast<TypedefNameDecl>(D); @@ -3071,6 +3163,24 @@ static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_uuid: handleUuidAttr(S, D, Attr); break; + + // Thread safety attributes: + case AttributeList::AT_guarded_var: + handleGuardedVarAttr(S, D, Attr); + break; + case AttributeList::AT_pt_guarded_var: + handleGuardedVarAttr(S, D, Attr, /*pointer = */true); + break; + case AttributeList::AT_scoped_lockable: + handleLockableAttr(S, D, Attr, /*scoped = */true); + break; + case AttributeList::AT_no_thread_safety_analysis: + handleNoThreadSafetyAttr(S, D, Attr); + break; + case AttributeList::AT_lockable: + handleLockableAttr(S, D, Attr); + break; + default: // Ask target about the attribute. const TargetAttributesSema &TargetAttrs = S.getTargetAttributesSema(); diff --git a/test/SemaCXX/warn-thread-safety.cpp b/test/SemaCXX/warn-thread-safety.cpp new file mode 100644 index 0000000000..39a3fa2e4d --- /dev/null +++ b/test/SemaCXX/warn-thread-safety.cpp @@ -0,0 +1,204 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +/*********************************** + * 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}} |