Skip to content

Commit 5d98ca6

Browse files
authored
Merge pull request #83168 from tshortli/custom-availability-domain-fix-its
AST/Sema: Generalize availability fix-its to support custom availability domains
2 parents cf50dd0 + 5601e66 commit 5d98ca6

17 files changed

+248
-123
lines changed

include/swift/AST/AvailabilityConstraint.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,21 @@ enum class AvailabilityConstraintFlag : uint8_t {
188188
};
189189
using AvailabilityConstraintFlags = OptionSet<AvailabilityConstraintFlag>;
190190

191-
/// Returns the set of availability constraints that restrict use of \p decl
191+
/// Returns the set of availability constraints that restricts use of \p decl
192192
/// when it is referenced from the given context. In other words, it is the
193-
/// collection of of `@available` attributes with unsatisfied conditions.
193+
/// collection of `@available` attributes with unsatisfied conditions.
194194
DeclAvailabilityConstraints getAvailabilityConstraintsForDecl(
195195
const Decl *decl, const AvailabilityContext &context,
196196
AvailabilityConstraintFlags flags = std::nullopt);
197+
198+
/// Returns the availability constraints that restricts use of \p decl
199+
/// in \p domain when it is referenced from the given context. In other words,
200+
/// it is the unsatisfied `@available` attribute that applies to \p domain in
201+
/// the given context.
202+
std::optional<AvailabilityConstraint> getAvailabilityConstraintForDeclInDomain(
203+
const Decl *decl, const AvailabilityContext &context,
204+
AvailabilityDomain domain,
205+
AvailabilityConstraintFlags flags = std::nullopt);
197206
} // end namespace swift
198207

