Skip to content

Commit 9455a0f

Browse files
committed
AST: Track multiple unavailable domains in AvailabilityContext.
1 parent e6d77e1 commit 9455a0f

File tree

3 files changed

+91
-66
lines changed

3 files changed

+91
-66
lines changed

include/swift/AST/AvailabilityContext.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,6 @@ class AvailabilityContext {
4848
assert(storage);
4949
};
5050

51-
/// Retrieves an `AvailabilityContext` with the given platform availability
52-
/// parameters.
53-
static AvailabilityContext
54-
get(const AvailabilityRange &platformAvailability,
55-
std::optional<AvailabilityDomain> unavailableDomain, bool deprecated,
56-
ASTContext &ctx);
57-
5851
public:
5952
/// Retrieves an `AvailabilityContext` constrained by the given platform
6053
/// availability range.

include/swift/AST/AvailabilityContextStorage.h

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ class AvailabilityContext::Info {
3131
/// The introduction version.
3232
AvailabilityRange Range;
3333

34-
/// The broadest unavailable domain.
35-
std::optional<AvailabilityDomain> UnavailableDomain;
34+
/// A sorted collection of disjoint domains that are known to be
35+
/// unavailable in this context.
36+
llvm::SmallVector<AvailabilityDomain, 1> UnavailableDomains;
3637

3738
/// Whether or not the context is considered deprecated on the current
3839
/// platform.
@@ -49,20 +50,17 @@ class AvailabilityContext::Info {
4950
bool constrainWith(const DeclAvailabilityConstraints &constraints,
5051
ASTContext &ctx);
5152

52-
bool constrainUnavailability(std::optional<AvailabilityDomain> domain);
53+
bool constrainUnavailability(
54+
const llvm::SmallVectorImpl<AvailabilityDomain> &domains);
55+
bool constrainUnavailability(AvailabilityDomain domain) {
56+
return constrainUnavailability(
57+
llvm::SmallVector<AvailabilityDomain>{domain});
58+
}
5359

5460
/// Returns true if `other` is as available or is more available.
5561
bool isContainedIn(const Info &other) const;
5662

57-
void Profile(llvm::FoldingSetNodeID &ID) const {
58-
Range.getRawVersionRange().Profile(ID);
59-
if (UnavailableDomain) {
60-
UnavailableDomain->Profile(ID);
61-
} else {
62-
ID.AddPointer(nullptr);
63-
}
64-
ID.AddBoolean(IsDeprecated);
65-
}
63+
void Profile(llvm::FoldingSetNodeID &ID) const;
6664
};
6765

6866
/// As an implementation detail, the values that make up an `Availability`

lib/AST/AvailabilityContext.cpp

Lines changed: 81 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -38,27 +38,10 @@ static bool constrainRange(AvailabilityRange &existing,
3838
return true;
3939
}
4040

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

6447
return isConstrained;
@@ -89,24 +72,72 @@ bool AvailabilityContext::Info::constrainWith(
8972
return isConstrained;
9073
}
9174

75+
/// Returns true if `domain` is not already contained in `unavailableDomains`.
76+
/// Also, removes domains from `unavailableDomains` that are contained in
77+
/// `domain`.
78+
static bool shouldConstrainUnavailableDomains(
79+
AvailabilityDomain domain,
80+
llvm::SmallVectorImpl<AvailabilityDomain> &unavailableDomains) {
81+
bool didRemove = false;
82+
for (auto iter = unavailableDomains.rbegin(), end = unavailableDomains.rend();
83+
iter != end; ++iter) {
84+
auto const &existingDomain = *iter;
85+
86+
// Check if the domain is already unavailable.
87+
if (existingDomain.contains(domain)) {
88+
ASSERT(!didRemove); // This would indicate that the context is malformed.
89+
return false;
90+
}
91+
92+
// Check if the existing domain would be absorbed by the new domain.
93+
if (domain.contains(existingDomain)) {
94+
unavailableDomains.erase((iter + 1).base());
95+
didRemove = true;
96+
}
97+
}
98+
99+
return true;
100+
}
101+
92102
bool AvailabilityContext::Info::constrainUnavailability(
93-
std::optional<AvailabilityDomain> domain) {
94-
return constrainUnavailableDomain(UnavailableDomain, domain);
103+
const llvm::SmallVectorImpl<AvailabilityDomain> &domains) {
104+
llvm::SmallVector<AvailabilityDomain, 2> domainsToAdd;
105+
106+
for (auto domain : domains) {
107+
if (shouldConstrainUnavailableDomains(domain, UnavailableDomains))
108+
domainsToAdd.push_back(domain);
109+
}
110+
111+
if (domainsToAdd.size() < 1)
112+
return false;
113+
114+
// Add the candidate domain and then re-sort.
115+
for (auto domain : domainsToAdd)
116+
UnavailableDomains.push_back(domain);
117+
118+
llvm::sort(UnavailableDomains, StableAvailabilityDomainComparator());
119+
return true;
95120
}
96121

97122
bool AvailabilityContext::Info::isContainedIn(const Info &other) const {
98123
// The available versions range be the same or smaller.
99124
if (!Range.isContainedIn(other.Range))
100125
return false;
101126

102-
// The set of unavailable domains should be the same or larger.
103-
if (auto otherUnavailableDomain = other.UnavailableDomain) {
104-
if (!UnavailableDomain)
105-
return false;
106-
107-
if (!UnavailableDomain->contains(otherUnavailableDomain.value()))
108-
return false;
109-
}
127+
// Every unavailable domain in the other context should be contained in some
128+
// unavailable domain in this context.
129+
bool disjointUnavailability = llvm::any_of(
130+
other.UnavailableDomains,
131+
[&](const AvailabilityDomain &otherUnavailableDomain) {
132+
return llvm::none_of(
133+
UnavailableDomains,
134+
[&otherUnavailableDomain](const AvailabilityDomain &domain) {
135+
return domain.contains(otherUnavailableDomain);
136+
});
137+
});
138+
139+
if (disjointUnavailability)
140+
return false;
110141

111142
// The set of deprecated domains should be the same or larger.
112143
if (!IsDeprecated && other.IsDeprecated)
@@ -115,10 +146,19 @@ bool AvailabilityContext::Info::isContainedIn(const Info &other) const {
115146
return true;
116147
}
117148

149+
void AvailabilityContext::Info::Profile(llvm::FoldingSetNodeID &ID) const {
150+
Range.getRawVersionRange().Profile(ID);
151+
ID.AddInteger(UnavailableDomains.size());
152+
for (auto domain : UnavailableDomains) {
153+
domain.Profile(ID);
154+
}
155+
ID.AddBoolean(IsDeprecated);
156+
}
157+
118158
AvailabilityContext
119159
AvailabilityContext::forPlatformRange(const AvailabilityRange &range,
120160
ASTContext &ctx) {
121-
Info info{range, /*UnavailableDomain*/ std::nullopt,
161+
Info info{range, /*UnavailableDomains*/ {},
122162
/*IsDeprecated*/ false};
123163
return AvailabilityContext(Storage::get(info, ctx));
124164
}
@@ -133,27 +173,20 @@ AvailabilityContext AvailabilityContext::forDeploymentTarget(ASTContext &ctx) {
133173
AvailabilityRange::forDeploymentTarget(ctx), ctx);
134174
}
135175

136-
AvailabilityContext
137-
AvailabilityContext::get(const AvailabilityRange &platformAvailability,
138-
std::optional<AvailabilityDomain> unavailableDomain,
139-
bool deprecated, ASTContext &ctx) {
140-
Info info{platformAvailability, unavailableDomain, deprecated};
141-
return AvailabilityContext(Storage::get(info, ctx));
142-
}
143-
144176
AvailabilityRange AvailabilityContext::getPlatformRange() const {
145177
return storage->info.Range;
146178
}
147179

148180
bool AvailabilityContext::isUnavailable() const {
149-
return storage->info.UnavailableDomain.has_value();
181+
return storage->info.UnavailableDomains.size() > 0;
150182
}
151183

152184
bool AvailabilityContext::containsUnavailableDomain(
153185
AvailabilityDomain domain) const {
154-
if (auto unavailableDomain = storage->info.UnavailableDomain)
155-
return unavailableDomain->contains(domain);
156-
186+
for (auto unavailableDomain : storage->info.UnavailableDomains) {
187+
if (unavailableDomain.contains(domain))
188+
return true;
189+
}
157190
return false;
158191
}
159192

@@ -217,10 +250,7 @@ void AvailabilityContext::constrainWithDeclAndPlatformRange(
217250
}
218251

219252
bool AvailabilityContext::isContainedIn(const AvailabilityContext other) const {
220-
if (!storage->info.isContainedIn(other.storage->info))
221-
return false;
222-
223-
return true;
253+
return storage->info.isContainedIn(other.storage->info);
224254
}
225255

226256
static std::string
@@ -236,8 +266,12 @@ stringForAvailability(const AvailabilityRange &availability) {
236266
void AvailabilityContext::print(llvm::raw_ostream &os) const {
237267
os << "version=" << stringForAvailability(getPlatformRange());
238268

239-
if (auto unavailableDomain = storage->info.UnavailableDomain)
240-
os << " unavailable=" << unavailableDomain->getNameForAttributePrinting();
269+
if (storage->info.UnavailableDomains.size() > 0) {
270+
os << " unavailable=";
271+
llvm::interleave(
272+
storage->info.UnavailableDomains, os,
273+
[&](const AvailabilityDomain &domain) { domain.print(os); }, ",");
274+
}
241275

242276
if (isDeprecated())
243277
os << " deprecated";

0 commit comments

Comments
 (0)