<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Jun 29, 2015, at 1:00 PM, b17 c0de <<a href="mailto:b17c0de@gmail.com" class="">b17c0de@gmail.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class="">Hi Doug,<div class="">Thanks for your changes. I think the choice you made makes the most sense.</div><div class=""><br class=""></div><div class="">Can you look at the <span style="white-space: pre-wrap;" class="">nullability-completeness bug with templated types? Or should I make a bug report?</span></div></div></div></blockquote><div><br class=""></div>Please file a bug report. It helps us track fixes.</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre">      </span>- Doug</div><div><br class=""><blockquote type="cite" class=""><div class=""><div class="gmail_extra"><br class=""><div class="gmail_quote">On Mon, Jun 29, 2015 at 8:12 PM, Douglas Gregor <span dir="ltr" class=""><<a href="mailto:dgregor@apple.com" target="_blank" class="">dgregor@apple.com</a>></span> wrote:<br class=""><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word" class=""><br class=""><div class=""><span class=""><blockquote type="cite" class=""><div class="">On Jun 29, 2015, at 10:57 AM, Douglas Gregor <<a href="mailto:dgregor@apple.com" target="_blank" class="">dgregor@apple.com</a>> wrote:</div><br class=""><div class=""><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><blockquote type="cite" class=""><div class=""><br class="">On Jun 27, 2015, at 8:03 AM, b17 c0de <<a href="mailto:b17c0de@gmail.com" target="_blank" class="">b17c0de@gmail.com</a>> wrote:</div><br class=""><div class=""><div dir="ltr" class=""><div class=""><span style="font-size:12.8000001907349px" class="">Just to clarify...</span></div><span style="font-size:12.8000001907349px" class=""><div class=""><span style="font-size:12.8000001907349px" class=""><br class=""></span></div>__has_extension(nullability) always returning true is exactly what I would expect from reading the clang documentation. I would also expect that __has_extension(</span><span style="font-size:12.8000001907349px" class="">assume_nonnull) always returns true (which it currently doesn't).</span><br class=""></div></div></blockquote><div class=""><br class=""></div><div class="">__has_extension(assume_nonnull) should always be true; fixed in r240969.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><span style="font-size:12.8000001907349px" class="">I am hoping someone can explain why GNU mode is required for </span><span style="font-size:12.8000001907349px" class="">__has_feature(</span><span style="font-size:12.8000001907349px" class="">nullability) and __has_feature(</span><span style="font-size:12.8000001907349px" class="">assume_nonnull) to return true.</span></div></div></div></blockquote><div class=""><br class=""></div><div class="">GNU mode was meant to capture â€œthe default behavior”, and gets disabled when one specifies stricter conformance to the language standard. Rather, nullability is an extension that is always available, and only a â€œstandardized” language feature in Objective-C (where Clang is essentially the standard). In retrospect, we shouldn’t straddle the fence this way with GNUMode: either it’s an always-on feature (__has_feature and __has_extension are always true for both) or it’s only a language feature in Objective-C (__has_feature is true only in Objective-C mode, __has_extension is always true). I tend to prefer the former.</div></div></div></blockquote><div class=""><br class=""></div></span>r240976 makes these always-true.</div><div class=""><br class=""></div><div class=""><span style="white-space:pre-wrap" class=""> </span>- Doug</div><div class=""><div class="h5"><div class=""><br class=""></div><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><span style="white-space:pre-wrap" class="">  </span>- Doug</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><br class=""><blockquote type="cite" class=""><div class=""><div class="gmail_extra"><br class=""><div class="gmail_quote">On Sat, Jun 27, 2015 at 4:54 PM, Aaron Ballman<span class=""> </span><span dir="ltr" class=""><<a href="mailto:aaron@aaronballman.com" target="_blank" class="">aaron@aaronballman.com</a>></span><span class=""> </span>wrote:<br class=""><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><span class="">On Sat, Jun 27, 2015 at 10:45 AM, b17 c0de <<a href="mailto:b17c0de@gmail.com" target="_blank" class="">b17c0de@gmail.com</a>> wrote:<br class="">> __has_extension(nullability) returns true in non-GNU mode, on the other<br class="">> hand,__has_extension(assume_nonnull) returns false in non-GNU mode. Are you<br class="">> saying this difference is by design. If so, why?<br class=""><br class=""></span>I answered a bit too early in the morning. ;-) I forgot that<br class="">__has_extension inherits functionality from __has_feature. So<br class="">assume_nonnull should be true with either __has_feature or<br class="">__has_extension in GNU or ObjC mode, but is currently false in other<br class="">modes. You are correct that nullability is a bit different, and I'm<br class="">not certain why. __has_feature(nullability) will return true for GNU<br class="">and ObjC mode. __has_extension(nullability) will always return true.<br class=""><br class="">I am not certain whether this is by design or is a bug, but perhaps<br class="">Doug can explain. (I'm also a bit curious as to why GNU mode is<br class="">required.)<br class=""><div class=""><div class=""><br class="">~Aaron<br class=""><br class="">><br class="">> On Sat, Jun 27, 2015 at 4:35 PM, Aaron Ballman <<a href="mailto:aaron@aaronballman.com" target="_blank" class="">aaron@aaronballman.com</a>><br class="">> wrote:<br class="">>><br class="">>> On Sat, Jun 27, 2015 at 8:06 AM, b17 c0de <<a href="mailto:b17c0de@gmail.com" target="_blank" class="">b17c0de@gmail.com</a>> wrote:<br class="">>> > I figured out my issue. I was compiling with -std=c++14 and the<br class="">>> > nullability<br class="">>> > and assume_nonnull features are only enabled for ObjC and GNU mode. Why<br class="">>> > are<br class="">>> > these only supported in GNU mode? I thought GNU mode was only for<br class="">>> > features<br class="">>> > that contradict the standard. How does this feature contradict the<br class="">>> > standard<br class="">>> > given that the names are double and single underscore prefixed? I would<br class="">>> > rather not have to compile my code in GNU mode just to enable<br class="">>> > nullability. I<br class="">>> > can check with __has_extension() but at least Apple headers seem to only<br class="">>> > use<br class="">>> > __has_feature so the checks there won't be enabled when not compiling in<br class="">>> > GNU<br class="">>> > mode. If the consensus is that __has_feature(nullability) should only be<br class="">>> > enabled for GNU mode, would it make sense to have an f-group flag like<br class="">>> > -fnullability to enable it for __has_feature when not compiling in GNU<br class="">>> > mode?<br class="">>> ><br class="">>> > Also I found a bug in clang. __has_extension(assume_nonnull) doesn't<br class="">>> > work<br class="">>> > properly. It is missing from the StringCase at the end of the<br class="">>> > HasExtension()<br class="">>> > function in lib/Lex/PPMacroExpansion.cpp. I think it should be there.<br class="">>><br class="">>> __has_feature(assume_nonnull) is the way to test for that feature<br class="">>> (which is also GNU and Obj-C only).<br class="">>><br class="">>> ~Aaron<br class="">>><br class="">>> ><br class="">>> > On Sat, Jun 27, 2015 at 12:44 AM, b17 c0de <<a href="mailto:b17c0de@gmail.com" target="_blank" class="">b17c0de@gmail.com</a>> wrote:<br class="">>> >><br class="">>> >> Apple please implement  __has_feature(nullability) in clang for Xcode 7<br class="">>> >> release. :-)<br class="">>> >><br class="">>> >><br class="">>> >> On Sat, Jun 27, 2015 at 12:31 AM, Aaron Ballman<br class="">>> >> <<a href="mailto:aaron@aaronballman.com" target="_blank" class="">aaron@aaronballman.com</a>><br class="">>> >> wrote:<br class="">>> >>><br class="">>> >>> On Fri, Jun 26, 2015 at 6:29 PM, b17 c0de <<a href="mailto:b17c0de@gmail.com" target="_blank" class="">b17c0de@gmail.com</a>> wrote:<br class="">>> >>> >  It also appears that the current versions of Apple clang (even the<br class="">>> >>> > newest<br class="">>> >>> > beta) don't even support __has_feature(nullability). I take it this<br class="">>> >>> > has<br class="">>> >>> > been<br class="">>> >>> > fixed in trunk?<br class="">>> >>><br class="">>> >>> Correct, trunk is likely also the only place that has _Nonnull and<br class="">>> >>> friends, too. If you're developing on OS X and don't need cross<br class="">>> >>> compiler support for your code base, I would stick with __nonnull<br class="">>> >>> there and you'll be fine. If you need cross compiler support, you'll<br class="">>> >>> likely have to piece it together with macros.<br class="">>> >>><br class="">>> >>> ~Aaron<br class="">>> >>><br class="">>> >>> ><br class="">>> >>> > On Fri, Jun 26, 2015 at 11:53 PM, Aaron Ballman<br class="">>> >>> > <<a href="mailto:aaron@aaronballman.com" target="_blank" class="">aaron@aaronballman.com</a>><br class="">>> >>> > wrote:<br class="">>> >>> >><br class="">>> >>> >> On Fri, Jun 26, 2015 at 5:44 PM, b17 c0de <<a href="mailto:b17c0de@gmail.com" target="_blank" class="">b17c0de@gmail.com</a>><br class="">>> >>> >> wrote:<br class="">>> >>> >> > OK. What would be the best way to detect if Apple clang supports<br class="">>> >>> >> > _Nonnull or<br class="">>> >>> >> > only __nonnull though.<br class="">>> >>> >><br class="">>> >>> >> I cannot speak for how Apple's Clang works in this regard, but<br class="">>> >>> >> perhaps<br class="">>> >>> >> Doug can.<br class="">>> >>> >><br class="">>> >>> >> ~Aaron<br class="">>> >>> >><br class="">>> >>> >> ><br class="">>> >>> >> > On Fri, Jun 26, 2015 at 11:40 PM, Aaron Ballman<br class="">>> >>> >> > <<a href="mailto:aaron@aaronballman.com" target="_blank" class="">aaron@aaronballman.com</a>><br class="">>> >>> >> > wrote:<br class="">>> >>> >> >><br class="">>> >>> >> >> On Fri, Jun 26, 2015 at 5:36 PM, b17 c0de <<a href="mailto:b17c0de@gmail.com" target="_blank" class="">b17c0de@gmail.com</a>><br class="">>> >>> >> >> wrote:<br class="">>> >>> >> >> > How can one detect if an Apple clang supports the new<br class="">>> >>> >> >> > nullability<br class="">>> >>> >> >> > attributes. I tried something like:<br class="">>> >>> >> >> ><br class="">>> >>> >> >> > #if __has_attribute(_Nonnull)<br class="">>> >>> >> >> > #elif __has_attribute(__nonnull)<br class="">>> >>> >> >> > #define _Nonnull __nonnull<br class="">>> >>> >> >> > #else<br class="">>> >>> >> >> > #define _Nonnull<br class="">>> >>> >> >> > #endif<br class="">>> >>> >> >> ><br class="">>> >>> >> >> > But this didn't work. Why doesn't _Nonnull/__nonnull work with<br class="">>> >>> >> >> > __has_attribute?<br class="">>> >>> >> >><br class="">>> >>> >> >> __has_attribute is used to test for GNU-style attribute support<br class="">>> >>> >> >> only.<br class="">>> >>> >> >> To test for nullability, you should use:<br class="">>> >>> >> >> __has_feature(nullability)<br class="">>> >>> >> >><br class="">>> >>> >> >> ~Aaron<br class="">>> >>> >> >><br class="">>> >>> >> >> ><br class="">>> >>> >> >> > On Wed, Jun 24, 2015 at 10:39 PM, Douglas Gregor<br class="">>> >>> >> >> > <<a href="mailto:dgregor@apple.com" target="_blank" class="">dgregor@apple.com</a>><br class="">>> >>> >> >> > wrote:<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Another addendum: due to the conflict with glibc’s __nonnull,<br class="">>> >>> >> >> >> we’ll<br class="">>> >>> >> >> >> be<br class="">>> >>> >> >> >> renaming the __double_underscored keywords to<br class="">>> >>> >> >> >> _Big_underscored<br class="">>> >>> >> >> >> keywords,<br class="">>> >>> >> >> >> e.g.,<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> __nonnull -> _Nonnull<br class="">>> >>> >> >> >> __nullable -> _Nullable<br class="">>> >>> >> >> >> __null_unspecified -> _Null_unspecified<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> On Darwin, we’ll add predefines<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> #define __nonnull _Nonnull<br class="">>> >>> >> >> >> #define __nullable _Nullable<br class="">>> >>> >> >> >> #define __null_unspecified _Null_unspecified<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> to keep the existing headers working.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> - Doug<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> On Mar 2, 2015, at 1:22 PM, Douglas Gregor<br class="">>> >>> >> >> >> <<a href="mailto:dgregor@apple.com" target="_blank" class="">dgregor@apple.com</a>><br class="">>> >>> >> >> >> wrote:<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Hello all,<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Null pointers are a significant source of problems in<br class="">>> >>> >> >> >> applications.<br class="">>> >>> >> >> >> Whether it’s SIGSEGV taking down a process or a foolhardy<br class="">>> >>> >> >> >> attempt to<br class="">>> >>> >> >> >> recover<br class="">>> >>> >> >> >> from NullPointerException breaking invariants everywhere,<br class="">>> >>> >> >> >> it’s a<br class="">>> >>> >> >> >> problem<br class="">>> >>> >> >> >> that’s bad enough for Tony Hoare to call the invention of the<br class="">>> >>> >> >> >> null<br class="">>> >>> >> >> >> reference<br class="">>> >>> >> >> >> his billion dollar mistake [1]. It’s not the ability to<br class="">>> >>> >> >> >> create a<br class="">>> >>> >> >> >> null<br class="">>> >>> >> >> >> pointer that is a problem—having a common sentinel value<br class="">>> >>> >> >> >> meaning<br class="">>> >>> >> >> >> â€œno<br class="">>> >>> >> >> >> value”<br class="">>> >>> >> >> >> is extremely useful—but that it’s very hard to determine<br class="">>> >>> >> >> >> whether,<br class="">>> >>> >> >> >> for a<br class="">>> >>> >> >> >> particular pointer, one is expected to be able to use null. C<br class="">>> >>> >> >> >> doesn’t<br class="">>> >>> >> >> >> distinguish between â€œnullable” and â€œnonnull” pointers, so we<br class="">>> >>> >> >> >> turn to<br class="">>> >>> >> >> >> documentation and experimentation. Consider strchr from the C<br class="">>> >>> >> >> >> standard<br class="">>> >>> >> >> >> library:<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> char *strchr(const char *s, int c);<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> It is â€œobvious” to a programmer who knows the semantics of<br class="">>> >>> >> >> >> strchr<br class="">>> >>> >> >> >> that<br class="">>> >>> >> >> >> it’s important to check for a returned null, because null is<br class="">>> >>> >> >> >> used as<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> sentinel for â€œnot found”. Of course, your tools don’t know<br class="">>> >>> >> >> >> that,<br class="">>> >>> >> >> >> so<br class="">>> >>> >> >> >> they<br class="">>> >>> >> >> >> cannot help when you completely forget to check for the null<br class="">>> >>> >> >> >> case.<br class="">>> >>> >> >> >> Bugs<br class="">>> >>> >> >> >> ensue.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Can I pass a null string to strchr? The standard is unclear<br class="">>> >>> >> >> >> [2],<br class="">>> >>> >> >> >> and<br class="">>> >>> >> >> >> my<br class="">>> >>> >> >> >> platform’s implementation happily accepts a null parameter<br class="">>> >>> >> >> >> and<br class="">>> >>> >> >> >> returns<br class="">>> >>> >> >> >> null,<br class="">>> >>> >> >> >> so obviously I shouldn’t worry about it… until I port my<br class="">>> >>> >> >> >> code,<br class="">>> >>> >> >> >> or<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> underlying implementation changes because my expectations and<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> library<br class="">>> >>> >> >> >> implementor’s expectations differ. Given the age of strchr, I<br class="">>> >>> >> >> >> suspect<br class="">>> >>> >> >> >> that<br class="">>> >>> >> >> >> every implementation out there has an explicit, defensive<br class="">>> >>> >> >> >> check<br class="">>> >>> >> >> >> for<br class="">>> >>> >> >> >> a<br class="">>> >>> >> >> >> null<br class="">>> >>> >> >> >> string, because it’s easier to add yet more defensive (and<br class="">>> >>> >> >> >> generally<br class="">>> >>> >> >> >> useless) null checks than it is to ask your clients to fix<br class="">>> >>> >> >> >> their<br class="">>> >>> >> >> >> code.<br class="">>> >>> >> >> >> Scale<br class="">>> >>> >> >> >> this up, and code bloat ensues, as well as wasted programmer<br class="">>> >>> >> >> >> effort<br class="">>> >>> >> >> >> that<br class="">>> >>> >> >> >> obscures the places where checking for null really does<br class="">>> >>> >> >> >> matter.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> In a recent version of Xcode, Apple introduced an extension<br class="">>> >>> >> >> >> to<br class="">>> >>> >> >> >> C/C++/Objective-C that expresses the nullability of pointers<br class="">>> >>> >> >> >> in<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> type<br class="">>> >>> >> >> >> system via new nullability qualifiers . Nullability<br class="">>> >>> >> >> >> qualifiers<br class="">>> >>> >> >> >> express<br class="">>> >>> >> >> >> nullability as part of the declaration of strchr  [2]:<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> __nullable char *strchr(__nonnull const char *s, int c);<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> With this, programmers and tools alike can better reason<br class="">>> >>> >> >> >> about<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> use<br class="">>> >>> >> >> >> of<br class="">>> >>> >> >> >> strchr with null pointers.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> We’d like to contribute the implementation (and there is a<br class="">>> >>> >> >> >> patch<br class="">>> >>> >> >> >> attached<br class="">>> >>> >> >> >> at the end [3]), but since this is a nontrivial extension to<br class="">>> >>> >> >> >> all<br class="">>> >>> >> >> >> of<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> C<br class="">>> >>> >> >> >> family of languages that Clang supports, we believe that it<br class="">>> >>> >> >> >> needs to<br class="">>> >>> >> >> >> be<br class="">>> >>> >> >> >> discussed here first.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Goals<br class="">>> >>> >> >> >> We have several specific goals that informed the design of<br class="">>> >>> >> >> >> this<br class="">>> >>> >> >> >> feature.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Allow the intended nullability to be expressed on all<br class="">>> >>> >> >> >> pointers:<br class="">>> >>> >> >> >> Pointers<br class="">>> >>> >> >> >> are used throughout library interfaces, and the nullability<br class="">>> >>> >> >> >> of<br class="">>> >>> >> >> >> those<br class="">>> >>> >> >> >> pointers is an important part of the API contract with users.<br class="">>> >>> >> >> >> It’s<br class="">>> >>> >> >> >> too<br class="">>> >>> >> >> >> simplistic to only allow function parameters to have<br class="">>> >>> >> >> >> nullability,<br class="">>> >>> >> >> >> for<br class="">>> >>> >> >> >> example, because it’s also important information for data<br class="">>> >>> >> >> >> members,<br class="">>> >>> >> >> >> pointers-to-pointers (e.g., "a nonnull pointer to a nullable<br class="">>> >>> >> >> >> pointer<br class="">>> >>> >> >> >> to<br class="">>> >>> >> >> >> an<br class="">>> >>> >> >> >> integer”), arrays of pointers, etc.<br class="">>> >>> >> >> >> Enable better tools support for detecting nullability<br class="">>> >>> >> >> >> problems:<br class="">>> >>> >> >> >> The<br class="">>> >>> >> >> >> nullability annotations should be useful for tools<br class="">>> >>> >> >> >> (especially<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> static<br class="">>> >>> >> >> >> analyzer) that can reason about the use of null, to give<br class="">>> >>> >> >> >> warnings<br class="">>> >>> >> >> >> about<br class="">>> >>> >> >> >> both<br class="">>> >>> >> >> >> missed null checks (the result of strchr could be null…) as<br class="">>> >>> >> >> >> well<br class="">>> >>> >> >> >> as<br class="">>> >>> >> >> >> for<br class="">>> >>> >> >> >> unnecessarily-defensive code.<br class="">>> >>> >> >> >> Support workflows where all interfaces provide nullability<br class="">>> >>> >> >> >> annotations:<br class="">>> >>> >> >> >> In<br class="">>> >>> >> >> >> moving from a world where there are no nullability<br class="">>> >>> >> >> >> annotations<br class="">>> >>> >> >> >> to<br class="">>> >>> >> >> >> one<br class="">>> >>> >> >> >> where<br class="">>> >>> >> >> >> we hope to see many such annotations, we’ve found it helpful<br class="">>> >>> >> >> >> to<br class="">>> >>> >> >> >> move<br class="">>> >>> >> >> >> header-by-header, auditing a complete header to give it<br class="">>> >>> >> >> >> nullability<br class="">>> >>> >> >> >> qualifiers. Once one has done that, additions to the header<br class="">>> >>> >> >> >> need<br class="">>> >>> >> >> >> to<br class="">>> >>> >> >> >> be<br class="">>> >>> >> >> >> held<br class="">>> >>> >> >> >> to the same standard, so we need a design that allows us to<br class="">>> >>> >> >> >> warn<br class="">>> >>> >> >> >> about<br class="">>> >>> >> >> >> pointers that don’t provide nullability annotations for some<br class="">>> >>> >> >> >> declarations in<br class="">>> >>> >> >> >> a header that already has some nullability annotations.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Zero effect on ABI or code generation: There are a huge<br class="">>> >>> >> >> >> number<br class="">>> >>> >> >> >> of<br class="">>> >>> >> >> >> interfaces that could benefit from the use of nullability<br class="">>> >>> >> >> >> qualifiers,<br class="">>> >>> >> >> >> but we<br class="">>> >>> >> >> >> won’t get widespread adoption if introducing the nullability<br class="">>> >>> >> >> >> qualifiers<br class="">>> >>> >> >> >> means breaking existing code, either in the ABI (say, because<br class="">>> >>> >> >> >> nullability<br class="">>> >>> >> >> >> qualifiers are mangled into the type) or at execution time<br class="">>> >>> >> >> >> (e.g.,<br class="">>> >>> >> >> >> because a<br class="">>> >>> >> >> >> non-null pointer ends up being null along some error path and<br class="">>> >>> >> >> >> causes<br class="">>> >>> >> >> >> undefined behavior).<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >><br class="">>> >>> >> >> >><br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Why not __attribute__((nonnull))?<br class="">>> >>> >> >> >> Clang already has an attribute to express nullability,<br class="">>> >>> >> >> >> â€œnonnull”,<br class="">>> >>> >> >> >> which<br class="">>> >>> >> >> >> we<br class="">>> >>> >> >> >> inherited from GCC [4]. The â€œnonnull” attribute can be placed<br class="">>> >>> >> >> >> on<br class="">>> >>> >> >> >> functions<br class="">>> >>> >> >> >> to indicate which parameters cannot be null: one either<br class="">>> >>> >> >> >> specifies<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> indices of the arguments that cannot be null, e.g.,<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> extern void *my_memcpy (void *dest, const void *src, size_t<br class="">>> >>> >> >> >> len)<br class="">>> >>> >> >> >> __attribute__((nonnull (1, 2)));<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> or omits the list of indices to state that all pointer<br class="">>> >>> >> >> >> arguments<br class="">>> >>> >> >> >> cannot<br class="">>> >>> >> >> >> be<br class="">>> >>> >> >> >> null, e.g.,<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> extern void *my_memcpy (void *dest, const void *src, size_t<br class="">>> >>> >> >> >> len)<br class="">>> >>> >> >> >> __attribute__((nonnull));<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> More recently, â€œnonnull”  has grown the ability to be applied<br class="">>> >>> >> >> >> to<br class="">>> >>> >> >> >> parameters, and one can use the companion attribute<br class="">>> >>> >> >> >> returns_nonnull<br class="">>> >>> >> >> >> to<br class="">>> >>> >> >> >> state<br class="">>> >>> >> >> >> that a function returns a non-null pointer:<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> extern void *my_memcpy (__attribute__((nonnull)) void *dest,<br class="">>> >>> >> >> >> __attribute__((nonnull)) const void *src, size_t len)<br class="">>> >>> >> >> >> __attribute__((returns_nonnull));<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> There are a number of problems here. First, there are<br class="">>> >>> >> >> >> different<br class="">>> >>> >> >> >> attributes<br class="">>> >>> >> >> >> to express the same idea at different places in the grammar,<br class="">>> >>> >> >> >> and<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> use of<br class="">>> >>> >> >> >> the â€œnonnull” attribute on the function actually has an<br class="">>> >>> >> >> >> effect<br class="">>> >>> >> >> >> on<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> function parameters can get very, very confusing. Quick,<br class="">>> >>> >> >> >> which<br class="">>> >>> >> >> >> pointers<br class="">>> >>> >> >> >> are<br class="">>> >>> >> >> >> nullable vs. non-null in this example?<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> __attribute__((nonnull)) void *my_realloc (void *ptr, size_t<br class="">>> >>> >> >> >> size);<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> According to that declaration, ptr is nonnull and the<br class="">>> >>> >> >> >> function<br class="">>> >>> >> >> >> returns<br class="">>> >>> >> >> >> a<br class="">>> >>> >> >> >> nullable pointer… but that’s the opposite of how it reads<br class="">>> >>> >> >> >> (and<br class="">>> >>> >> >> >> behaves,<br class="">>> >>> >> >> >> if<br class="">>> >>> >> >> >> this is anything like a realloc that cannot fail). Moreover,<br class="">>> >>> >> >> >> because<br class="">>> >>> >> >> >> these<br class="">>> >>> >> >> >> two attributes are declaration attributes, not type<br class="">>> >>> >> >> >> attributes,<br class="">>> >>> >> >> >> you<br class="">>> >>> >> >> >> cannot<br class="">>> >>> >> >> >> express that nullability of the inner pointer in a<br class="">>> >>> >> >> >> multi-level<br class="">>> >>> >> >> >> pointer<br class="">>> >>> >> >> >> or an<br class="">>> >>> >> >> >> array of pointers, which makes these attributes verbose,<br class="">>> >>> >> >> >> confusing,<br class="">>> >>> >> >> >> and<br class="">>> >>> >> >> >> not<br class="">>> >>> >> >> >> sufficiently generally. These attributes fail the first of<br class="">>> >>> >> >> >> our<br class="">>> >>> >> >> >> goals.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> These attributes aren’t as useful as they could be for tools<br class="">>> >>> >> >> >> support<br class="">>> >>> >> >> >> (the<br class="">>> >>> >> >> >> second and third goals), because they only express the<br class="">>> >>> >> >> >> nonnull<br class="">>> >>> >> >> >> case,<br class="">>> >>> >> >> >> leaving<br class="">>> >>> >> >> >> no way to distinguish between the unannotated case (nobody<br class="">>> >>> >> >> >> has<br class="">>> >>> >> >> >> documented<br class="">>> >>> >> >> >> the nullability of some parameter) and the nullable case (we<br class="">>> >>> >> >> >> know<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> pointer can be null). From a tooling perspective, this is a<br class="">>> >>> >> >> >> killer:<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> static analyzer absolutely cannot warn that one has forgotten<br class="">>> >>> >> >> >> to<br class="">>> >>> >> >> >> check<br class="">>> >>> >> >> >> for<br class="">>> >>> >> >> >> null for every unannotated pointer, because the<br class="">>> >>> >> >> >> false-positive<br class="">>> >>> >> >> >> rate<br class="">>> >>> >> >> >> would be<br class="">>> >>> >> >> >> astronomical.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Finally, we’ve recently started considering violations of the<br class="">>> >>> >> >> >> __attribute__((nonnull)) contract to be undefined behavior,<br class="">>> >>> >> >> >> which<br class="">>> >>> >> >> >> fails<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> last of our goals. This is something we could debate further<br class="">>> >>> >> >> >> if<br class="">>> >>> >> >> >> it<br class="">>> >>> >> >> >> were<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> only problem, but these declaration attributes fall all of<br class="">>> >>> >> >> >> our<br class="">>> >>> >> >> >> criteria, so<br class="">>> >>> >> >> >> it’s not worth discussing.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Nullability Qualifiers<br class="">>> >>> >> >> >> We propose the addition of a new set of type qualifiers,<br class="">>> >>> >> >> >> spelled<br class="">>> >>> >> >> >> __nullable, __nonnull, and __null_unspecified, to Clang.<br class="">>> >>> >> >> >> These<br class="">>> >>> >> >> >> are<br class="">>> >>> >> >> >> collectively known as nullability qualifiers and may be<br class="">>> >>> >> >> >> written<br class="">>> >>> >> >> >> anywhere any<br class="">>> >>> >> >> >> other type qualifier may be written (such as const) on any<br class="">>> >>> >> >> >> type<br class="">>> >>> >> >> >> subject<br class="">>> >>> >> >> >> to<br class="">>> >>> >> >> >> the following restrictions:<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Two nullability qualifiers shall not appear in the same set<br class="">>> >>> >> >> >> of<br class="">>> >>> >> >> >> qualifiers.<br class="">>> >>> >> >> >> A nullability qualifier shall qualify any pointer type,<br class="">>> >>> >> >> >> including<br class="">>> >>> >> >> >> pointers<br class="">>> >>> >> >> >> to objects, pointers to functions, C++ pointers to members,<br class="">>> >>> >> >> >> block<br class="">>> >>> >> >> >> pointers,<br class="">>> >>> >> >> >> and Objective-C object pointers.<br class="">>> >>> >> >> >> A nullability qualifier in the declaration-specifiers applies<br class="">>> >>> >> >> >> to<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> innermost pointer type of each declarator (e.g., __nonnull<br class="">>> >>> >> >> >> int *<br class="">>> >>> >> >> >> is<br class="">>> >>> >> >> >> equivalent to int * __nonnull).<br class="">>> >>> >> >> >> A nullability qualifier applied to a typedef of a<br class="">>> >>> >> >> >> nullability-qualified<br class="">>> >>> >> >> >> pointer type shall specify the same nullability as the<br class="">>> >>> >> >> >> underlying<br class="">>> >>> >> >> >> type<br class="">>> >>> >> >> >> of<br class="">>> >>> >> >> >> the typedef.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> The meanings of the three nullability qualifiers are as<br class="">>> >>> >> >> >> follows:<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> __nullable: the pointer may store a null value at runtime (as<br class="">>> >>> >> >> >> part<br class="">>> >>> >> >> >> of<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> API contract)<br class="">>> >>> >> >> >> __nonnull: the pointer should not store a null value at<br class="">>> >>> >> >> >> runtime<br class="">>> >>> >> >> >> (as<br class="">>> >>> >> >> >> part<br class="">>> >>> >> >> >> of the API contract). it is possible that the value can be<br class="">>> >>> >> >> >> null,<br class="">>> >>> >> >> >> e.g.,<br class="">>> >>> >> >> >> in<br class="">>> >>> >> >> >> erroneous historic uses of an API, and it is up to the<br class="">>> >>> >> >> >> library<br class="">>> >>> >> >> >> implementor<br class="">>> >>> >> >> >> to decide to what degree she will accommodate such clients.<br class="">>> >>> >> >> >> __null_unspecified: it is unclear whether the pointer can be<br class="">>> >>> >> >> >> null or<br class="">>> >>> >> >> >> not.<br class="">>> >>> >> >> >> Use of this type qualifier is extremely rare in practice, but<br class="">>> >>> >> >> >> it<br class="">>> >>> >> >> >> fills<br class="">>> >>> >> >> >> a<br class="">>> >>> >> >> >> small but important niche when auditing a particular header<br class="">>> >>> >> >> >> to<br class="">>> >>> >> >> >> add<br class="">>> >>> >> >> >> nullability qualifiers: sometimes the nullability contract<br class="">>> >>> >> >> >> for a<br class="">>> >>> >> >> >> few<br class="">>> >>> >> >> >> APIs in<br class="">>> >>> >> >> >> the header is unclear even when looking at the implementation<br class="">>> >>> >> >> >> for<br class="">>> >>> >> >> >> historical<br class="">>> >>> >> >> >> reasons, and establishing the contract requires more<br class="">>> >>> >> >> >> extensive<br class="">>> >>> >> >> >> study.<br class="">>> >>> >> >> >> In<br class="">>> >>> >> >> >> such cases, it’s often best to mark that pointer as<br class="">>> >>> >> >> >> __null_unspecified<br class="">>> >>> >> >> >> (which will help silence the warning about unannotated<br class="">>> >>> >> >> >> pointers<br class="">>> >>> >> >> >> in a<br class="">>> >>> >> >> >> header)<br class="">>> >>> >> >> >> and move on, coming back to __null_unspecified pointers when<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> appropriate<br class="">>> >>> >> >> >> graybeard has been summoned out of retirement [5].<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Assumes-nonnull Regions<br class="">>> >>> >> >> >> We’ve found that it's fairly common for the majority of<br class="">>> >>> >> >> >> pointers<br class="">>> >>> >> >> >> within<br class="">>> >>> >> >> >> a<br class="">>> >>> >> >> >> particular header to be __nonnull. Therefore, we’ve<br class="">>> >>> >> >> >> introduced<br class="">>> >>> >> >> >> assumes-nonnull regions that assume that certain unannotated<br class="">>> >>> >> >> >> pointers<br class="">>> >>> >> >> >> implicitly get the __nonnull nullability qualifiers.<br class="">>> >>> >> >> >> Assumes-nonnull<br class="">>> >>> >> >> >> regions<br class="">>> >>> >> >> >> are marked by pragmas:<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> #pragma clang assume_nonnull begin<br class="">>> >>> >> >> >>         __nullable char *strchr(const char *s, int c); // s<br class="">>> >>> >> >> >> is<br class="">>> >>> >> >> >> inferred<br class="">>> >>> >> >> >> to<br class="">>> >>> >> >> >> be __nonnull<br class="">>> >>> >> >> >> void *my_realloc (__nullable void *ptr, size_t size); //<br class="">>> >>> >> >> >> my_realloc<br class="">>> >>> >> >> >> is<br class="">>> >>> >> >> >> inferred to return __nonnull<br class="">>> >>> >> >> >> #pragma clang assume_nonnull end<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> We infer __nonnull within an assumes_nonnull region when:<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> The pointer is a non-typedef declaration, such as a function<br class="">>> >>> >> >> >> parameter,<br class="">>> >>> >> >> >> variable, or data member, or the result type of a function.<br class="">>> >>> >> >> >> It’s<br class="">>> >>> >> >> >> very<br class="">>> >>> >> >> >> rare<br class="">>> >>> >> >> >> for one to warn typedefs to specify nullability information;<br class="">>> >>> >> >> >> rather,<br class="">>> >>> >> >> >> it’s<br class="">>> >>> >> >> >> usually the user of the typedef that needs to specify<br class="">>> >>> >> >> >> nullability.<br class="">>> >>> >> >> >> The pointer is a single-level pointer, e.g., int* but not<br class="">>> >>> >> >> >> int**,<br class="">>> >>> >> >> >> because<br class="">>> >>> >> >> >> we’ve found that programmers can get confused about the<br class="">>> >>> >> >> >> nullability<br class="">>> >>> >> >> >> of<br class="">>> >>> >> >> >> multi-level pointers (is it a __nullable pointer to __nonnull<br class="">>> >>> >> >> >> pointers,<br class="">>> >>> >> >> >> or<br class="">>> >>> >> >> >> the other way around?) and inferring nullability for any of<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> pointers in<br class="">>> >>> >> >> >> a multi-level pointer compounds the situation.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Note that no #include may occur within an assumes_nonnull<br class="">>> >>> >> >> >> region,<br class="">>> >>> >> >> >> and<br class="">>> >>> >> >> >> assumes_nonnull regions cannot cross header boundaries.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Type System Impact<br class="">>> >>> >> >> >> Nullability qualifiers are mapped to type attributes within<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> Clang<br class="">>> >>> >> >> >> type<br class="">>> >>> >> >> >> system, but a nullability-qualified pointer type is not<br class="">>> >>> >> >> >> semantically<br class="">>> >>> >> >> >> distinct from its unqualified pointer type. Therefore, one<br class="">>> >>> >> >> >> may<br class="">>> >>> >> >> >> freely<br class="">>> >>> >> >> >> convert between nullability-qualified and<br class="">>> >>> >> >> >> non-nullability-qualified<br class="">>> >>> >> >> >> pointers, or between nullability-qualified pointers with<br class="">>> >>> >> >> >> different<br class="">>> >>> >> >> >> nullability qualifiers. One cannot overload on nullability<br class="">>> >>> >> >> >> qualifiers,<br class="">>> >>> >> >> >> write<br class="">>> >>> >> >> >> C++ class template partial specializations that identify<br class="">>> >>> >> >> >> nullability<br class="">>> >>> >> >> >> qualifiers, or inspect nullability via type traits in any<br class="">>> >>> >> >> >> way.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Said more strongly, removing nullability qualifiers from a<br class="">>> >>> >> >> >> well-formed<br class="">>> >>> >> >> >> program will not change its behavior in any way, nor will the<br class="">>> >>> >> >> >> semantics<br class="">>> >>> >> >> >> of a<br class="">>> >>> >> >> >> program change when any set of (well-formed) nullability<br class="">>> >>> >> >> >> qualifiers<br class="">>> >>> >> >> >> are<br class="">>> >>> >> >> >> added to it. Operationally, this means that nullability<br class="">>> >>> >> >> >> qualifiers<br class="">>> >>> >> >> >> are<br class="">>> >>> >> >> >> not<br class="">>> >>> >> >> >> part of the canonical type in Clang’s type system, and that<br class="">>> >>> >> >> >> any<br class="">>> >>> >> >> >> warnings we<br class="">>> >>> >> >> >> produce based on nullability information will necessarily be<br class="">>> >>> >> >> >> dependent<br class="">>> >>> >> >> >> on<br class="">>> >>> >> >> >> Clang’s ability to retain type sugar during semantic<br class="">>> >>> >> >> >> analysis.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> While it’s somewhat exceptional for us to introduce new type<br class="">>> >>> >> >> >> qualifiers<br class="">>> >>> >> >> >> that don’t produce semantically distinct types, we feel that<br class="">>> >>> >> >> >> this is<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> only plausible design and implementation strategy for this<br class="">>> >>> >> >> >> feature:<br class="">>> >>> >> >> >> pushing<br class="">>> >>> >> >> >> nullability qualifiers into the type system semantically<br class="">>> >>> >> >> >> would<br class="">>> >>> >> >> >> cause<br class="">>> >>> >> >> >> significant changes to the language (e.g., overloading,<br class="">>> >>> >> >> >> partial<br class="">>> >>> >> >> >> specialization) and break ABI (due to name mangling) that<br class="">>> >>> >> >> >> would<br class="">>> >>> >> >> >> drastically<br class="">>> >>> >> >> >> reduce the number of potential users, and we feel that<br class="">>> >>> >> >> >> Clang’s<br class="">>> >>> >> >> >> support<br class="">>> >>> >> >> >> for<br class="">>> >>> >> >> >> maintaining type sugar throughout semantic analysis is<br class="">>> >>> >> >> >> generally<br class="">>> >>> >> >> >> good<br class="">>> >>> >> >> >> enough<br class="">>> >>> >> >> >> [6] to get the benefits of nullability annotations in our<br class="">>> >>> >> >> >> tools.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> Looking forward to our discussion.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> - Doug (with Jordan Rose and Anna Zaks)<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> [1]<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >><br class="">>> >>> >> >> >><span class=""> </span><a href="https://urldefense.proofpoint.com/v2/url?u=http-3A__en.wikipedia.org_wiki_Tony-5FHoare-23Apologies-5Fand-5Fretractions&d=AwMFaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=CnzuN65ENJ1H9py9XLiRvC_UQz6u3oG6GUNn7_wosSM&m=bljRv73Hdesdao5bJgcUiiE_nylTGrbpSZs4E6AE8Fo&s=_PIx9QaKZlsYZF-JpRLnkzkjNK6U-ip_DqRWv3Xrhcw&e=" rel="noreferrer" target="_blank" class="">http://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions</a><br class="">>> >>> >> >> >> [2] The standard description of strchr seems to imply that<br class="">>> >>> >> >> >> the<br class="">>> >>> >> >> >> parameter<br class="">>> >>> >> >> >> cannot be null<br class="">>> >>> >> >> >> [3] The patch is complete, but should be reviewed on<br class="">>> >>> >> >> >> cfe-commits<br class="">>> >>> >> >> >> rather<br class="">>> >>> >> >> >> than here. There are also several logic parts to this<br class="">>> >>> >> >> >> monolithic<br class="">>> >>> >> >> >> patch:<br class="">>> >>> >> >> >> (a) __nonnull/__nullable/__null_unspecified type specifiers<br class="">>> >>> >> >> >> (b) nonnull/nullable/null_unspecified syntactic sugar for<br class="">>> >>> >> >> >> Objective-C<br class="">>> >>> >> >> >> (c) Warning about inconsistent application of nullability<br class="">>> >>> >> >> >> specifiers<br class="">>> >>> >> >> >> within a given header<br class="">>> >>> >> >> >> (d) assume_nonnnull begin/end pragmas<br class="">>> >>> >> >> >> (e) Objective-C null_resettable property attribute<br class="">>> >>> >> >> >> [4]<br class="">>> >>> >> >> >><span class=""> </span><a href="https://urldefense.proofpoint.com/v2/url?u=https-3A__gcc.gnu.org_onlinedocs_gcc_Function-2DAttributes.html&d=AwMFaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=CnzuN65ENJ1H9py9XLiRvC_UQz6u3oG6GUNn7_wosSM&m=bljRv73Hdesdao5bJgcUiiE_nylTGrbpSZs4E6AE8Fo&s=7XK1ONRGcffCS5T_y1QAAPCMo3IPe50NIVPvlF08JmM&e=" rel="noreferrer" target="_blank" class="">https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html</a><br class="">>> >>> >> >> >> (search<br class="">>> >>> >> >> >> for â€œnonnull”)<br class="">>> >>> >> >> >> [5] No graybeards were harmed in the making of this feature.<br class="">>> >>> >> >> >> [6] Template instantiation is the notable exception here,<br class="">>> >>> >> >> >> because it<br class="">>> >>> >> >> >> always canonicalizes types.<br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> <nullability.patch><br class="">>> >>> >> >> >> _______________________________________________<br class="">>> >>> >> >> >> cfe-dev mailing list<br class="">>> >>> >> >> >><span class=""> </span><a href="mailto:cfe-dev@cs.uiuc.edu" target="_blank" class="">cfe-dev@cs.uiuc.edu</a><br class="">>> >>> >> >> >><span class=""> </span><a href="http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev" rel="noreferrer" target="_blank" class="">http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev</a><br class="">>> >>> >> >> >><br class="">>> >>> >> >> >><br class="">>> >>> >> >> >><br class="">>> >>> >> >> >> _______________________________________________<br class="">>> >>> >> >> >> cfe-dev mailing list<br class="">>> >>> >> >> >><span class=""> </span><a href="mailto:cfe-dev@cs.uiuc.edu" target="_blank" class="">cfe-dev@cs.uiuc.edu</a><br class="">>> >>> >> >> >><span class=""> </span><a href="http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev" rel="noreferrer" target="_blank" class="">http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev</a><br class="">>> >>> >> >> >><br class="">>> >>> >> >> ><br class="">>> >>> >> >> ><br class="">>> >>> >> >> > _______________________________________________<br class="">>> >>> >> >> > cfe-dev mailing list<br class="">>> >>> >> >> ><span class=""> </span><a href="mailto:cfe-dev@cs.uiuc.edu" target="_blank" class="">cfe-dev@cs.uiuc.edu</a><br class="">>> >>> >> >> ><span class=""> </span><a href="http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev" rel="noreferrer" target="_blank" class="">http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev</a><br class="">>> >>> >> >> ><br class="">>> >>> >> ><br class="">>> >>> >> ><br class="">>> >>> ><br class="">>> >>> ><br class="">>> >><br class="">>> >><br class="">>> ><br class="">>> ><br class="">>> > _______________________________________________<br class="">>> > cfe-dev mailing list<br class="">>> ><span class=""> </span><a href="mailto:cfe-dev@cs.uiuc.edu" target="_blank" class="">cfe-dev@cs.uiuc.edu</a><br class="">>> ><span class=""> </span><a href="http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev" rel="noreferrer" target="_blank" class="">http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev</a><br class="">>> ><br class="">><br class="">><br class=""></div></div></blockquote></div><br class=""></div></div></blockquote></div><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important" class="">_______________________________________________</span><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important" class="">cfe-dev mailing list</span><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><a href="mailto:cfe-dev@cs.uiuc.edu" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" target="_blank" class="">cfe-dev@cs.uiuc.edu</a><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><a href="http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" target="_blank" class="">http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev</a></div></blockquote></div><br class=""></div></div></div></blockquote></div><br class=""></div>
</div></blockquote></div><br class=""></body></html>