Skip to content

Commit cb778b7

Browse files
committed
AST/Sema: Expand potential unavailability diagnostics to arbitrary domains.
When emitting potential unavailability diagnostics, don't assume that they can only apply to the target platform `AvailabilityDomain`.
1 parent 375bc35 commit cb778b7

File tree

5 files changed

+80
-66
lines changed

5 files changed

+80
-66
lines changed

include/swift/AST/AvailabilityConstraint.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,10 @@ class AvailabilityConstraint {
132132
/// Returns the domain that the constraint applies to.
133133
AvailabilityDomain getDomain() const { return getAttr().getDomain(); }
134134

135-
/// Returns the platform that this constraint applies to, or
136-
/// `PlatformKind::none` if it is not platform specific.
137-
PlatformKind getPlatform() const;
138-
139135
/// Returns the required range for `IntroducedInNewerVersion` requirements, or
140136
/// `std::nullopt` otherwise.
141137
std::optional<AvailabilityRange>
142-
getRequiredNewerAvailabilityRange(const ASTContext &ctx) const;
138+
getPotentiallyUnavailableRange(const ASTContext &ctx) const;
143139

144140
/// Some availability constraints are active for type-checking but cannot
145141
/// be translated directly into an `if #available(...)` runtime query.

lib/AST/AvailabilityConstraint.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,8 @@
1818

1919
using namespace swift;
2020

21-
PlatformKind AvailabilityConstraint::getPlatform() const {
22-
return getAttr().getPlatform();
23-
}
24-
2521
std::optional<AvailabilityRange>
26-
AvailabilityConstraint::getRequiredNewerAvailabilityRange(
22+
AvailabilityConstraint::getPotentiallyUnavailableRange(
2723
const ASTContext &ctx) const {
2824
switch (getReason()) {
2925
case Reason::UnconditionallyUnavailable:

lib/Sema/DerivedConformanceRawRepresentable.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -258,15 +258,18 @@ checkAvailability(const EnumElementDecl *elt,
258258
if (!constraint->isActiveForRuntimeQueries(C))
259259
return true;
260260

261-
// It's conditionally available; create a version constraint and return true.
262-
auto platform = constraint->getPlatform();
263-
auto range = constraint->getRequiredNewerAvailabilityRange(C);
261+
auto domain = constraint->getDomain();
264262

265263
// Only platform version constraints are supported currently.
266-
ASSERT(platform != PlatformKind::none);
267-
ASSERT(range);
264+
// FIXME: [availability] Support non-platform domain availability checks
265+
if (!domain.isPlatform())
266+
return true;
268267

269-
versionCheck.emplace(platform, range->getRawMinimumVersion());
268+
// It's conditionally available; create a version constraint and return true.
269+
auto range = constraint->getPotentiallyUnavailableRange(C);
270+
271+
ASSERT(range);
272+
versionCheck.emplace(domain.getPlatformKind(), range->getRawMinimumVersion());
270273
return true;
271274
}
272275

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1953,10 +1953,9 @@ static void findAvailabilityFixItNodes(
19531953

19541954
/// Emit a diagnostic note and Fix-It to add an @available attribute
19551955
/// on the given declaration for the given version range.
1956-
static void
1957-
fixAvailabilityForDecl(SourceRange ReferenceRange, const Decl *D,
1958-
const AvailabilityRange &RequiredAvailability,
1959-
ASTContext &Context) {
1956+
static void fixAvailabilityForDecl(
1957+
SourceRange ReferenceRange, const Decl *D, AvailabilityDomain Domain,
1958+
const AvailabilityRange &RequiredAvailability, ASTContext &Context) {
19601959
assert(D);
19611960

19621961
// Don't suggest adding an @available() to a declaration where we would
@@ -1989,11 +1988,10 @@ fixAvailabilityForDecl(SourceRange ReferenceRange, const Decl *D,
19891988

19901989
StringRef OriginalIndent =
19911990
Lexer::getIndentationForLine(Context.SourceMgr, InsertLoc);
1992-
PlatformKind Target = targetPlatform(Context.LangOpts);
19931991

19941992
D->diagnose(diag::availability_add_attribute, KindForDiagnostic)
19951993
.fixItInsert(InsertLoc, diag::insert_available_attr,
1996-
platformString(Target),
1994+
Domain.getNameForAttributePrinting(),
19971995
RequiredAvailability.getVersionString(), OriginalIndent);
19981996
}
19991997

@@ -2004,14 +2002,19 @@ fixAvailabilityForDecl(SourceRange ReferenceRange, const Decl *D,
20042002
/// condition to the required range.
20052003
static bool fixAvailabilityByNarrowingNearbyVersionCheck(
20062004
SourceRange ReferenceRange, const DeclContext *ReferenceDC,
2007-
const AvailabilityRange &RequiredAvailability, ASTContext &Context,
2008-
InFlightDiagnostic &Err) {
2005+
AvailabilityDomain Domain, const AvailabilityRange &RequiredAvailability,
2006+
ASTContext &Context, InFlightDiagnostic &Err) {
2007+
// FIXME: [availability] Support fixing availability for non-platform domains
2008+
if (!Domain.isPlatform())
2009+
return false;
2010+
20092011
const AvailabilityScope *scope = nullptr;
20102012
(void)TypeChecker::overApproximateAvailabilityAtLocation(ReferenceRange.Start,
20112013
ReferenceDC, &scope);
20122014
if (!scope)
20132015
return false;
20142016

2017+
// FIXME: [availability] Support fixing availability for versionless domains.
20152018
auto ExplicitAvailability = scope->getExplicitAvailabilityRange();
20162019
if (ExplicitAvailability && !RequiredAvailability.isAlwaysAvailable() &&
20172020
scope->getReason() != AvailabilityScope::Reason::Root &&
@@ -2123,11 +2126,16 @@ static void fixAvailabilityByAddingVersionCheck(
21232126
/// requiting the given OS version range.
21242127
static void fixAvailability(SourceRange ReferenceRange,
21252128
const DeclContext *ReferenceDC,
2129+
AvailabilityDomain Domain,
21262130
const AvailabilityRange &RequiredAvailability,
21272131
ASTContext &Context) {
21282132
if (ReferenceRange.isInvalid())
21292133
return;
21302134

2135+
// FIXME: [availability] Support non-platform domains.
2136+
if (!Domain.isPlatform())
2137+
return;
2138+
21312139
std::optional<ASTNode> NodeToWrapInVersionCheck;
21322140
const Decl *FoundMemberDecl = nullptr;
21332141
const Decl *FoundTypeLevelDecl = nullptr;
@@ -2145,12 +2153,12 @@ static void fixAvailability(SourceRange ReferenceRange,
21452153

21462154
// Suggest adding availability attributes.
21472155
if (FoundMemberDecl) {
2148-
fixAvailabilityForDecl(ReferenceRange, FoundMemberDecl,
2156+
fixAvailabilityForDecl(ReferenceRange, FoundMemberDecl, Domain,
21492157
RequiredAvailability, Context);
21502158
}
21512159

21522160
if (FoundTypeLevelDecl) {
2153-
fixAvailabilityForDecl(ReferenceRange, FoundTypeLevelDecl,
2161+
fixAvailabilityForDecl(ReferenceRange, FoundTypeLevelDecl, Domain,
21542162
RequiredAvailability, Context);
21552163
}
21562164
}
@@ -2160,19 +2168,19 @@ static void diagnosePotentialUnavailability(
21602168
llvm::function_ref<InFlightDiagnostic(AvailabilityDomain,
21612169
llvm::VersionTuple)>
21622170
Diagnose,
2163-
const DeclContext *ReferenceDC, const AvailabilityRange &Availability) {
2171+
const DeclContext *ReferenceDC, AvailabilityDomain Domain,
2172+
const AvailabilityRange &Availability) {
21642173
ASTContext &Context = ReferenceDC->getASTContext();
21652174

21662175
{
2167-
auto Err = Diagnose(Context.getTargetAvailabilityDomain(),
2168-
Availability.getRawMinimumVersion());
2176+
auto Err = Diagnose(Domain, Availability.getRawMinimumVersion());
21692177

21702178
// Direct a fixit to the error if an existing guard is nearly-correct
21712179
if (fixAvailabilityByNarrowingNearbyVersionCheck(
2172-
ReferenceRange, ReferenceDC, Availability, Context, Err))
2180+
ReferenceRange, ReferenceDC, Domain, Availability, Context, Err))
21732181
return;
21742182
}
2175-
fixAvailability(ReferenceRange, ReferenceDC, Availability, Context);
2183+
fixAvailability(ReferenceRange, ReferenceDC, Domain, Availability, Context);
21762184
}
21772185

21782186
bool TypeChecker::checkAvailability(SourceRange ReferenceRange,
@@ -2185,12 +2193,16 @@ bool TypeChecker::checkAvailability(SourceRange ReferenceRange,
21852193
if (ctx.LangOpts.DisableAvailabilityChecking)
21862194
return false;
21872195

2196+
auto domain = ctx.getTargetAvailabilityDomain();
2197+
if (domain.isUniversal())
2198+
return false;
2199+
21882200
auto availabilityAtLocation =
21892201
TypeChecker::overApproximateAvailabilityAtLocation(ReferenceRange.Start,
21902202
ReferenceDC);
21912203
if (!availabilityAtLocation.isContainedIn(RequiredAvailability)) {
21922204
diagnosePotentialUnavailability(ReferenceRange, Diagnose, ReferenceDC,
2193-
RequiredAvailability);
2205+
domain, RequiredAvailability);
21942206
return true;
21952207
}
21962208

@@ -2219,22 +2231,28 @@ void TypeChecker::checkConcurrencyAvailability(SourceRange ReferenceRange,
22192231
}
22202232

22212233
static bool
2222-
requiresDeploymentTargetOrEarlier(const AvailabilityRange &availability,
2234+
requiresDeploymentTargetOrEarlier(AvailabilityDomain domain,
2235+
const AvailabilityRange &availability,
22232236
ASTContext &ctx) {
2224-
auto deploymentTarget = AvailabilityRange::forDeploymentTarget(ctx);
2225-
return deploymentTarget.isContainedIn(availability);
2237+
if (auto deploymentRange = domain.getDeploymentRange(ctx))
2238+
return deploymentRange->isContainedIn(availability);
2239+
return false;
22262240
}
22272241

22282242
/// Returns the diagnostic to emit for the potentially unavailable decl and sets
22292243
/// \p IsError accordingly.
22302244
static Diagnostic getPotentialUnavailabilityDiagnostic(
22312245
const ValueDecl *D, const DeclContext *ReferenceDC,
2232-
const AvailabilityRange &Availability, bool WarnBeforeDeploymentTarget,
2233-
bool &IsError) {
2246+
AvailabilityDomain Domain, const AvailabilityRange &Availability,
2247+
bool WarnBeforeDeploymentTarget, bool &IsError) {
22342248
ASTContext &Context = ReferenceDC->getASTContext();
2235-
auto Domain = Context.getTargetAvailabilityDomain();
22362249

2237-
if (requiresDeploymentTargetOrEarlier(Availability, Context)) {
2250+
if (!Availability.hasMinimumVersion()) {
2251+
return Diagnostic(diag::availability_decl_only_version_newer, D, Domain,
2252+
{});
2253+
}
2254+
2255+
if (requiresDeploymentTargetOrEarlier(Domain, Availability, Context)) {
22382256
// The required OS version is at or before the deployment target so this
22392257
// diagnostic should indicate that the decl could be unavailable to clients
22402258
// of the module containing the reference.
@@ -2258,6 +2276,7 @@ static Diagnostic getPotentialUnavailabilityDiagnostic(
22582276
static bool
22592277
diagnosePotentialUnavailability(const ValueDecl *D, SourceRange ReferenceRange,
22602278
const DeclContext *ReferenceDC,
2279+
AvailabilityDomain Domain,
22612280
const AvailabilityRange &Availability,
22622281
bool WarnBeforeDeploymentTarget = false) {
22632282
ASTContext &Context = ReferenceDC->getASTContext();
@@ -2267,26 +2286,26 @@ diagnosePotentialUnavailability(const ValueDecl *D, SourceRange ReferenceRange,
22672286
bool IsError;
22682287
{
22692288
auto Diag = Context.Diags.diagnose(
2270-
ReferenceRange.Start,
2271-
getPotentialUnavailabilityDiagnostic(
2272-
D, ReferenceDC, Availability, WarnBeforeDeploymentTarget, IsError));
2289+
ReferenceRange.Start, getPotentialUnavailabilityDiagnostic(
2290+
D, ReferenceDC, Domain, Availability,
2291+
WarnBeforeDeploymentTarget, IsError));
22732292

22742293
// Direct a fixit to the error if an existing guard is nearly-correct
22752294
if (fixAvailabilityByNarrowingNearbyVersionCheck(
2276-
ReferenceRange, ReferenceDC, Availability, Context, Diag))
2295+
ReferenceRange, ReferenceDC, Domain, Availability, Context, Diag))
22772296
return IsError;
22782297
}
22792298

2280-
fixAvailability(ReferenceRange, ReferenceDC, Availability, Context);
2299+
fixAvailability(ReferenceRange, ReferenceDC, Domain, Availability, Context);
22812300
return IsError;
22822301
}
22832302

22842303
/// Emits a diagnostic for a reference to a storage accessor that is
22852304
/// potentially unavailable.
22862305
static void diagnosePotentialAccessorUnavailability(
22872306
const AccessorDecl *Accessor, SourceRange ReferenceRange,
2288-
const DeclContext *ReferenceDC, const AvailabilityRange &Availability,
2289-
bool ForInout) {
2307+
const DeclContext *ReferenceDC, AvailabilityDomain Domain,
2308+
const AvailabilityRange &Availability, bool ForInout) {
22902309
ASTContext &Context = ReferenceDC->getASTContext();
22912310

22922311
assert(Accessor->isGetterOrSetter());
@@ -2301,11 +2320,11 @@ static void diagnosePotentialAccessorUnavailability(
23012320

23022321
// Direct a fixit to the error if an existing guard is nearly-correct
23032322
if (fixAvailabilityByNarrowingNearbyVersionCheck(
2304-
ReferenceRange, ReferenceDC, Availability, Context, Err))
2323+
ReferenceRange, ReferenceDC, Domain, Availability, Context, Err))
23052324
return;
23062325
}
23072326

2308-
fixAvailability(ReferenceRange, ReferenceDC, Availability, Context);
2327+
fixAvailability(ReferenceRange, ReferenceDC, Domain, Availability, Context);
23092328
}
23102329

23112330
static DiagnosticBehavior
@@ -2329,11 +2348,10 @@ behaviorLimitForExplicitUnavailability(
23292348

23302349
/// Emits a diagnostic for a protocol conformance that is potentially
23312350
/// unavailable at the given source location.
2332-
static bool
2333-
diagnosePotentialUnavailability(const RootProtocolConformance *rootConf,
2334-
const ExtensionDecl *ext, SourceLoc loc,
2335-
const DeclContext *dc,
2336-
const AvailabilityRange &availability) {
2351+
static bool diagnosePotentialUnavailability(
2352+
const RootProtocolConformance *rootConf, const ExtensionDecl *ext,
2353+
SourceLoc loc, const DeclContext *dc, AvailabilityDomain domain,
2354+
const AvailabilityRange &availability) {
23372355
ASTContext &ctx = dc->getASTContext();
23382356
if (ctx.LangOpts.DisableAvailabilityChecking)
23392357
return false;
@@ -2343,7 +2361,7 @@ diagnosePotentialUnavailability(const RootProtocolConformance *rootConf,
23432361
auto proto = rootConf->getProtocol()->getDeclaredInterfaceType();
23442362
auto err = ctx.Diags.diagnose(
23452363
loc, diag::conformance_availability_only_version_newer, type, proto,
2346-
ctx.getTargetAvailabilityDomain(), availability.getRawMinimumVersion());
2364+
domain, availability.getRawMinimumVersion());
23472365

23482366
auto behaviorLimit = behaviorLimitForExplicitUnavailability(rootConf, dc);
23492367
if (behaviorLimit >= DiagnosticBehavior::Warning)
@@ -2352,12 +2370,12 @@ diagnosePotentialUnavailability(const RootProtocolConformance *rootConf,
23522370
err.warnUntilSwiftVersion(6);
23532371

23542372
// Direct a fixit to the error if an existing guard is nearly-correct
2355-
if (fixAvailabilityByNarrowingNearbyVersionCheck(loc, dc, availability, ctx,
2356-
err))
2373+
if (fixAvailabilityByNarrowingNearbyVersionCheck(loc, dc, domain,
2374+
availability, ctx, err))
23572375
return true;
23582376
}
23592377

2360-
fixAvailability(loc, dc, availability, ctx);
2378+
fixAvailability(loc, dc, domain, availability, ctx);
23612379
return true;
23622380
}
23632381

@@ -4176,24 +4194,24 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
41764194
if (!constraint)
41774195
return false;
41784196

4179-
auto requiredRange = constraint->getRequiredNewerAvailabilityRange(ctx);
4180-
41814197
// Diagnose (and possibly signal) for potential unavailability
4198+
auto domain = constraint->getDomain();
4199+
auto requiredRange = constraint->getPotentiallyUnavailableRange(ctx);
41824200
if (!requiredRange)
41834201
return false;
41844202

41854203
if (Flags.contains(
41864204
DeclAvailabilityFlag::
41874205
AllowPotentiallyUnavailableAtOrBelowDeploymentTarget) &&
4188-
requiresDeploymentTargetOrEarlier(*requiredRange, ctx))
4206+
requiresDeploymentTargetOrEarlier(domain, *requiredRange, ctx))
41894207
return false;
41904208

41914209
if (accessor) {
41924210
bool forInout = Flags.contains(DeclAvailabilityFlag::ForInout);
4193-
diagnosePotentialAccessorUnavailability(accessor, R, DC, *requiredRange,
4194-
forInout);
4211+
diagnosePotentialAccessorUnavailability(accessor, R, DC, domain,
4212+
*requiredRange, forInout);
41954213
} else {
4196-
if (!diagnosePotentialUnavailability(D, R, DC, *requiredRange))
4214+
if (!diagnosePotentialUnavailability(D, R, DC, domain, *requiredRange))
41974215
return false;
41984216
}
41994217

@@ -4680,8 +4698,9 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
46804698

46814699
// Diagnose (and possibly signal) for potential unavailability
46824700
if (auto requiredRange =
4683-
constraint->getRequiredNewerAvailabilityRange(ctx)) {
4701+
constraint->getPotentiallyUnavailableRange(ctx)) {
46844702
if (diagnosePotentialUnavailability(rootConf, ext, loc, DC,
4703+
constraint->getDomain(),
46854704
*requiredRange)) {
46864705
maybeEmitAssociatedTypeNote();
46874706
return true;

test/Parse/diagnose_availability_windows.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ unavailable()
1111
@available(Windows, introduced: 10.0.17763, deprecated: 10.0.19140)
1212
func introduced_deprecated() {}
1313

14-
// expected-error@+1 {{'introduced_deprecated()' is only available in * 10.0.17763 or newe}}
14+
// expected-error@+1 {{'introduced_deprecated()' is only available in Windows 10.0.17763 or newer}}
1515
introduced_deprecated()
1616
// expected-note@-1 {{add 'if #available' version check}}
1717

1818
@available(Windows 10, *)
1919
func windows10() {}
2020

21-
// expected-error@+1 {{'windows10()' is only available in * 10 or newer}}
21+
// expected-error@+1 {{'windows10()' is only available in Windows 10 or newer}}
2222
windows10()
2323
// expected-note@-1 {{add 'if #available' version check}}
2424

0 commit comments

Comments
 (0)