mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
AST: Adopt new AvailableAttr constructor in attribute inference.
It was difficult to preserve the existing, buggy behavior of availability attribute inference with respect to attributes specifying availability for non-platform-specific domains. Instead, this change improves attribute merging by tracking every domain independently, and only merging attributes from the same domain.
This commit is contained in:
@@ -836,6 +836,23 @@ public:
|
||||
Bits.AvailableAttr.PlatformAgnostic);
|
||||
}
|
||||
|
||||
/// Returns the kind of availability the attribute specifies.
|
||||
Kind getKind() const {
|
||||
switch (getPlatformAgnosticAvailability()) {
|
||||
case PlatformAgnosticAvailabilityKind::None:
|
||||
case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
|
||||
case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific:
|
||||
return Kind::Default;
|
||||
case PlatformAgnosticAvailabilityKind::Deprecated:
|
||||
return Kind::Deprecated;
|
||||
case PlatformAgnosticAvailabilityKind::UnavailableInSwift:
|
||||
case PlatformAgnosticAvailabilityKind::Unavailable:
|
||||
return Kind::Unavailable;
|
||||
case PlatformAgnosticAvailabilityKind::NoAsync:
|
||||
return Kind::NoAsync;
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an `AvailableAttr` that specifies universal unavailability, e.g.
|
||||
/// `@available(*, unavailable)`.
|
||||
static AvailableAttr *createUniversallyUnavailable(ASTContext &C,
|
||||
|
||||
@@ -33,14 +33,14 @@ public:
|
||||
/// universally unavailable or deprecated, for example.
|
||||
Universal,
|
||||
|
||||
/// Represents availability for a specific operating system platform.
|
||||
Platform,
|
||||
|
||||
/// Represents availability with respect to Swift language mode.
|
||||
SwiftLanguage,
|
||||
|
||||
/// Represents PackageDescription availability.
|
||||
PackageDescription,
|
||||
|
||||
/// Represents availability for a specific operating system platform.
|
||||
Platform,
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -98,6 +98,40 @@ public:
|
||||
|
||||
/// Returns the string to use when printing an `@available` attribute.
|
||||
llvm::StringRef getNameForAttributePrinting() const;
|
||||
|
||||
bool operator==(const AvailabilityDomain &other) const {
|
||||
if (getKind() != other.getKind())
|
||||
return false;
|
||||
|
||||
switch (getKind()) {
|
||||
case Kind::Universal:
|
||||
case Kind::SwiftLanguage:
|
||||
case Kind::PackageDescription:
|
||||
// These availability domains are singletons.
|
||||
return true;
|
||||
case Kind::Platform:
|
||||
return getPlatformKind() == other.getPlatformKind();
|
||||
}
|
||||
}
|
||||
|
||||
bool operator!=(const AvailabilityDomain &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const AvailabilityDomain &other) const {
|
||||
if (getKind() != other.getKind())
|
||||
return getKind() < other.getKind();
|
||||
|
||||
switch (getKind()) {
|
||||
case Kind::Universal:
|
||||
case Kind::SwiftLanguage:
|
||||
case Kind::PackageDescription:
|
||||
// These availability domains are singletons.
|
||||
return false;
|
||||
case Kind::Platform:
|
||||
return getPlatformKind() < other.getPlatformKind();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace swift
|
||||
|
||||
@@ -109,12 +109,14 @@ namespace {
|
||||
/// The inferred availability required to access a group of declarations
|
||||
/// on a single platform.
|
||||
struct InferredAvailability {
|
||||
PlatformAgnosticAvailabilityKind PlatformAgnostic
|
||||
= PlatformAgnosticAvailabilityKind::None;
|
||||
AvailableAttr::Kind Kind = AvailableAttr::Kind::Default;
|
||||
|
||||
std::optional<llvm::VersionTuple> Introduced;
|
||||
std::optional<llvm::VersionTuple> Deprecated;
|
||||
std::optional<llvm::VersionTuple> Obsoleted;
|
||||
|
||||
StringRef Message;
|
||||
StringRef Rename;
|
||||
bool IsSPI = false;
|
||||
};
|
||||
|
||||
@@ -148,10 +150,9 @@ mergeIntoInferredVersion(const std::optional<llvm::VersionTuple> &Version,
|
||||
static void mergeWithInferredAvailability(SemanticAvailableAttr Attr,
|
||||
InferredAvailability &Inferred) {
|
||||
auto *ParsedAttr = Attr.getParsedAttr();
|
||||
Inferred.PlatformAgnostic = static_cast<PlatformAgnosticAvailabilityKind>(
|
||||
std::max(static_cast<unsigned>(Inferred.PlatformAgnostic),
|
||||
static_cast<unsigned>(
|
||||
ParsedAttr->getPlatformAgnosticAvailability())));
|
||||
Inferred.Kind = static_cast<AvailableAttr::Kind>(
|
||||
std::max(static_cast<unsigned>(Inferred.Kind),
|
||||
static_cast<unsigned>(ParsedAttr->getKind())));
|
||||
|
||||
// The merge of two introduction versions is the maximum of the two versions.
|
||||
if (mergeIntoInferredVersion(Attr.getIntroduced(), Inferred.Introduced,
|
||||
@@ -162,21 +163,24 @@ static void mergeWithInferredAvailability(SemanticAvailableAttr Attr,
|
||||
// The merge of deprecated and obsoleted versions takes the minimum.
|
||||
mergeIntoInferredVersion(Attr.getDeprecated(), Inferred.Deprecated, std::min);
|
||||
mergeIntoInferredVersion(Attr.getObsoleted(), Inferred.Obsoleted, std::min);
|
||||
|
||||
if (Inferred.Message.empty() && !Attr.getMessage().empty())
|
||||
Inferred.Message = Attr.getMessage();
|
||||
|
||||
if (Inferred.Rename.empty() && !Attr.getRename().empty())
|
||||
Inferred.Rename = Attr.getRename();
|
||||
}
|
||||
|
||||
/// Create an implicit availability attribute for the given platform
|
||||
/// Create an implicit availability attribute for the given domain
|
||||
/// and with the inferred availability.
|
||||
static AvailableAttr *createAvailableAttr(PlatformKind Platform,
|
||||
static AvailableAttr *createAvailableAttr(AvailabilityDomain Domain,
|
||||
const InferredAvailability &Inferred,
|
||||
StringRef Message,
|
||||
StringRef Rename,
|
||||
ValueDecl *RenameDecl,
|
||||
ASTContext &Context) {
|
||||
// If there is no information that would go into the availability attribute,
|
||||
// don't create one.
|
||||
if (Inferred.PlatformAgnostic == PlatformAgnosticAvailabilityKind::None &&
|
||||
!Inferred.Introduced && !Inferred.Deprecated && !Inferred.Obsoleted &&
|
||||
Message.empty() && Rename.empty() && !RenameDecl)
|
||||
if (Inferred.Kind == AvailableAttr::Kind::Default && !Inferred.Introduced &&
|
||||
!Inferred.Deprecated && !Inferred.Obsoleted && Inferred.Message.empty() &&
|
||||
Inferred.Rename.empty())
|
||||
return nullptr;
|
||||
|
||||
llvm::VersionTuple Introduced =
|
||||
@@ -186,36 +190,26 @@ static AvailableAttr *createAvailableAttr(PlatformKind Platform,
|
||||
llvm::VersionTuple Obsoleted =
|
||||
Inferred.Obsoleted.value_or(llvm::VersionTuple());
|
||||
|
||||
return new (Context)
|
||||
AvailableAttr(SourceLoc(), SourceRange(), Platform, Message, Rename,
|
||||
Introduced, SourceRange(), Deprecated, SourceRange(),
|
||||
Obsoleted, SourceRange(), Inferred.PlatformAgnostic,
|
||||
/*Implicit=*/true, Inferred.IsSPI);
|
||||
return new (Context) AvailableAttr(
|
||||
SourceLoc(), SourceRange(), Domain, Inferred.Kind, Inferred.Message,
|
||||
Inferred.Rename, Introduced, SourceRange(), Deprecated, SourceRange(),
|
||||
Obsoleted, SourceRange(), /*Implicit=*/true, Inferred.IsSPI);
|
||||
}
|
||||
|
||||
void AvailabilityInference::applyInferredAvailableAttrs(
|
||||
Decl *ToDecl, ArrayRef<const Decl *> InferredFromDecls) {
|
||||
auto &Context = ToDecl->getASTContext();
|
||||
|
||||
// Let the new AvailabilityAttr inherit the message and rename.
|
||||
// The first encountered message / rename will win; this matches the
|
||||
// behaviour of diagnostics for 'non-inherited' AvailabilityAttrs.
|
||||
StringRef Message;
|
||||
StringRef Rename;
|
||||
ValueDecl *RenameDecl = nullptr;
|
||||
|
||||
// Iterate over the declarations and infer required availability on
|
||||
// a per-platform basis.
|
||||
// FIXME: [availability] Generalize to AvailabilityDomain.
|
||||
std::map<PlatformKind, InferredAvailability> Inferred;
|
||||
std::map<AvailabilityDomain, InferredAvailability> Inferred;
|
||||
for (const Decl *D : InferredFromDecls) {
|
||||
llvm::SmallVector<SemanticAvailableAttr, 8> MergedAttrs;
|
||||
|
||||
do {
|
||||
llvm::SmallVector<SemanticAvailableAttr, 8> PendingAttrs;
|
||||
|
||||
for (auto AvAttr :
|
||||
D->getSemanticAvailableAttrs()) {
|
||||
for (auto AvAttr : D->getSemanticAvailableAttrs()) {
|
||||
// Skip an attribute from an outer declaration if it is for a platform
|
||||
// that was already handled implicitly by an attribute from an inner
|
||||
// declaration.
|
||||
@@ -226,14 +220,8 @@ void AvailabilityInference::applyInferredAvailableAttrs(
|
||||
}))
|
||||
continue;
|
||||
|
||||
mergeWithInferredAvailability(AvAttr, Inferred[AvAttr.getPlatform()]);
|
||||
mergeWithInferredAvailability(AvAttr, Inferred[AvAttr.getDomain()]);
|
||||
PendingAttrs.push_back(AvAttr);
|
||||
|
||||
if (Message.empty() && !AvAttr.getMessage().empty())
|
||||
Message = AvAttr.getMessage();
|
||||
|
||||
if (Rename.empty() && !AvAttr.getRename().empty())
|
||||
Rename = AvAttr.getRename();
|
||||
}
|
||||
|
||||
MergedAttrs.append(PendingAttrs);
|
||||
@@ -245,20 +233,12 @@ void AvailabilityInference::applyInferredAvailableAttrs(
|
||||
}
|
||||
|
||||
DeclAttributes &Attrs = ToDecl->getAttrs();
|
||||
auto *ToValueDecl = dyn_cast<ValueDecl>(ToDecl);
|
||||
|
||||
// Create an availability attribute for each observed platform and add
|
||||
// to ToDecl.
|
||||
for (auto &Pair : Inferred) {
|
||||
auto *Attr = createAvailableAttr(Pair.first, Pair.second, Message,
|
||||
Rename, RenameDecl, Context);
|
||||
|
||||
if (Attr) {
|
||||
if (RenameDecl && ToValueDecl)
|
||||
ToValueDecl->setRenamedDecl(Attr, RenameDecl);
|
||||
|
||||
if (auto Attr = createAvailableAttr(Pair.first, Pair.second, Context))
|
||||
Attrs.add(Attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,12 +31,12 @@ llvm::StringRef AvailabilityDomain::getNameForDiagnostics() const {
|
||||
switch (kind) {
|
||||
case Kind::Universal:
|
||||
return "";
|
||||
case Kind::Platform:
|
||||
return swift::prettyPlatformString(getPlatformKind());
|
||||
case Kind::SwiftLanguage:
|
||||
return "Swift";
|
||||
case Kind::PackageDescription:
|
||||
return "PackageDescription";
|
||||
case Kind::Platform:
|
||||
return swift::prettyPlatformString(getPlatformKind());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,11 +44,11 @@ llvm::StringRef AvailabilityDomain::getNameForAttributePrinting() const {
|
||||
switch (kind) {
|
||||
case Kind::Universal:
|
||||
return "*";
|
||||
case Kind::Platform:
|
||||
return swift::platformString(getPlatformKind());
|
||||
case Kind::SwiftLanguage:
|
||||
return "swift";
|
||||
case Kind::PackageDescription:
|
||||
return "_PackageDescription";
|
||||
case Kind::Platform:
|
||||
return swift::platformString(getPlatformKind());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,20 @@ public actor UnavailableActor {
|
||||
// CHECK-NEXT: }
|
||||
}
|
||||
|
||||
// CHECK: @_hasMissingDesignatedInitializers @available(*, deprecated, message: "Will be unavailable Swift 6")
|
||||
// CHECK-NEXT: @available(swift, obsoleted: 6)
|
||||
// CHECK-NEXT: public actor DeprecatedAndObsoleteInSwift6Actor {
|
||||
@available(*, deprecated, message: "Will be unavailable Swift 6")
|
||||
@available(swift, obsoleted: 6)
|
||||
public actor DeprecatedAndObsoleteInSwift6Actor {
|
||||
// CHECK: @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
||||
// CHECK-NEXT: @available(*, deprecated, message: "Will be unavailable Swift 6")
|
||||
// CHECK-NEXT: @available(swift, obsoleted: 6)
|
||||
// CHECK-NEXT: @_semantics("defaultActor") nonisolated final public var unownedExecutor: _Concurrency.UnownedSerialExecutor {
|
||||
// CHECK-NEXT: get
|
||||
// CHECK-NEXT: }
|
||||
}
|
||||
|
||||
// CHECK: @available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *)
|
||||
// CHECK-NEXT: public enum Enum {
|
||||
@available(SwiftStdlib 5.2, *)
|
||||
|
||||
Reference in New Issue
Block a user