199208
namespace llvm {

include/swift/AST/AvailabilityDomain.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,12 @@ class AvailabilityDomain final {
260260
/// universal domain (`*`) is the bottom element.
261261
bool contains(const AvailabilityDomain &other) const;
262262

263+
/// Returns true if availability in `other` is a subset of availability in
264+
/// this domain or vice-versa.
265+
bool isRelated(const AvailabilityDomain &other) const {
266+
return contains(other) || other.contains(*this);
267+
}
268+
263269
/// Returns true for domains that are not contained by any domain other than
264270
/// the universal domain.
265271
bool isRoot() const;

include/swift/AST/AvailabilityScope.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,11 @@ class AvailabilityScope : public ASTAllocated<AvailabilityScope> {
272272
AvailabilityDomain Domain, const llvm::VersionTuple &Version) const;
273273

274274
/// Returns the availability version range that was explicitly written in
275-
/// source, if applicable. Otherwise, returns null.
276-
std::optional<const AvailabilityRange> getExplicitAvailabilityRange() const;
275+
/// source for the given domain, if applicable. Otherwise, returns
276+
/// `std::nullopt`.
277+
std::optional<const AvailabilityRange>
278+
getExplicitAvailabilityRange(AvailabilityDomain Domain,
279+
ASTContext &Ctx) const;
277280

278281
/// Returns the source range this scope represents.
279282
SourceRange getSourceRange() const { return SrcRange; }

include/swift/AST/Decl.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,6 +1464,27 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
14641464
std::optional<SemanticAvailableAttr>
14651465
getAvailableAttrForPlatformIntroduction(bool checkExtension = true) const;
14661466

1467+
/// Returns true if `decl` has any active `@available` attribute attached to
1468+
/// it.
1469+
bool hasAnyActiveAvailableAttr() const {
1470+
return hasAnyMatchingActiveAvailableAttr(
1471+
[](SemanticAvailableAttr attr) -> bool { return true; });
1472+
}
1473+
1474+
/// Returns true if `predicate` returns true for any active availability
1475+
/// attribute attached to `decl`. The predicate function should accept a
1476+
/// `SemanticAvailableAttr`.
1477+
template <typename F>
1478+
bool hasAnyMatchingActiveAvailableAttr(F predicate) const {
1479+
auto &ctx = getASTContext();
1480+
auto decl = getAbstractSyntaxDeclForAttributes();
1481+
for (auto attr : decl->getSemanticAvailableAttrs()) {
1482+
if (attr.isActive(ctx) && predicate(attr))
1483+
return true;
1484+
}
1485+
return false;
1486+
}
1487+
14671488
/// Returns true if the declaration is deprecated at the current deployment
14681489
/// target.
14691490
bool isDeprecated() const { return getDeprecatedAttr().has_value(); }

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7070,9 +7070,6 @@ NOTE(availability_guard_with_version_check, none,
70707070

70717071
NOTE(availability_add_attribute, none,
70727072
"add '@available' attribute to enclosing %kindonly0", (const Decl *))
7073-
FIXIT(insert_available_attr,
7074-
"@available(%0 %1, *)\n%2",
7075-
(StringRef, StringRef, StringRef))
70767073

70777074
ERROR(availability_inout_accessor_only_in, none,
70787075
"cannot pass as inout because %0 is only available in %1"

lib/AST/AvailabilityConstraint.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,3 +331,15 @@ swift::getAvailabilityConstraintsForDecl(const Decl *decl,
331331
return constraints;
332332
}
333333

334+
std::optional<AvailabilityConstraint>
335+
swift::getAvailabilityConstraintForDeclInDomain(
336+
const Decl *decl, const AvailabilityContext &context,
337+
AvailabilityDomain domain, AvailabilityConstraintFlags flags) {
338+
auto constraints = getAvailabilityConstraintsForDecl(decl, context, flags);
339+
for (auto const &constraint : constraints) {
340+
if (constraint.getDomain().isRelated(domain))
341+
return constraint;
342+
}
343+
344+
return std::nullopt;
345+
}

lib/AST/AvailabilityScope.cpp

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/AST/AvailabilityScope.h"
1818

1919
#include "swift/AST/ASTContext.h"
20+
#include "swift/AST/AvailabilityConstraint.h"
2021
#include "swift/AST/AvailabilityInference.h"
2122
#include "swift/AST/AvailabilitySpec.h"
2223
#include "swift/AST/Decl.h"
@@ -356,15 +357,17 @@ SourceRange AvailabilityScope::getAvailabilityConditionVersionSourceRange(
356357
}
357358

358359
std::optional<const AvailabilityRange>
359-
AvailabilityScope::getExplicitAvailabilityRange() const {
360+
AvailabilityScope::getExplicitAvailabilityRange(AvailabilityDomain domain,
361+
ASTContext &ctx) const {
360362
switch (getReason()) {
361363
case Reason::Root:
362364
return std::nullopt;
363365

364366
case Reason::Decl: {
365367
auto decl = Node.getAsDecl();
366-
if (auto attr = decl->getAvailableAttrForPlatformIntroduction())
367-
return attr->getIntroducedRange(decl->getASTContext());
368+
if (auto constraint = swift::getAvailabilityConstraintForDeclInDomain(
369+
decl, AvailabilityContext::forAlwaysAvailable(ctx), domain))
370+
return constraint->getAttr().getIntroducedRange(ctx);
368371

369372
return std::nullopt;
370373
}
@@ -379,22 +382,12 @@ AvailabilityScope::getExplicitAvailabilityRange() const {
379382
case Reason::GuardStmtElseBranch:
380383
case Reason::WhileStmtBody:
381384
// Availability is inherently explicit for all of these nodes.
382-
return getPlatformAvailabilityRange();
385+
return getAvailabilityContext().getAvailabilityRange(domain, ctx);
383386
}
384387

385388
llvm_unreachable("Unhandled Reason in switch.");
386389
}
387390

388-
static std::string
389-
stringForAvailability(const AvailabilityRange &availability) {
390-
if (availability.isAlwaysAvailable())
391-
return "all";
392-
if (availability.isKnownUnreachable())
393-
return "none";
394-
395-
return availability.getVersionString();
396-
}
397-
398391
void AvailabilityScope::print(raw_ostream &OS, SourceManager &SrcMgr,
399392
unsigned Indent) const {
400393
OS.indent(Indent);
@@ -446,9 +439,6 @@ void AvailabilityScope::print(raw_ostream &OS, SourceManager &SrcMgr,
446439
}
447440
}
448441

449-
if (auto explicitAvailability = getExplicitAvailabilityRange())
450-
OS << " explicit_version=" << stringForAvailability(*explicitAvailability);
451-
452442
for (AvailabilityScope *Child : Children) {
453443
OS << '\n';
454444
Child->print(OS, SrcMgr, Indent + 2);

lib/AST/AvailabilityScopeBuilder.cpp

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,6 @@
3030

3131
using namespace swift;
3232

33-
/// Returns true if there is any availability attribute on the declaration
34-
/// that is active.
35-
// FIXME: [availability] De-duplicate this with TypeCheckAvailability.cpp.
36-
static bool hasActiveAvailableAttribute(const Decl *decl, ASTContext &ctx) {
37-
decl = decl->getAbstractSyntaxDeclForAttributes();
38-
39-
for (auto attr : decl->getSemanticAvailableAttrs()) {
40-
if (attr.isActive(ctx))
41-
return true;
42-
}
43-
44-
return false;
45-
}
46-
4733
static bool computeContainedByDeploymentTarget(AvailabilityScope *scope,
4834
ASTContext &ctx) {
4935
return scope->getPlatformAvailabilityRange().isContainedIn(
@@ -397,7 +383,7 @@ class AvailabilityScopeBuilder : private ASTWalker {
397383
return nullptr;
398384

399385
// Declarations with explicit availability attributes always get a scope.
400-
if (hasActiveAvailableAttribute(decl, Context)) {
386+
if (decl->hasAnyActiveAvailableAttr()) {
401387
return AvailabilityScope::createForDecl(
402388
Context, decl, getCurrentScope(),
403389
getEffectiveAvailabilityForDeclSignature(decl),
@@ -423,16 +409,20 @@ class AvailabilityScopeBuilder : private ASTWalker {
423409
getEffectiveAvailabilityForDeclSignature(const Decl *decl) {
424410
auto effectiveIntroduction = AvailabilityRange::alwaysAvailable();
425411

426-
// Availability attributes are found abstract syntax decls.
412+
// Availability attributes are found on abstract syntax decls.
427413
decl = decl->getAbstractSyntaxDeclForAttributes();
428414

429415
// As a special case, extension decls are treated as effectively as
430416
// available as the nominal type they extend, up to the deployment target.
431417
// This rule is a convenience for library authors who have written
432-
// extensions without specifying availabilty on the extension itself.
418+
// extensions without specifying platform availabilty on the extension
419+
// itself.
433420
if (auto *extension = dyn_cast<ExtensionDecl>(decl)) {
434421
auto extendedType = extension->getExtendedType();
435-
if (extendedType && !hasActiveAvailableAttribute(decl, Context)) {
422+
if (extendedType && !decl->hasAnyMatchingActiveAvailableAttr(
423+
[](SemanticAvailableAttr attr) -> bool {
424+
return attr.getDomain().isPlatform();
425+
})) {
436426
effectiveIntroduction.intersectWith(
437427
swift::AvailabilityInference::inferForType(extendedType));
438428

@@ -1087,9 +1077,9 @@ class AvailabilityScopeBuilder : private ASTWalker {
10871077
// current scope is completely contained in the range for the spec, then
10881078
// a version query can never be false, so the spec is useless.
10891079
// If so, report this.
1090-
// FIXME: [availability] Diagnose non-platform queries as useless too.
1091-
auto explicitRange = currentScope->getExplicitAvailabilityRange();
1092-
if (domain.isPlatform() && explicitRange && trueRange &&
1080+
auto explicitRange =
1081+
currentScope->getExplicitAvailabilityRange(domain, Context);
1082+
if (explicitRange && trueRange &&
10931083
explicitRange->isContainedIn(*trueRange)) {
10941084
// Platform unavailability queries never refine availability so don't
10951085
// diangose them.
@@ -1098,17 +1088,9 @@ class AvailabilityScopeBuilder : private ASTWalker {
10981088

10991089
DiagnosticEngine &diags = Context.Diags;
11001090
if (currentScope->getReason() != AvailabilityScope::Reason::Root) {
1101-
PlatformKind bestPlatform = targetPlatform(Context.LangOpts);
1102-
1103-
// If possible, try to report the diagnostic in terms for the
1104-
// platform the user uttered in the '#available()'. For a platform
1105-
// that inherits availability from another platform it may be
1106-
// different from the platform specified in the target triple.
1107-
if (domain.getPlatformKind() != PlatformKind::none)
1108-
bestPlatform = domain.getPlatformKind();
11091091
diags.diagnose(query->getLoc(),
11101092
diag::availability_query_useless_enclosing_scope,
1111-
platformString(bestPlatform));
1093+
domain.getNameForAttributePrinting());
11121094
diags.diagnose(
11131095
currentScope->getIntroductionLoc(),
11141096
diag::availability_query_useless_enclosing_scope_here);

lib/Sema/TypeCheckAttr.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2200,13 +2200,19 @@ static Decl *getEnclosingDeclForDecl(Decl *D) {
22002200
}
22012201

22022202
static std::optional<std::pair<SemanticAvailableAttr, const Decl *>>
2203-
getSemanticAvailableRangeDeclAndAttr(const Decl *decl) {
2204-
if (auto attr = decl->getAvailableAttrForPlatformIntroduction(
2205-
/*checkExtension=*/false))
2206-
return std::make_pair(*attr, decl);
2203+
getSemanticAvailableRangeDeclAndAttr(const Decl *decl,
2204+
AvailabilityDomain domain) {
2205+
auto &ctx = decl->getASTContext();
2206+
AvailabilityConstraintFlags flags =
2207+
AvailabilityConstraintFlag::SkipEnclosingExtension;
2208+
if (auto constraint = swift::getAvailabilityConstraintForDeclInDomain(
2209+
decl, AvailabilityContext::forAlwaysAvailable(ctx), domain, flags)) {
2210+
if (constraint->isPotentiallyAvailable())
2211+
return std::make_pair(constraint->getAttr(), decl);
2212+
}
22072213

22082214
if (auto *parent = decl->parentDeclForAvailability())
2209-
return getSemanticAvailableRangeDeclAndAttr(parent);
2215+
return getSemanticAvailableRangeDeclAndAttr(parent, domain);
22102216

22112217
return std::nullopt;
22122218
}
@@ -2300,7 +2306,7 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *parsedAttr) {
23002306

23012307
if (auto *parent = getEnclosingDeclForDecl(D)) {
23022308
if (auto enclosingAvailable =
2303-
getSemanticAvailableRangeDeclAndAttr(parent)) {
2309+
getSemanticAvailableRangeDeclAndAttr(parent, attr->getDomain())) {
23042310
SemanticAvailableAttr enclosingAttr = enclosingAvailable->first;
23052311
const Decl *enclosingDecl = enclosingAvailable->second;
23062312
enclosingIntroducedRange = enclosingAttr.getIntroducedRange(Ctx);
@@ -5245,8 +5251,8 @@ void AttributeChecker::checkBackDeployedAttrs(
52455251
// If it's not, the attribute doesn't make sense since the back deployment
52465252
// fallback could never be executed at runtime.
52475253
if (auto availableRangeAttrPair =
5248-
getSemanticAvailableRangeDeclAndAttr(VD)) {
5249-
auto beforeDomain = Attr->getAvailabilityDomain();
5254+
getSemanticAvailableRangeDeclAndAttr(VD, Domain)) {
5255+
auto beforeDomain = Domain;
52505256
auto beforeVersion = Attr->getVersion();
52515257
auto availableAttr = availableRangeAttrPair.value().first;
52525258
auto introVersion = availableAttr.getIntroduced().value();

0 commit comments

Comments
 (0)