@@ -44,60 +44,96 @@ static bool constrainRange(AvailabilityRange &existing,
44
44
return true ;
45
45
}
46
46
47
- // / Returns true if `domain` is not already contained in `domainInfos` as an
48
- // / unavailable domain. Also, removes domains from `unavailableDomains` that are
49
- // / contained in `domain`.
50
- static bool shouldConstrainUnavailableDomains (
51
- AvailabilityDomain domain,
52
- llvm::SmallVectorImpl<AvailabilityContext::DomainInfo> &domainInfos) {
53
- bool didRemove = false ;
54
- for (auto iter = domainInfos.rbegin (), end = domainInfos.rend (); iter != end;
55
- ++iter) {
56
- auto const &domainInfo = *iter;
57
- auto existingDomain = domainInfo.getDomain ();
58
-
59
- if (!domainInfo.isUnavailable ())
47
+ // / Returns true if `domainInfos` will be constrained by merging the domain
48
+ // / availability represented by `otherDomainInfo`. Additionally, this function
49
+ // / has a couple of side-effects:
50
+ // /
51
+ // / - If any existing domain availability ought to be constrained by
52
+ // / `otherDomainInfo` then that value will be updated in place.
53
+ // / - If any existing value in `domainInfos` should be replaced when
54
+ // / `otherDomainInfo` is added, then that existing value is removed
55
+ // / and `otherDomainInfo` is appended to `domainInfosToAdd`.
56
+ // /
57
+ static bool constrainDomainInfos (
58
+ AvailabilityContext::DomainInfo otherDomainInfo,
59
+ llvm::SmallVectorImpl<AvailabilityContext::DomainInfo> &domainInfos,
60
+ llvm::SmallVectorImpl<AvailabilityContext::DomainInfo> &domainInfosToAdd) {
61
+ bool isConstrained = false ;
62
+ bool shouldAdd = true ;
63
+ auto otherDomain = otherDomainInfo.getDomain ();
64
+ auto end = domainInfos.rend ();
65
+
66
+ // Iterate over domainInfos in reverse order to allow items to be removed
67
+ // during iteration.
68
+ for (auto iter = domainInfos.rbegin (); iter != end; ++iter) {
69
+ auto &domainInfo = *iter;
70
+ auto domain = domainInfo.getDomain ();
71
+
72
+ // We found an existing available range for the domain. Constrain it if
73
+ // necessary.
74
+ if (domain == otherDomain) {
75
+ shouldAdd = false ;
76
+ isConstrained |= domainInfo.constrainRange (otherDomainInfo.getRange ());
60
77
continue ;
78
+ }
61
79
62
- // Check if the domain is already unavailable.
63
- if (existingDomain.contains (domain)) {
64
- ASSERT (!didRemove); // This would indicate that the context is malformed.
80
+ // Check whether an existing unavailable domain contains the domain that
81
+ // would be added. If so, there's nothing to do because the availability of
82
+ // the domain is already as constrained as it can be.
83
+ if (domainInfo.isUnavailable () && domain.contains (otherDomain)) {
84
+ DEBUG_ASSERT (!isConstrained);
65
85
return false ;
66
86
}
67
87
68
- // Check if the existing domain would be absorbed by the new domain.
69
- if (domain.contains (existingDomain)) {
88
+ // If the domain that will be added is unavailable, check whether the
89
+ // existing domain is contained within it. If it is, availability for the
90
+ // existing domain should be removed because it has been superseded.
91
+ if (otherDomainInfo.isUnavailable () && otherDomain.contains (domain)) {
70
92
domainInfos.erase ((iter + 1 ).base ());
71
- didRemove = true ;
93
+ isConstrained = true ;
72
94
}
73
95
}
74
96
75
- return true ;
97
+ // If the new domain availability isn't already covered by an item in
98
+ // `domainInfos`, then it needs to be added. Defer adding the new domain
99
+ // availability until later when the entire set of domain infos can be
100
+ // re-sorted once.
101
+ if (shouldAdd) {
102
+ domainInfosToAdd.push_back (otherDomainInfo);
103
+ return true ;
104
+ }
105
+
106
+ return isConstrained;
76
107
}
77
108
109
+ // / Constrains `domainInfos` by merging them with `otherDomainInfos`. Returns
110
+ // / true if any changes were made to `domainInfos`.
78
111
static bool constrainDomainInfos (
79
112
llvm::SmallVectorImpl<AvailabilityContext::DomainInfo> &domainInfos,
80
113
llvm::ArrayRef<AvailabilityContext::DomainInfo> otherDomainInfos) {
114
+ bool isConstrained = false ;
81
115
llvm::SmallVector<AvailabilityContext::DomainInfo, 4 > domainInfosToAdd;
82
-
83
116
for (auto otherDomainInfo : otherDomainInfos) {
84
- if (otherDomainInfo.isUnavailable ())
85
- if (shouldConstrainUnavailableDomains (otherDomainInfo.getDomain (),
86
- domainInfos))
87
- domainInfosToAdd.push_back (otherDomainInfo);
117
+ isConstrained |=
118
+ constrainDomainInfos (otherDomainInfo, domainInfos, domainInfosToAdd);
88
119
}
89
120
90
- if (domainInfosToAdd. size () < 1 )
121
+ if (!isConstrained )
91
122
return false ;
92
123
93
- // Add the candidate domain and then re-sort.
124
+ // Add the new domains and then re-sort.
94
125
for (auto domainInfo : domainInfosToAdd)
95
126
domainInfos.push_back (domainInfo);
96
127
97
128
llvm::sort (domainInfos, AvailabilityDomainInfoComparator ());
98
129
return true ;
99
130
}
100
131
132
+ bool AvailabilityContext::DomainInfo::constrainRange (
133
+ const AvailabilityRange &otherRange) {
134
+ return ::constrainRange (range, otherRange);
135
+ }
136
+
101
137
AvailabilityContext
102
138
AvailabilityContext::forPlatformRange (const AvailabilityRange &range,
103
139
const ASTContext &ctx) {
@@ -121,6 +157,23 @@ AvailabilityRange AvailabilityContext::getPlatformRange() const {
121
157
return storage->platformRange ;
122
158
}
123
159
160
+ std::optional<AvailabilityRange>
161
+ AvailabilityContext::getAvailabilityRange (AvailabilityDomain domain,
162
+ const ASTContext &ctx) const {
163
+ DEBUG_ASSERT (domain.supportsContextRefinement ());
164
+
165
+ if (domain.isActiveForTargetPlatform (ctx)) {
166
+ return storage->platformRange ;
167
+ }
168
+
169
+ for (auto domainInfo : storage->getDomainInfos ()) {
170
+ if (domain == domainInfo.getDomain () && !domainInfo.isUnavailable ())
171
+ return domainInfo.getRange ();
172
+ }
173
+
174
+ return std::nullopt;
175
+ }
176
+
124
177
bool AvailabilityContext::isUnavailable () const {
125
178
for (auto domainInfo : storage->getDomainInfos ()) {
126
179
if (domainInfo.isUnavailable ())
@@ -162,16 +215,32 @@ void AvailabilityContext::constrainWithContext(const AvailabilityContext &other,
162
215
}
163
216
164
217
void AvailabilityContext::constrainWithPlatformRange (
165
- const AvailabilityRange &otherPlatformRange, const ASTContext &ctx) {
166
-
218
+ const AvailabilityRange &range, const ASTContext &ctx) {
167
219
auto platformRange = storage->platformRange ;
168
- if (!constrainRange (platformRange, otherPlatformRange ))
220
+ if (!constrainRange (platformRange, range ))
169
221
return ;
170
222
171
223
storage = Storage::get (platformRange, storage->isDeprecated ,
172
224
storage->getDomainInfos (), ctx);
173
225
}
174
226
227
+ void AvailabilityContext::constrainWithAvailabilityRange (
228
+ const AvailabilityRange &range, AvailabilityDomain domain,
229
+ const ASTContext &ctx) {
230
+
231
+ if (domain.isActiveForTargetPlatform (ctx)) {
232
+ constrainWithPlatformRange (range, ctx);
233
+ return ;
234
+ }
235
+
236
+ auto domainInfos = storage->copyDomainInfos ();
237
+ if (!constrainDomainInfos (domainInfos, {DomainInfo (domain, range)}))
238
+ return ;
239
+
240
+ storage = Storage::get (storage->platformRange , storage->isDeprecated ,
241
+ domainInfos, ctx);
242
+ }
243
+
175
244
void AvailabilityContext::constrainWithUnavailableDomain (
176
245
AvailabilityDomain domain, const ASTContext &ctx) {
177
246
auto domainInfos = storage->copyDomainInfos ();
@@ -196,6 +265,9 @@ void AvailabilityContext::constrainWithDeclAndPlatformRange(
196
265
bool isDeprecated = storage->isDeprecated ;
197
266
isConstrained |= constrainBool (isDeprecated, decl->isDeprecated ());
198
267
268
+ // Compute the availability constraints for the decl when used in this context
269
+ // and then map those constraints to domain infos. The result will be merged
270
+ // into the existing domain infos for this context.
199
271
llvm::SmallVector<DomainInfo, 4 > declDomainInfos;
200
272
AvailabilityConstraintFlags flags =
201
273
AvailabilityConstraintFlag::SkipEnclosingExtension;
@@ -211,13 +283,13 @@ void AvailabilityContext::constrainWithDeclAndPlatformRange(
211
283
declDomainInfos.push_back (DomainInfo::unavailable (domain));
212
284
break ;
213
285
case AvailabilityConstraint::Reason::PotentiallyUnavailable:
214
- DEBUG_ASSERT (domain.isPlatform ());
215
- if (domain.isPlatform ()) {
216
- if (auto introducedRange = attr.getIntroducedRange (ctx))
286
+ if (auto introducedRange = attr.getIntroducedRange (ctx)) {
287
+ if (domain.isActiveForTargetPlatform (ctx)) {
217
288
isConstrained |= constrainRange (platformRange, *introducedRange);
289
+ } else {
290
+ declDomainInfos.push_back ({domain, *introducedRange});
291
+ }
218
292
}
219
- // FIXME: [availability] Store other potentially unavailable domains in
220
- // domainInfos.
221
293
break ;
222
294
}
223
295
}
@@ -285,16 +357,38 @@ void AvailabilityContext::print(llvm::raw_ostream &os) const {
285
357
286
358
void AvailabilityContext::dump () const { print (llvm::errs ()); }
287
359
288
- bool AvailabilityContext::verify (const ASTContext &ctx) const {
289
- // Domain infos must be sorted to ensure folding set node lookups yield
290
- // consistent results.
291
- if (!llvm::is_sorted (storage->getDomainInfos (),
292
- AvailabilityDomainInfoComparator ()))
293
- return false ;
360
+ bool verifyDomainInfos (
361
+ llvm::ArrayRef<AvailabilityContext::DomainInfo> domainInfos) {
362
+ // Checks that the following invariants hold:
363
+ // - The domain infos are sorted using AvailabilityDomainInfoComparator.
364
+ // - There is not more than one info per-domain.
365
+ if (domainInfos.empty ())
366
+ return true ;
367
+
368
+ AvailabilityDomainInfoComparator compare;
369
+ auto prev = domainInfos.begin ();
370
+ auto next = prev;
371
+ auto end = domainInfos.end ();
372
+ for (++next; next != end; prev = next, ++next) {
373
+ const auto &prevInfo = *prev;
374
+ const auto &nextInfo = *next;
375
+
376
+ if (compare (nextInfo, prevInfo))
377
+ return false ;
378
+
379
+ // Since the infos are sorted by domain, infos with the same domain should
380
+ // be adjacent.
381
+ if (prevInfo.getDomain () == nextInfo.getDomain ())
382
+ return false ;
383
+ }
294
384
295
385
return true ;
296
386
}
297
387
388
+ bool AvailabilityContext::verify (const ASTContext &ctx) const {
389
+ return verifyDomainInfos (storage->getDomainInfos ());
390
+ }
391
+
298
392
void AvailabilityContext::Storage::Profile (
299
393
llvm::FoldingSetNodeID &ID, const AvailabilityRange &platformRange,
300
394
bool isDeprecated,
0 commit comments