11a17032bSKristof Umann================== 21a17032bSKristof UmannNullability Checks 31a17032bSKristof Umann================== 41a17032bSKristof Umann 51a17032bSKristof UmannThis document is a high level description of the nullablility checks. 61a17032bSKristof UmannThese checks intended to use the annotations that is described in this 7166f9be3SHans WennborgRFC: https://discourse.llvm.org/t/rfc-nullability-qualifiers/35672 8166f9be3SHans Wennborg(`Mailman <https://lists.llvm.org/pipermail/cfe-dev/2015-March/041779.html>`_) 91a17032bSKristof Umann 101a17032bSKristof UmannLet's consider the following 2 categories: 111a17032bSKristof Umann 121a17032bSKristof Umann**1) nullable** 131a17032bSKristof Umann 141a17032bSKristof UmannIf a pointer ``p`` has a nullable annotation and no explicit null check or assert, we should warn in the following cases: 151a17032bSKristof Umann 161a17032bSKristof Umann* ``p`` gets implicitly converted into nonnull pointer, for example, we are passing it to a function that takes a nonnull parameter. 171a17032bSKristof Umann* ``p`` gets dereferenced 181a17032bSKristof Umann 191a17032bSKristof UmannTaking a branch on nullable pointers are the same like taking branch on null unspecified pointers. 201a17032bSKristof Umann 21cc8237d9SAyushi ShuklaExplicit cast from nullable to nonnull: 221a17032bSKristof Umann 231a17032bSKristof Umann.. code-block:: cpp 241a17032bSKristof Umann 251a17032bSKristof Umann __nullable id foo; 261a17032bSKristof Umann id bar = foo; 271a17032bSKristof Umann takesNonNull((_nonnull) bar); // should not warn here (backward compatibility hack) 281a17032bSKristof Umann anotherTakesNonNull(bar); // would be great to warn here, but not necessary(*) 291a17032bSKristof Umann 301a17032bSKristof UmannBecause bar corresponds to the same symbol all the time it is not easy to implement the checker that way the cast only suppress the first call but not the second. For this reason in the first implementation after a contradictory cast happens, I will treat bar as nullable unspecified, this way all of the warnings will be suppressed. Treating the symbol as nullable unspecified also has an advantage that in case the takesNonNull function body is being inlined, the will be no warning, when the symbol is dereferenced. In case I have time after the initial version I might spend additional time to try to find a more sophisticated solution, in which we would produce the second warning (*). 311a17032bSKristof Umann 321a17032bSKristof Umann**2) nonnull** 331a17032bSKristof Umann 341a17032bSKristof Umann* Dereferencing a nonnull, or sending message to it is ok. 351a17032bSKristof Umann* Converting nonnull to nullable is Ok. 361a17032bSKristof Umann* When there is an explicit cast from nonnull to nullable I will trust the cast (it is probable there for a reason, because this cast does not suppress any warnings or errors). 371a17032bSKristof Umann* But what should we do about null checks?: 381a17032bSKristof Umann 391a17032bSKristof Umann.. code-block:: cpp 401a17032bSKristof Umann 411a17032bSKristof Umann __nonnull id takesNonnull(__nonnull id x) { 421a17032bSKristof Umann if (x == nil) { 431a17032bSKristof Umann // Defensive backward compatible code: 441a17032bSKristof Umann .... 451a17032bSKristof Umann return nil; // Should the analyzer cover this piece of code? Should we require the cast (__nonnull)nil? 461a17032bSKristof Umann } 471a17032bSKristof Umann .... 481a17032bSKristof Umann } 491a17032bSKristof Umann 501a17032bSKristof UmannThere are these directions: 511a17032bSKristof Umann 521a17032bSKristof Umann* We can either take the branch; this way the branch is analyzed 531a17032bSKristof Umann* Should we not warn about any nullability issues in that branch? Probably not, it is ok to break the nullability postconditions when the nullability preconditions are violated. 541a17032bSKristof Umann* We can assume that these pointers are not null and we lose coverage with the analyzer. (This can be implemented either in constraint solver or in the checker itself.) 551a17032bSKristof Umann 561a17032bSKristof UmannOther Issues to keep in mind/take care of: 571a17032bSKristof Umann 581a17032bSKristof Umann* Messaging: 591a17032bSKristof Umann 601a17032bSKristof Umann * Sending a message to a nullable pointer 611a17032bSKristof Umann 621a17032bSKristof Umann * Even though the method might return a nonnull pointer, when it was sent to a nullable pointer the return type will be nullable. 631a17032bSKristof Umann * The result is nullable unless the receiver is known to be non null. 641a17032bSKristof Umann 65*3a14993fSKazu Hirata * Sending a message to an unspecified or nonnull pointer 661a17032bSKristof Umann 671a17032bSKristof Umann * If the pointer is not assumed to be nil, we should be optimistic and use the nullability implied by the method. 681a17032bSKristof Umann 691a17032bSKristof Umann * This will not happen automatically, since the AST will have null unspecified in this case. 701a17032bSKristof Umann 711a17032bSKristof UmannInlining 721a17032bSKristof Umann-------- 731a17032bSKristof Umann 741a17032bSKristof UmannA symbol may need to be treated differently inside an inlined body. For example, consider these conversions from nonnull to nullable in presence of inlining: 751a17032bSKristof Umann 761a17032bSKristof Umann.. code-block:: cpp 771a17032bSKristof Umann 781a17032bSKristof Umann id obj = getNonnull(); 791a17032bSKristof Umann takesNullable(obj); 801a17032bSKristof Umann takesNonnull(obj); 811a17032bSKristof Umann 821a17032bSKristof Umann void takesNullable(nullable id obj) { 831a17032bSKristof Umann obj->ivar // we should assume obj is nullable and warn here 841a17032bSKristof Umann } 851a17032bSKristof Umann 861a17032bSKristof UmannWith no special treatment, when the takesNullable is inlined the analyzer will not warn when the obj symbol is dereferenced. One solution for this is to reanalyze takesNullable as a top level function to get possible violations. The alternative method, deducing nullability information from the arguments after inlining is not robust enough (for example there might be more parameters with different nullability, but in the given path the two parameters might end up being the same symbol or there can be nested functions that take different view of the nullability of the same symbol). So the symbol will remain nonnull to avoid false positives but the functions that takes nullable parameters will be analyzed separately as well without inlining. 871a17032bSKristof Umann 881a17032bSKristof UmannAnnotations on multi level pointers 891a17032bSKristof Umann----------------------------------- 901a17032bSKristof Umann 911a17032bSKristof UmannTracking multiple levels of annotations for pointers pointing to pointers would make the checker more complicated, because this way a vector of nullability qualifiers would be needed to be tracked for each symbol. This is not a big caveat, since once the top level pointer is dereferenced, the symvol for the inner pointer will have the nullability information. The lack of multi level annotation tracking only observable, when multiple levels of pointers are passed to a function which has a parameter with multiple levels of annotations. So for now the checker support the top level nullability qualifiers only.: 921a17032bSKristof Umann 931a17032bSKristof Umann.. code-block:: cpp 941a17032bSKristof Umann 951a17032bSKristof Umann int * __nonnull * __nullable p; 961a17032bSKristof Umann int ** q = p; 971a17032bSKristof Umann takesStarNullableStarNullable(q); 981a17032bSKristof Umann 991a17032bSKristof UmannImplementation notes 1001a17032bSKristof Umann-------------------- 1011a17032bSKristof Umann 1021a17032bSKristof UmannWhat to track? 1031a17032bSKristof Umann 1041a17032bSKristof Umann* The checker would track memory regions, and to each relevant region a qualifier information would be attached which is either nullable, nonnull or null unspecified (or contradicted to suppress warnings for a specific region). 1051a17032bSKristof Umann* On a branch, where a nullable pointer is known to be non null, the checker treat it as a same way as a pointer annotated as nonnull. 1061a17032bSKristof Umann* When there is an explicit cast from a null unspecified to either nonnull or nullable I will trust the cast. 1071a17032bSKristof Umann* Unannotated pointers are treated the same way as pointers annotated with nullability unspecified qualifier, unless the region is wrapped in ASSUME_NONNULL macros. 1081a17032bSKristof Umann* We might want to implement a callback for entry points to top level functions, where the pointer nullability assumptions would be made. 109