Skip to content

Commit 5c7509b

Browse files
authored
Merge pull request swiftlang#77218 from tshortli/unmet-availability-requirement
2 parents 65f7c9e + c02dccf commit 5c7509b

File tree

4 files changed

+180
-23
lines changed

4 files changed

+180
-23
lines changed

include/swift/AST/Availability.h

+9-4
Original file line numberDiff line numberDiff line change
@@ -350,15 +350,20 @@ class AvailabilityInference {
350350

351351
static AvailabilityRange inferForType(Type t);
352352

353-
/// Returns the context where a declaration is available
354-
/// We assume a declaration without an annotation is always available.
353+
/// Returns the range of platform versions in which the decl is available.
355354
static AvailabilityRange availableRange(const Decl *D);
356355

356+
/// Returns the range of platform versions in which the decl is available and
357+
/// the attribute which determined this range (which may be `nullptr` if the
358+
/// declaration is always available.
359+
static std::pair<AvailabilityRange, const AvailableAttr *>
360+
availableRangeAndAttr(const Decl *D);
361+
357362
/// Returns true is the declaration is `@_spi_available`.
358363
static bool isAvailableAsSPI(const Decl *D);
359364

360-
/// Returns the availability context for a declaration with the given
361-
/// @available attribute.
365+
/// Returns the range of platform versions in which a declaration with the
366+
/// given `@available` attribute is available.
362367
///
363368
/// NOTE: The attribute must be active on the current platform.
364369
static AvailabilityRange availableRange(const AvailableAttr *attr,

lib/AST/Availability.cpp

+10-4
Original file line numberDiff line numberDiff line change
@@ -580,12 +580,18 @@ static const AvailableAttr *attrForAvailableRange(const Decl *D) {
580580
return nullptr;
581581
}
582582

583-
AvailabilityRange AvailabilityInference::availableRange(const Decl *D) {
584-
if (auto attr = attrForAvailableRange(D))
585-
return availableRange(attr, D->getASTContext());
583+
std::pair<AvailabilityRange, const AvailableAttr *>
584+
AvailabilityInference::availableRangeAndAttr(const Decl *D) {
585+
if (auto attr = attrForAvailableRange(D)) {
586+
return {availableRange(attr, D->getASTContext()), attr};
587+
}
586588

587589
// Treat unannotated declarations as always available.
588-
return AvailabilityRange::alwaysAvailable();
590+
return {AvailabilityRange::alwaysAvailable(), nullptr};
591+
}
592+
593+
AvailabilityRange AvailabilityInference::availableRange(const Decl *D) {
594+
return availableRangeAndAttr(D).first;
589595
}
590596

591597
bool AvailabilityInference::isAvailableAsSPI(const Decl *D) {

lib/Sema/TypeCheckAvailability.cpp

+83-15
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,19 @@ ExportContext::getExportabilityReason() const {
315315
return std::nullopt;
316316
}
317317

318+
std::optional<AvailabilityRange>
319+
UnmetAvailabilityRequirement::getRequiredNewerAvailabilityRange(
320+
ASTContext &ctx) const {
321+
switch (kind) {
322+
case Kind::AlwaysUnavailable:
323+
case Kind::RequiresVersion:
324+
case Kind::Obsoleted:
325+
return std::nullopt;
326+
case Kind::IntroducedInNewerVersion:
327+
return AvailabilityInference::availableRange(attr, ctx);
328+
}
329+
}
330+
318331
/// Returns the first availability attribute on the declaration that is active
319332
/// on the target platform.
320333
static const AvailableAttr *getActiveAvailableAttribute(const Decl *D,
@@ -345,8 +358,9 @@ static bool computeContainedByDeploymentTarget(TypeRefinementContext *TRC,
345358
/// Returns true if the reference or any of its parents is an
346359
/// unconditional unavailable declaration for the same platform.
347360
static bool isInsideCompatibleUnavailableDeclaration(
348-
const Decl *D, const ExportContext &where, const AvailableAttr *attr) {
349-
auto referencedPlatform = where.getUnavailablePlatformKind();
361+
const Decl *D, AvailabilityContext availabilityContext,
362+
const AvailableAttr *attr) {
363+
auto referencedPlatform = availabilityContext.getUnavailablePlatformKind();
350364
if (!referencedPlatform)
351365
return false;
352366

@@ -374,7 +388,7 @@ ExportContext::shouldDiagnoseDeclAsUnavailable(const Decl *D) const {
374388
if (!attr)
375389
return nullptr;
376390

377-
if (isInsideCompatibleUnavailableDeclaration(D, *this, attr))
391+
if (isInsideCompatibleUnavailableDeclaration(D, Availability, attr))
378392
return nullptr;
379393

380394
return attr;
@@ -1496,7 +1510,8 @@ TypeChecker::checkDeclarationAvailability(const Decl *D,
14961510
// Skip computing potential unavailability if the declaration is explicitly
14971511
// unavailable and the context is also unavailable.
14981512
if (const AvailableAttr *Attr = AvailableAttr::isUnavailable(D))
1499-
if (isInsideCompatibleUnavailableDeclaration(D, Where, Attr))
1513+
if (isInsideCompatibleUnavailableDeclaration(D, Where.getAvailability(),
1514+
Attr))
15001515
return std::nullopt;
15011516

15021517
if (isDeclarationUnavailable(D, Where.getDeclContext(), [&Where] {
@@ -3063,6 +3078,51 @@ bool diagnoseExplicitUnavailability(
30633078
return true;
30643079
}
30653080

3081+
std::optional<UnmetAvailabilityRequirement>
3082+
swift::checkDeclarationAvailability(const Decl *decl,
3083+
const DeclContext *declContext,
3084+
AvailabilityContext availabilityContext) {
3085+
auto &ctx = declContext->getASTContext();
3086+
if (ctx.LangOpts.DisableAvailabilityChecking)
3087+
return std::nullopt;
3088+
3089+
// Generic parameters are always available.
3090+
if (isa<GenericTypeParamDecl>(decl))
3091+
return std::nullopt;
3092+
3093+
if (auto attr = AvailableAttr::isUnavailable(decl)) {
3094+
if (isInsideCompatibleUnavailableDeclaration(decl, availabilityContext,
3095+
attr))
3096+
return std::nullopt;
3097+
3098+
switch (attr->getVersionAvailability(ctx)) {
3099+
case AvailableVersionComparison::Available:
3100+
case AvailableVersionComparison::PotentiallyUnavailable:
3101+
llvm_unreachable("Decl should be unavailable");
3102+
3103+
case AvailableVersionComparison::Unavailable:
3104+
if ((attr->isLanguageVersionSpecific() ||
3105+
attr->isPackageDescriptionVersionSpecific()) &&
3106+
attr->Introduced.has_value())
3107+
return UnmetAvailabilityRequirement::forRequiresVersion(attr);
3108+
3109+
return UnmetAvailabilityRequirement::forAlwaysUnavailable(attr);
3110+
3111+
case AvailableVersionComparison::Obsoleted:
3112+
return UnmetAvailabilityRequirement::forObsoleted(attr);
3113+
}
3114+
}
3115+
3116+
// Check whether the declaration is available in a newer platform version.
3117+
auto rangeAndAttr = AvailabilityInference::availableRangeAndAttr(decl);
3118+
if (!availabilityContext.getPlatformRange().isContainedIn(rangeAndAttr.first))
3119+
return UnmetAvailabilityRequirement::forIntroducedInNewerVersion(
3120+
rangeAndAttr.second);
3121+
3122+
return std::nullopt;
3123+
}
3124+
3125+
30663126
/// Check if this is a subscript declaration inside String or
30673127
/// Substring that returns String, and if so return true.
30683128
bool isSubscriptReturningString(const ValueDecl *D, ASTContext &Context) {
@@ -4055,8 +4115,20 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
40554115
return false;
40564116
}
40574117

4058-
if (diagnoseExplicitUnavailability(D, R, Where, call, Flags))
4059-
return true;
4118+
auto *DC = Where.getDeclContext();
4119+
auto &ctx = DC->getASTContext();
4120+
auto unmetRequirement =
4121+
checkDeclarationAvailability(D, DC, Where.getAvailability());
4122+
auto requiredRange =
4123+
unmetRequirement
4124+
? unmetRequirement->getRequiredNewerAvailabilityRange(ctx)
4125+
: std::nullopt;
4126+
4127+
if (unmetRequirement && !requiredRange) {
4128+
// FIXME: diagnoseExplicitUnavailability should take an unmet requirement
4129+
if (diagnoseExplicitUnavailability(D, R, Where, call, Flags))
4130+
return true;
4131+
}
40604132

40614133
if (diagnoseDeclAsyncAvailability(D, R, call, Where))
40624134
return true;
@@ -4077,25 +4149,21 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
40774149
return false;
40784150

40794151
// Diagnose (and possibly signal) for potential unavailability
4080-
auto maybeUnavail = TypeChecker::checkDeclarationAvailability(D, Where);
4081-
if (!maybeUnavail.has_value())
4152+
if (!requiredRange)
40824153
return false;
40834154

4084-
auto requiredAvailability = maybeUnavail.value();
4085-
auto *DC = Where.getDeclContext();
4086-
auto &ctx = DC->getASTContext();
40874155
if (Flags.contains(
40884156
DeclAvailabilityFlag::
40894157
AllowPotentiallyUnavailableAtOrBelowDeploymentTarget) &&
4090-
requiresDeploymentTargetOrEarlier(requiredAvailability, ctx))
4158+
requiresDeploymentTargetOrEarlier(*requiredRange, ctx))
40914159
return false;
40924160

40934161
if (accessor) {
40944162
bool forInout = Flags.contains(DeclAvailabilityFlag::ForInout);
4095-
diagnosePotentialAccessorUnavailability(accessor, R, DC,
4096-
requiredAvailability, forInout);
4163+
diagnosePotentialAccessorUnavailability(accessor, R, DC, *requiredRange,
4164+
forInout);
40974165
} else {
4098-
if (!diagnosePotentialUnavailability(D, R, DC, requiredAvailability))
4166+
if (!diagnosePotentialUnavailability(D, R, DC, *requiredRange))
40994167
return false;
41004168
}
41014169

lib/Sema/TypeCheckAvailability.h

+78
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ class ExportContext {
156156

157157
DeclContext *getDeclContext() const { return DC; }
158158

159+
AvailabilityContext getAvailability() const { return Availability; }
160+
159161
AvailabilityRange getAvailabilityRange() const {
160162
return Availability.getPlatformRange();
161163
}
@@ -198,6 +200,74 @@ class ExportContext {
198200
const AvailableAttr *shouldDiagnoseDeclAsUnavailable(const Decl *decl) const;
199201
};
200202

203+
/// Represents the reason a declaration is considered unavailable in a certain
204+
/// context.
205+
class UnmetAvailabilityRequirement {
206+
public:
207+
enum class Kind {
208+
/// The declaration is referenced in a context in which it is
209+
/// generally unavailable. For example, a reference to a declaration that is
210+
/// unavailable on macOS from a context that may execute on macOS has this
211+
/// unmet requirement.
212+
AlwaysUnavailable,
213+
214+
/// The declaration is referenced in a context in which it is considered
215+
/// obsolete. For example, a reference to a declaration that is obsolete in
216+
/// macOS 13 from a context that may execute on macOS 13 or later has this
217+
/// unmet requirement.
218+
Obsoleted,
219+
220+
/// The declaration is only available in a different version. For example,
221+
/// the declaration might only be introduced in the Swift 6 language mode
222+
/// while the module is being compiled in the Swift 5 language mode.
223+
RequiresVersion,
224+
225+
/// The declaration is referenced in a context that does not have an
226+
/// adequate minimum version constraint. For example, a reference to a
227+
/// declaration that is introduced in macOS 13 from a context that may
228+
/// execute on earlier versions of macOS has this unmet requirement. This
229+
/// kind of unmet requirement can be addressed by tightening the minimum
230+
/// version of the context with `if #available(...)` or by adding or
231+
/// adjusting an `@available` attribute.
232+
IntroducedInNewerVersion,
233+
};
234+
235+
private:
236+
Kind kind;
237+
const AvailableAttr *attr;
238+
239+
UnmetAvailabilityRequirement(Kind kind, const AvailableAttr *attr)
240+
: kind(kind), attr(attr){};
241+
242+
public:
243+
static UnmetAvailabilityRequirement
244+
forAlwaysUnavailable(const AvailableAttr *attr) {
245+
return UnmetAvailabilityRequirement(Kind::AlwaysUnavailable, attr);
246+
}
247+
248+
static UnmetAvailabilityRequirement forObsoleted(const AvailableAttr *attr) {
249+
return UnmetAvailabilityRequirement(Kind::Obsoleted, attr);
250+
}
251+
252+
static UnmetAvailabilityRequirement
253+
forRequiresVersion(const AvailableAttr *attr) {
254+
return UnmetAvailabilityRequirement(Kind::RequiresVersion, attr);
255+
}
256+
257+
static UnmetAvailabilityRequirement
258+
forIntroducedInNewerVersion(const AvailableAttr *attr) {
259+
return UnmetAvailabilityRequirement(Kind::IntroducedInNewerVersion, attr);
260+
}
261+
262+
Kind getKind() const { return kind; }
263+
const AvailableAttr *getAttr() const { return attr; }
264+
265+
/// Returns the required range for `IntroducedInNewerVersion` requirements, or
266+
/// `std::nullopt` otherwise.
267+
std::optional<AvailabilityRange>
268+
getRequiredNewerAvailabilityRange(ASTContext &ctx) const;
269+
};
270+
201271
/// Check if a declaration is exported as part of a module's external interface.
202272
/// This includes public and @usableFromInline decls.
203273
bool isExported(const ValueDecl *VD);
@@ -248,6 +318,14 @@ bool diagnoseExplicitUnavailability(const ValueDecl *D, SourceRange R,
248318
const Expr *call,
249319
DeclAvailabilityFlags Flags = std::nullopt);
250320

321+
/// Checks whether a declaration should be considered unavailable when referred
322+
/// to in the given declaration context and availability context and, if so,
323+
/// returns a result that describes the unmet availability requirements.
324+
/// Returns `std::nullopt` if the declaration is available.
325+
std::optional<UnmetAvailabilityRequirement>
326+
checkDeclarationAvailability(const Decl *decl, const DeclContext *declContext,
327+
AvailabilityContext availabilityContext);
328+
251329
/// Diagnose uses of the runtime support of the given type, such as
252330
/// type metadata and dynamic casting.
253331
///

0 commit comments

Comments
 (0)