Skip to content

Commit 0bf464b

Browse files
committed
AST: Return optional AvailabilityRange from SemanticAvailableAttr queries.
Introduction, deprecation, and obsoleteion ranges should only be returned by the accessors on `SemanticAvailableAttr` when the attribute actually has an affect on the corresponding kind of availability.
1 parent cc14548 commit 0bf464b

File tree

9 files changed

+100
-53
lines changed

9 files changed

+100
-53
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; }

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: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -146,21 +146,24 @@ getAvailabilityConstraintForAttr(const Decl *decl,
146146
auto &ctx = decl->getASTContext();
147147
auto deploymentRange = attr.getDomain().getDeploymentRange(ctx);
148148

149-
auto obsoletedRange = attr.getObsoletedRange(ctx);
150-
if (deploymentRange && deploymentRange->isContainedIn(obsoletedRange))
151-
return AvailabilityConstraint::obsoleted(attr);
152-
153-
AvailabilityRange introducedRange = attr.getIntroducedRange(ctx);
154-
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);
149+
// Is the decl obsoleted in the deployment context?
150+
if (auto obsoletedRange = attr.getObsoletedRange(ctx)) {
151+
if (deploymentRange && deploymentRange->isContainedIn(*obsoletedRange))
152+
return AvailabilityConstraint::obsoleted(attr);
162153
}
163154

155+
if (auto introducedRange = attr.getIntroducedRange(ctx)) {
156+
// FIXME: [availability] Expand this to cover custom versioned domains
157+
if (attr.isPlatformSpecific()) {
158+
if (!context.getPlatformRange().isContainedIn(*introducedRange))
159+
return AvailabilityConstraint::potentiallyUnavailable(attr);
160+
} else if (deploymentRange &&
161+
!deploymentRange->isContainedIn(*introducedRange)) {
162+
return AvailabilityConstraint::unavailableForDeployment(attr);
163+
}
164+
}
165+
166+
// FIXME: [availability] Model deprecation as an availability constraint.
164167
return std::nullopt;
165168
}
166169

