Potential unavailability of a declaration has always been diagnosed in contexts
that do not have a sufficient platform introduction constraint, even when those
contexts are also unavailable on the target platform. This behavior is overly
strict, since the potential unavailability will never matter, but it's a
longstanding quirk of availability checking. As a result, some source code has
been written to work around this quirk by marking declarations as
simultaneously unavailable and introduced for a given platform:
```
@available(macOS, unavailable, introduced: 15)
func unavailableAndIntroducedInMacOS15() {
// ... allowed to call functions introduced in macOS 15.
}
```
When availability checking was refactored to be based on a constraint engine in
https://github.com/swiftlang/swift/pull/79260, the compiler started effectively
treating `@available(macOS, unavailable, introduced: 15)` as just
`@available(macOS, unavailable)` because the introduction constraint was
treated as lower priority and therefore superseded by the unavailability
constraint. This caused a regression for the code that was written to work
around the availability checker's strictness.
We could try to match the behavior from previous releases, but it's actually
tricky to match the behavior well enough in the new availability checking
architecture to fully fix source compatibility. Consequently, it seems like the
best fix is actually to address this long standing issue and stop diagnosing
potential unavailability in unavailable contexts. The main risk of this
approach is source compatibility for regions of unavailable code. It's
theoretically possible that restricting available declarations by introduction
version in unavailable contexts is important to prevent ambiguities during
overload resolution in some codebases. If we find that is a problem that is too
prevalent, we may have to take a different approach.
Resolves rdar://147945883.
Switch to calling `swift::getAvailabilityConstraintsForDecl()` to get the
unsatisfied availability constraints that should be diagnosed.
This was intended to be NFC, but it turns out it fixed a bug in the recently
introduced objc_implementation_direct_to_storage.swift test. In the test,
the stored properties are as unavailable as the context that is accessing them
so the accesses should not be diagnosed. However, this test demonstrates a
bigger issue with `@objc @implementation`, which is that it allows the
implementations of Obj-C interfaces to be less available than the interface,
which effectively provides an availability checking loophole that can be used
to invoke unavailable code.
Recent refactoring fixed a bug that previously caused `f()` to be checked as if
it were unavailable only on macOS in the following example:
```
@available(macOS, unavailable)
struct Outer {
@available(*, unavailable)
func f() {
someFunctionUnavailableOnMacOS()
}
}
```
Unfortunately, fixing that bug made a different existing availability checking
rule more problematic. References to declarations that are unavailable on the
current platform have been diagnosed as unavailable even in contexts that are
universally unavailable. This long standing behavior is overly strict but it
rarely had consequences. However, now that the example above is modeled
correctly, this overly strict behavior is causing some source compatibility
issues. The easiest solution is to relax the overly strict checking.
Resolves rdar://141124478.
Availability checking for types was only suppressed when the immediate context
for the use of the type was explicitly marked unavailable. Availability is
lexical so the checking should be suppressed in the entire scope instead.
Rewrite attr_availability_transitive_osx.swift to be more use a more thorough
cartesian product approach to testing possible combinations. Free up
Sema/availability.swift to run on platforms besides macOS.
NFC.
It turns out that we must allow declarations with `introduced:` availability
nested inside of other declarations that are `unavailable` in order to
influence weak linking. Stop diagnosing declarations as being more available
than their unavailable containers and revert some changes to availability
inference that were designed to avoid creating these nestings but caused
regressions for declarations marked `@_spi_available.`
Reverts parts of https://github.com/apple/swift/pull/64310,
https://github.com/apple/swift/pull/64015, and
https://github.com/apple/swift/pull/62900.
Previously, `TypeChecker::checkDeclarationAvailability()` would behave this way for most explicitly unavailable decls by _accident_. An explicitly unavailable decl has no introduction version, and the existing logic therefore would fail to find a lower bound on the availability of the decl. The edge case that exposed the fragility of this logic was an unavailable extension containing a member with it's own explicit availability. The unavailability of the extension ought to trump the availability of the member, but the existing logic couldn't detect that.
The compiler also ought to diagnose the conflicting availability annotations but I'd like to address that separately.
Resolves rdar://92551870
Because initial value expressions aren't actually considered /within/
the VarDecl or PatternBindingDecl they're initializing, the existing
logic to search for availability attributes wasn't kicking in, leading
to errors when a conditionally-unavailable value was used in an
initial value expression for a conditionally-unavailable binding. Fix
this by walking the enclosing type or extension to find the appropriate
PatternBindingDecl.
https://bugs.swift.org/browse/SR-9867
Add TypeChecker::isInsideCompatibleUnavailableDeclaration().
Pass in the call site's AvailableAttr and compare its platform to those
in the enclosing lexical scopes.
Added some basic tests.