Skip to content

Commit 05f7cb7

Browse files
committed
AST: Refactor availability version remapping.
Availability version remapping currently only applies to code built for visionOS. We plan to introduce more platform kinds and standalone availability domains that will require version remapping, though, so it's time to rearchitect and simplify the code to make it easier to generalize. `AvailabilityDomain` is now responsible for version remapping and much of the previously duplicated utilities have been consolidated.
1 parent 88bc79f commit 05f7cb7

File tree

6 files changed

+151
-267
lines changed

6 files changed

+151
-267
lines changed

include/swift/AST/AvailabilityDomain.h

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,21 @@
3333

3434
namespace swift {
3535
class ASTContext;
36+
class AvailabilityDomainAndRange;
3637
class CustomAvailabilityDomain;
3738
class DeclContext;
3839
class FuncDecl;
3940
class ModuleDecl;
4041
class ValueDecl;
4142

43+
/// Discriminates whether a version tuple represents the `introduced:`,
44+
/// `deprecated:`, or `obsoleted:` version of an `@available` attribute.
45+
enum class AvailabilityVersionKind {
46+
Introduced,
47+
Deprecated,
48+
Obsoleted,
49+
};
50+
4251
/// Represents a dimension of availability (e.g. macOS platform or Swift
4352
/// language mode).
4453
class AvailabilityDomain final {
@@ -131,6 +140,9 @@ class AvailabilityDomain final {
131140
: std::nullopt;
132141
}
133142

143+
std::optional<AvailabilityDomain>
144+
getRemappedDomainOrNull(const ASTContext &ctx) const;
145+
134146
public:
135147
AvailabilityDomain() {}
136148

@@ -294,19 +306,22 @@ class AvailabilityDomain final {
294306
/// descendants of the iOS domain.
295307
AvailabilityDomain getRootDomain() const;
296308

297-
/// Returns the canonical domain that versions in this domain must be remapped
298-
/// to before making availability comparisons in the current compilation
299-
/// context. Sets \p didRemap to `true` if a remap was required.
300-
const AvailabilityDomain getRemappedDomain(const ASTContext &ctx,
301-
bool &didRemap) const;
302-
303309
/// Returns the canonical domain that versions in this domain must be remapped
304310
/// to before making availability comparisons in the current compilation
305311
/// context.
306312
const AvailabilityDomain getRemappedDomain(const ASTContext &ctx) const {
307-
bool unused;
308-
return getRemappedDomain(ctx, unused);
309-
}
313+
auto remappedDomain = getRemappedDomainOrNull(ctx);
314+
return remappedDomain ? *remappedDomain : *this;
315+
}
316+
317+
/// Converts the domain and the given version into a canonical domain and
318+
/// range that can be used for availability comparisons in the current current
319+
/// compilation context. If no conversion is necessary or possible, the domain
320+
/// and range are returned unmodified.
321+
AvailabilityDomainAndRange
322+
getRemappedDomainAndRange(const llvm::VersionTuple &version,
323+
AvailabilityVersionKind versionKind,
324+
const ASTContext &ctx) const;
310325

311326
/// Returns true for a domain that is permanently always available, and
312327
/// therefore availability constraints in the domain are effectively the same

include/swift/AST/AvailabilityInference.h

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
namespace swift {
2626
class ASTContext;
2727
class AvailabilityDomain;
28-
class BackDeployedAttr;
2928
class Decl;
3029
class SemanticAvailableAttr;
3130

@@ -49,37 +48,8 @@ class AvailabilityInference {
4948
static std::optional<AvailabilityRange>
5049
annotatedAvailableRange(const Decl *D);
5150

52-
static AvailabilityRange
53-
annotatedAvailableRangeForAttr(const Decl *D, const AbstractSpecializeAttr *attr,
54-
ASTContext &ctx);
55-
56-
/// For the attribute's introduction version, update the platform and version
57-
/// values to the re-mapped platform's, if using a fallback platform.
58-
/// Returns `true` if a remap occured.
59-
static bool updateIntroducedAvailabilityDomainForFallback(
60-
const SemanticAvailableAttr &attr, const ASTContext &ctx,
61-
AvailabilityDomain &domain, llvm::VersionTuple &platformVer);
62-
63-
/// For the attribute's deprecation version, update the platform and version
64-
/// values to the re-mapped platform's, if using a fallback platform.
65-
/// Returns `true` if a remap occured.
66-
static bool updateDeprecatedAvailabilityDomainForFallback(
67-
const SemanticAvailableAttr &attr, const ASTContext &ctx,
68-
AvailabilityDomain &domain, llvm::VersionTuple &platformVer);
69-
70-
/// For the attribute's obsoletion version, update the platform and version
71-
/// values to the re-mapped platform's, if using a fallback platform.
72-
/// Returns `true` if a remap occured.
73-
static bool updateObsoletedAvailabilityDomainForFallback(
74-
const SemanticAvailableAttr &attr, const ASTContext &ctx,
75-
AvailabilityDomain &domain, llvm::VersionTuple &platformVer);
76-
77-
/// For the attribute's before version, update the platform and version
78-
/// values to the re-mapped platform's, if using a fallback platform.
79-
/// Returns `true` if a remap occured.
80-
static bool updateBeforeAvailabilityDomainForFallback(
81-
const BackDeployedAttr *attr, const ASTContext &ctx,
82-
AvailabilityDomain &domain, llvm::VersionTuple &platformVer);
51+
static AvailabilityRange annotatedAvailableRangeForAttr(
52+
const Decl *D, const AbstractSpecializeAttr *attr, ASTContext &ctx);
8353
};
8454

8555
} // end namespace swift

lib/AST/Availability.cpp

Lines changed: 37 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -256,128 +256,6 @@ static bool isBetterThan(const SemanticAvailableAttr &newAttr,
256256
prevAttr->getPlatform());
257257
}
258258

259-
static const clang::DarwinSDKInfo::RelatedTargetVersionMapping *
260-
getFallbackVersionMapping(const ASTContext &Ctx,
261-
clang::DarwinSDKInfo::OSEnvPair Kind) {
262-
auto *SDKInfo = Ctx.getDarwinSDKInfo();
263-
if (SDKInfo)
264-
return SDKInfo->getVersionMapping(Kind);
265-
266-
return Ctx.getAuxiliaryDarwinPlatformRemapInfo(Kind);
267-
}
268-
269-
static std::optional<clang::VersionTuple>
270-
getRemappedIntroducedVersionForFallbackPlatform(
271-
const ASTContext &Ctx, const llvm::VersionTuple &Version) {
272-
const auto *Mapping = getFallbackVersionMapping(
273-
Ctx, clang::DarwinSDKInfo::OSEnvPair(
274-
llvm::Triple::IOS, llvm::Triple::UnknownEnvironment,
275-
llvm::Triple::XROS, llvm::Triple::UnknownEnvironment));
276-
if (!Mapping)
277-
return std::nullopt;
278-
return Mapping->mapIntroducedAvailabilityVersion(Version);
279-
}
280-
281-
static std::optional<clang::VersionTuple>
282-
getRemappedDeprecatedObsoletedVersionForFallbackPlatform(
283-
const ASTContext &Ctx, const llvm::VersionTuple &Version) {
284-
const auto *Mapping = getFallbackVersionMapping(
285-
Ctx, clang::DarwinSDKInfo::OSEnvPair(
286-
llvm::Triple::IOS, llvm::Triple::UnknownEnvironment,
287-
llvm::Triple::XROS, llvm::Triple::UnknownEnvironment));
288-
if (!Mapping)
289-
return std::nullopt;
290-
return Mapping->mapDeprecatedObsoletedAvailabilityVersion(Version);
291-
}
292-
293-
bool AvailabilityInference::updateIntroducedAvailabilityDomainForFallback(
294-
const SemanticAvailableAttr &attr, const ASTContext &ctx,
295-
AvailabilityDomain &domain, llvm::VersionTuple &platformVer) {
296-
std::optional<llvm::VersionTuple> introducedVersion = attr.getIntroduced();
297-
if (!introducedVersion.has_value())
298-
return false;
299-
300-
bool hasRemap = false;
301-
auto remappedDomain = attr.getDomain().getRemappedDomain(ctx, hasRemap);
302-
if (!hasRemap)
303-
return false;
304-
305-
auto potentiallyRemappedIntroducedVersion =
306-
getRemappedIntroducedVersionForFallbackPlatform(ctx, *introducedVersion);
307-
if (potentiallyRemappedIntroducedVersion.has_value()) {
308-
domain = remappedDomain;
309-
platformVer = potentiallyRemappedIntroducedVersion.value();
310-
return true;
311-
}
312-
return false;
313-
}
314-
315-
bool AvailabilityInference::updateDeprecatedAvailabilityDomainForFallback(
316-
const SemanticAvailableAttr &attr, const ASTContext &ctx,
317-
AvailabilityDomain &domain, llvm::VersionTuple &platformVer) {
318-
std::optional<llvm::VersionTuple> deprecatedVersion = attr.getDeprecated();
319-
if (!deprecatedVersion.has_value())
320-
return false;
321-
322-
bool hasRemap = false;
323-
auto remappedDomain = attr.getDomain().getRemappedDomain(ctx, hasRemap);
324-
if (!hasRemap)
325-
return false;
326-
327-
auto potentiallyRemappedDeprecatedVersion =
328-
getRemappedDeprecatedObsoletedVersionForFallbackPlatform(
329-
ctx, *deprecatedVersion);
330-
if (potentiallyRemappedDeprecatedVersion.has_value()) {
331-
domain = remappedDomain;
332-
platformVer = potentiallyRemappedDeprecatedVersion.value();
333-
return true;
334-
}
335-
return false;
336-
}
337-
338-
bool AvailabilityInference::updateObsoletedAvailabilityDomainForFallback(
339-
const SemanticAvailableAttr &attr, const ASTContext &ctx,
340-
AvailabilityDomain &domain, llvm::VersionTuple &platformVer) {
341-
std::optional<llvm::VersionTuple> obsoletedVersion = attr.getObsoleted();
342-
if (!obsoletedVersion.has_value())
343-
return false;
344-
345-
bool hasRemap = false;
346-
auto remappedDomain = attr.getDomain().getRemappedDomain(ctx, hasRemap);
347-
if (!hasRemap)
348-
return false;
349-
350-
auto potentiallyRemappedObsoletedVersion =
351-
getRemappedDeprecatedObsoletedVersionForFallbackPlatform(
352-
ctx, *obsoletedVersion);
353-
if (potentiallyRemappedObsoletedVersion.has_value()) {
354-
domain = remappedDomain;
355-
platformVer = potentiallyRemappedObsoletedVersion.value();
356-
return true;
357-
}
358-
return false;
359-
}
360-
361-
bool AvailabilityInference::updateBeforeAvailabilityDomainForFallback(
362-
const BackDeployedAttr *attr, const ASTContext &ctx,
363-
AvailabilityDomain &domain, llvm::VersionTuple &platformVer) {
364-
bool hasRemap = false;
365-
auto remappedDomain =
366-
attr->getAvailabilityDomain().getRemappedDomain(ctx, hasRemap);
367-
if (!hasRemap)
368-
return false;
369-
370-
auto beforeVersion = attr->getVersion();
371-
auto potentiallyRemappedIntroducedVersion =
372-
getRemappedIntroducedVersionForFallbackPlatform(ctx, beforeVersion);
373-
if (potentiallyRemappedIntroducedVersion.has_value()) {
374-
domain = remappedDomain;
375-
platformVer = potentiallyRemappedIntroducedVersion.value();
376-
return true;
377-
}
378-
return false;
379-
}
380-
381259
static std::optional<SemanticAvailableAttr>
382260
getDeclAvailableAttrForPlatformIntroduction(const Decl *D) {
383261
std::optional<SemanticAvailableAttr> bestAvailAttr;
@@ -917,40 +795,31 @@ std::optional<llvm::VersionTuple> SemanticAvailableAttr::getIntroduced() const {
917795
std::optional<AvailabilityDomainAndRange>
918796
SemanticAvailableAttr::getIntroducedDomainAndRange(
919797
const ASTContext &Ctx) const {
920-
auto *attr = getParsedAttr();
921798
auto domain = getDomain();
922-
923799
if (domain.isUniversal())
924800
return std::nullopt;
925801

926-
if (!attr->getRawIntroduced().has_value()) {
927-
// For versioned domains, an "introduced:" version is always required to
928-
// indicate introduction.
929-
if (domain.isVersioned())
930-
return std::nullopt;
802+
if (auto introduced = getIntroduced())
803+
return domain.getRemappedDomainAndRange(
804+
*introduced, AvailabilityVersionKind::Introduced, Ctx);
931805

932-
// For version-less domains, an attribute that does not indicate some other
933-
// kind of unconditional availability constraint implicitly specifies that
934-
// the decl is available in all versions of the domain.
935-
switch (attr->getKind()) {
936-
case AvailableAttr::Kind::Default:
937-
return AvailabilityDomainAndRange(domain.getRemappedDomain(Ctx),
938-
AvailabilityRange::alwaysAvailable());
939-
case AvailableAttr::Kind::Deprecated:
940-
case AvailableAttr::Kind::Unavailable:
941-
case AvailableAttr::Kind::NoAsync:
942-
return std::nullopt;
943-
}
944-
}
945-
946-
llvm::VersionTuple introducedVersion = getIntroduced().value();
947-
llvm::VersionTuple remappedVersion;
948-
if (AvailabilityInference::updateIntroducedAvailabilityDomainForFallback(
949-
*this, Ctx, domain, remappedVersion))
950-
introducedVersion = remappedVersion;
806+
// For versioned domains, an "introduced:" version is always required to
807+
// indicate introduction.
808+
if (domain.isVersioned())
809+
return std::nullopt;
951810

952-
return AvailabilityDomainAndRange(domain,
953-
AvailabilityRange{introducedVersion});
811+
// For version-less domains, an attribute that does not indicate some other
812+
// kind of unconditional availability constraint implicitly specifies that
813+
// the decl is available in all versions of the domain.
814+
switch (attr->getKind()) {
815+
case AvailableAttr::Kind::Default:
816+
return AvailabilityDomainAndRange(domain.getRemappedDomain(Ctx),
817+
AvailabilityRange::alwaysAvailable());
818+
case AvailableAttr::Kind::Deprecated:
819+
case AvailableAttr::Kind::Unavailable:
820+
case AvailableAttr::Kind::NoAsync:
821+
return std::nullopt;
822+
}
954823
}
955824

956825
std::optional<llvm::VersionTuple> SemanticAvailableAttr::getDeprecated() const {
@@ -962,28 +831,18 @@ std::optional<llvm::VersionTuple> SemanticAvailableAttr::getDeprecated() const {
962831
std::optional<AvailabilityDomainAndRange>
963832
SemanticAvailableAttr::getDeprecatedDomainAndRange(
964833
const ASTContext &Ctx) const {
965-
auto *attr = getParsedAttr();
966-
AvailabilityDomain domain = getDomain();
967-
968-
if (!attr->getRawDeprecated().has_value()) {
969-
// Regardless of the whether the domain supports versions or not, an
970-
// unconditional deprecation attribute indicates the decl is always
971-
// deprecated.
972-
if (isUnconditionallyDeprecated())
973-
return AvailabilityDomainAndRange(domain.getRemappedDomain(Ctx),
974-
AvailabilityRange::alwaysAvailable());
975-
976-
return std::nullopt;
977-
}
834+
if (auto deprecated = getDeprecated())
835+
return getDomain().getRemappedDomainAndRange(
836+
*deprecated, AvailabilityVersionKind::Deprecated, Ctx);
978837

979-
llvm::VersionTuple deprecatedVersion = getDeprecated().value();
980-
llvm::VersionTuple remappedVersion;
981-
if (AvailabilityInference::updateDeprecatedAvailabilityDomainForFallback(
982-
*this, Ctx, domain, remappedVersion))
983-
deprecatedVersion = remappedVersion;
838+
// Regardless of the whether the domain supports versions or not, an
839+
// unconditional deprecation attribute indicates the decl is always
840+
// deprecated.
841+
if (isUnconditionallyDeprecated())
842+
return AvailabilityDomainAndRange(getDomain().getRemappedDomain(Ctx),
843+
AvailabilityRange::alwaysAvailable());
984844

985-
return AvailabilityDomainAndRange(domain,
986-
AvailabilityRange{deprecatedVersion});
845+
return std::nullopt;
987846
}
988847

989848
std::optional<llvm::VersionTuple> SemanticAvailableAttr::getObsoleted() const {
@@ -994,26 +853,16 @@ std::optional<llvm::VersionTuple> SemanticAvailableAttr::getObsoleted() const {
994853

995854
std::optional<AvailabilityDomainAndRange>
996855
SemanticAvailableAttr::getObsoletedDomainAndRange(const ASTContext &Ctx) const {
997-
auto *attr = getParsedAttr();
998-
AvailabilityDomain domain = getDomain();
999-
1000-
if (!attr->getRawObsoleted().has_value()) {
1001-
// An "unavailable" attribute effectively means obsolete in all versions.
1002-
if (attr->isUnconditionallyUnavailable())
1003-
return AvailabilityDomainAndRange(domain.getRemappedDomain(Ctx),
1004-
AvailabilityRange::alwaysAvailable());
856+
if (auto obsoleted = getObsoleted())
857+
return getDomain().getRemappedDomainAndRange(
858+
*obsoleted, AvailabilityVersionKind::Obsoleted, Ctx);
1005859

1006-
return std::nullopt;
1007-
}
860+
// An "unavailable" attribute effectively means obsolete in all versions.
861+
if (isUnconditionallyUnavailable())
862+
return AvailabilityDomainAndRange(getDomain().getRemappedDomain(Ctx),
863+
AvailabilityRange::alwaysAvailable());
1008864

1009-
llvm::VersionTuple obsoletedVersion = getObsoleted().value();
1010-
llvm::VersionTuple remappedVersion;
1011-
if (AvailabilityInference::updateObsoletedAvailabilityDomainForFallback(
1012-
*this, Ctx, domain, remappedVersion))
1013-
obsoletedVersion = remappedVersion;
1014-
1015-
return AvailabilityDomainAndRange(domain,
1016-
AvailabilityRange{obsoletedVersion});
865+
return std::nullopt;
1017866
}
1018867

1019868
namespace {

0 commit comments

Comments
 (0)