lib/AST/AvailabilityContext.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,10 @@ void AvailabilityContext::constrainWithDeclAndPlatformRange(
212212
break;
213213
case AvailabilityConstraint::Reason::PotentiallyUnavailable:
214214
DEBUG_ASSERT(domain.isPlatform());
215-
if (domain.isPlatform())
216-
isConstrained |=
217-
constrainRange(platformRange, attr.getIntroducedRange(ctx));
215+
if (domain.isPlatform()) {
216+
if (auto introducedRange = attr.getIntroducedRange(ctx))
217+
isConstrained |= constrainRange(platformRange, *introducedRange);
218+
}
218219
// FIXME: [availability] Store other potentially unavailable domains in
219220
// domainInfos.
220221
break;

lib/AST/DeclContext.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1747,6 +1747,7 @@ bool DeclContext::isAlwaysAvailableConformanceContext() const {
17471747

17481748
auto &ctx = getASTContext();
17491749

1750+
// FIXME: [availability] Query AvailabilityContext, not platform range.
17501751
AvailabilityRange conformanceAvailability{
17511752
AvailabilityInference::availableRange(ext)};
17521753

lib/IDE/CodeCompletionDiagnostics.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ bool CodeCompletionDiagnostics::getDiagnosticForDeprecated(
8888
// So getter/setter specific availability doesn't work in code completion.
8989

9090
auto Domain = Attr.getDomain();
91-
auto DeprecatedRange = Attr.getDeprecatedRange(Ctx);
91+
auto DeprecatedRange = Attr.getDeprecatedRange(Ctx).value();
9292
auto Message = Attr.getMessage();
9393
auto NewName = Attr.getRename();
9494
if (!isSoftDeprecated) {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2396,23 +2396,24 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *parsedAttr) {
23962396

23972397
// The remaining diagnostics are only for attributes with introduced versions
23982398
// for specific platforms.
2399-
if (!attr->isPlatformSpecific() || !attr->getIntroduced().has_value())
2399+
auto introducedRange = attr->getIntroducedRange(Ctx);
2400+
if (!attr->isPlatformSpecific() || !introducedRange)
24002401
return;
24012402

24022403
// Find the innermost enclosing declaration with an availability
24032404
// range annotation and ensure that this attribute's available version range
24042405
// is fully contained within that declaration's range. If there is no such
24052406
// enclosing declaration, then there is nothing to check.
2406-
std::optional<AvailabilityRange> EnclosingAnnotatedRange;
2407-
AvailabilityRange AttrRange = attr->getIntroducedRange(Ctx);
2407+
std::optional<AvailabilityRange> enclosingIntroducedRange;
24082408

24092409
if (auto *parent = getEnclosingDeclForDecl(D)) {
24102410
if (auto enclosingAvailable =
24112411
getSemanticAvailableRangeDeclAndAttr(parent)) {
24122412
SemanticAvailableAttr enclosingAttr = enclosingAvailable->first;
24132413
const Decl *enclosingDecl = enclosingAvailable->second;
2414-
EnclosingAnnotatedRange.emplace(enclosingAttr.getIntroducedRange(Ctx));
2415-
if (!AttrRange.isContainedIn(*EnclosingAnnotatedRange)) {
2414+
enclosingIntroducedRange = enclosingAttr.getIntroducedRange(Ctx);
2415+
if (enclosingIntroducedRange &&
2416+
!introducedRange->isContainedIn(*enclosingIntroducedRange)) {
24162417
auto limit = DiagnosticBehavior::Unspecified;
24172418
if (D->isImplicit()) {
24182419
// Incorrect availability for an implicit declaration is likely a
@@ -2433,10 +2434,10 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *parsedAttr) {
24332434
diagnose(enclosingDecl->getLoc(),
24342435
diag::availability_implicit_decl_here,
24352436
D->getDescriptiveKind(), Ctx.getTargetAvailabilityDomain(),
2436-
AttrRange);
2437+
*introducedRange);
24372438
diagnose(enclosingDecl->getLoc(),
24382439
diag::availability_decl_more_than_enclosing_here,
2439-
Ctx.getTargetAvailabilityDomain(), *EnclosingAnnotatedRange);
2440+
Ctx.getTargetAvailabilityDomain(), *enclosingIntroducedRange);
24402441
}
24412442
}
24422443
}

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2840,7 +2840,7 @@ static void diagnoseIfDeprecated(SourceRange ReferenceRange,
28402840
return;
28412841

28422842
auto Domain = Attr->getDomain();
2843-
auto DeprecatedRange = Attr->getDeprecatedRange(Context);
2843+
auto DeprecatedRange = Attr->getDeprecatedRange(Context).value();
28442844
auto Message = Attr->getMessage();
28452845
auto NewName = Attr->getRename();
28462846
if (Message.empty() && NewName.empty()) {
@@ -2919,7 +2919,7 @@ static bool diagnoseIfDeprecated(SourceLoc loc,
29192919
auto proto = rootConf->getProtocol()->getDeclaredInterfaceType();
29202920

29212921
auto domain = attr->getDomain();
2922-
auto deprecatedRange = attr->getDeprecatedRange(ctx);
2922+
auto deprecatedRange = attr->getDeprecatedRange(ctx).value();
29232923
auto message = attr->getMessage();
29242924
if (message.empty()) {
29252925
ctx.Diags
@@ -3071,12 +3071,12 @@ bool diagnoseExplicitUnavailability(SourceLoc loc,
30713071
break;
30723072
case AvailabilityConstraint::Reason::UnavailableForDeployment:
30733073
diags.diagnose(ext, diag::conformance_availability_introduced_in_version,
3074-
type, proto, domain, attr.getIntroducedRange(ctx));
3074+
type, proto, domain, attr.getIntroducedRange(ctx).value());
30753075
break;
30763076
case AvailabilityConstraint::Reason::Obsoleted:
30773077
diags
30783078
.diagnose(ext, diag::conformance_availability_obsoleted, type, proto,
3079-
domain, attr.getObsoletedRange(ctx))
3079+
domain, attr.getObsoletedRange(ctx).value())
30803080
.highlight(attr.getParsedAttr()->getRange());
30813081
break;
30823082
case AvailabilityConstraint::Reason::PotentiallyUnavailable:
@@ -3492,13 +3492,13 @@ bool diagnoseExplicitUnavailability(
34923492
case AvailabilityConstraint::Reason::UnavailableForDeployment:
34933493
diags
34943494
.diagnose(D, diag::availability_introduced_in_version, D, domain,
3495-
Attr.getIntroducedRange(ctx))
3495+
Attr.getIntroducedRange(ctx).value())
34963496
.highlight(sourceRange);
34973497
break;
34983498
case AvailabilityConstraint::Reason::Obsoleted:
34993499
diags
35003500
.diagnose(D, diag::availability_obsoleted, D, domain,
3501-
Attr.getObsoletedRange(ctx))
3501+
Attr.getObsoletedRange(ctx).value())
35023502
.highlight(sourceRange);
35033503
break;
35043504
case AvailabilityConstraint::Reason::PotentiallyUnavailable:

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3128,6 +3128,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
31283128

31293129
// An associated type that was introduced after the protocol
31303130
auto module = AT->getDeclContext()->getParentModule();
3131+
// FIXME: [availability] Query AvailabilityContext, not platform range.
31313132
if (!defaultType &&
31323133
module->getResilienceStrategy() == ResilienceStrategy::Resilient &&
31333134
AvailabilityInference::availableRange(proto).isSupersetOf(

0 commit comments

Comments
 (0)