Skip to content

Commit c02dccf

Browse files
committed
Sema: Introduce UnmetAvailabilityRequirement.
This class is used to represent the reason a declaration is unavailable in a specific context.
1 parent e1adee1 commit c02dccf

File tree

2 files changed

+153
-11
lines changed

2 files changed

+153
-11
lines changed

lib/Sema/TypeCheckAvailability.cpp

+77-11
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,
@@ -3065,6 +3078,51 @@ bool diagnoseExplicitUnavailability(
30653078
return true;
30663079
}
30673080

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+
30683126
/// Check if this is a subscript declaration inside String or
30693127
/// Substring that returns String, and if so return true.
30703128
bool isSubscriptReturningString(const ValueDecl *D, ASTContext &Context) {
@@ -4057,8 +4115,20 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
40574115
return false;
40584116
}
40594117

4060-
if (diagnoseExplicitUnavailability(D, R, Where, call, Flags))
4061-
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+
}
40624132

40634133
if (diagnoseDeclAsyncAvailability(D, R, call, Where))
40644134
return true;
@@ -4079,25 +4149,21 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
40794149
return false;
40804150

40814151
// Diagnose (and possibly signal) for potential unavailability
4082-
auto maybeUnavail = TypeChecker::checkDeclarationAvailability(D, Where);
4083-
if (!maybeUnavail.has_value())
4152+
if (!requiredRange)
40844153
return false;
40854154

4086-
auto requiredAvailability = maybeUnavail.value();
4087-
auto *DC = Where.getDeclContext();
4088-
auto &ctx = DC->getASTContext();
40894155
if (Flags.contains(
40904156
DeclAvailabilityFlag::
40914157
AllowPotentiallyUnavailableAtOrBelowDeploymentTarget) &&
4092-
requiresDeploymentTargetOrEarlier(requiredAvailability, ctx))
4158+
requiresDeploymentTargetOrEarlier(*requiredRange, ctx))
40934159
return false;
40944160

40954161
if (accessor) {
40964162
bool forInout = Flags.contains(DeclAvailabilityFlag::ForInout);
4097-
diagnosePotentialAccessorUnavailability(accessor, R, DC,
4098-
requiredAvailability, forInout);
4163+
diagnosePotentialAccessorUnavailability(accessor, R, DC, *requiredRange,
4164+
forInout);
40994165
} else {
4100-
if (!diagnosePotentialUnavailability(D, R, DC, requiredAvailability))
4166+
if (!diagnosePotentialUnavailability(D, R, DC, *requiredRange))
41014167
return false;
41024168
}
41034169

lib/Sema/TypeCheckAvailability.h

+76
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,74 @@ class ExportContext {
200200
const AvailableAttr *shouldDiagnoseDeclAsUnavailable(const Decl *decl) const;
201201
};
202202

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+
203271
/// Check if a declaration is exported as part of a module's external interface.
204272
/// This includes public and @usableFromInline decls.
205273
bool isExported(const ValueDecl *VD);
@@ -250,6 +318,14 @@ bool diagnoseExplicitUnavailability(const ValueDecl *D, SourceRange R,
250318
const Expr *call,
251319
DeclAvailabilityFlags Flags = std::nullopt);
252320

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+
253329
/// Diagnose uses of the runtime support of the given type, such as
254330
/// type metadata and dynamic casting.
255331
///

0 commit comments

Comments
 (0)