Skip to content

Commit 0288eaa

Browse files
committed
AST: Introduce Decl::getSemanticAvailableAttrs().
This new attribute iterator returned from the query makes it simpler to implement algorithms that need access to both the `AvailableAttr *` and its corresponding `AvailabilityDomain`. This is also work towards making it possible to return an optional `AvailabilityDomain` from `Decl::getDomainForAvailableAttr()`.
1 parent a3d9776 commit 0288eaa

13 files changed

+175
-71
lines changed

include/swift/AST/Attr.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/AST/ASTAllocated.h"
2121
#include "swift/AST/AttrKind.h"
2222
#include "swift/AST/AutoDiff.h"
23+
#include "swift/AST/AvailabilityDomain.h"
2324
#include "swift/AST/ConcreteDeclRef.h"
2425
#include "swift/AST/DeclNameLoc.h"
2526
#include "swift/AST/Identifier.h"
@@ -3190,6 +3191,52 @@ class ParsedDeclAttributes {
31903191
}
31913192
};
31923193

3194+
/// A wrapper for `AvailableAttr` that is enriched with additional semantic
3195+
/// informaton, like its corresponding `AvailabilityDomain`.
3196+
class SemanticAvailableAttr final {
3197+
const AvailableAttr *attr;
3198+
const AvailabilityDomain domain;
3199+
3200+
public:
3201+
SemanticAvailableAttr(const AvailableAttr *attr, AvailabilityDomain domain)
3202+
: attr(attr), domain(domain) {}
3203+
3204+
const AvailableAttr *getParsedAttr() const { return attr; }
3205+
const AvailabilityDomain getDomain() const { return domain; }
3206+
};
3207+
3208+
/// An iterable range of `SemanticAvailableAttr`s.
3209+
class SemanticAvailableAttributes {
3210+
public:
3211+
class Filter final {
3212+
const Decl *decl;
3213+
bool includeInactive;
3214+
3215+
public:
3216+
Filter(const Decl *decl, bool includeInactive)
3217+
: decl(decl), includeInactive(includeInactive) {}
3218+
3219+
std::optional<SemanticAvailableAttr>
3220+
operator()(const DeclAttribute *attr) const;
3221+
};
3222+
3223+
using Range =
3224+
OptionalTransformRange<iterator_range<DeclAttributes::const_iterator>,
3225+
Filter>;
3226+
3227+
private:
3228+
Range attrRange;
3229+
3230+
public:
3231+
SemanticAvailableAttributes(const DeclAttributes &attrs, const Decl *decl,
3232+
bool includeInactive = false)
3233+
: attrRange(make_range(attrs.begin(), attrs.end()),
3234+
Filter(decl, includeInactive)) {}
3235+
3236+
Range::iterator begin() const { return attrRange.begin(); }
3237+
Range::iterator end() const { return attrRange.end(); }
3238+
};
3239+
31933240
class alignas(1 << AttrAlignInBits) TypeAttribute
31943241
: public ASTAllocated<TypeAttribute> {
31953242
protected:

include/swift/AST/AvailabilityDomain.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/Basic/LLVM.h"
2323

2424
namespace swift {
25+
class ASTContext;
2526

2627
/// Represents a dimension of availability (e.g. macOS platform or Swift
2728
/// language mode).
@@ -88,6 +89,10 @@ class AvailabilityDomain final {
8889
return platform;
8990
}
9091

92+
/// Returns true if this domain is considered active in the current
93+
/// compilation context.
94+
bool isActive(ASTContext &ctx) const;
95+
9196
/// Returns the string to use in diagnostics to identify the domain. May
9297
/// return an empty string.
9398
llvm::StringRef getNameForDiagnostics() const;

include/swift/AST/Decl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,6 +1395,12 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
13951395
/// and its DeclContext does not.
13961396
bool isOutermostPrivateOrFilePrivateScope() const;
13971397

1398+
/// Returns an iterable list of the valid `AvailableAttr` and
1399+
/// `AvailabilityDomain` pairs. Unless \p includeInactive is true, attributes
1400+
/// that are considered inactive for the compilation context are filtered out.
1401+
SemanticAvailableAttributes
1402+
getSemanticAvailableAttrs(bool includeInactive = true) const;
1403+
13981404
/// Returns the availability domain associated with the given `AvailableAttr`
13991405
/// that is attached to this decl.
14001406
AvailabilityDomain getDomainForAvailableAttr(const AvailableAttr *attr) const;

lib/APIDigester/ModuleAnalyzerNodes.cpp

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,14 +1339,16 @@ std::optional<uint8_t> SDKContext::getFixedBinaryOrder(ValueDecl *VD) const {
13391339
// check for if it has @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
13401340
static bool isABIPlaceHolder(Decl *D) {
13411341
llvm::SmallSet<PlatformKind, 4> Platforms;
1342-
for (auto *ATT: D->getAttrs()) {
1343-
if (auto *AVA = dyn_cast<AvailableAttr>(ATT)) {
1344-
if (AVA->getPlatform() != PlatformKind::none && AVA->Introduced &&
1345-
AVA->Introduced->getMajor() == 9999) {
1346-
Platforms.insert(AVA->getPlatform());
1347-
}
1342+
for (auto semanticAttr : D->getSemanticAvailableAttrs()) {
1343+
auto attr = semanticAttr.getParsedAttr();
1344+
auto domain = semanticAttr.getDomain();
1345+
if (domain.isPlatform() && attr->Introduced &&
1346+
attr->Introduced->getMajor() == 9999) {
1347+
Platforms.insert(attr->getPlatform());
13481348
}
13491349
}
1350+
1351+
// FIXME: This probably isn't correct anymore, now that visionOS exists
13501352
return Platforms.size() == 4;
13511353
}
13521354

@@ -1361,11 +1363,11 @@ static bool isABIPlaceholderRecursive(Decl *D) {
13611363
StringRef SDKContext::getPlatformIntroVersion(Decl *D, PlatformKind Kind) {
13621364
if (!D)
13631365
return StringRef();
1364-
for (auto *ATT: D->getAttrs()) {
1365-
if (auto *AVA = dyn_cast<AvailableAttr>(ATT)) {
1366-
if (AVA->getPlatform() == Kind && AVA->Introduced) {
1367-
return buffer(AVA->Introduced->getAsString());
1368-
}
1366+
for (auto semanticAttr : D->getSemanticAvailableAttrs()) {
1367+
auto attr = semanticAttr.getParsedAttr();
1368+
auto domain = semanticAttr.getDomain();
1369+
if (domain.getPlatformKind() == Kind && attr->Introduced) {
1370+
return buffer(attr->Introduced->getAsString());
13691371
}
13701372
}
13711373
return StringRef();
@@ -1374,11 +1376,12 @@ StringRef SDKContext::getPlatformIntroVersion(Decl *D, PlatformKind Kind) {
13741376
StringRef SDKContext::getLanguageIntroVersion(Decl *D) {
13751377
if (!D)
13761378
return StringRef();
1377-
for (auto *ATT: D->getAttrs()) {
1378-
if (auto *AVA = dyn_cast<AvailableAttr>(ATT)) {
1379-
if (AVA->isLanguageVersionSpecific() && AVA->Introduced) {
1380-
return buffer(AVA->Introduced->getAsString());
1381-
}
1379+
for (auto semanticAttr : D->getSemanticAvailableAttrs()) {
1380+
auto attr = semanticAttr.getParsedAttr();
1381+
auto domain = semanticAttr.getDomain();
1382+
1383+
if (domain.isSwiftLanguage() && attr->Introduced) {
1384+
return buffer(attr->Introduced->getAsString());
13821385
}
13831386
}
13841387
return getLanguageIntroVersion(D->getDeclContext()->getAsDecl());

lib/AST/Attr.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,23 @@ ParsedDeclAttrFilter::operator()(const DeclAttribute *Attr) const {
909909
return Attr;
910910
}
911911

912+
std::optional<SemanticAvailableAttr>
913+
SemanticAvailableAttributes::Filter::operator()(
914+
const DeclAttribute *attr) const {
915+
auto availableAttr = dyn_cast<AvailableAttr>(attr);
916+
if (!availableAttr)
917+
return std::nullopt;
918+
919+
if (availableAttr->isInvalid())
920+
return std::nullopt;
921+
922+
auto domain = decl->getDomainForAvailableAttr(availableAttr);
923+
if (!includeInactive && !domain.isActive(decl->getASTContext()))
924+
return std::nullopt;
925+
926+
return SemanticAvailableAttr(availableAttr, domain);
927+
}
928+
912929
static void printAvailableAttr(const Decl *D, const AvailableAttr *Attr,
913930
ASTPrinter &Printer,
914931
const PrintOptions &Options) {

lib/AST/Availability.cpp

Lines changed: 34 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "swift/AST/ASTContext.h"
1919
#include "swift/AST/Attr.h"
2020
#include "swift/AST/AvailabilityConstraint.h"
21+
#include "swift/AST/AvailabilityDomain.h"
2122
#include "swift/AST/Decl.h"
2223
#include "swift/AST/PlatformKind.h"
2324
#include "swift/AST/TypeCheckRequests.h"
@@ -432,22 +433,19 @@ bool AvailabilityInference::updateBeforePlatformForFallback(
432433

433434
const AvailableAttr *
434435
AvailabilityInference::attrForAnnotatedAvailableRange(const Decl *D) {
435-
ASTContext &Ctx = D->getASTContext();
436436
const AvailableAttr *bestAvailAttr = nullptr;
437437

438438
D = abstractSyntaxDeclForAvailableAttribute(D);
439439

440-
for (auto Attr : D->getAttrs()) {
441-
auto *AvailAttr = dyn_cast<AvailableAttr>(Attr);
442-
if (AvailAttr == nullptr || !AvailAttr->Introduced.has_value() ||
443-
!AvailAttr->isActivePlatform(Ctx) ||
444-
AvailAttr->isLanguageVersionSpecific() ||
445-
AvailAttr->isPackageDescriptionVersionSpecific()) {
440+
for (auto semanticAttr :
441+
D->getSemanticAvailableAttrs(/*includingInactive=*/false)) {
442+
auto *attr = semanticAttr.getParsedAttr();
443+
444+
if (!attr->hasPlatform() || !attr->Introduced.has_value())
446445
continue;
447-
}
448446

449-
if (isBetterThan(AvailAttr, bestAvailAttr))
450-
bestAvailAttr = AvailAttr;
447+
if (isBetterThan(attr, bestAvailAttr))
448+
bestAvailAttr = attr;
451449
}
452450

453451
return bestAvailAttr;
@@ -468,12 +466,13 @@ bool Decl::isAvailableAsSPI() const {
468466

469467
const AvailableAttr *
470468
Decl::getActiveAvailableAttrForCurrentPlatform(bool ignoreAppExtensions) const {
471-
auto &ctx = getASTContext();
472469
const AvailableAttr *bestAttr = nullptr;
473470

474-
for (auto attr :
475-
getAttrs().getAttributes<AvailableAttr, /*AllowInvalid=*/false>()) {
476-
if (!attr->hasPlatform() || !attr->isActivePlatform(ctx))
471+
for (auto semanticAttr :
472+
getSemanticAvailableAttrs(/*includingInactive=*/false)) {
473+
auto attr = semanticAttr.getParsedAttr();
474+
475+
if (!attr->hasPlatform())
477476
continue;
478477

479478
if (ignoreAppExtensions &&
@@ -493,17 +492,14 @@ Decl::getActiveAvailableAttrForCurrentPlatform(bool ignoreAppExtensions) const {
493492

494493
const AvailableAttr *Decl::getDeprecatedAttr() const {
495494
auto &ctx = getASTContext();
496-
auto attrs = getAttrs();
497495
const AvailableAttr *result = nullptr;
498496
const AvailableAttr *bestActive = getActiveAvailableAttrForCurrentPlatform();
499497

500-
for (auto attr :
501-
attrs.getAttributes<AvailableAttr, /*AllowInvalid=*/false>()) {
502-
if (attr->hasPlatform() && (!bestActive || attr != bestActive))
503-
continue;
498+
for (auto semanticAttr :
499+
getSemanticAvailableAttrs(/*includingInactive=*/false)) {
500+
auto attr = semanticAttr.getParsedAttr();
504501

505-
if (!attr->isActivePlatform(ctx) && !attr->isLanguageVersionSpecific() &&
506-
!attr->isPackageDescriptionVersionSpecific())
502+
if (attr->hasPlatform() && (!bestActive || attr != bestActive))
507503
continue;
508504

509505
// Unconditional deprecated.
@@ -534,17 +530,14 @@ const AvailableAttr *Decl::getDeprecatedAttr() const {
534530

535531
const AvailableAttr *Decl::getSoftDeprecatedAttr() const {
536532
auto &ctx = getASTContext();
537-
auto attrs = getAttrs();
538533
const AvailableAttr *result = nullptr;
539534
const AvailableAttr *bestActive = getActiveAvailableAttrForCurrentPlatform();
540535

541-
for (auto attr :
542-
attrs.getAttributes<AvailableAttr, /*AllowInvalid=*/false>()) {
543-
if (attr->hasPlatform() && (!bestActive || attr != bestActive))
544-
continue;
536+
for (auto semanticAttr :
537+
getSemanticAvailableAttrs(/*includingInactive=*/false)) {
538+
auto attr = semanticAttr.getParsedAttr();
545539

546-
if (!attr->isActivePlatform(ctx) && !attr->isLanguageVersionSpecific() &&
547-
!attr->isPackageDescriptionVersionSpecific())
540+
if (attr->hasPlatform() && (!bestActive || attr != bestActive))
548541
continue;
549542

550543
std::optional<llvm::VersionTuple> deprecatedVersion = attr->Deprecated;
@@ -560,19 +553,16 @@ const AvailableAttr *Decl::getSoftDeprecatedAttr() const {
560553
}
561554

562555
const AvailableAttr *Decl::getNoAsyncAttr() const {
563-
auto &ctx = getASTContext();
564556
const AvailableAttr *bestAttr = nullptr;
565557

566-
for (auto attr :
567-
getAttrs().getAttributes<AvailableAttr, /*AllowInvalid=*/false>()) {
558+
for (auto semanticAttr :
559+
getSemanticAvailableAttrs(/*includingInactive=*/false)) {
560+
auto attr = semanticAttr.getParsedAttr();
568561

569562
if (attr->getPlatformAgnosticAvailability() !=
570563
PlatformAgnosticAvailabilityKind::NoAsync)
571564
continue;
572565

573-
if (attr->hasPlatform() && !attr->isActivePlatform(ctx))
574-
continue;
575-
576566
if (!bestAttr) {
577567
bestAttr = attr;
578568
continue;
@@ -597,9 +587,12 @@ const AvailableAttr *Decl::getNoAsyncAttr() const {
597587

598588
bool Decl::isUnavailableInCurrentSwiftVersion() const {
599589
llvm::VersionTuple vers = getASTContext().LangOpts.EffectiveLanguageVersion;
600-
for (auto attr :
601-
getAttrs().getAttributes<AvailableAttr, /*AllowInvalid=*/false>()) {
602-
if (attr->isLanguageVersionSpecific()) {
590+
for (auto semanticAttr :
591+
getSemanticAvailableAttrs(/*includingInactive=*/false)) {
592+
auto attr = semanticAttr.getParsedAttr();
593+
auto domain = semanticAttr.getDomain();
594+
595+
if (domain.isSwiftLanguage()) {
603596
if (attr->Introduced.has_value() && attr->Introduced.value() > vers)
604597
return true;
605598
if (attr->Obsoleted.has_value() && attr->Obsoleted.value() <= vers)
@@ -617,18 +610,15 @@ getDeclUnavailableAttr(const Decl *D, bool ignoreAppExtensions) {
617610
const AvailableAttr *bestActive =
618611
D->getActiveAvailableAttrForCurrentPlatform(ignoreAppExtensions);
619612

620-
for (auto attr :
621-
D->getAttrs().getAttributes<AvailableAttr, /*AllowInvalid=*/false>()) {
613+
for (auto semanticAttr :
614+
D->getSemanticAvailableAttrs(/*includingInactive=*/false)) {
615+
auto attr = semanticAttr.getParsedAttr();
616+
622617
// If this is a platform-specific attribute and it isn't the most
623618
// specific attribute for the current platform, we're done.
624619
if (attr->hasPlatform() && (!bestActive || attr != bestActive))
625620
continue;
626621

627-
// If this attribute doesn't apply to the active platform, we're done.
628-
if (!attr->isActivePlatform(ctx) && !attr->isLanguageVersionSpecific() &&
629-
!attr->isPackageDescriptionVersionSpecific())
630-
continue;
631-
632622
if (ignoreAppExtensions &&
633623
isApplicationExtensionPlatform(attr->getPlatform()))
634624
continue;

lib/AST/AvailabilityDomain.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/AST/AvailabilityDomain.h"
14+
#include "swift/AST/ASTContext.h"
1415
#include "swift/AST/Decl.h"
1516

1617
using namespace swift;
1718

19+
SemanticAvailableAttributes
20+
Decl::getSemanticAvailableAttrs(bool includeInactive) const {
21+
return SemanticAvailableAttributes(getAttrs(), this, includeInactive);
22+
}
23+
1824
AvailabilityDomain
1925
Decl::getDomainForAvailableAttr(const AvailableAttr *attr) const {
2026
if (attr->hasPlatform())
@@ -36,6 +42,17 @@ Decl::getDomainForAvailableAttr(const AvailableAttr *attr) const {
3642
}
3743
}
3844

45+
bool AvailabilityDomain::isActive(ASTContext &ctx) const {
46+
switch (kind) {
47+
case Kind::Universal:
48+
case Kind::SwiftLanguage:
49+
case Kind::PackageDescription:
50+
return true;
51+
case Kind::Platform:
52+
return isPlatformActive(getPlatformKind(), ctx.LangOpts);
53+
}
54+
}
55+
3956
llvm::StringRef AvailabilityDomain::getNameForDiagnostics() const {
4057
switch (kind) {
4158
case Kind::Universal:

lib/AST/Decl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9278,7 +9278,9 @@ AbstractFunctionDecl *AbstractFunctionDecl::getAsyncAlternative() const {
92789278
// `getAttrs` is in reverse source order, so the last attribute is the
92799279
// first in source.
92809280
AbstractFunctionDecl *alternative = nullptr;
9281-
for (const auto *attr : getAttrs().getAttributes<AvailableAttr>()) {
9281+
for (auto semanticAttr : getSemanticAvailableAttrs()) {
9282+
auto attr = semanticAttr.getParsedAttr();
9283+
92829284
if (attr->isNoAsync())
92839285
continue;
92849286

lib/Frontend/ModuleInterfaceSupport.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,9 @@ class InheritedProtocolCollector {
451451

452452
cache.emplace();
453453
while (D) {
454-
for (auto *nextAttr : D->getAttrs().getAttributes<AvailableAttr>()) {
454+
for (auto semanticAttr : D->getSemanticAvailableAttrs()) {
455+
auto nextAttr = semanticAttr.getParsedAttr();
456+
455457
// FIXME: This is just approximating the effects of nested availability
456458
// attributes for the same platform; formally they'd need to be merged.
457459
bool alreadyHasMoreSpecificAttrForThisPlatform =

0 commit comments

Comments
 (0)