Skip to content

Commit 9a5ccb6

Browse files
committed
AST: Store unavailability and deprecation status in AvailabilityContext.
1 parent 5b6b462 commit 9a5ccb6

File tree

4 files changed

+173
-8
lines changed

4 files changed

+173
-8
lines changed

include/swift/AST/Availability.h

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,18 @@ class AvailabilityContext : public llvm::FoldingSetNode {
345345
/// The introduction version.
346346
AvailabilityRange Range;
347347

348+
/// When `IsUnavailable` is true, this value stores the broadest platform
349+
/// kind for which the context is unavailable.
350+
PlatformKind UnavailablePlatform;
351+
352+
/// Whether or not the context is considered unavailable on the current
353+
/// platform.
354+
unsigned IsUnavailable : 1;
355+
356+
/// Whether or not the context is considered deprecated on the current
357+
/// platform.
358+
unsigned IsDeprecated : 1;
359+
348360
/// Sets `Range` to `other` if `other` is more restrictive. Returns true if
349361
/// any property changed as a result of adding this constraint.
350362
bool constrainRange(const AvailabilityRange &other) {
@@ -360,8 +372,23 @@ class AvailabilityContext : public llvm::FoldingSetNode {
360372
/// any property changed as a result of adding this constraint.
361373
bool constrainRange(const Decl *decl);
362374

375+
/// Updates `UnavailablePlatform` and `IsUnavailable` to reflect the status
376+
/// of `decl` if its platform unavailability is more restrictive. Returns
377+
/// true if any property changed as a result of adding this constraint.
378+
bool constrainUnavailability(const Decl *decl);
379+
380+
/// If `decl` is deprecated, sets `IsDeprecated` to true. Returns true if
381+
/// any property changed as a result of adding this constraint.
382+
bool constrainDeprecated(const Decl *decl);
383+
384+
/// Returns true if `other` is as available or is more available.
385+
bool isContainedIn(const PlatformInfo &other) const;
386+
363387
void Profile(llvm::FoldingSetNodeID &ID) const {
364388
Range.getRawVersionRange().Profile(ID);
389+
ID.AddBoolean(IsUnavailable);
390+
ID.AddInteger(static_cast<uint8_t>(UnavailablePlatform));
391+
ID.AddBoolean(IsDeprecated);
365392
}
366393
};
367394
PlatformInfo PlatformAvailability;
@@ -381,8 +408,14 @@ class AvailabilityContext : public llvm::FoldingSetNode {
381408
/// Retrieves a uniqued `AvailabilityContext` with the given platform
382409
/// availability parameters.
383410
static const AvailabilityContext *
384-
get(const AvailabilityRange &platformAvailability, ASTContext &ctx) {
385-
PlatformInfo platformInfo{platformAvailability};
411+
get(const AvailabilityRange &platformAvailability,
412+
std::optional<PlatformKind> unavailablePlatform, bool deprecated,
413+
ASTContext &ctx) {
414+
PlatformInfo platformInfo{platformAvailability,
415+
unavailablePlatform.has_value()
416+
? *unavailablePlatform
417+
: PlatformKind::none,
418+
unavailablePlatform.has_value(), deprecated};
386419
return get(platformInfo, ctx);
387420
}
388421

@@ -392,6 +425,18 @@ class AvailabilityContext : public llvm::FoldingSetNode {
392425
return PlatformAvailability.Range;
393426
}
394427

428+
/// When the context is unavailable on the current platform this returns the
429+
/// broadest `PlatformKind` for which the context is unavailable. Otherwise,
430+
/// returns `nullopt`.
431+
std::optional<PlatformKind> getUnavailablePlatformKind() const {
432+
if (PlatformAvailability.IsUnavailable)
433+
return PlatformAvailability.UnavailablePlatform;
434+
return std::nullopt;
435+
}
436+
437+
/// Returns true if this context is deprecated on the current platform.
438+
bool isDeprecated() const { return PlatformAvailability.IsDeprecated; }
439+
395440
/// Returns the unique context that is the result of constraining the current
396441
/// context's platform availability range with `platformRange`.
397442
const AvailabilityContext *
@@ -404,6 +449,12 @@ class AvailabilityContext : public llvm::FoldingSetNode {
404449
const AvailabilityContext *constrainWithDeclAndPlatformRange(
405450
Decl *decl, const AvailabilityRange &platformRange) const;
406451

452+
/// Returns true if `other` is as available or is more available.
453+
bool isContainedIn(const AvailabilityContext *other) const;
454+
455+
void print(llvm::raw_ostream &os) const;
456+
SWIFT_DEBUG_DUMP;
457+
407458
void Profile(llvm::FoldingSetNodeID &ID) const;
408459
};
409460

lib/AST/Availability.cpp

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,63 @@ bool AvailabilityContext::PlatformInfo::constrainRange(const Decl *decl) {
7171
return false;
7272
}
7373

74+
bool AvailabilityContext::PlatformInfo::constrainUnavailability(
75+
const Decl *decl) {
76+
auto &ctx = decl->getASTContext();
77+
auto *attr = decl->getAttrs().getUnavailable(ctx);
78+
if (!attr)
79+
return false;
80+
81+
// Check whether the decl's unavailability reason is the same.
82+
if (IsUnavailable && UnavailablePlatform == attr->Platform)
83+
return false;
84+
85+
// Check whether the decl's unavailability reason is more specific.
86+
if (attr->Platform != PlatformKind::none &&
87+
inheritsAvailabilityFromPlatform(attr->Platform, UnavailablePlatform))
88+
return false;
89+
90+
IsUnavailable = true;
91+
UnavailablePlatform = attr->Platform;
92+
return true;
93+
}
94+
95+
bool AvailabilityContext::PlatformInfo::constrainDeprecated(const Decl *decl) {
96+
auto &ctx = decl->getASTContext();
97+
if (IsDeprecated || !decl->getAttrs().isDeprecated(ctx))
98+
return false;
99+
100+
IsDeprecated = true;
101+
return true;
102+
}
103+
104+
bool AvailabilityContext::PlatformInfo::isContainedIn(
105+
const PlatformInfo &other) const {
106+
if (!Range.isContainedIn(other.Range))
107+
return false;
108+
109+
if (!IsUnavailable && other.IsUnavailable)
110+
return false;
111+
112+
if (IsUnavailable && other.IsUnavailable) {
113+
if (UnavailablePlatform != other.UnavailablePlatform &&
114+
UnavailablePlatform != PlatformKind::none &&
115+
!inheritsAvailabilityFromPlatform(UnavailablePlatform,
116+
other.UnavailablePlatform))
117+
return false;
118+
}
119+
120+
if (!IsDeprecated && other.IsDeprecated)
121+
return false;
122+
123+
return true;
124+
}
125+
74126
const AvailabilityContext *AvailabilityContext::getDefault(ASTContext &ctx) {
75-
PlatformInfo platformInfo{AvailabilityRange::forInliningTarget(ctx)};
127+
PlatformInfo platformInfo{AvailabilityRange::forInliningTarget(ctx),
128+
PlatformKind::none,
129+
/*IsUnavailable*/ false,
130+
/*IsDeprecated*/ false};
76131
return AvailabilityContext::get(platformInfo, ctx);
77132
}
78133

@@ -94,13 +149,45 @@ AvailabilityContext::constrainWithDeclAndPlatformRange(
94149
bool isConstrained = false;
95150
isConstrained |= platformAvailability.constrainRange(decl);
96151
isConstrained |= platformAvailability.constrainRange(platformRange);
152+
isConstrained |= platformAvailability.constrainUnavailability(decl);
153+
isConstrained |= platformAvailability.constrainDeprecated(decl);
97154

98155
if (!isConstrained)
99156
return this;
100157

101158
return get(platformAvailability, decl->getASTContext());
102159
}
103160

161+
bool AvailabilityContext::isContainedIn(
162+
const AvailabilityContext *other) const {
163+
if (!PlatformAvailability.isContainedIn(other->PlatformAvailability))
164+
return false;
165+
166+
return true;
167+
}
168+
169+
static std::string
170+
stringForAvailability(const AvailabilityRange &availability) {
171+
if (availability.isAlwaysAvailable())
172+
return "all";
173+
if (availability.isKnownUnreachable())
174+
return "none";
175+
176+
return availability.getVersionString();
177+
}
178+
179+
void AvailabilityContext::print(llvm::raw_ostream &os) const {
180+
os << "version=" << stringForAvailability(getPlatformRange());
181+
182+
if (auto unavailablePlatform = getUnavailablePlatformKind())
183+
os << " unavailable=" << platformString(*unavailablePlatform);
184+
185+
if (isDeprecated())
186+
os << " deprecated";
187+
}
188+
189+
void AvailabilityContext::dump() const { print(llvm::errs()); }
190+
104191
void AvailabilityContext::Profile(llvm::FoldingSetNodeID &id) const {
105192
PlatformAvailability.Profile(id);
106193
}

lib/AST/TypeRefinementContext.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ TypeRefinementContext::TypeRefinementContext(ASTContext &Ctx, IntroNode Node,
3636
if (Parent) {
3737
assert(SrcRange.isValid());
3838
Parent->addChild(this, Ctx);
39-
assert(Info->getPlatformRange().isContainedIn(
40-
Parent->getPlatformAvailabilityRange()));
39+
assert(Info->isContainedIn(Parent->getAvailabilityContext()));
4140
}
4241
Ctx.addDestructorCleanup(Children);
4342
}
@@ -400,7 +399,8 @@ void TypeRefinementContext::print(raw_ostream &OS, SourceManager &SrcMgr,
400399
OS.indent(Indent);
401400
OS << "(" << getReasonName(getReason());
402401

403-
OS << " version=" << stringForAvailability(getPlatformAvailabilityRange());
402+
OS << " ";
403+
AvailabilityInfo->print(OS);
404404

405405
if (getReason() == Reason::Decl || getReason() == Reason::DeclImplicit) {
406406
Decl *D = Node.getAsDecl();
@@ -581,8 +581,8 @@ void TypeRefinementContext::verify(const TypeRefinementContext *parent,
581581
{{"child", this}, {"parent", parent}});
582582
}
583583

584-
if (!getPlatformAvailabilityRange().isContainedIn(
585-
parent->getPlatformAvailabilityRange()))
584+
if (!getAvailabilityContext()->isContainedIn(
585+
parent->getAvailabilityContext()))
586586
verificationError(ctx, "child availability range not contained",
587587
{{"child", this}, {"parent", parent}});
588588
}

test/Sema/availability_refinement_contexts.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,33 @@ func testStringInterpolation() {
311311
"""
312312
}
313313

314+
// CHECK-NEXT: {{^}} (decl_implicit version=50 unavailable=macOS decl=unavailableOnMacOS()
315+
// CHECK-NEXT: {{^}} (decl_implicit version=50 unavailable=macOS decl=x
316+
317+
@available(macOS, unavailable)
318+
func unavailableOnMacOS() {
319+
let x = 1
320+
}
321+
322+
// CHECK-NEXT: {{^}} (decl_implicit version=50 deprecated decl=deprecatedOnMacOS()
323+
// CHECK-NEXT: {{^}} (decl_implicit version=50 deprecated decl=x
324+
325+
@available(macOS, deprecated)
326+
func deprecatedOnMacOS() {
327+
let x = 1
328+
}
329+
330+
// CHECK-NEXT: {{^}} (decl_implicit version=50 decl=extension.SomeEnum
331+
// CHECK-NEXT: {{^}} (decl_implicit version=50 unavailable=macOS decl=extension.SomeEnum
332+
// CHECK-NEXT: {{^}} (decl_implicit version=50 unavailable=macOS decl=propertyInUnavailableExtension
333+
// CHECK-NEXT: {{^}} (decl version=52 unavailable=macOS decl=propertyInUnavailableExtension
334+
335+
@available(macOS, unavailable)
336+
extension SomeEnum {
337+
@available(OSX 52, *)
338+
var propertyInUnavailableExtension: Int { 1 }
339+
}
340+
314341
// CHECK-NEXT: {{^}} (decl version=51 decl=FinalDecl
315342

316343
@available(OSX 51, *)

0 commit comments

Comments
 (0)