Skip to content

Commit 35fed04

Browse files
authored
Merge pull request #79807 from tshortli/availability-context-available-custom-domains
AST: Teach AvailabilityContext to represent version-less availability
2 parents 35beeca + 0462cfd commit 35fed04

17 files changed

+409
-131
lines changed

include/swift/AST/Attr.h

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3308,29 +3308,41 @@ class SemanticAvailableAttr final {
33083308
/// The source range of the `introduced:` version component.
33093309
SourceRange getIntroducedSourceRange() const { return attr->IntroducedRange; }
33103310

3311-
/// Returns the effective availability range for the attribute's `introduced:`
3312-
/// component (remapping or canonicalizing if necessary).
3313-
AvailabilityRange getIntroducedRange(const ASTContext &Ctx) const;
3311+
/// Returns the effective introduction range indicated by this attribute.
3312+
/// This may correspond to the version specified by the `introduced:`
3313+
/// component (remapped or canonicalized if necessary) or it may be "always"
3314+
/// for an attribute indicating availability in a version-less domain. Returns
3315+
/// `std::nullopt` if the attribute does not indicate introduction.
3316+
std::optional<AvailabilityRange>
3317+
getIntroducedRange(const ASTContext &Ctx) const;
33143318

33153319
/// The version tuple for the `deprecated:` component.
33163320
std::optional<llvm::VersionTuple> getDeprecated() const;
33173321

33183322
/// The source range of the `deprecated:` version component.
33193323
SourceRange getDeprecatedSourceRange() const { return attr->DeprecatedRange; }
33203324

3321-
/// Returns the effective availability range for the attribute's `deprecated:`
3322-
/// component (remapping or canonicalizing if necessary).
3323-
AvailabilityRange getDeprecatedRange(const ASTContext &Ctx) const;
3325+
/// Returns the effective deprecation range indicated by this attribute.
3326+
/// This may correspond to the version specified by the `deprecated:`
3327+
/// component (remapped or canonicalized if necessary) or it may be "always"
3328+
/// for an unconditional deprecation attribute. Returns `std::nullopt` if the
3329+
/// attribute does not indicate deprecation.
3330+
std::optional<AvailabilityRange>
3331+
getDeprecatedRange(const ASTContext &Ctx) const;
33243332

33253333
/// The version tuple for the `obsoleted:` component.
33263334
std::optional<llvm::VersionTuple> getObsoleted() const;
33273335

33283336
/// The source range of the `obsoleted:` version component.
33293337
SourceRange getObsoletedSourceRange() const { return attr->ObsoletedRange; }
33303338

3331-
/// Returns the effective availability range for the attribute's `obsoleted:`
3332-
/// component (remapping or canonicalizing if necessary).
3333-
AvailabilityRange getObsoletedRange(const ASTContext &Ctx) const;
3339+
/// Returns the effective obsoletion range indicated by this attribute.
3340+
/// This always corresponds to the version specified by the `obsoleted:`
3341+
/// component (remapped or canonicalized if necessary). Returns `std::nullopt`
3342+
/// if the attribute does not indicate obsoletion (note that unavailability is
3343+
/// separate from obsoletion.
3344+
std::optional<AvailabilityRange>
3345+
getObsoletedRange(const ASTContext &Ctx) const;
33343346

33353347
/// Returns the `message:` field of the attribute, or an empty string.
33363348
StringRef getMessage() const { return attr->Message; }

include/swift/AST/AvailabilityContext.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,15 @@ class AvailabilityContext {
6464

6565
/// Returns the range of platform versions which may execute code in the
6666
/// availability context, starting at its introduction version.
67+
// FIXME: [availability] Remove; superseded by getAvailableRange().
6768
AvailabilityRange getPlatformRange() const;
6869

70+
/// Returns the range which is available for `domain` in this context. If
71+
/// there are not any constraints established for `domain`, returns
72+
/// `std::nullopt`.
73+
std::optional<AvailabilityRange>
74+
getAvailabilityRange(AvailabilityDomain domain, const ASTContext &ctx) const;
75+
6976
/// Returns true if this context contains any unavailable domains.
7077
bool isUnavailable() const;
7178

@@ -80,9 +87,15 @@ class AvailabilityContext {
8087
const ASTContext &ctx);
8188

8289
/// Constrain the platform availability range with `platformRange`.
90+
// FIXME: [availability] Remove; superseded by constrainWithAvailableRange().
8391
void constrainWithPlatformRange(const AvailabilityRange &platformRange,
8492
const ASTContext &ctx);
8593

94+
/// Constrain the available range for `domain` by `range`.
95+
void constrainWithAvailabilityRange(const AvailabilityRange &range,
96+
AvailabilityDomain domain,
97+
const ASTContext &ctx);
98+
8699
/// Constrain the context by adding \p domain to the set of unavailable
87100
/// domains.
88101
void constrainWithUnavailableDomain(AvailabilityDomain domain,

include/swift/AST/AvailabilityContextStorage.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class AvailabilityContext::DomainInfo final {
4343
AvailabilityRange getRange() const { return range; }
4444
bool isUnavailable() const { return range.isKnownUnreachable(); }
4545

46+
bool constrainRange(const AvailabilityRange &range);
47+
4648
void Profile(llvm::FoldingSetNodeID &ID) const {
4749
ID.AddPointer(domain.getOpaqueValue());
4850
range.getRawVersionRange().Profile(ID);

include/swift/AST/AvailabilityDomain.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,13 +209,24 @@ class AvailabilityDomain final {
209209
/// version ranges.
210210
bool isVersioned() const;
211211

212+
/// Returns true if availability of the domain can be refined using
213+
/// `@available` attributes and `if #available` queries. If not, then the
214+
/// domain's availability is fixed by compilation settings. For example,
215+
/// macOS platform availability supports contextual refinement, whereas Swift
216+
/// language availability does not.
217+
bool supportsContextRefinement() const;
218+
212219
/// Returns true if the domain supports `#available`/`#unavailable` queries.
213220
bool supportsQueries() const;
214221

215222
/// Returns true if this domain is considered active in the current
216223
/// compilation context.
217224
bool isActive(const ASTContext &ctx) const;
218225

226+
/// Returns true if this domain is a platform domain that is contained by the
227+
/// set of active platform-specific domains.
228+
bool isActiveForTargetPlatform(const ASTContext &ctx) const;
229+
219230
/// Returns the minimum available range for the attribute's domain. For
220231
/// example, for the domain of the platform that compilation is targeting,
221232
/// this will be the deployment target. For the Swift language domain, this

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6823,20 +6823,15 @@ NOTE(availability_decl_more_than_enclosing_here, none,
68236823
"enclosing scope requires availability of %0 %1 or newer",
68246824
(AvailabilityDomain, AvailabilityRange))
68256825

6826-
ERROR(availability_decl_only_version_newer, none,
6827-
"%0 is only available in %1 %2 or newer",
6828-
(const ValueDecl *, AvailabilityDomain, AvailabilityRange))
6829-
6830-
ERROR(availability_decl_only_version_newer_for_clients, none,
6831-
"%0 is only available in %1 %2 or newer; clients of %3 may have a lower"
6832-
" deployment target",
6833-
(const ValueDecl *, AvailabilityDomain, AvailabilityRange, ModuleDecl *))
6826+
ERROR(availability_decl_only_in, none,
6827+
"%0 is only available in %1%select{| %3 or newer}2",
6828+
(const ValueDecl *, AvailabilityDomain, bool, AvailabilityRange))
68346829

6835-
WARNING(availability_decl_only_version_newer_for_clients_warn, none,
6836-
"%0 is only available in %1 %2 or newer; clients of %3 may have a lower"
6837-
" deployment target",
6838-
(const ValueDecl *, AvailabilityDomain, AvailabilityRange,
6839-
ModuleDecl *))
6830+
ERROR(availability_decl_only_in_for_clients, none,
6831+
"%0 is only available in %1%select{| %3 or newer}2"
6832+
"%select{|; clients of %4 may have a lower deployment target}2",
6833+
(const ValueDecl *, AvailabilityDomain, bool, AvailabilityRange,
6834+
ModuleDecl *))
68406835

68416836
ERROR(availability_opaque_types_only_version_newer, none,
68426837
"'some' return types are only available in %0 %1 or newer",
@@ -6884,9 +6879,10 @@ FIXIT(insert_available_attr,
68846879
"@available(%0 %1, *)\n%2",
68856880
(StringRef, StringRef, StringRef))
68866881

6887-
ERROR(availability_inout_accessor_only_version_newer, none,
6888-
"cannot pass as inout because %0 is only available in %1 %2 or newer",
6889-
(const ValueDecl *, AvailabilityDomain, AvailabilityRange))
6882+
ERROR(availability_inout_accessor_only_in, none,
6883+
"cannot pass as inout because %0 is only available in %1"
6884+
"%select{| %3 or newer}2",
6885+
(const ValueDecl *, AvailabilityDomain, bool, AvailabilityRange))
68906886

68916887
ERROR(availability_query_required_for_platform, none,
68926888
"condition required for target platform '%0'", (StringRef))

lib/AST/Availability.cpp

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -453,13 +453,13 @@ std::optional<SemanticAvailableAttr> Decl::getDeprecatedAttr() const {
453453
return attr;
454454

455455
auto deprecatedRange = attr.getDeprecatedRange(ctx);
456-
if (deprecatedRange.isKnownUnreachable())
456+
if (!deprecatedRange)
457457
continue;
458458

459459
// We treat the declaration as deprecated if it is deprecated on
460460
// all deployment targets.
461461
auto deploymentRange = attr.getDomain().getDeploymentRange(ctx);
462-
if (deploymentRange && deploymentRange->isContainedIn(deprecatedRange))
462+
if (deploymentRange && deploymentRange->isContainedIn(*deprecatedRange))
463463
result.emplace(attr);
464464
}
465465
return result;
@@ -475,13 +475,13 @@ std::optional<SemanticAvailableAttr> Decl::getSoftDeprecatedAttr() const {
475475
continue;
476476

477477
auto deprecatedRange = attr.getDeprecatedRange(ctx);
478-
if (deprecatedRange.isKnownUnreachable())
478+
if (!deprecatedRange)
479479
continue;
480480

481481
// We treat the declaration as soft-deprecated if it is deprecated in a
482482
// future version.
483483
auto deploymentRange = attr.getDomain().getDeploymentRange(ctx);
484-
if (!deploymentRange || !deploymentRange->isContainedIn(deprecatedRange))
484+
if (!deploymentRange || !deploymentRange->isContainedIn(*deprecatedRange))
485485
result.emplace(attr);
486486
}
487487
return result;
@@ -705,7 +705,8 @@ AvailabilityRange AvailabilityInference::annotatedAvailableRangeForAttr(
705705
}
706706

707707
if (bestAvailAttr)
708-
return bestAvailAttr->getIntroducedRange(ctx);
708+
return bestAvailAttr->getIntroducedRange(ctx).value_or(
709+
AvailabilityRange::alwaysAvailable());
709710

710711
return AvailabilityRange::alwaysAvailable();
711712
}
@@ -737,8 +738,10 @@ Decl::getAvailableAttrForPlatformIntroduction(bool checkExtension) const {
737738
}
738739

739740
AvailabilityRange AvailabilityInference::availableRange(const Decl *D) {
741+
// ALLANXXX
740742
if (auto attr = D->getAvailableAttrForPlatformIntroduction())
741-
return attr->getIntroducedRange(D->getASTContext());
743+
return attr->getIntroducedRange(D->getASTContext())
744+
.value_or(AvailabilityRange::alwaysAvailable());
742745

743746
return AvailabilityRange::alwaysAvailable();
744747
}
@@ -854,13 +857,29 @@ std::optional<llvm::VersionTuple> SemanticAvailableAttr::getIntroduced() const {
854857
return std::nullopt;
855858
}
856859

857-
AvailabilityRange
860+
std::optional<AvailabilityRange>
858861
SemanticAvailableAttr::getIntroducedRange(const ASTContext &Ctx) const {
859862
DEBUG_ASSERT(getDomain().isActive(Ctx));
860863

861864
auto *attr = getParsedAttr();
862-
if (!attr->getRawIntroduced().has_value())
863-
return AvailabilityRange::alwaysAvailable();
865+
if (!attr->getRawIntroduced().has_value()) {
866+
// For versioned domains, an "introduced:" version is always required to
867+
// indicate introduction.
868+
if (getDomain().isVersioned())
869+
return std::nullopt;
870+
871+
// For version-less domains, an attribute that does not indicate some other
872+
// kind of unconditional availability constraint implicitly specifies that
873+
// the decl is available in all versions of the domain.
874+
switch (attr->getKind()) {
875+
case AvailableAttr::Kind::Default:
876+
return AvailabilityRange::alwaysAvailable();
877+
case AvailableAttr::Kind::Deprecated:
878+
case AvailableAttr::Kind::Unavailable:
879+
case AvailableAttr::Kind::NoAsync:
880+
return std::nullopt;
881+
}
882+
}
864883

865884
llvm::VersionTuple introducedVersion = getIntroduced().value();
866885
AvailabilityDomain unusedDomain;
@@ -878,13 +897,20 @@ std::optional<llvm::VersionTuple> SemanticAvailableAttr::getDeprecated() const {
878897
return std::nullopt;
879898
}
880899

881-
AvailabilityRange
900+
std::optional<AvailabilityRange>
882901
SemanticAvailableAttr::getDeprecatedRange(const ASTContext &Ctx) const {
883902
DEBUG_ASSERT(getDomain().isActive(Ctx));
884903

885904
auto *attr = getParsedAttr();
886-
if (!attr->getRawDeprecated().has_value())
887-
return AvailabilityRange::neverAvailable();
905+
if (!attr->getRawDeprecated().has_value()) {
906+
// Regardless of the whether the domain supports versions or not, an
907+
// unconditional deprecation attribute indicates the decl is always
908+
// deprecated.
909+
if (isUnconditionallyDeprecated())
910+
return AvailabilityRange::alwaysAvailable();
911+
912+
return std::nullopt;
913+
}
888914

889915
llvm::VersionTuple deprecatedVersion = getDeprecated().value();
890916
AvailabilityDomain unusedDomain;
@@ -902,13 +928,15 @@ std::optional<llvm::VersionTuple> SemanticAvailableAttr::getObsoleted() const {
902928
return std::nullopt;
903929
}
904930

905-
AvailabilityRange
931+
std::optional<AvailabilityRange>
906932
SemanticAvailableAttr::getObsoletedRange(const ASTContext &Ctx) const {
907933
DEBUG_ASSERT(getDomain().isActive(Ctx));
908934

909935
auto *attr = getParsedAttr();
936+
937+
// Obsoletion always requires a version.
910938
if (!attr->getRawObsoleted().has_value())
911-
return AvailabilityRange::neverAvailable();
939+
return std::nullopt;
912940

913941
llvm::VersionTuple obsoletedVersion = getObsoleted().value();
914942
AvailabilityDomain unusedDomain;

lib/AST/AvailabilityConstraint.cpp

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -140,27 +140,36 @@ static std::optional<AvailabilityConstraint>
140140
getAvailabilityConstraintForAttr(const Decl *decl,
141141
const SemanticAvailableAttr &attr,
142142
const AvailabilityContext &context) {
143+
// Is the decl unconditionally unavailable?
143144
if (attr.isUnconditionallyUnavailable())
144145
return AvailabilityConstraint::unconditionallyUnavailable(attr);
145146

146147
auto &ctx = decl->getASTContext();
147-
auto deploymentRange = attr.getDomain().getDeploymentRange(ctx);
148+
auto domain = attr.getDomain();
149+
auto deploymentRange = domain.getDeploymentRange(ctx);
150+
151+
// Is the decl obsoleted in the deployment context?
152+
if (auto obsoletedRange = attr.getObsoletedRange(ctx)) {
153+
if (deploymentRange && deploymentRange->isContainedIn(*obsoletedRange))
154+
return AvailabilityConstraint::obsoleted(attr);
155+
}
148156

149-
auto obsoletedRange = attr.getObsoletedRange(ctx);
150-
if (deploymentRange && deploymentRange->isContainedIn(obsoletedRange))
151-
return AvailabilityConstraint::obsoleted(attr);
157+
// Is the decl not yet introduced in the local context?
158+
if (auto introducedRange = attr.getIntroducedRange(ctx)) {
159+
if (domain.supportsContextRefinement()) {
160+
auto availableRange = context.getAvailabilityRange(domain, ctx);
161+
if (!availableRange || !availableRange->isContainedIn(*introducedRange))
162+
return AvailabilityConstraint::potentiallyUnavailable(attr);
152163

153-
AvailabilityRange introducedRange = attr.getIntroducedRange(ctx);
164+
return std::nullopt;
165+
}
154166

155-
// FIXME: [availability] Expand this to cover custom versioned domains
156-
if (attr.isPlatformSpecific()) {
157-
if (!context.getPlatformRange().isContainedIn(introducedRange))
158-
return AvailabilityConstraint::potentiallyUnavailable(attr);
159-
} else if (deploymentRange &&
160-
!deploymentRange->isContainedIn(introducedRange)) {
161-
return AvailabilityConstraint::unavailableForDeployment(attr);
167+
// Is the decl not yet introduced in the deployment context?
168+
if (deploymentRange && !deploymentRange->isContainedIn(*introducedRange))
169+
return AvailabilityConstraint::unavailableForDeployment(attr);
162170
}
163171

172+
// FIXME: [availability] Model deprecation as an availability constraint.
164173
return std::nullopt;
165174
}
166175

0 commit comments

Comments
 (0)