1======================== 2Function Effect Analysis 3======================== 4 5.. contents:: 6 :depth: 3 7 :local: 8 9 10Introduction 11============ 12 13Clang Function Effect Analysis is a language extension which can warn about "unsafe" 14constructs. The feature is currently tailored for the Performance Constraint attributes 15``nonblocking`` and ``nonallocating``; functions with these attributes are verified as not 16containing any language constructs or calls to other functions which violate the constraint. 17(See :doc:`AttributeReference`.) 18 19 20The ``nonblocking`` and ``nonallocating`` attributes 21==================================================== 22 23Attribute syntax 24---------------- 25 26The ``nonblocking`` and ``nonallocating`` attributes apply to function types, allowing them to be 27attached to functions, blocks, function pointers, lambdas, and member functions. 28 29.. code-block:: c++ 30 31 // Functions 32 void nonblockingFunction() [[clang::nonblocking]]; 33 void nonallocatingFunction() [[clang::nonallocating]]; 34 35 // Function pointers 36 void (*nonblockingFunctionPtr)() [[clang::nonblocking]]; 37 38 // Typedefs, type aliases. 39 typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]]; 40 using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)(); 41 using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]]; 42 43 // C++ methods 44 struct Struct { 45 void NBMethod() [[clang::nonblocking]]; 46 }; 47 48 // C++ lambdas 49 auto nbLambda = []() [[clang::nonblocking]] {}; 50 51 // Blocks 52 void (^nbBlock)() = ^() [[clang::nonblocking]] {}; 53 54The attribute applies only to the function itself. In particular, it does not apply to any nested 55functions or declarations, such as blocks, lambdas, and local classes. 56 57This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it parallels the placement 58of the ``noexcept`` specifier, and the attributes have other similarities to ``noexcept``. The GNU 59``__attribute__((nonblocking))`` syntax is also supported. Note that it requires a different 60placement on a C++ type alias. 61 62Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional argument, a compile-time 63constant boolean expression. By default, the argument is ``true``, so ``[[clang::nonblocking]]`` 64is equivalent to ``[[clang::nonblocking(true)]]``, and declares the function type as never blocking. 65 66 67Attribute semantics 68------------------- 69 70Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` attributes define an ordered 71series of performance constraints. From weakest to strongest: 72 73- ``noexcept`` (as per the C++ standard): The function type will never throw an exception. 74- ``nonallocating``: The function type will never allocate memory on the heap or throw an 75 exception. 76- ``nonblocking``: The function type will never block on a lock, allocate memory on the heap, 77 or throw an exception. 78 79``nonblocking`` includes the ``nonallocating`` guarantee. 80 81While ``nonblocking`` and ``nonallocating`` are conceptually a superset of ``noexcept``, neither 82attribute implicitly specifies ``noexcept``. Further, ``noexcept`` has a specified runtime behavior of 83aborting if an exception is thrown, while the ``nonallocating`` and ``nonblocking`` attributes are 84mainly for compile-time analysis and have no runtime behavior, except in code built 85with Clang's :doc:`RealtimeSanitizer`. Nonetheless, Clang emits a 86warning if, in C++, a function is declared ``nonblocking`` or ``nonallocating`` without 87``noexcept``. This diagnostic is controlled by ``-Wperf-constraint-implies-noexcept``. 88 89``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, and by extension, to 90function-like declarations. When applied to a declaration with a body, the compiler verifies the 91function, as described in the section "Analysis and warnings", below. 92 93``blocking`` and ``allocating`` are synonyms for ``nonblocking(false)`` and 94``nonallocating(false)``, respectively. They can be used on a function-like declaration to 95explicitly disable any potential inference of ``nonblocking`` or ``nonallocating`` during 96verification. (Inference is described later in this document). ``nonblocking(false)`` and 97``nonallocating(false)`` are legal, but superfluous when applied to a function *type* 98that is not part of a declarator: ``float (int) [[nonblocking(false)]]`` and 99``float (int)`` are identical types. 100 101For functions with no explicit performance constraint, the worst is assumed: the function 102allocates memory and potentially blocks, unless it can be inferred otherwise. This is detailed in the 103discussion of verification. 104 105The following example describes the meanings of all permutations of the two attributes and arguments: 106 107.. code-block:: c++ 108 109 void nb1_na1() [[clang::nonblocking(true)]] [[clang::nonallocating(true)]]; 110 // Valid; nonallocating(true) is superfluous but doesn't contradict the guarantee. 111 112 void nb1_na0() [[clang::nonblocking(true)]] [[clang::nonallocating(false)]]; 113 // error: 'allocating' and 'nonblocking' attributes are not compatible 114 115 void nb0_na1() [[clang::nonblocking(false)]] [[clang::nonallocating(true)]]; 116 // Valid; the function does not allocate memory, but may lock for other reasons. 117 118 void nb0_na0() [[clang::nonblocking(false)]] [[clang::nonallocating(false)]]; 119 // Valid. 120 121 122Type conversions 123---------------- 124 125A performance constraint can be removed or weakened via an implicit conversion. An attempt to add 126or strengthen a performance constraint is unsafe and results in a warning. The rules for this 127are comparable to that for ``noexcept`` in C++17 and later. 128 129.. code-block:: c++ 130 131 void unannotated(); 132 void nonblocking() [[clang::nonblocking]]; 133 void nonallocating() [[clang::nonallocating]]; 134 135 void example() 136 { 137 // It's fine to remove a performance constraint. 138 void (*fp_plain)(); 139 fp_plain = unannotated; 140 fp_plain = nonblocking; 141 fp_plain = nonallocating; 142 143 // Adding/spoofing nonblocking is unsafe. 144 void (*fp_nonblocking)() [[clang::nonblocking]]; 145 fp_nonblocking = nullptr; 146 fp_nonblocking = nonblocking; 147 fp_nonblocking = unannotated; 148 // ^ warning: attribute 'nonblocking' should not be added via type conversion 149 fp_nonblocking = nonallocating; 150 // ^ warning: attribute 'nonblocking' should not be added via type conversion 151 152 // Adding/spoofing nonallocating is unsafe. 153 void (*fp_nonallocating)() [[clang::nonallocating]]; 154 fp_nonallocating = nullptr; 155 fp_nonallocating = nonallocating; 156 fp_nonallocating = nonblocking; // no warning because nonblocking includes nonallocating 157 fp_nonallocating = unannotated; 158 // ^ warning: attribute 'nonallocating' should not be added via type conversion 159 } 160 161Virtual methods 162--------------- 163 164In C++, when a virtual method has a performance constraint, overriding methods in 165subclasses inherit the constraint. 166 167.. code-block:: c++ 168 169 struct Base { 170 virtual void unsafe(); 171 virtual void safe() noexcept [[clang::nonblocking]]; 172 }; 173 174 struct Derived : public Base { 175 void unsafe() [[clang::nonblocking]] override; 176 // It's okay for an overridden method to be more constrained 177 178 void safe() noexcept override; 179 // This method is implicitly declared `nonblocking`, inherited from Base. 180 }; 181 182Redeclarations, overloads, and name mangling 183-------------------------------------------- 184 185The ``nonblocking`` and ``nonallocating`` attributes, like ``noexcept``, do not factor into 186argument-dependent lookup and overloaded functions/methods. 187 188First, consider that ``noexcept`` is integral to a function's type: 189 190.. code-block:: c++ 191 192 void f1(int); 193 void f1(int) noexcept; 194 // error: exception specification in declaration does not match previous 195 // declaration 196 197Unlike ``noexcept``, a redeclaration of ``f2`` with an added or stronger performance constraint is 198legal and propagates the attribute to the previous declaration: 199 200.. code-block:: c++ 201 202 int f2(); 203 int f2() [[clang::nonblocking]]; // redeclaration with stronger constraint is OK. 204 205This greatly eases adoption by making it possible to annotate functions in external libraries 206without modifying library headers. 207 208A redeclaration with a removed or weaker performance constraint produces a warning, paralleling 209the behavior of ``noexcept``: 210 211.. code-block:: c++ 212 213 int f2() { return 42; } 214 // warning: attribute 'nonblocking' on function does not match previous declaration 215 216In C++14, the following two declarations of `f3` are identical (a single function). In C++17 they 217are separate overloads: 218 219.. code-block:: c++ 220 221 void f3(void (*)()); 222 void f3(void (*)() noexcept); 223 224Similarly, the following two declarations of `f4` are separate overloads. This pattern may pose 225difficulties due to ambiguity: 226 227.. code-block:: c++ 228 229 void f4(void (*)()); 230 void f4(void (*)() [[clang::nonblocking]]); 231 232The attributes have no effect on the mangling of function and method names. 233 234Objective-C 235----------- 236 237The attributes are currently unsupported on Objective-C methods. 238 239Analysis and warnings 240===================== 241 242Constraints 243----------- 244 245Functions declared ``nonallocating`` or ``nonblocking``, when defined, are verified according to the 246following rules. Such functions: 247 2481. May not allocate or deallocate memory on the heap. The analysis follows the calls to 249 ``operator new`` and ``operator delete`` generated by the ``new`` and ``delete`` keywords, and 250 treats them like any other function call. The global ``operator new`` and ``operator delete`` 251 aren't declared ``nonblocking`` or ``nonallocating`` and so they are considered unsafe. (This 252 is correct because most memory allocators are not lock-free. Note that the placement form of 253 ``operator new`` is implemented inline in libc++'s ``<new>`` header, and is verifiably 254 ``nonblocking``, since it merely casts the supplied pointer to the result type.) 255 2562. May not throw or catch exceptions. To throw, the compiler must allocate the exception on the 257 heap. (Also, many subclasses of ``std::exception`` allocate a string). Exceptions are 258 deallocated when caught. 259 2603. May not make any indirect function call, via a virtual method, function pointer, or 261 pointer-to-member function, unless the target is explicitly declared with the same 262 ``nonblocking`` or ``nonallocating`` attribute (or stronger). 263 2644. May not make direct calls to any other function, with the following exceptions: 265 266 a. The callee is also explicitly declared with the same ``nonblocking`` or ``nonallocating`` 267 attribute (or stronger). 268 b. The callee is defined in the same translation unit as the caller, does not have the ``false`` 269 form of the required attribute, and can be verified to have the same attribute or stronger, 270 according to these same rules. 271 c. The callee is a built-in function that is known not to block or allocate. 272 d. The callee is declared ``noreturn`` and, if compiling C++, the callee is also declared 273 ``noexcept``. This special case excludes functions such as ``abort()`` and ``std::terminate()`` 274 from the analysis. (The reason for requiring ``noexcept`` in C++ is that a function declared 275 ``noreturn`` could be a wrapper for ``throw``.) 276 2775. May not invoke or access an Objective-C method or property, since ``objc_msgSend()`` calls into 278 the Objective-C runtime, which may allocate memory or otherwise block. 279 2806. May not access thread-local variables. Typically, thread-local variables are allocated on the 281 heap when first accessed. 282 283Functions declared ``nonblocking`` have an additional constraint: 284 2857. May not declare static local variables (e.g. Meyers singletons). The compiler generates a lock 286 protecting the initialization of the variable. 287 288Violations of any of these rules result in warnings, in the ``-Wfunction-effects`` category: 289 290.. code-block:: c++ 291 292 void notInline(); 293 294 void example() [[clang::nonblocking]] 295 { 296 auto* x = new int; 297 // warning: function with 'nonblocking' attribute must not allocate or deallocate 298 // memory 299 300 if (x == nullptr) { 301 static Logger* logger = createLogger(); 302 // warning: function with 'nonblocking' attribute must not have static local variables 303 304 throw std::runtime_warning{ "null" }; 305 // warning: 'nonblocking" function 'example' must not throw exceptions 306 } 307 notInline(); 308 // warning: 'function with 'nonblocking' attribute must not call non-'nonblocking' function 309 // 'notInline' 310 // note (on notInline()): declaration cannot be inferred 'nonblocking' because it has no 311 // definition in this translation unit 312 } 313 314Inferring ``nonblocking`` or ``nonallocating`` 315---------------------------------------------- 316 317In the absence of a ``nonblocking`` or ``nonallocating`` attribute (whether ``true`` or ``false``), 318a function that is called from a performance-constrained function may be analyzed to 319infer whether it has a desired attribute. This analysis happens when the function is not a virtual 320method, and it has a visible definition within the current translation unit (i.e. its body can be 321traversed). 322 323.. code-block:: c++ 324 325 void notInline(); 326 int implicitlySafe() { return 42; } 327 void implicitlyUnsafe() { notInline(); } 328 329 void example() [[clang::nonblocking]] 330 { 331 int x = implicitlySafe(); // OK 332 implicitlyUnsafe(); 333 // warning: function with 'nonblocking' attribute must not call non-'nonblocking' function 334 // 'implicitlyUnsafe' 335 // note (on implicitlyUnsafe): function cannot be inferred 'nonblocking' because it calls 336 // non-'nonblocking' function 'notInline' 337 // note (on notInline()): declaration cannot be inferred 'nonblocking' because it has no 338 // definition in this translation unit 339 } 340 341Lambdas and blocks 342------------------ 343 344As mentioned earlier, the performance constraint attributes apply only to a single function and not 345to any code nested inside it, including blocks, lambdas, and local classes. It is possible for a 346nonblocking function to schedule the execution of a blocking lambda on another thread. Similarly, a 347blocking function may create a ``nonblocking`` lambda for use in a realtime context. 348 349Operations which create, destroy, copy, and move lambdas and blocks are analyzed in terms of the 350underlying function calls. For example, the creation of a lambda with captures generates a function 351call to an anonymous struct's constructor, passing the captures as parameters. 352 353Implicit function calls in the AST 354---------------------------------- 355 356The ``nonblocking`` / ``nonallocating`` analysis occurs at the Sema phase of analysis in Clang. 357During Sema, there are some constructs which will eventually become function calls, but do not 358appear as function calls in the AST. For example, ``auto* foo = new Foo;`` becomes a declaration 359containing a ``CXXNewExpr`` which is understood as a function call to the global ``operator new`` 360(in this example), and a ``CXXConstructExpr``, which, for analysis purposes, is a function call to 361``Foo``'s constructor. Most gaps in the analysis would be due to incomplete knowledge of AST 362constructs which become function calls. 363 364Disabling diagnostics 365--------------------- 366 367Function effect diagnostics are controlled by ``-Wfunction-effects``. 368 369A construct like this can be used to exempt code from the checks described here: 370 371.. code-block:: c++ 372 373 #define NONBLOCKING_UNSAFE(...) \ 374 _Pragma("clang diagnostic push") \ 375 _Pragma("clang diagnostic ignored \"-Wunknown-warning-option\"") \ 376 _Pragma("clang diagnostic ignored \"-Wfunction-effects\"") \ 377 __VA_ARGS__ \ 378 _Pragma("clang diagnostic pop") 379 380Disabling the diagnostic allows for: 381 382- constructs which do block, but which in practice are used in ways to avoid unbounded blocking, 383 e.g. a thread pool with semaphores to coordinate multiple realtime threads; 384- using libraries which are safe but not yet annotated; 385- incremental adoption in a large codebase. 386 387Adoption 388======== 389 390There are a few common issues that arise when adopting the ``nonblocking`` and ``nonallocating`` 391attributes. 392 393C++ exceptions 394-------------- 395 396Exceptions pose a challenge to the adoption of the performance constraints. Common library functions 397which throw exceptions include: 398 399+----------------------------------+-----------------------------------------------------------------------+ 400| Method | Alternative | 401+==================================+=======================================================================+ 402| ``std::vector<T>::at()`` | ``operator[](size_t)``, after verifying that the index is in range. | 403+----------------------------------+-----------------------------------------------------------------------+ 404| ``std::optional<T>::value()`` | ``operator*``, after checking ``has_value()`` or ``operator bool()``. | 405+----------------------------------+-----------------------------------------------------------------------+ 406| ``std::expected<T, E>::value()`` | Same as for ``std::optional<T>::value()``. | 407+----------------------------------+-----------------------------------------------------------------------+ 408 409 410``std::function<R(Args...)>`` 411----------------------------- 412 413``std::function<R(Args...)>`` is generally incompatible with ``nonblocking`` and ``nonallocating`` 414code, because a typical implementation may allocate heap memory in the constructor. 415 416Alternatives: 417 418- ``std::function_ref`` (available in C++26 or as ``llvm::function_ref``). This is appropriate and 419 optimal when a functor's lifetime does not need to extend past the function that created it. 420 421- ``inplace_function`` from WG14. This solves the allocation problem by giving the functor wrapper 422 a fixed size known at compile time and using an inline buffer. 423 424While these alternatives both address the heap allocation of ``std::function``, they are still 425obstacles to ``nonblocking/nonallocating`` verification, for reasons detailed in the next section. 426 427 428Interactions with type-erasure techniques 429----------------------------------------- 430 431``std::function<R(Args...)>`` illustrates a common C++ type-erasure technique. Using template 432argument deduction, it decomposes a function type into its return and parameter types. Additional 433components of the function type, including ``noexcept``, ``nonblocking``, ``nonallocating``, and any 434other attributes, are discarded. 435 436Standard library support for these components of a function type is not immediately forthcoming. 437 438Code can work around this limitation in either of two ways: 439 4401. Avoid abstractions like ``std::function`` and instead work directly with the original lambda type. 441 4422. Create a specialized alternative, e.g. ``nonblocking_function_ref<R(Args...)>`` where all function 443 pointers used in the implementation and its interface are ``nonblocking``. 444 445As an example of the first approach, when using a lambda as a *Callable* template parameter, the 446attribute is preserved: 447 448.. code-block:: c++ 449 450 std::sort(vec.begin(), vec.end(), 451 [](const Elem& a, const Elem& b) [[clang::nonblocking]] { return a.mem < b.mem; }); 452 453Here, the type of the ``Compare`` template parameter is an anonymous class generated from the 454lambda, with an ``operator()`` method holding the ``nonblocking`` attribute. 455 456A complication arises when a *Callable* template parameter, instead of being a lambda or class 457implementing ``operator()``, is a function pointer: 458 459.. code-block:: c++ 460 461 static bool compare_elems(const Elem& a, const Elem& b) [[clang::nonblocking]] { 462 return a.mem < b.mem; }; 463 464 std::sort(vec.begin(), vec.end(), compare_elems); 465 466Here, the type of ``compare_elems`` is decomposed to ``bool(const Elem&, const Elem&)``, without 467``nonblocking``, when forming the template parameter. This can be solved using the second approach, 468creating a specialized alternative which explicitly requires the attribute. In this case, it's 469possible to use a small wrapper to transform the function pointer into a functor: 470 471.. code-block:: c++ 472 473 template <typename> 474 class nonblocking_fp; 475 476 template <typename R, typename... Args> 477 class nonblocking_fp<R(Args...)> { 478 public: 479 using impl_t = R (*)(Args...) [[clang::nonblocking]]; 480 481 private: 482 impl_t mImpl{ nullptr_t }; 483 public: 484 nonblocking_fp() = default; 485 nonblocking_fp(impl_t f) : mImpl{ f } {} 486 487 R operator()(Args... args) const 488 { 489 return mImpl(std::forward<Args>(args)...); 490 } 491 }; 492 493 // deduction guide (like std::function's) 494 template< class R, class... ArgTypes > 495 nonblocking_fp( R(*)(ArgTypes...) ) -> nonblocking_fp<R(ArgTypes...)>; 496 497 // -- 498 499 // Wrap the function pointer in a functor which preserves ``nonblocking``. 500 std::sort(vec.begin(), vec.end(), nonblocking_fp{ compare_elems }); 501 502Now, the ``nonblocking`` attribute of ``compare_elems`` is verified when it is converted to a 503``nonblocking`` function pointer, as the argument to ``nonblocking_fp``'s constructor. The template 504parameter is the functor class ``nonblocking_fp``. 505 506 507Static local variables 508---------------------- 509 510Static local variables are often used for lazily-constructed globals (Meyers singletons). Beyond the 511compiler's use of a lock to ensure thread-safe initialization, it is dangerously easy to 512inadvertently trigger initialization, involving heap allocation, from a ``nonblocking`` or 513``nonallocating`` context. 514 515Generally, such singletons need to be replaced by globals, and care must be taken to ensure their 516initialization before they are used from ``nonblocking`` or ``nonallocating`` contexts. 517 518 519Annotating libraries 520-------------------- 521 522It can be surprising that the analysis does not depend on knowledge of any primitives; it simply 523assumes the worst, that all function calls are unsafe unless explicitly marked as safe or able to be 524inferred as safe. With ``nonblocking``, this appears to suffice for all but the most primitive of 525spinlocks. 526 527At least for an operating system's C functions, it is possible to define an override header which 528redeclares safe common functions (e.g. ``pthread_self()``) with the addition of ``nonblocking``. 529This may help in adopting the feature incrementally. 530 531It also helps that many of the functions in the standard C libraries (notably ``<math.h>``) 532are treated as built-in functions by Clang, which the diagnosis understands to be safe. 533 534Much of the C++ standard library consists of inline templated functions which work well with 535inference. A small number of primitives may need explicit ``nonblocking/nonallocating`` attributes. 536