<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 10:57 AM, Douglas Gregor <<a href="mailto:dgregor@apple.com" class="">dgregor@apple.com</a>> wrote:</div><br class="Apple-interchange-newline"><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; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><blockquote type="cite" class=""><div class=""><br class="Apple-interchange-newline">On Jun 27, 2015, at 8:03 AM, 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=""><div class=""><span class="" style="font-size: 12.8000001907349px;">Just to clarify...</span></div><span class="" style="font-size: 12.8000001907349px;"><div class=""><span class="" style="font-size: 12.8000001907349px;"><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 class="" style="font-size: 12.8000001907349px;">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 class="" style="font-size: 12.8000001907349px;">I am hoping someone can explain why GNU mode is required for </span><span class="" style="font-size: 12.8000001907349px;">__has_feature(</span><span class="" style="font-size: 12.8000001907349px;">nullability) and __has_feature(</span><span class="" style="font-size: 12.8000001907349px;">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><br class=""></div>r240976 makes these always-true.</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre"> </span>- Doug</div><div><br class=""></div><div><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; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span class="Apple-tab-span" style="white-space: pre;"> </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; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 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="Apple-converted-space"> </span><span dir="ltr" class=""><<a href="mailto:aaron@aaronballman.com" target="_blank" class="">aaron@aaronballman.com</a>></span><span class="Apple-converted-space"> </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" 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="HOEnZb"><div class="h5"><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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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="Apple-converted-space"> </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="Apple-converted-space"> </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="Apple-converted-space"> </span><a href="mailto:cfe-dev@cs.uiuc.edu" class="">cfe-dev@cs.uiuc.edu</a><br class="">>> >>> >> >> >><span class="Apple-converted-space"> </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="Apple-converted-space"> </span><a href="mailto:cfe-dev@cs.uiuc.edu" class="">cfe-dev@cs.uiuc.edu</a><br class="">>> >>> >> >> >><span class="Apple-converted-space"> </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="Apple-converted-space"> </span><a href="mailto:cfe-dev@cs.uiuc.edu" class="">cfe-dev@cs.uiuc.edu</a><br class="">>> >>> >> >> ><span class="Apple-converted-space"> </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="Apple-converted-space"> </span><a href="mailto:cfe-dev@cs.uiuc.edu" class="">cfe-dev@cs.uiuc.edu</a><br class="">>> ><span class="Apple-converted-space"> </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 class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 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; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 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; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 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; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 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; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" 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; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 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; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class="">http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev</a></div></blockquote></div><br class=""></body></html>