Skip to content

Commit 190779e

Browse files
authored
Merge pull request #78797 from tshortli/availability-context-unavailable-domain
AST: Track an unavailable domain instead of platform in AvailabilityContext
2 parents 5d0fe0a + fe138e0 commit 190779e

12 files changed

+181
-121
lines changed

include/swift/AST/AvailabilityContext.h

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#ifndef SWIFT_AST_AVAILABILITY_CONTEXT_H
1919
#define SWIFT_AST_AVAILABILITY_CONTEXT_H
2020

21+
#include "swift/AST/AvailabilityDomain.h"
2122
#include "swift/AST/AvailabilityRange.h"
2223
#include "swift/AST/PlatformKind.h"
2324
#include "swift/Basic/Debug.h"
@@ -51,7 +52,7 @@ class AvailabilityContext {
5152
/// parameters.
5253
static AvailabilityContext
5354
get(const AvailabilityRange &platformAvailability,
54-
std::optional<PlatformKind> unavailablePlatform, bool deprecated,
55+
std::optional<AvailabilityDomain> unavailableDomain, bool deprecated,
5556
ASTContext &ctx);
5657

5758
public:
@@ -73,22 +74,16 @@ class AvailabilityContext {
7374
/// availability context, starting at its introduction version.
7475
AvailabilityRange getPlatformRange() const;
7576

76-
/// When the context is unavailable on the current platform this returns the
77-
/// broadest `PlatformKind` for which the context is unavailable. Otherwise,
78-
/// returns `nullopt`.
79-
std::optional<PlatformKind> getUnavailablePlatformKind() const;
77+
/// Returns the broadest AvailabilityDomain that is unavailable in this
78+
/// context.
79+
std::optional<AvailabilityDomain> getUnavailableDomain() const;
8080

8181
/// Returns true if this context is unavailable.
82-
bool isUnavailable() const {
83-
return getUnavailablePlatformKind().has_value();
84-
}
82+
bool isUnavailable() const { return getUnavailableDomain().has_value(); }
8583

8684
/// Returns true if this context is deprecated on the current platform.
8785
bool isDeprecated() const;
8886

89-
/// Returns true if this context is `@_unavailableInEmbedded`.
90-
bool isUnavailableInEmbedded() const;
91-
9287
/// Constrain with another `AvailabilityContext`.
9388
void constrainWithContext(const AvailabilityContext &other, ASTContext &ctx);
9489

include/swift/AST/AvailabilityContextStorage.h

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,8 @@ class AvailabilityContext::Info {
2929
/// The introduction version.
3030
AvailabilityRange Range;
3131

32-
/// When `IsUnavailable` is true, this value stores the broadest platform
33-
/// kind for which the context is unavailable.
34-
PlatformKind UnavailablePlatform;
35-
36-
/// Whether or not the context is considered unavailable on the current
37-
/// platform.
38-
unsigned IsUnavailable : 1;
39-
40-
/// Whether or not the context is `@_unavailableInEmbedded`.
41-
unsigned IsUnavailableInEmbedded : 1;
32+
/// The broadest unavailable domain.
33+
std::optional<AvailabilityDomain> UnavailableDomain;
4234

4335
/// Whether or not the context is considered deprecated on the current
4436
/// platform.
@@ -53,16 +45,18 @@ class AvailabilityContext::Info {
5345
/// availability is more restrictive. Returns true if any field was updated.
5446
bool constrainWith(const Decl *decl);
5547

56-
bool constrainUnavailability(std::optional<PlatformKind> unavailablePlatform);
48+
bool constrainUnavailability(std::optional<AvailabilityDomain> domain);
5749

5850
/// Returns true if `other` is as available or is more available.
5951
bool isContainedIn(const Info &other) const;
6052

6153
void Profile(llvm::FoldingSetNodeID &ID) const {
6254
Range.getRawVersionRange().Profile(ID);
63-
ID.AddBoolean(IsUnavailable);
64-
ID.AddBoolean(IsUnavailableInEmbedded);
65-
ID.AddInteger(static_cast<uint8_t>(UnavailablePlatform));
55+
if (UnavailableDomain) {
56+
UnavailableDomain->Profile(ID);
57+
} else {
58+
ID.AddPointer(nullptr);
59+
}
6660
ID.AddBoolean(IsDeprecated);
6761
}
6862
};
@@ -77,7 +71,7 @@ class AvailabilityContext::Storage final : public llvm::FoldingSetNode {
7771

7872
static const Storage *get(const Info &info, ASTContext &ctx);
7973

80-
void Profile(llvm::FoldingSetNodeID &ID) const;
74+
void Profile(llvm::FoldingSetNodeID &ID) const { info.Profile(ID); }
8175
};
8276

8377
} // end namespace swift

include/swift/AST/AvailabilityDomain.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "swift/AST/PlatformKind.h"
2222
#include "swift/Basic/LLVM.h"
23+
#include "llvm/ADT/FoldingSet.h"
2324
#include "llvm/ADT/PointerEmbeddedInt.h"
2425
#include "llvm/ADT/PointerUnion.h"
2526

@@ -177,6 +178,11 @@ class AvailabilityDomain final {
177178
/// Returns the string to use when printing an `@available` attribute.
178179
llvm::StringRef getNameForAttributePrinting() const;
179180

181+
/// Returns true if availability in `other` is a subset of availability in
182+
/// this domain. The set of all availability domains form a lattice where the
183+
/// universal domain (`*`) is the bottom element.
184+
bool contains(const AvailabilityDomain &other) const;
185+
180186
bool operator==(const AvailabilityDomain &other) const {
181187
return storage.getOpaqueValue() == other.storage.getOpaqueValue();
182188
}
@@ -185,6 +191,7 @@ class AvailabilityDomain final {
185191
return !(*this == other);
186192
}
187193

194+
/// A total, stable ordering on domains.
188195
bool operator<(const AvailabilityDomain &other) const {
189196
if (getKind() != other.getKind())
190197
return getKind() < other.getKind();
@@ -200,6 +207,10 @@ class AvailabilityDomain final {
200207
return getPlatformKind() < other.getPlatformKind();
201208
}
202209
}
210+
211+
void Profile(llvm::FoldingSetNodeID &ID) const {
212+
ID.AddPointer(getOpaqueValue());
213+
}
203214
};
204215

205216
} // end namespace swift

lib/AST/AvailabilityContext.cpp

Lines changed: 36 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,27 @@ static bool constrainRange(AvailabilityRange &existing,
3737
return true;
3838
}
3939

40+
static bool constrainUnavailableDomain(
41+
std::optional<AvailabilityDomain> &domain,
42+
const std::optional<AvailabilityDomain> &otherDomain) {
43+
// If the other domain is absent or is the same domain, it's a noop.
44+
if (!otherDomain || domain == otherDomain)
45+
return false;
46+
47+
// Check if the other domain is a superset and constrain to it if it is.
48+
if (!domain || otherDomain->contains(*domain)) {
49+
domain = otherDomain;
50+
return true;
51+
}
52+
53+
return false;
54+
}
55+
4056
bool AvailabilityContext::Info::constrainWith(const Info &other) {
4157
bool isConstrained = false;
4258
isConstrained |= constrainRange(Range, other.Range);
43-
if (other.IsUnavailable) {
44-
isConstrained |= constrainUnavailability(other.UnavailablePlatform);
45-
isConstrained |=
46-
CONSTRAIN_BOOL(IsUnavailableInEmbedded, other.IsUnavailableInEmbedded);
47-
}
59+
if (other.UnavailableDomain)
60+
isConstrained |= constrainUnavailability(other.UnavailableDomain);
4861
isConstrained |= CONSTRAIN_BOOL(IsDeprecated, other.IsDeprecated);
4962

5063
return isConstrained;
@@ -56,77 +69,44 @@ bool AvailabilityContext::Info::constrainWith(const Decl *decl) {
5669
if (auto range = AvailabilityInference::annotatedAvailableRange(decl))
5770
isConstrained |= constrainRange(Range, *range);
5871

59-
if (auto attr = decl->getUnavailableAttr()) {
60-
isConstrained |= constrainUnavailability(attr->getPlatform());
61-
isConstrained |=
62-
CONSTRAIN_BOOL(IsUnavailableInEmbedded, attr->isEmbeddedSpecific());
63-
}
72+
if (auto attr = decl->getUnavailableAttr())
73+
isConstrained |= constrainUnavailability(attr->getDomain());
6474

6575
isConstrained |= CONSTRAIN_BOOL(IsDeprecated, decl->isDeprecated());
6676

6777
return isConstrained;
6878
}
6979

7080
bool AvailabilityContext::Info::constrainUnavailability(
71-
std::optional<PlatformKind> unavailablePlatform) {
72-
if (!unavailablePlatform)
73-
return false;
74-
75-
if (IsUnavailable) {
76-
// Universal unavailability cannot be refined.
77-
if (UnavailablePlatform == PlatformKind::none)
78-
return false;
79-
80-
// There's nothing to do if the platforms already match.
81-
if (UnavailablePlatform == *unavailablePlatform)
82-
return false;
83-
84-
// The new platform must be more restrictive.
85-
if (*unavailablePlatform != PlatformKind::none &&
86-
inheritsAvailabilityFromPlatform(*unavailablePlatform,
87-
UnavailablePlatform))
88-
return false;
89-
}
90-
91-
IsUnavailable = true;
92-
UnavailablePlatform = *unavailablePlatform;
93-
return true;
81+
std::optional<AvailabilityDomain> domain) {
82+
return constrainUnavailableDomain(UnavailableDomain, domain);
9483
}
9584

9685
bool AvailabilityContext::Info::isContainedIn(const Info &other) const {
86+
// The available versions range be the same or smaller.
9787
if (!Range.isContainedIn(other.Range))
9888
return false;
9989

100-
if (!IsUnavailable && other.IsUnavailable)
101-
return false;
102-
103-
if (IsUnavailable && other.IsUnavailable) {
104-
if (UnavailablePlatform != other.UnavailablePlatform &&
105-
UnavailablePlatform != PlatformKind::none &&
106-
inheritsAvailabilityFromPlatform(UnavailablePlatform,
107-
other.UnavailablePlatform))
90+
// The set of unavailable domains should be the same or larger.
91+
if (auto otherUnavailableDomain = other.UnavailableDomain) {
92+
if (!UnavailableDomain)
10893
return false;
10994

110-
if (IsUnavailableInEmbedded && !other.IsUnavailableInEmbedded)
95+
if (!UnavailableDomain->contains(otherUnavailableDomain.value()))
11196
return false;
11297
}
11398

99+
// The set of deprecated domains should be the same or larger.
114100
if (!IsDeprecated && other.IsDeprecated)
115101
return false;
116102

117103
return true;
118104
}
119105

120-
void AvailabilityContext::Storage::Profile(llvm::FoldingSetNodeID &id) const {
121-
info.Profile(id);
122-
}
123-
124106
AvailabilityContext
125107
AvailabilityContext::forPlatformRange(const AvailabilityRange &range,
126108
ASTContext &ctx) {
127-
Info info{range, PlatformKind::none,
128-
/*IsUnavailable*/ false,
129-
/*IsUnavailableInEmbedded*/ false,
109+
Info info{range, /*UnavailableDomain*/ std::nullopt,
130110
/*IsDeprecated*/ false};
131111
return AvailabilityContext(Storage::get(info, ctx));
132112
}
@@ -143,29 +123,19 @@ AvailabilityContext AvailabilityContext::forDeploymentTarget(ASTContext &ctx) {
143123

144124
AvailabilityContext
145125
AvailabilityContext::get(const AvailabilityRange &platformAvailability,
146-
std::optional<PlatformKind> unavailablePlatform,
126+
std::optional<AvailabilityDomain> unavailableDomain,
147127
bool deprecated, ASTContext &ctx) {
148-
Info info{platformAvailability,
149-
unavailablePlatform.has_value() ? *unavailablePlatform
150-
: PlatformKind::none,
151-
unavailablePlatform.has_value(),
152-
/*IsUnavailableInEmbedded*/ false, deprecated};
128+
Info info{platformAvailability, unavailableDomain, deprecated};
153129
return AvailabilityContext(Storage::get(info, ctx));
154130
}
155131

156132
AvailabilityRange AvailabilityContext::getPlatformRange() const {
157133
return storage->info.Range;
158134
}
159135

160-
std::optional<PlatformKind>
161-
AvailabilityContext::getUnavailablePlatformKind() const {
162-
if (storage->info.IsUnavailable)
163-
return storage->info.UnavailablePlatform;
164-
return std::nullopt;
165-
}
166-
167-
bool AvailabilityContext::isUnavailableInEmbedded() const {
168-
return storage->info.IsUnavailableInEmbedded;
136+
std::optional<AvailabilityDomain>
137+
AvailabilityContext::getUnavailableDomain() const {
138+
return storage->info.UnavailableDomain;
169139
}
170140

171141
bool AvailabilityContext::isDeprecated() const {
@@ -233,8 +203,8 @@ stringForAvailability(const AvailabilityRange &availability) {
233203
void AvailabilityContext::print(llvm::raw_ostream &os) const {
234204
os << "version=" << stringForAvailability(getPlatformRange());
235205

236-
if (auto unavailablePlatform = getUnavailablePlatformKind())
237-
os << " unavailable=" << platformString(*unavailablePlatform);
206+
if (auto unavailableDomain = getUnavailableDomain())
207+
os << " unavailable=" << unavailableDomain->getNameForAttributePrinting();
238208

239209
if (isDeprecated())
240210
os << " deprecated";

lib/AST/AvailabilityDomain.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,25 @@ llvm::StringRef AvailabilityDomain::getNameForAttributePrinting() const {
5757
return swift::platformString(getPlatformKind());
5858
}
5959
}
60+
61+
bool AvailabilityDomain::contains(const AvailabilityDomain &other) const {
62+
// FIXME: [availability] This currently implements something closer to a
63+
// total ordering instead of the more flexible partial ordering that it
64+
// would ideally represent. Until AvailabilityContext supports tracking
65+
// multiple unavailable domains simultaneously, a stricter ordering is
66+
// necessary to support source compatibility.
67+
switch (getKind()) {
68+
case Kind::Universal:
69+
return true;
70+
case Kind::SwiftLanguage:
71+
return !other.isUniversal();
72+
case Kind::PackageDescription:
73+
case Kind::Embedded:
74+
return !other.isUniversal() && !other.isSwiftLanguage();
75+
case Kind::Platform:
76+
if (getPlatformKind() == other.getPlatformKind())
77+
return true;
78+
return inheritsAvailabilityFromPlatform(other.getPlatformKind(),
79+
getPlatformKind());
80+
}
81+
}

lib/Sema/TypeCheckAccess.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2019,7 +2019,7 @@ swift::getDisallowedOriginKind(const Decl *decl,
20192019
// Decls with @_spi_available aren't hidden entirely from public interfaces,
20202020
// thus public interfaces may still refer them. Be forgiving here so public
20212021
// interfaces can compile.
2022-
if (where.getUnavailablePlatformKind().has_value())
2022+
if (where.getAvailability().isUnavailable())
20232023
return DisallowedOriginKind::None;
20242024
// We should only diagnose SPI_AVAILABLE usage when the library level is API.
20252025
// Using SPI_AVAILABLE symbols in private frameworks or executable targets

lib/Sema/TypeCheckAttr.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5050,13 +5050,13 @@ void AttributeChecker::checkBackDeployedAttrs(
50505050
if (Ctx.LangOpts.DisableAvailabilityChecking)
50515051
continue;
50525052

5053-
auto availability =
5054-
TypeChecker::availabilityAtLocation(D->getLoc(), D->getDeclContext());
5053+
auto availability = TypeChecker::availabilityAtLocation(
5054+
D->getLoc(), D->getInnermostDeclContext());
50555055

50565056
// Unavailable decls cannot be back deployed.
5057-
if (auto unavailablePlatform = availability.getUnavailablePlatformKind()) {
5058-
if (!inheritsAvailabilityFromPlatform(*unavailablePlatform,
5059-
Attr->Platform)) {
5057+
if (auto unavailableDomain = availability.getUnavailableDomain()) {
5058+
auto backDeployedDomain = AvailabilityDomain::forPlatform(Attr->Platform);
5059+
if (unavailableDomain->contains(backDeployedDomain)) {
50605060
auto platformString = prettyPlatformString(Attr->Platform);
50615061
llvm::VersionTuple ignoredVersion;
50625062

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -349,28 +349,22 @@ static bool computeContainedByDeploymentTarget(AvailabilityScope *scope,
349349
static bool isInsideCompatibleUnavailableDeclaration(
350350
const Decl *D, AvailabilityContext availabilityContext,
351351
const SemanticAvailableAttr &attr) {
352-
auto contextPlatform = availabilityContext.getUnavailablePlatformKind();
353-
if (!contextPlatform)
352+
auto contextDomain = availabilityContext.getUnavailableDomain();
353+
if (!contextDomain)
354354
return false;
355355

356356
if (!attr.isUnconditionallyUnavailable())
357357
return false;
358358

359359
// Refuse calling universally unavailable functions from unavailable code,
360360
// but allow the use of types.
361-
PlatformKind declPlatform = attr.getPlatform();
362-
if (declPlatform == PlatformKind::none && !attr.isEmbeddedSpecific() &&
363-
!isa<TypeDecl>(D) && !isa<ExtensionDecl>(D))
364-
return false;
365-
366-
// @_unavailableInEmbedded declarations may be used in contexts that are
367-
// also @_unavailableInEmbedded.
368-
if (attr.isEmbeddedSpecific())
369-
return availabilityContext.isUnavailableInEmbedded();
361+
auto declDomain = attr.getDomain();
362+
if (!isa<TypeDecl>(D) && !isa<ExtensionDecl>(D)) {
363+
if (declDomain.isUniversal() || declDomain.isSwiftLanguage())
364+
return false;
365+
}
370366

371-
return (*contextPlatform == PlatformKind::none ||
372-
*contextPlatform == declPlatform ||
373-
inheritsAvailabilityFromPlatform(declPlatform, *contextPlatform));
367+
return contextDomain->contains(declDomain);
374368
}
375369

376370
std::optional<SemanticAvailableAttr>

0 commit comments

Comments
 (0)