Skip to content

Embedded: Distinguish @_unavailableInEmbedded from @availble(*, unavailable) #77307

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,14 @@ class DeclAttribute : public AttributeBase {
Value : 32
);

SWIFT_INLINE_BITFIELD(AvailableAttr, DeclAttribute, 1+1,
/// Whether this attribute was spelled `@_spi_available`.
IsSPI : 1,

/// Whether this attribute was spelled `@_unavailableInEmbedded`.
IsForEmbedded : 1
);

SWIFT_INLINE_BITFIELD(ClangImporterSynthesizedTypeAttr, DeclAttribute, 1,
kind : 1
);
Expand Down Expand Up @@ -674,8 +682,6 @@ enum class PlatformAgnosticAvailabilityKind {
/// Defines the @available attribute.
class AvailableAttr : public DeclAttribute {
public:
#define INIT_VER_TUPLE(X) X(X.empty() ? std::optional<llvm::VersionTuple>() : X)

AvailableAttr(SourceLoc AtLoc, SourceRange Range, PlatformKind Platform,
StringRef Message, StringRef Rename, ValueDecl *RenameDecl,
const llvm::VersionTuple &Introduced,
Expand All @@ -684,15 +690,7 @@ class AvailableAttr : public DeclAttribute {
SourceRange DeprecatedRange,
const llvm::VersionTuple &Obsoleted, SourceRange ObsoletedRange,
PlatformAgnosticAvailabilityKind PlatformAgnostic,
bool Implicit, bool IsSPI)
: DeclAttribute(DeclAttrKind::Available, AtLoc, Range, Implicit),
Message(Message), Rename(Rename), RenameDecl(RenameDecl),
INIT_VER_TUPLE(Introduced), IntroducedRange(IntroducedRange),
INIT_VER_TUPLE(Deprecated), DeprecatedRange(DeprecatedRange),
INIT_VER_TUPLE(Obsoleted), ObsoletedRange(ObsoletedRange),
PlatformAgnostic(PlatformAgnostic), Platform(Platform), IsSPI(IsSPI) {}

#undef INIT_VER_TUPLE
bool Implicit, bool IsSPI, bool IsForEmbedded = false);

/// The optional message.
const StringRef Message;
Expand Down Expand Up @@ -735,9 +733,6 @@ class AvailableAttr : public DeclAttribute {
/// The platform of the availability.
const PlatformKind Platform;

/// Whether this is available as SPI.
const bool IsSPI;

/// Whether this is a language-version-specific entity.
bool isLanguageVersionSpecific() const;

Expand All @@ -753,6 +748,12 @@ class AvailableAttr : public DeclAttribute {
/// Whether this is a noasync attribute.
bool isNoAsync() const;

/// Whether this attribute was spelled `@_spi_available`.
bool isSPI() const { return Bits.AvailableAttr.IsSPI; }

/// Whether this attribute was spelled `@_unavailableInEmbedded`.
bool isForEmbedded() const { return Bits.AvailableAttr.IsForEmbedded; }

/// Returns the platform-agnostic availability.
PlatformAgnosticAvailabilityKind getPlatformAgnosticAvailability() const {
return PlatformAgnostic;
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ constexpr static const StringLiteral CLANG_MODULE_DEFAULT_SPI_GROUP_NAME =
constexpr static const StringLiteral SPI_AVAILABLE_ATTRNAME =
"_spi_available";

/// The attribute name for @_unavailableInEmbedded
constexpr static const StringLiteral UNAVAILABLE_IN_EMBEDDED_ATTRNAME =
"_unavailableInEmbedded";

/// A composition class containing a StringLiteral for the names of
/// Swift builtins. The reason we use this is to ensure that we when
/// necessary slice off the "Builtin." prefix from these names in a
Expand Down
44 changes: 40 additions & 4 deletions lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ static bool isShortAvailable(const DeclAttribute *DA) {
if (!AvailAttr)
return false;

if (AvailAttr->IsSPI)
if (AvailAttr->isSPI())
return false;

if (!AvailAttr->Introduced.has_value())
Expand Down Expand Up @@ -1371,7 +1371,7 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
auto Attr = cast<AvailableAttr>(this);
if (Options.SuppressNoAsyncAvailabilityAttr && Attr->isNoAsync())
return false;
if (Options.printPublicInterface() && Attr->IsSPI) {
if (Options.printPublicInterface() && Attr->isSPI()) {
assert(Attr->hasPlatform());
assert(Attr->Introduced.has_value());
Printer.printAttrName("@available");
Expand All @@ -1380,7 +1380,14 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
Printer << ", unavailable)";
break;
}
if (Attr->IsSPI) {
if (Attr->isForEmbedded()) {
std::string atUnavailableInEmbedded =
(llvm::Twine("@") + UNAVAILABLE_IN_EMBEDDED_ATTRNAME).str();
Printer.printAttrName(atUnavailableInEmbedded);
break;
}

if (Attr->isSPI()) {
std::string atSPI = (llvm::Twine("@") + SPI_AVAILABLE_ATTRNAME).str();
Printer.printAttrName(atSPI);
} else {
Expand Down Expand Up @@ -2243,6 +2250,34 @@ Type RawLayoutAttr::getResolvedCountType(StructDecl *sd) const {
ErrorType::get(ctx));
}

#define INIT_VER_TUPLE(X) X(X.empty() ? std::optional<llvm::VersionTuple>() : X)

AvailableAttr::AvailableAttr(
SourceLoc AtLoc, SourceRange Range, PlatformKind Platform,
StringRef Message, StringRef Rename, ValueDecl *RenameDecl,
const llvm::VersionTuple &Introduced, SourceRange IntroducedRange,
const llvm::VersionTuple &Deprecated, SourceRange DeprecatedRange,
const llvm::VersionTuple &Obsoleted, SourceRange ObsoletedRange,
PlatformAgnosticAvailabilityKind PlatformAgnostic, bool Implicit,
bool IsSPI, bool IsForEmbedded)
: DeclAttribute(DeclAttrKind::Available, AtLoc, Range, Implicit),
Message(Message), Rename(Rename), RenameDecl(RenameDecl),
INIT_VER_TUPLE(Introduced), IntroducedRange(IntroducedRange),
INIT_VER_TUPLE(Deprecated), DeprecatedRange(DeprecatedRange),
INIT_VER_TUPLE(Obsoleted), ObsoletedRange(ObsoletedRange),
PlatformAgnostic(PlatformAgnostic), Platform(Platform) {
Bits.AvailableAttr.IsSPI = IsSPI;

if (IsForEmbedded) {
// FIXME: The IsForEmbedded bit should be removed when library availability
// conditions are implemented (rdar://138802876)
Bits.AvailableAttr.IsForEmbedded = true;
assert(Platform == PlatformKind::none);
}
}

#undef INIT_VER_TUPLE

AvailableAttr *
AvailableAttr::createPlatformAgnostic(ASTContext &C,
StringRef Message,
Expand Down Expand Up @@ -2294,7 +2329,8 @@ AvailableAttr *AvailableAttr::clone(ASTContext &C, bool implicit) const {
implicit ? SourceRange() : ObsoletedRange,
PlatformAgnostic,
implicit,
IsSPI);
isSPI(),
isForEmbedded());
}

std::optional<OriginallyDefinedInAttr::ActiveVersion>
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/Availability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ static void mergeWithInferredAvailability(const AvailableAttr *Attr,

// The merge of two introduction versions is the maximum of the two versions.
if (mergeIntoInferredVersion(Attr->Introduced, Inferred.Introduced, std::max)) {
Inferred.IsSPI = Attr->IsSPI;
Inferred.IsSPI = Attr->isSPI();
}

// The merge of deprecated and obsoleted versions takes the minimum.
Expand Down Expand Up @@ -596,7 +596,7 @@ AvailabilityRange AvailabilityInference::availableRange(const Decl *D) {

bool AvailabilityInference::isAvailableAsSPI(const Decl *D) {
if (auto attr = attrForAvailableRange(D))
return attr->IsSPI;
return attr->isSPI();

return false;
}
Expand Down
16 changes: 7 additions & 9 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4501,18 +4501,16 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes,

// Rewrite @_unavailableInEmbedded into @available(*, unavailable) when in
// embedded Swift mode, or into nothing when in regular mode.
if (!DK && Tok.getText() == "_unavailableInEmbedded") {
if (!DK && Tok.getText() == UNAVAILABLE_IN_EMBEDDED_ATTRNAME) {
SourceLoc attrLoc = consumeToken();
if (Context.LangOpts.hasFeature(Feature::Embedded)) {
StringRef Message = "unavailable in embedded Swift", Renamed;
auto attr = new (Context) AvailableAttr(AtLoc, SourceRange(AtLoc, attrLoc),
PlatformKind::none,
Message, Renamed, /*RenameDecl=*/nullptr,
llvm::VersionTuple(), SourceRange(),
llvm::VersionTuple(), SourceRange(),
llvm::VersionTuple(), SourceRange(),
PlatformAgnosticAvailabilityKind::Unavailable,
/*Implicit=*/false, /*IsSPI=*/false);
auto attr = new (Context) AvailableAttr(
AtLoc, SourceRange(AtLoc, attrLoc), PlatformKind::none, Message,
Renamed, /*RenameDecl=*/nullptr, llvm::VersionTuple(), SourceRange(),
llvm::VersionTuple(), SourceRange(), llvm::VersionTuple(),
SourceRange(), PlatformAgnosticAvailabilityKind::Unavailable,
/*Implicit=*/false, /*IsSPI=*/false, /*IsForEmbedded=*/true);
Attributes.add(attr);
}
return makeParserSuccess();
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2105,7 +2105,7 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) {
return;

// FIXME: This seems like it could be diagnosed during parsing instead.
while (attr->IsSPI) {
while (attr->isSPI()) {
if (attr->hasPlatform() && attr->Introduced.has_value())
break;
diagnoseAndRemoveAttr(attr, diag::spi_available_malformed);
Expand Down Expand Up @@ -4673,7 +4673,7 @@ void AttributeChecker::checkAvailableAttrs(ArrayRef<AvailableAttr *> Attrs) {
if (!D->getDeclContext()->getInnermostDeclarationDeclContext()) {
// If all available are spi available, we should use @_spi instead.
if (std::all_of(Attrs.begin(), Attrs.end(), [](AvailableAttr *AV) {
return AV->IsSPI;
return AV->isSPI();
})) {
diagnose(D->getLoc(), diag::spi_preferred_over_spi_available);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6604,7 +6604,7 @@ static void addUnavailableAttrs(ExtensionDecl *ext, NominalTypeDecl *nominal) {
available->Obsoleted.value_or(noVersion), SourceRange(),
PlatformAgnosticAvailabilityKind::Unavailable,
/*implicit=*/true,
available->IsSPI);
available->isSPI());
ext->getAttrs().add(attr);
anyPlatformSpecificAttrs = true;
}
Expand Down
5 changes: 3 additions & 2 deletions lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5602,6 +5602,7 @@ DeclDeserializer::readAvailable_DECL_ATTR(SmallVectorImpl<uint64_t> &scratch,
bool isNoAsync;
bool isPackageDescriptionVersionSpecific;
bool isSPI;
bool isForEmbedded;
DEF_VER_TUPLE_PIECES(Introduced);
DEF_VER_TUPLE_PIECES(Deprecated);
DEF_VER_TUPLE_PIECES(Obsoleted);
Expand All @@ -5611,7 +5612,7 @@ DeclDeserializer::readAvailable_DECL_ATTR(SmallVectorImpl<uint64_t> &scratch,
// Decode the record, pulling the version tuple information.
serialization::decls_block::AvailableDeclAttrLayout::readRecord(
scratch, isImplicit, isUnavailable, isDeprecated, isNoAsync,
isPackageDescriptionVersionSpecific, isSPI,
isPackageDescriptionVersionSpecific, isSPI, isForEmbedded,
LIST_VER_TUPLE_PIECES(Introduced), LIST_VER_TUPLE_PIECES(Deprecated),
LIST_VER_TUPLE_PIECES(Obsoleted), rawPlatform, renameDeclID, messageSize,
renameSize);
Expand Down Expand Up @@ -5655,7 +5656,7 @@ DeclDeserializer::readAvailable_DECL_ATTR(SmallVectorImpl<uint64_t> &scratch,
auto attr = new (ctx) AvailableAttr(
SourceLoc(), SourceRange(), platform, message, rename, renameDecl,
Introduced, SourceRange(), Deprecated, SourceRange(), Obsoleted,
SourceRange(), platformAgnostic, isImplicit, isSPI);
SourceRange(), platformAgnostic, isImplicit, isSPI, isForEmbedded);
return attr;
}

Expand Down
3 changes: 2 additions & 1 deletion lib/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
/// Don't worry about adhering to the 80-column limit for this line.
const uint16_t SWIFTMODULE_VERSION_MINOR = 899; // Builtin.FixedArray serialize
const uint16_t SWIFTMODULE_VERSION_MINOR = 900; // @_unavailableInEmbedded

/// A standard hash seed used for all string hashes in a serialized module.
///
Expand Down Expand Up @@ -2351,6 +2351,7 @@ namespace decls_block {
BCFixed<1>, // is unavailable from async?
BCFixed<1>, // is this PackageDescription version-specific kind?
BCFixed<1>, // is SPI?
BCFixed<1>, // is for Embedded
BC_AVAIL_TUPLE, // Introduced
BC_AVAIL_TUPLE, // Deprecated
BC_AVAIL_TUPLE, // Obsoleted
Expand Down
3 changes: 2 additions & 1 deletion lib/Serialization/Serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3031,7 +3031,8 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
theAttr->isUnconditionallyDeprecated(),
theAttr->isNoAsync(),
theAttr->isPackageDescriptionVersionSpecific(),
theAttr->IsSPI,
theAttr->isSPI(),
theAttr->isForEmbedded(),
LIST_VER_TUPLE_PIECES(Introduced),
LIST_VER_TUPLE_PIECES(Deprecated),
LIST_VER_TUPLE_PIECES(Obsoleted),
Expand Down
13 changes: 13 additions & 0 deletions test/attr/attr_unavailable_in_embedded.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// RUN: %target-swift-frontend -typecheck %s -parse-stdlib -print-ast | %FileCheck %s --check-prefix=CHECK-NON-EMBEDDED
// RUN: %target-swift-frontend -typecheck %s -parse-stdlib -enable-experimental-feature Embedded -print-ast | %FileCheck %s --check-prefix=CHECK-EMBEDDED

// CHECK-NON-EMBEDDED-NOT: @available
// CHECK-NON-EMBEDDED-NOT: @_unavailableInEmbedded
// CHECK-NON-EMBEDDED: public func unavailable()

// CHECK-EMBEDDED-NOT: @available
// CHECK-EMBEDDED: @_unavailableInEmbedded
// CHECK-EMBEDDED-NEXT: public func unavailable()

@_unavailableInEmbedded
public func unavailable() {}
21 changes: 21 additions & 0 deletions test/embedded/attr-unavailable-in-embedded.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// RUN: %empty-directory(%t)
// RUN: %{python} %utils/split_file.py -o %t %s

// RUN: %target-swift-frontend -emit-module -o %t/MyModule.swiftmodule %t/MyModule.swift -enable-experimental-feature Embedded -parse-as-library
// RUN: %target-swift-frontend -typecheck -verify -I %t %t/Main.swift -enable-experimental-feature Embedded

// REQUIRES: swift_in_compiler

// BEGIN MyModule.swift

@_unavailableInEmbedded
public func unavailable() { }

// BEGIN Main.swift

import MyModule

func available() {
unavailable() // expected-error {{'unavailable()' is unavailable: unavailable in embedded Swift}}
}