Skip to content

Commit 3f626f5

Browse files
authored
Merge pull request #76203 from tshortli/availability-context-cleanup
AST: Clean up `AvailabilityContext` and uses
2 parents f29f67c + 89ea92d commit 3f626f5

19 files changed

+425
-334
lines changed

include/swift/AST/ASTContext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,10 @@ class ASTContext final {
15781578

15791579
public:
15801580
clang::DarwinSDKInfo *getDarwinSDKInfo() const;
1581+
1582+
/// Returns the string to use in diagnostics when printing the platform being
1583+
/// targetted.
1584+
StringRef getTargetPlatformStringForDiagnostics() const;
15811585
};
15821586

15831587
} // end namespace swift

include/swift/AST/Availability.h

Lines changed: 37 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -194,30 +194,7 @@ class VersionRange {
194194
}
195195
};
196196

197-
/// Records the reason a declaration is potentially unavailable.
198-
class UnavailabilityReason {
199-
private:
200-
VersionRange RequiredDeploymentRange;
201-
202-
explicit UnavailabilityReason(const VersionRange RequiredDeploymentRange)
203-
: RequiredDeploymentRange(RequiredDeploymentRange) {}
204-
205-
public:
206-
static UnavailabilityReason requiresVersionRange(const VersionRange Range) {
207-
return UnavailabilityReason(Range);
208-
}
209-
210-
const VersionRange &getRequiredOSVersionRange() const {
211-
return RequiredDeploymentRange;
212-
}
213-
214-
/// Returns true if the required OS version range's lower endpoint is at or
215-
/// below the deployment target of the given ASTContext.
216-
bool requiresDeploymentTargetOrEarlier(ASTContext &Ctx) const;
217-
};
218-
219-
/// Represents everything that a particular chunk of code may assume about its
220-
/// runtime environment.
197+
/// Represents a version range in which something is available.
221198
///
222199
/// The AvailabilityContext structure forms a [lattice][], which allows it to
223200
/// have meaningful union and intersection operations ("join" and "meet"),
@@ -229,14 +206,10 @@ class UnavailabilityReason {
229206
/// NOTE: Generally you should use the utilities on \c AvailabilityInference
230207
/// to create an \c AvailabilityContext, rather than creating one directly.
231208
class AvailabilityContext {
232-
VersionRange OSVersion;
233-
std::optional<bool> SPI;
209+
VersionRange Range;
234210

235211
public:
236-
/// Creates a context that requires certain versions of the target OS.
237-
explicit AvailabilityContext(VersionRange OSVersion,
238-
std::optional<bool> SPI = std::nullopt)
239-
: OSVersion(OSVersion), SPI(SPI) {}
212+
explicit AvailabilityContext(VersionRange Range) : Range(Range) {}
240213

241214
/// Creates a context that imposes the constraints of the ASTContext's
242215
/// deployment target.
@@ -264,21 +237,34 @@ class AvailabilityContext {
264237
return AvailabilityContext(VersionRange::empty());
265238
}
266239

267-
/// Returns the range of possible OS versions required by this context.
268-
VersionRange getOSVersion() const { return OSVersion; }
240+
/// Returns the range of possible versions required by this context.
241+
VersionRange getRawVersionRange() const { return Range; }
242+
243+
/// Returns true if there is a version tuple for this context.
244+
bool hasMinimumVersion() const { return Range.hasLowerEndpoint(); }
245+
246+
/// Returns the minimum version required by this context. This convenience
247+
/// is meant for debugging, diagnostics, serialization, etc. Use of the set
248+
/// algebra operations on `AvailabilityContext` should be preferred over
249+
/// direct comparison of raw versions.
250+
///
251+
/// Only call when `hasMinimumVersion()` returns true.
252+
llvm::VersionTuple getRawMinimumVersion() const {
253+
return Range.getLowerEndpoint();
254+
}
269255

270256
/// Returns true if \p other makes stronger guarantees than this context.
271257
///
272258
/// That is, `a.isContainedIn(b)` implies `a.union(b) == b`.
273259
bool isContainedIn(const AvailabilityContext &other) const {
274-
return OSVersion.isContainedIn(other.OSVersion);
260+
return Range.isContainedIn(other.Range);
275261
}
276262

277263
/// Returns true if \p other is a strict subset of this context.
278264
///
279265
/// That is, `a.isSupersetOf(b)` implies `a != b` and `a.union(b) == a`.
280266
bool isSupersetOf(const AvailabilityContext &other) const {
281-
return OSVersion.isSupersetOf(other.OSVersion);
267+
return Range.isSupersetOf(other.Range);
282268
}
283269

284270
/// Returns true if this context has constraints that make it impossible to
@@ -287,13 +273,13 @@ class AvailabilityContext {
287273
/// For example, the else branch of a `#available` check for iOS 8.0 when the
288274
/// containing function already requires iOS 9.
289275
bool isKnownUnreachable() const {
290-
return OSVersion.isEmpty();
276+
return Range.isEmpty();
291277
}
292278

293279
/// Returns true if there are no constraints on this context; that is,
294280
/// nothing can be assumed.
295281
bool isAlwaysAvailable() const {
296-
return OSVersion.isAll();
282+
return Range.isAll();
297283
}
298284

299285
/// Produces an under-approximation of the intersection of the two
@@ -306,7 +292,7 @@ class AvailabilityContext {
306292
/// As an example, this is used when figuring out the required availability
307293
/// for a type that references multiple nominal decls.
308294
void intersectWith(const AvailabilityContext &other) {
309-
OSVersion.intersectWith(other.getOSVersion());
295+
Range.intersectWith(other.Range);
310296
}
311297

312298
/// Produces an over-approximation of the intersection of the two
@@ -317,7 +303,7 @@ class AvailabilityContext {
317303
///
318304
/// As an example, this is used for the true branch of `#available`.
319305
void constrainWith(const AvailabilityContext &other) {
320-
OSVersion.constrainWith(other.getOSVersion());
306+
Range.constrainWith(other.Range);
321307
}
322308

323309
/// Produces an over-approximation of the union of two availability contexts.
@@ -329,15 +315,19 @@ class AvailabilityContext {
329315
/// As an example, this is used for the else branch of a conditional with
330316
/// multiple `#available` checks.
331317
void unionWith(const AvailabilityContext &other) {
332-
OSVersion.unionWith(other.getOSVersion());
318+
Range.unionWith(other.Range);
333319
}
334320

335-
bool isAvailableAsSPI() const { return SPI && *SPI; }
336-
337321
/// Returns a representation of this range as a string for debugging purposes.
338322
std::string getAsString() const {
339-
return "AvailabilityContext(" + OSVersion.getAsString() +
340-
(isAvailableAsSPI() ? ", spi" : "") + ")";
323+
return "AvailabilityContext(" + getVersionString() + ")";
324+
}
325+
326+
/// Returns a representation of the raw version range as a string for
327+
/// debugging purposes.
328+
std::string getVersionString() const {
329+
assert(Range.hasLowerEndpoint());
330+
return Range.getLowerEndpoint().getAsString();
341331
}
342332
};
343333

@@ -358,9 +348,12 @@ class AvailabilityInference {
358348
static AvailabilityContext inferForType(Type t);
359349

360350
/// Returns the context where a declaration is available
361-
/// We assume a declaration without an annotation is always available.
351+
/// We assume a declaration without an annotation is always available.
362352
static AvailabilityContext availableRange(const Decl *D, ASTContext &C);
363353

354+
/// Returns true is the declaration is `@_spi_available`.
355+
static bool isAvailableAsSPI(const Decl *D, ASTContext &C);
356+
364357
/// Returns the availability context for a declaration with the given
365358
/// @available attribute.
366359
///

lib/AST/ASTContext.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6782,3 +6782,7 @@ ValueOwnership swift::asValueOwnership(ParameterOwnership o) {
67826782
}
67836783
llvm_unreachable("exhaustive switch");
67846784
}
6785+
6786+
StringRef ASTContext::getTargetPlatformStringForDiagnostics() const {
6787+
return prettyPlatformString(targetPlatform(LangOpts));
6788+
}

lib/AST/Availability.cpp

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,7 @@ AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) {
423423
}
424424

425425
bool Decl::isAvailableAsSPI() const {
426-
return AvailabilityInference::availableRange(this, getASTContext())
427-
.isAvailableAsSPI();
426+
return AvailabilityInference::isAvailableAsSPI(this, getASTContext());
428427
}
429428

430429
std::optional<AvailableAttrDeclPair>
@@ -518,14 +517,6 @@ bool Decl::requiresUnavailableDeclABICompatibilityStubs() const {
518517
return isUnreachableAtRuntime();
519518
}
520519

521-
bool UnavailabilityReason::requiresDeploymentTargetOrEarlier(
522-
ASTContext &Ctx) const {
523-
return RequiredDeploymentRange.getLowerEndpoint() <=
524-
AvailabilityContext::forDeploymentTarget(Ctx)
525-
.getOSVersion()
526-
.getLowerEndpoint();
527-
}
528-
529520
AvailabilityContext
530521
AvailabilityInference::annotatedAvailableRangeForAttr(const SpecializeAttr* attr,
531522
ASTContext &ctx) {
@@ -550,13 +541,10 @@ AvailabilityInference::annotatedAvailableRangeForAttr(const SpecializeAttr* attr
550541
return AvailabilityContext::alwaysAvailable();
551542
}
552543

553-
AvailabilityContext AvailabilityInference::availableRange(const Decl *D,
554-
ASTContext &Ctx) {
555-
std::optional<AvailabilityContext> AnnotatedRange =
556-
annotatedAvailableRange(D, Ctx);
557-
if (AnnotatedRange.has_value()) {
558-
return AnnotatedRange.value();
559-
}
544+
static const AvailableAttr *attrForAvailableRange(const Decl *D,
545+
ASTContext &Ctx) {
546+
if (auto attr = AvailabilityInference::attrForAnnotatedAvailableRange(D, Ctx))
547+
return attr;
560548

561549
// Unlike other declarations, extensions can be used without referring to them
562550
// by name (they don't have one) in the source. For this reason, when checking
@@ -568,16 +556,30 @@ AvailabilityContext AvailabilityInference::availableRange(const Decl *D,
568556

569557
DeclContext *DC = D->getDeclContext();
570558
if (auto *ED = dyn_cast<ExtensionDecl>(DC)) {
571-
AnnotatedRange = annotatedAvailableRange(ED, Ctx);
572-
if (AnnotatedRange.has_value()) {
573-
return AnnotatedRange.value();
574-
}
559+
if (auto attr =
560+
AvailabilityInference::attrForAnnotatedAvailableRange(ED, Ctx))
561+
return attr;
575562
}
576563

564+
return nullptr;
565+
}
566+
567+
AvailabilityContext AvailabilityInference::availableRange(const Decl *D,
568+
ASTContext &Ctx) {
569+
if (auto attr = attrForAvailableRange(D, Ctx))
570+
return availableRange(attr, Ctx);
571+
577572
// Treat unannotated declarations as always available.
578573
return AvailabilityContext::alwaysAvailable();
579574
}
580575

576+
bool AvailabilityInference::isAvailableAsSPI(const Decl *D, ASTContext &Ctx) {
577+
if (auto attr = attrForAvailableRange(D, Ctx))
578+
return attr->IsSPI;
579+
580+
return false;
581+
}
582+
581583
AvailabilityContext
582584
AvailabilityInference::availableRange(const AvailableAttr *attr,
583585
ASTContext &Ctx) {
@@ -590,8 +592,7 @@ AvailabilityInference::availableRange(const AvailableAttr *attr,
590592
attr, Ctx, Platform, RemappedIntroducedVersion))
591593
IntroducedVersion = RemappedIntroducedVersion;
592594

593-
return AvailabilityContext{VersionRange::allGTE(IntroducedVersion),
594-
attr->IsSPI};
595+
return AvailabilityContext{VersionRange::allGTE(IntroducedVersion)};
595596
}
596597

597598
namespace {

lib/AST/TypeRefinementContext.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,12 +360,22 @@ TypeRefinementContext::getAvailabilityConditionVersionSourceRange(
360360
llvm_unreachable("Unhandled Reason in switch.");
361361
}
362362

363+
static std::string
364+
stringForAvailability(const AvailabilityContext &availability) {
365+
if (availability.isAlwaysAvailable())
366+
return "all";
367+
if (availability.isKnownUnreachable())
368+
return "none";
369+
370+
return availability.getVersionString();
371+
}
372+
363373
void TypeRefinementContext::print(raw_ostream &OS, SourceManager &SrcMgr,
364374
unsigned Indent) const {
365375
OS.indent(Indent);
366376
OS << "(" << getReasonName(getReason());
367377

368-
OS << " versions=" << AvailabilityInfo.getOSVersion().getAsString();
378+
OS << " version=" << stringForAvailability(AvailabilityInfo);
369379

370380
if (getReason() == Reason::Decl || getReason() == Reason::DeclImplicit) {
371381
Decl *D = Node.getAsDecl();
@@ -390,8 +400,8 @@ void TypeRefinementContext::print(raw_ostream &OS, SourceManager &SrcMgr,
390400
}
391401

392402
if (!ExplicitAvailabilityInfo.isAlwaysAvailable())
393-
OS << " explicit_versions="
394-
<< ExplicitAvailabilityInfo.getOSVersion().getAsString();
403+
OS << " explicit_version="
404+
<< stringForAvailability(ExplicitAvailabilityInfo);
395405

396406
for (TypeRefinementContext *Child : Children) {
397407
OS << '\n';

lib/ClangImporter/ImportDecl.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ static void applyAvailableAttribute(Decl *decl, AvailabilityContext &info,
540540
/*Message=*/StringRef(),
541541
/*Rename=*/StringRef(),
542542
/*RenameDecl=*/nullptr,
543-
info.getOSVersion().getLowerEndpoint(),
543+
info.getRawMinimumVersion(),
544544
/*IntroducedRange*/SourceRange(),
545545
/*Deprecated=*/noVersion,
546546
/*DeprecatedRange*/SourceRange(),
@@ -2803,13 +2803,13 @@ namespace {
28032803
if (auto classDecl = dyn_cast<ClassDecl>(result)) {
28042804
validateForeignReferenceType(decl, classDecl);
28052805

2806-
auto ctx = Impl.SwiftContext.getSwift58Availability();
2807-
if (!ctx.isAlwaysAvailable()) {
2808-
assert(ctx.getOSVersion().hasLowerEndpoint());
2806+
auto availability = Impl.SwiftContext.getSwift58Availability();
2807+
if (!availability.isAlwaysAvailable()) {
2808+
assert(availability.hasMinimumVersion());
28092809
auto AvAttr = new (Impl.SwiftContext) AvailableAttr(
28102810
SourceLoc(), SourceRange(),
28112811
targetPlatform(Impl.SwiftContext.LangOpts), "", "",
2812-
/*RenameDecl=*/nullptr, ctx.getOSVersion().getLowerEndpoint(),
2812+
/*RenameDecl=*/nullptr, availability.getRawMinimumVersion(),
28132813
/*IntroducedRange=*/SourceRange(), {},
28142814
/*DeprecatedRange=*/SourceRange(), {},
28152815
/*ObsoletedRange=*/SourceRange(),
@@ -6636,9 +6636,9 @@ bool SwiftDeclConverter::existingConstructorIsWorse(
66366636
if (!introduced.empty())
66376637
return false;
66386638
} else {
6639-
VersionRange existingIntroduced = existingAvailability.getOSVersion();
6640-
if (introduced != existingIntroduced.getLowerEndpoint()) {
6641-
return introduced < existingIntroduced.getLowerEndpoint();
6639+
auto existingIntroduced = existingAvailability.getRawMinimumVersion();
6640+
if (introduced != existingIntroduced) {
6641+
return introduced < existingIntroduced;
66426642
}
66436643
}
66446644

lib/IRGen/GenMeta.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5115,9 +5115,9 @@ diagnoseUnsupportedObjCImplLayout(IRGenModule &IGM, ClassDecl *classDecl,
51155115
diags.diagnose(
51165116
field.getVarDecl(),
51175117
diag::attr_objc_implementation_resilient_property_deployment_target,
5118-
prettyPlatformString(targetPlatform(ctx.LangOpts)),
5119-
currentAvailability.getOSVersion().getLowerEndpoint(),
5120-
requiredAvailability.getOSVersion().getLowerEndpoint());
5118+
ctx.getTargetPlatformStringForDiagnostics(),
5119+
currentAvailability.getRawMinimumVersion(),
5120+
requiredAvailability.getRawMinimumVersion());
51215121
else
51225122
diags.diagnose(
51235123
field.getVarDecl(),

lib/SIL/IR/SILPrinter.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3441,8 +3441,7 @@ void SILFunction::print(SILPrintContext &PrintCtx) const {
34413441
OS << "[weak_imported] ";
34423442
auto availability = getAvailabilityForLinkage();
34433443
if (!availability.isAlwaysAvailable()) {
3444-
auto version = availability.getOSVersion().getLowerEndpoint();
3445-
OS << "[available " << version.getAsString() << "] ";
3444+
OS << "[available " << availability.getVersionString() << "] ";
34463445
}
34473446

34483447
switch (getInlineStrategy()) {
@@ -4413,9 +4412,8 @@ void SILSpecializeAttr::print(llvm::raw_ostream &OS) const {
44134412
if (targetFunction) {
44144413
OS << "target: \"" << targetFunction->getName() << "\", ";
44154414
}
4416-
if (!availability.isAlwaysAvailable()) {
4417-
auto version = availability.getOSVersion().getLowerEndpoint();
4418-
OS << "available: " << version.getAsString() << ", ";
4415+
if (!availability.isAlwaysAvailable()) {
4416+
OS << "available: " << availability.getVersionString() << ", ";
44194417
}
44204418
if (!requirements.empty()) {
44214419
OS << "where ";

0 commit comments

Comments
 (0)