@@ -44,60 +44,96 @@ static bool constrainRange(AvailabilityRange &existing,
4444 return true ;
4545}
4646
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 ());
6077 continue ;
78+ }
6179
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);
6585 return false ;
6686 }
6787
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)) {
7092 domainInfos.erase ((iter + 1 ).base ());
71- didRemove = true ;
93+ isConstrained = true ;
7294 }
7395 }
7496
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;
76107}
77108
109+ // / Constrains `domainInfos` by merging them with `otherDomainInfos`. Returns
110+ // / true if any changes were made to `domainInfos`.
78111static bool constrainDomainInfos (
79112 llvm::SmallVectorImpl<AvailabilityContext::DomainInfo> &domainInfos,
80113 llvm::ArrayRef<AvailabilityContext::DomainInfo> otherDomainInfos) {
114+ bool isConstrained = false ;
81115 llvm::SmallVector<AvailabilityContext::DomainInfo, 4 > domainInfosToAdd;
82-
83116 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);
88119 }
89120
90- if (domainInfosToAdd. size () < 1 )
121+ if (!isConstrained )
91122 return false ;
92123
93- // Add the candidate domain and then re-sort.
124+ // Add the new domains and then re-sort.
94125 for (auto domainInfo : domainInfosToAdd)
95126 domainInfos.push_back (domainInfo);
96127
97128 llvm::sort (domainInfos, AvailabilityDomainInfoComparator ());
98129 return true ;
99130}
100131
132+ bool AvailabilityContext::DomainInfo::constrainRange (
133+ const AvailabilityRange &otherRange) {
134+ return ::constrainRange (range, otherRange);
135+ }
136+
101137AvailabilityContext
102138AvailabilityContext::forPlatformRange (const AvailabilityRange &range,
103139 const ASTContext &ctx) {
@@ -121,6 +157,23 @@ AvailabilityRange AvailabilityContext::getPlatformRange() const {
121157 return storage->platformRange ;
122158}
123159
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+
124177bool AvailabilityContext::isUnavailable () const {
125178 for (auto domainInfo : storage->getDomainInfos ()) {
126179 if (domainInfo.isUnavailable ())
@@ -162,16 +215,32 @@ void AvailabilityContext::constrainWithContext(const AvailabilityContext &other,
162215}
163216
164217void AvailabilityContext::constrainWithPlatformRange (
165- const AvailabilityRange &otherPlatformRange, const ASTContext &ctx) {
166-
218+ const AvailabilityRange &range, const ASTContext &ctx) {
167219 auto platformRange = storage->platformRange ;
168- if (!constrainRange (platformRange, otherPlatformRange ))
220+ if (!constrainRange (platformRange, range ))
169221 return ;
170222
171223 storage = Storage::get (platformRange, storage->isDeprecated ,
172224 storage->getDomainInfos (), ctx);
173225}
174226
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+
175244void AvailabilityContext::constrainWithUnavailableDomain (
176245 AvailabilityDomain domain, const ASTContext &ctx) {
177246 auto domainInfos = storage->copyDomainInfos ();
@@ -196,6 +265,9 @@ void AvailabilityContext::constrainWithDeclAndPlatformRange(
196265 bool isDeprecated = storage->isDeprecated ;
197266 isConstrained |= constrainBool (isDeprecated, decl->isDeprecated ());
198267
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.
199271 llvm::SmallVector<DomainInfo, 4 > declDomainInfos;
200272 AvailabilityConstraintFlags flags =
201273 AvailabilityConstraintFlag::SkipEnclosingExtension;
@@ -211,13 +283,13 @@ void AvailabilityContext::constrainWithDeclAndPlatformRange(
211283 declDomainInfos.push_back (DomainInfo::unavailable (domain));
212284 break ;
213285 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)) {
217288 isConstrained |= constrainRange (platformRange, *introducedRange);
289+ } else {
290+ declDomainInfos.push_back ({domain, *introducedRange});
291+ }
218292 }
219- // FIXME: [availability] Store other potentially unavailable domains in
220- // domainInfos.
221293 break ;
222294 }
223295 }
@@ -285,16 +357,38 @@ void AvailabilityContext::print(llvm::raw_ostream &os) const {
285357
286358void AvailabilityContext::dump () const { print (llvm::errs ()); }
287359
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+ }
294384
295385 return true ;
296386}
297387
388+ bool AvailabilityContext::verify (const ASTContext &ctx) const {
389+ return verifyDomainInfos (storage->getDomainInfos ());
390+ }
391+
298392void AvailabilityContext::Storage::Profile (
299393 llvm::FoldingSetNodeID &ID, const AvailabilityRange &platformRange,
300394 bool isDeprecated,
0 commit comments