Skip to content

Commit bb89d33

Browse files
committed
AST: Allow overloads to be disambiguated by obsoletion version.
Previously, whether a declaration is unavailable because it is obsolete was determined based solely on the deployment target and not based on contextual availability. Taking contextual availability into account makes availability checking more internally consistent and allows library authors to evolve APIs by obsoleting the previous declaration while introducing a new declaration in the same version: ``` @available(macOS, obsoleted: 15) func foo(_ x: Int) { } @available(macOS, introduced: 15) func foo(_ x: Int, y: Int = 0) { } foo(42) // unambiguous, regardless of contextual version of macOS ``` This change primarily accepts more code that wasn't accepted previously, but it could also be source breaking for some code that was previously allowed to use obsoleted declarations in contexts that will always run on OS versions where the declaration is obsolete. That code was clearly taking advantage of an availabilty loophole, though, and in practice I don't expect it to be common. Resolves rdar://144647964.
1 parent cda069f commit bb89d33

File tree

5 files changed

+297
-86
lines changed

5 files changed

+297
-86
lines changed

lib/AST/Availability.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,9 @@ SemanticAvailableAttr::getIntroducedDomainAndRange(
926926
auto *attr = getParsedAttr();
927927
auto domain = getDomain();
928928

929+
if (domain.isUniversal())
930+
return std::nullopt;
931+
929932
if (!attr->getRawIntroduced().has_value()) {
930933
// For versioned domains, an "introduced:" version is always required to
931934
// indicate introduction.

lib/AST/AvailabilityConstraint.cpp

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,24 @@ void AvailabilityConstraint::print(llvm::raw_ostream &os) const {
4444
getAttr().getDomain().print(os);
4545
os << ", ";
4646

47+
std::optional<llvm::VersionTuple> version;
4748
switch (getReason()) {
4849
case Reason::UnavailableUnconditionally:
4950
os << "unavailable";
5051
break;
5152
case Reason::UnavailableObsolete:
52-
os << "obsoleted: " << getAttr().getObsoleted().value();
53+
os << "obsoleted";
54+
version = getAttr().getObsoleted();
5355
break;
5456
case Reason::UnavailableUnintroduced:
5557
case Reason::Unintroduced:
56-
os << "introduced: " << getAttr().getIntroduced().value();
58+
os << "introduced";
59+
version = getAttr().getIntroduced();
5760
break;
5861
}
5962

63+
if (version)
64+
os << ": " << *version;
6065
os << ")";
6166
}
6267

@@ -219,26 +224,23 @@ getAvailabilityConstraintForAttr(const Decl *decl,
219224
auto &ctx = decl->getASTContext();
220225
auto domain = attr.getDomain();
221226
auto deploymentRange = domain.getDeploymentRange(ctx);
227+
bool domainSupportsRefinement = domain.supportsContextRefinement();
228+
std::optional<AvailabilityRange> availableRange =
229+
domainSupportsRefinement ? context.getAvailabilityRange(domain, ctx)
230+
: deploymentRange;
222231

223-
// Is the decl obsoleted in the deployment context?
232+
// Is the decl obsoleted in this context?
224233
if (auto obsoletedRange = attr.getObsoletedRange(ctx)) {
225-
if (deploymentRange && deploymentRange->isContainedIn(*obsoletedRange))
234+
if (availableRange && availableRange->isContainedIn(*obsoletedRange))
226235
return AvailabilityConstraint::unavailableObsolete(attr);
227236
}
228237

229-
// Is the decl not yet introduced in the local context?
238+
// Is the decl not yet introduced in this context?
230239
if (auto introducedRange = attr.getIntroducedRange(ctx)) {
231-
if (domain.supportsContextRefinement()) {
232-
auto availableRange = context.getAvailabilityRange(domain, ctx);
233-
if (!availableRange || !availableRange->isContainedIn(*introducedRange))
234-
return AvailabilityConstraint::unintroduced(attr);
235-
236-
return std::nullopt;
237-
}
238-
239-
// Is the decl not yet introduced in the deployment context?
240-
if (deploymentRange && !deploymentRange->isContainedIn(*introducedRange))
241-
return AvailabilityConstraint::unavailableUnintroduced(attr);
240+
if (!availableRange || !availableRange->isContainedIn(*introducedRange))
241+
return domainSupportsRefinement
242+
? AvailabilityConstraint::unintroduced(attr)
243+
: AvailabilityConstraint::unavailableUnintroduced(attr);
242244
}
243245

244246
return std::nullopt;

0 commit comments

Comments
 (0)