[cfe-dev] RFC: Adding availability attributes to the libc++ headers
Duncan P. N. Exon Smith via cfe-dev
cfe-dev at lists.llvm.org
Mon Feb 8 22:14:12 PST 2016
> On 2016-Feb-08, at 22:12, Duncan P. N. Exon Smith <dexonsmith at apple.com> wrote:
>
> Manman and I are working on adding availability attributes to the libc++
> headers to express to `clang` which dylib feautures are available on
> Apple platforms (and at what versions).
>
> I'd love some feedback! Please read on.
>
> What are availability attributes?
> =================================
>
> Availability attributes describe when API is available. It's widely
> used on Apple platforms; I'm not sure how much use it has outside of
> that. For example:
>
> int foo()
> __attribute__((availability(macosx,introduced=10.9,
> deprecated=10.10,obsoleted=10.11)))
> __attribute__((availability(ios,introduced=8.0)))
> __attribute__((availability(android,unavailable)));
>
> This says that `foo()` was added to the dylib in Mac OS X 10.9,
> deprecated in 10.10, and obsoleted in 10.11; added to iOS in 8.0; and
> isn't available at all on Android.
>
> Availability attributes can be attached to most (all?) declarations,
> including types, variables, and functions.
>
> Since libc++ has a stable API, I only expect to use "introduced" and
> "unavailable" (and we're only working on macosx, ios, tvos and watchos).
>
> What are the semantics when API isn't available?
> ================================================
>
> There are two cases here. The simple case is when API is straight-up
> "unavailable": the frontend gives a diagnostic when it is used by a
> function which is *not* marked unavailable. For example:
>
> int __foo() __attribute__((unavailable));
> inline int foo() __attribute__((unavailable)) {
> return __foo(); // no diagnostic
> }
> int bar() { return foo(); } // error: foo() unavailable
> int baz() { return __foo(); } // error: __foo() unavailable
>
> What about deploying to old versions?
> -------------------------------------
>
> The seecond case is when a deployment target has been set on the
> command-line, and it's *older* than when an API has been introduced.
> I'll start with an example:
>
> $ cat t.cpp
> int foo() __attribute__((availability(macosx,introduced=10.10)));
> int bar() { return foo(); }
> $ clang t.cpp -mmacosx-version-min=10.9
>
> The default semantics are: weak-link the symbol for foo(). The user is
> expected to check at runtime to see if it's available. Continuing the
> example:
>
> return foo ? foo() : my_workaround_when_foo_is_missing();
>
> This is not a good usage model for libc++ API. We'd rather give a hard
> diagnostic in this case. This requires a new attribute flag, "noweak":
>
> $ cat t.cpp
> int foo() __attribute__((availability(macosx,noweak,introduced=10.10)));
> int bar() { return foo(); } // error: 'foo' not available until OS X 10.10
> $ clang t.cpp -mmacosx-version-min=10.9
>
> Instead of weak-linking foo(), diagnose all uses of it.
>
> What would this look like in the libc++ headers?
> ================================================
>
> This only affects header code that depends on the dylib. Most of libc++
> is header-only, and doesn't need any markup at all.
>
> I've attached WIP-libcxx-availability.patch,
Sadly forgot the attachment. Got it this time.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: WIP-libcxx-availability.patch
Type: application/octet-stream
Size: 13506 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20160208/a8312a89/attachment.obj>
-------------- next part --------------
> which handles all of
> <__locale>, and the not-yet-supported parts of <exception>,
> <experimental/optional>, <new>, and <shared_mutex>. It *does not* use
> `noweak`, but adding -Werror=partial-availability will have a similar
> effect.
>
> - The code in __config is too strict. As currently written, it would
> prevent libc++ developers from testing new library code. It needs a
> macro to control whether to apply the availability markup. I
> haven't thought much about this; please let me know if there's an
> obvious solution.
> - There's currently a relevant bug in clang. If an unavailable
> templated function has a non-template-dependent parameter that is
> also unavailable, clang incorrectly emits a diagnostic. I sent a
> [patch](http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20160208/149470.html)
> to cfe-commits.
> - I'd welcome feedback on how I structed the macros. In the WIP
> patch I named macros based on functional groups, but there are other
> options.
>
> Rejected alternative: delay diagnostics until first ODR-use of caller
> =====================================================================
>
> We looked at the possibility of delaying availability diagnostics until
> the caller's first ODR-use if the caller was a `inline`, `template`d, or
> `static` (anything that is only emitted when it's ODR-used).
> Effectively, this would let us decorate *only* the functions on the
> dylib boundary, and then clang would auto-magically propagate the
> attributes up through the headers.
>
> However, cases like this seemed prohibitively expensive to diagnose:
>
> int foo();
> int bar() { return foo(); } // error: 'foo' unavailable
>
> int __foo() __attribute__((unavailable)); // note: '__foo' explicitly unavailable
> inline int foo() { return __foo(); } // note: 'foo' implicitly unavailable
>
> Our goal was: diagnose that bar() cannot call foo() because it's
> unavailable; foo() is unavailable because it calls __foo(). The order
> of definitions makes this pretty awkward though. In general, I think it
> would require keeping track of all ODR-users of every function call, so
> that on parsing the body of foo() we could diagnose its call in bar().
>
> Explicitly marking foo() unavailable keeps the frontend simple, and it
> also gives a better experience to library users. They don't care about
> the full ODR-use chain that leads to the dylib; they just need to know
> that the library feature isn't available.
More information about the cfe-dev
mailing list