From 2908520841d7bfc21678cadbed28fc47deacb16e Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Wed, 22 Jan 2025 08:51:39 -0800 Subject: [PATCH 1/2] AST: Introduce SemanticAvailableAttrRequest. This request will finish type checking an AvailableAttr by resolving its domain and then enforcing any restrictions that the domain has on the attribute, like disallowing version specifications. This change just introduces the request and plumbs it through. NFC. --- include/swift/AST/Attr.h | 28 ++++++++++++++++----- include/swift/AST/TypeCheckRequests.h | 21 ++++++++++++++++ include/swift/AST/TypeCheckerTypeIDZone.def | 5 ++++ lib/AST/Attr.cpp | 2 ++ lib/AST/Availability.cpp | 4 ++- lib/AST/TypeCheckRequests.cpp | 23 +++++++++++++++++ lib/Sema/TypeCheckAttr.cpp | 7 ++++++ 7 files changed, 83 insertions(+), 7 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 3523c04f5b3eb..21b6df275ef88 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -151,10 +151,14 @@ class DeclAttribute : public AttributeBase { Value : 32 ); - SWIFT_INLINE_BITFIELD(AvailableAttr, DeclAttribute, 4+1+1+1, + SWIFT_INLINE_BITFIELD(AvailableAttr, DeclAttribute, 4+1+1+1+1+1, /// An `AvailableAttr::Kind` value. Kind : 4, + /// State storage for `SemanticAvailableAttrRequest`. + HasComputedSemanticAttr : 1, + HasDomain : 1, + /// State storage for `RenamedDeclRequest`. HasComputedRenamedDecl : 1, HasRenamedDecl : 1, @@ -806,14 +810,15 @@ class AvailableAttr : public DeclAttribute { /// Returns the `AvailabilityDomain` associated with the attribute, or /// `std::nullopt` if it has either not yet been resolved or could not be /// resolved successfully. - std::optional getCachedDomain() const { return Domain; } + std::optional getCachedDomain() const { + if (hasCachedDomain()) + return Domain; + return std::nullopt; + } /// Returns true if the `AvailabilityDomain` associated with the attribute /// has been resolved successfully. - bool hasCachedDomain() const { - // For now, domains are always set on construction of the attribute. - return true; - } + bool hasCachedDomain() const { return Bits.AvailableAttr.HasDomain; } /// Returns the kind of availability the attribute specifies. Kind getKind() const { return static_cast(Bits.AvailableAttr.Kind); } @@ -873,6 +878,17 @@ class AvailableAttr : public DeclAttribute { Bits.AvailableAttr.HasComputedRenamedDecl = true; Bits.AvailableAttr.HasRenamedDecl = hasRenamedDecl; } + +private: + friend class SemanticAvailableAttrRequest; + + bool hasComputedSemanticAttr() const { + return Bits.AvailableAttr.HasComputedSemanticAttr; + } + + void setComputedSemanticAttr() { + Bits.AvailableAttr.HasComputedSemanticAttr = true; + } }; /// Indicates that the given declaration is visible to Objective-C. diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 0fddd90a1614a..36161d468b10c 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -5225,6 +5225,27 @@ class CustomDerivativesRequest bool isCached() const { return true; } }; +class SemanticAvailableAttrRequest + : public SimpleRequest( + const AvailableAttr *, const Decl *), + RequestFlags::SeparatelyCached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + std::optional evaluate(Evaluator &evaluator, + const AvailableAttr *attr, + const Decl *decl) const; + +public: + bool isCached() const { return true; } + std::optional> getCachedResult() const; + void cacheResult(std::optional value) const; +}; + #define SWIFT_TYPEID_ZONE TypeChecker #define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def" #include "swift/Basic/DefineTypeIDZone.h" diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 7caaebc740b8e..cd2e22f845269 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -614,3 +614,8 @@ SWIFT_REQUEST(TypeChecker, CustomDerivativesRequest, SWIFT_REQUEST(TypeChecker, GenericTypeParamDeclGetValueTypeRequest, Type(GenericTypeParamDecl *), Cached, NoLocationInfo) + +SWIFT_REQUEST(TypeChecker, SemanticAvailableAttrRequest, + std::optional + (const AvailableAttr *, const Decl *), + SeparatelyCached, NoLocationInfo) diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 2d3bd310250b6..99d9c31cab009 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -2123,6 +2123,8 @@ AvailableAttr::AvailableAttr( INIT_VER_TUPLE(Deprecated), DeprecatedRange(DeprecatedRange), INIT_VER_TUPLE(Obsoleted), ObsoletedRange(ObsoletedRange) { Bits.AvailableAttr.Kind = static_cast(Kind); + Bits.AvailableAttr.HasComputedSemanticAttr = false; + Bits.AvailableAttr.HasDomain = true; Bits.AvailableAttr.HasComputedRenamedDecl = false; Bits.AvailableAttr.HasRenamedDecl = false; Bits.AvailableAttr.IsSPI = IsSPI; diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 6963b0f405271..9a4cd5556f9be 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -447,7 +447,9 @@ Decl::getSemanticAvailableAttrs(bool includeInactive) const { std::optional Decl::getSemanticAvailableAttr(const AvailableAttr *attr) const { - return SemanticAvailableAttr(attr); + return evaluateOrDefault(getASTContext().evaluator, + SemanticAvailableAttrRequest{attr, this}, + std::nullopt); } std::optional diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 3cb245426e256..6b9c5d5fa37ca 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -2730,3 +2730,26 @@ void IsUnsafeRequest::cacheResult(bool value) const { auto *decl = std::get<0>(getStorage()); decl->setUnsafe(value); } + +//----------------------------------------------------------------------------// +// SemanticAvailableAttrRequest computation. +//----------------------------------------------------------------------------// + +std::optional> +SemanticAvailableAttrRequest::getCachedResult() const { + const AvailableAttr *attr = std::get<0>(getStorage()); + if (!attr->hasComputedSemanticAttr()) + return std::nullopt; + + if (!attr->hasCachedDomain()) { + return std::optional{}; + } + + return SemanticAvailableAttr(attr); +} + +void SemanticAvailableAttrRequest::cacheResult( + std::optional value) const { + AvailableAttr *attr = const_cast(std::get<0>(getStorage())); + attr->setComputedSemanticAttr(); +} diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 72c4e5918c230..9d9ceaacd6f14 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -8252,6 +8252,13 @@ ValueDecl *RenamedDeclRequest::evaluate(Evaluator &evaluator, return renamedDecl; } +std::optional +SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator, + const AvailableAttr *attr, + const Decl *decl) const { + return SemanticAvailableAttr(attr); +} + template static void forEachCustomAttribute( Decl *decl, From dfc741e30aa2e5c2a3a4cd0264cc9cf9b0c5de48 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Wed, 22 Jan 2025 15:32:17 -0800 Subject: [PATCH 2/2] AST: Optimize storage of VersionTuples in AvailableAttr. Representing introduced, deprecated, and obsoleted versions at rest as optional version tuples is redundant, since the empty version tuple already represents "version not present". NFC. --- include/swift/AST/Attr.h | 21 ++++++++++++++------- lib/AST/Attr.cpp | 25 +++++++++---------------- lib/AST/Availability.cpp | 4 ++-- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 21b6df275ef88..cce4c9c2d318d 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -754,26 +754,32 @@ class AvailableAttr : public DeclAttribute { const StringRef Message; const StringRef Rename; - const std::optional Introduced; + const llvm::VersionTuple Introduced; const SourceRange IntroducedRange; - const std::optional Deprecated; + const llvm::VersionTuple Deprecated; const SourceRange DeprecatedRange; - const std::optional Obsoleted; + const llvm::VersionTuple Obsoleted; const SourceRange ObsoletedRange; public: /// Returns the parsed version for `introduced:`. std::optional getRawIntroduced() const { + if (Introduced.empty()) + return std::nullopt; return Introduced; } /// Returns the parsed version for `deprecated:`. std::optional getRawDeprecated() const { + if (Deprecated.empty()) + return std::nullopt; return Deprecated; } /// Returns the parsed version for `obsoleted:`. std::optional getRawObsoleted() const { + if (Obsoleted.empty()) + return std::nullopt; return Obsoleted; } @@ -3268,7 +3274,7 @@ class SemanticAvailableAttr final { /// The version tuple written in source for the `introduced:` component. std::optional getIntroduced() const { - return attr->Introduced; + return attr->getRawIntroduced(); } /// The source range of the `introduced:` component. @@ -3280,12 +3286,12 @@ class SemanticAvailableAttr final { /// The version tuple written in source for the `deprecated:` component. std::optional getDeprecated() const { - return attr->Deprecated; + return attr->getRawDeprecated(); } /// The version tuple written in source for the `obsoleted:` component. std::optional getObsoleted() const { - return attr->Obsoleted; + return attr->getRawObsoleted(); } /// Returns the `message:` field of the attribute, or an empty string. @@ -3318,7 +3324,8 @@ class SemanticAvailableAttr final { /// Whether this attribute has an introduced, deprecated, or obsoleted /// version. bool isVersionSpecific() const { - return attr->Introduced || attr->Deprecated || attr->Obsoleted; + return getIntroduced().has_value() || getDeprecated().has_value() || + getObsoleted().has_value(); } /// Whether this is a language mode specific attribute. diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 99d9c31cab009..3b48deb966b7e 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -2108,8 +2108,6 @@ Type RawLayoutAttr::getResolvedCountType(StructDecl *sd) const { ErrorType::get(ctx)); } -#define INIT_VER_TUPLE(X) X(X.empty() ? std::optional() : X) - AvailableAttr::AvailableAttr( SourceLoc AtLoc, SourceRange Range, const AvailabilityDomain &Domain, Kind Kind, StringRef Message, StringRef Rename, @@ -2118,10 +2116,10 @@ AvailableAttr::AvailableAttr( const llvm::VersionTuple &Obsoleted, SourceRange ObsoletedRange, bool Implicit, bool IsSPI) : DeclAttribute(DeclAttrKind::Available, AtLoc, Range, Implicit), - Domain(Domain), Message(Message), Rename(Rename), - INIT_VER_TUPLE(Introduced), IntroducedRange(IntroducedRange), - INIT_VER_TUPLE(Deprecated), DeprecatedRange(DeprecatedRange), - INIT_VER_TUPLE(Obsoleted), ObsoletedRange(ObsoletedRange) { + Domain(Domain), Message(Message), Rename(Rename), Introduced(Introduced), + IntroducedRange(IntroducedRange), Deprecated(Deprecated), + DeprecatedRange(DeprecatedRange), Obsoleted(Obsoleted), + ObsoletedRange(ObsoletedRange) { Bits.AvailableAttr.Kind = static_cast(Kind); Bits.AvailableAttr.HasComputedSemanticAttr = false; Bits.AvailableAttr.HasDomain = true; @@ -2130,8 +2128,6 @@ AvailableAttr::AvailableAttr( Bits.AvailableAttr.IsSPI = IsSPI; } -#undef INIT_VER_TUPLE - AvailableAttr *AvailableAttr::createUniversallyUnavailable(ASTContext &C, StringRef Message, StringRef Rename) { @@ -2199,12 +2195,9 @@ bool BackDeployedAttr::isActivePlatform(const ASTContext &ctx, AvailableAttr *AvailableAttr::clone(ASTContext &C, bool implicit) const { return new (C) AvailableAttr( implicit ? SourceLoc() : AtLoc, implicit ? SourceRange() : getRange(), - Domain, getKind(), Message, Rename, - Introduced ? *Introduced : llvm::VersionTuple(), - implicit ? SourceRange() : IntroducedRange, - Deprecated ? *Deprecated : llvm::VersionTuple(), - implicit ? SourceRange() : DeprecatedRange, - Obsoleted ? *Obsoleted : llvm::VersionTuple(), + Domain, getKind(), Message, Rename, Introduced, + implicit ? SourceRange() : IntroducedRange, Deprecated, + implicit ? SourceRange() : DeprecatedRange, Obsoleted, implicit ? SourceRange() : ObsoletedRange, implicit, isSPI()); } @@ -2294,7 +2287,7 @@ SemanticAvailableAttr::getVersionAvailability(const ASTContext &ctx) const { return AvailableVersionComparison::Unavailable; llvm::VersionTuple queryVersion = getActiveVersion(ctx); - std::optional ObsoletedVersion = attr->Obsoleted; + std::optional ObsoletedVersion = getObsoleted(); StringRef ObsoletedPlatform; llvm::VersionTuple RemappedObsoletedVersion; @@ -2307,7 +2300,7 @@ SemanticAvailableAttr::getVersionAvailability(const ASTContext &ctx) const { if (ObsoletedVersion && *ObsoletedVersion <= queryVersion) return AvailableVersionComparison::Obsoleted; - std::optional IntroducedVersion = attr->Introduced; + std::optional IntroducedVersion = getIntroduced(); StringRef IntroducedPlatform; llvm::VersionTuple RemappedIntroducedVersion; if (AvailabilityInference::updateIntroducedPlatformForFallback( diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 9a4cd5556f9be..89c698d67ee7e 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -794,10 +794,10 @@ SemanticAvailableAttr::getIntroducedRange(const ASTContext &Ctx) const { assert(getDomain().isActive(Ctx)); auto *attr = getParsedAttr(); - if (!attr->Introduced.has_value()) + if (!attr->getRawIntroduced().has_value()) return AvailabilityRange::alwaysAvailable(); - llvm::VersionTuple IntroducedVersion = attr->Introduced.value(); + llvm::VersionTuple IntroducedVersion = attr->getRawIntroduced().value(); StringRef Platform; llvm::VersionTuple RemappedIntroducedVersion; if (AvailabilityInference::updateIntroducedPlatformForFallback(