@@ -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,24 @@ 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.isPlatform ()) {
166
+ DEBUG_ASSERT (domain.isActiveForTargetPlatform (ctx));
167
+ return storage->platformRange ;
168
+ }
169
+
170
+ for (auto domainInfo : storage->getDomainInfos ()) {
171
+ if (domain == domainInfo.getDomain () && !domainInfo.isUnavailable ())
172
+ return domainInfo.getRange ();
173
+ }
174
+
175
+ return std::nullopt;
176
+ }
177
+
124
178
bool AvailabilityContext::isUnavailable () const {
125
179
for (auto domainInfo : storage->getDomainInfos ()) {
126
180
if (domainInfo.isUnavailable ())
@@ -162,16 +216,33 @@ void AvailabilityContext::constrainWithContext(const AvailabilityContext &other,
162
216
}
163
217
164
218
void AvailabilityContext::constrainWithPlatformRange (
165
- const AvailabilityRange &otherPlatformRange, const ASTContext &ctx) {
166
-
219
+ const AvailabilityRange &range, const ASTContext &ctx) {
167
220
auto platformRange = storage->platformRange ;
168
- if (!constrainRange (platformRange, otherPlatformRange ))
221
+ if (!constrainRange (platformRange, range ))
169
222
return ;
170
223
171
224
storage = Storage::get (platformRange, storage->isDeprecated ,
172
225
storage->getDomainInfos (), ctx);
173
226
}
174
227
228
+ void AvailabilityContext::constrainWithAvailabilityRange (
229
+ const AvailabilityRange &range, AvailabilityDomain domain,
230
+ const ASTContext &ctx) {
231
+
232
+ if (domain.isPlatform ()) {
233
+ DEBUG_ASSERT (domain.isActiveForTargetPlatform (ctx));
234
+ constrainWithPlatformRange (range, ctx);
235
+ return ;
236
+ }
237
+
238
+ auto domainInfos = storage->copyDomainInfos ();
239
+ if (!constrainDomainInfos (domainInfos, {DomainInfo (domain, range)}))
240
+ return ;
241
+
242
+ storage = Storage::get (storage->platformRange , storage->isDeprecated ,
243
+ domainInfos, ctx);
244
+ }
245
+
175
246
void AvailabilityContext::constrainWithUnavailableDomain (
176
247
AvailabilityDomain domain, const ASTContext &ctx) {
177
248
auto domainInfos = storage->copyDomainInfos ();
@@ -196,6 +267,9 @@ void AvailabilityContext::constrainWithDeclAndPlatformRange(
196
267
bool isDeprecated = storage->isDeprecated ;
197
268
isConstrained |= constrainBool (isDeprecated, decl->isDeprecated ());
198
269
270
+ // Compute the availability constraints for the decl when used in this context
271
+ // and then map those constraints to domain infos. The result will be merged
272
+ // into the existing domain infos for this context.
199
273
llvm::SmallVector<DomainInfo, 4 > declDomainInfos;
200
274
AvailabilityConstraintFlags flags =
201
275
AvailabilityConstraintFlag::SkipEnclosingExtension;
@@ -211,12 +285,12 @@ void AvailabilityContext::constrainWithDeclAndPlatformRange(
211
285
declDomainInfos.push_back (DomainInfo::unavailable (domain));
212
286
break ;
213
287
case AvailabilityConstraint::Reason::PotentiallyUnavailable:
214
- DEBUG_ASSERT (domain. isPlatform () );
215
- if (domain.isPlatform ())
216
- isConstrained |=
217
- constrainRange (platformRange, attr. getIntroducedRange (ctx));
218
- // FIXME: [availability] Store other potentially unavailable domains in
219
- // domainInfos.
288
+ auto range = attr. getIntroducedRange (ctx );
289
+ if (domain.isPlatform ()) {
290
+ isConstrained |= constrainRange (platformRange, range);
291
+ } else {
292
+ declDomainInfos. push_back ({domain, range});
293
+ }
220
294
break ;
221
295
}
222
296
}
@@ -284,16 +358,38 @@ void AvailabilityContext::print(llvm::raw_ostream &os) const {
284
358
285
359
void AvailabilityContext::dump () const { print (llvm::errs ()); }
286
360
287
- bool AvailabilityContext::verify (const ASTContext &ctx) const {
288
- // Domain infos must be sorted to ensure folding set node lookups yield
289
- // consistent results.
290
- if (!llvm::is_sorted (storage->getDomainInfos (),
291
- AvailabilityDomainInfoComparator ()))
292
- return false ;
361
+ bool verifyDomainInfos (
362
+ llvm::ArrayRef<AvailabilityContext::DomainInfo> domainInfos) {
363
+ // Checks that the following invariants hold:
364
+ // - The domain infos are sorted using AvailabilityDomainInfoComparator.
365
+ // - There is not more than one info per-domain.
366
+ if (domainInfos.empty ())
367
+ return true ;
368
+
369
+ AvailabilityDomainInfoComparator compare;
370
+ auto prev = domainInfos.begin ();
371
+ auto next = prev;
372
+ auto end = domainInfos.end ();
373
+ for (++next; next != end; prev = next, ++next) {
374
+ const auto &prevInfo = *prev;
375
+ const auto &nextInfo = *next;
376
+
377
+ if (compare (nextInfo, prevInfo))
378
+ return false ;
379
+
380
+ // Since the infos are sorted by domain, infos with the same domain should
381
+ // be adjacent.
382
+ if (prevInfo.getDomain () == nextInfo.getDomain ())
383
+ return false ;
384
+ }
293
385
294
386
return true ;
295
387
}
296
388
389
+ bool AvailabilityContext::verify (const ASTContext &ctx) const {
390
+ return verifyDomainInfos (storage->getDomainInfos ());
391
+ }
392
+
297
393
void AvailabilityContext::Storage::Profile (
298
394
llvm::FoldingSetNodeID &ID, const AvailabilityRange &platformRange,
299
395
bool isDeprecated,
0 commit comments