Skip to content

Commit fe138e0

Browse files
committed
AST: Track an unavailable domain instead of platform in AvailabilityContext.
Now that most of the compiler tracks availability in terms of AvailabilityDomain, it's time to do so in AvailabilityContext as well. This will ensure that the compiler accurately suppresses diagnostics about a decl being unavailable in an arbitrary domain when the context of the use is already unavailable in that domain. With this change, most of the special-casing for the Embedded Swift availability domain has been removed from the compiler, outside of parsing and interface printing.
1 parent b8c4298 commit fe138e0

File tree

9 files changed

+75
-121
lines changed

9 files changed

+75
-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: 5 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

@@ -206,6 +207,10 @@ class AvailabilityDomain final {
206207
return getPlatformKind() < other.getPlatformKind();
207208
}
208209
}
210+
211+
void Profile(llvm::FoldingSetNodeID &ID) const {
212+
ID.AddPointer(getOpaqueValue());
213+
}
209214
};
210215

211216
} // 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/Sema/TypeCheckAccess.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2017,7 +2017,7 @@ swift::getDisallowedOriginKind(const Decl *decl,
20172017
// Decls with @_spi_available aren't hidden entirely from public interfaces,
20182018
// thus public interfaces may still refer them. Be forgiving here so public
20192019
// interfaces can compile.
2020-
if (where.getUnavailablePlatformKind().has_value())
2020+
if (where.getAvailability().isUnavailable())
20212021
return DisallowedOriginKind::None;
20222022
// We should only diagnose SPI_AVAILABLE usage when the library level is API.
20232023
// 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>

lib/Sema/TypeCheckAvailability.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,6 @@ class ExportContext {
193193
/// reference other deprecated declarations without warning.
194194
bool isDeprecated() const { return Availability.isDeprecated(); }
195195

196-
std::optional<PlatformKind> getUnavailablePlatformKind() const {
197-
return Availability.getUnavailablePlatformKind();
198-
}
199-
200196
/// If true, the context can only reference exported declarations, either
201197
/// because it is the signature context of an exported declaration, or
202198
/// because it is the function body context of an inlinable function.

test/embedded/availability.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public struct UniverallyUnavailable {}
1313

1414
@_unavailableInEmbedded
1515
public func unavailable_in_embedded() { }
16-
// expected-note@-1 2 {{'unavailable_in_embedded()' has been explicitly marked unavailable here}}
16+
// expected-note@-1 {{'unavailable_in_embedded()' has been explicitly marked unavailable here}}
1717

1818
@available(*, unavailable, message: "always unavailable")
1919
public func universally_unavailable() { }
@@ -22,8 +22,8 @@ public func universally_unavailable() { }
2222
@_unavailableInEmbedded
2323
public func unused() { } // no error
2424

25-
public struct S1 {} // expected-note {{found this candidate}}
26-
public struct S2 {} // expected-note {{found this candidate}}
25+
public struct S1 {} // expected-note 2 {{found this candidate}}
26+
public struct S2 {} // expected-note 2 {{found this candidate}}
2727

2828
@_unavailableInEmbedded
2929
public func has_unavailable_in_embedded_overload(_ s1: S1) { }
@@ -61,8 +61,8 @@ public func also_universally_unavailable(
6161
_ uie: UnavailableInEmbedded, // OK
6262
_ uu: UniverallyUnavailable // OK
6363
) {
64-
unavailable_in_embedded() // expected-error {{'unavailable_in_embedded()' is unavailable: unavailable in embedded Swift}}
64+
unavailable_in_embedded()
6565
universally_unavailable() // expected-error {{'universally_unavailable()' is unavailable: always unavailable}}
66-
has_unavailable_in_embedded_overload(.init()) // not ambiguous, selects available overload
66+
has_unavailable_in_embedded_overload(.init()) // expected-error {{ambiguous use of 'init()'}}
6767
has_universally_unavailable_overload(.init()) // not ambiguous, selects available overload
6868
}

0 commit comments

Comments
 (0)