Skip to content

[SE-0376] Rename @_backDeploy(before:) to @backDeployed(before:) #63357

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 4 commits into from
Feb 2, 2023
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
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,33 @@ _**Note:** This is in reverse chronological order, so newer entries are added to

## Swift 5.8

* [SE-0376][]:

The `@backDeployed(before:)` attribute may now be used to extend the availability of a function to OS releases prior to the introduction of that function as ABI.

For example, suppose that `struct Temperature` was introduced in a macOS SDK framework in macOS 12. Later in macOS 13 the framework authors decided to add a `degreesFahrenheit` property as a convenience:

```swift
@available(macOS 12, *)
public struct Temperature {
public var degreesCelsius: Double

// ...
}

extension Temperature {
@available(macOS 12, *)
@backDeployed(before: macOS 13)
public var degreesFahrenheit: Double {
return (degreesCelsius * 9 / 5) + 32
}
}
```

Adding the `@backDeployed` attribute to `degreesFahrenheit` enables the framework author to make this new declaration available to apps with a minimum deployment target of macOS 12, even though the ABI entry point for `degreesFahrenheit` is only present in macOS 13 and up.

When a function with `@backDeployed` is called, the compiler wraps the invocation of the function in a thunk. The thunk checks whether the library entry point for the declaration is available at runtime, and invokes it if it is. Otherwise, a copy of the function that was emitted into the client is called instead.

* [#56139][]:

Сollection downcasts in cast patterns are now supported. For example:
Expand Down Expand Up @@ -9611,6 +9638,7 @@ using the `.dynamicType` member to retrieve the type of an expression should mig
[SE-0362]: <https://github.com/apple/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md>
[SE-0365]: <https://github.com/apple/swift-evolution/blob/main/proposals/0365-implicit-self-weak-capture.md>
[SE-0370]: <https://github.com/apple/swift-evolution/blob/main/proposals/0370-pointer-family-initialization-improvements.md>
[SE-0376]: <https://github.com/apple/swift-evolution/blob/main/proposals/0376-function-back-deployment.md>

[#42697]: <https://github.com/apple/swift/issues/42697>
[#42728]: <https://github.com/apple/swift/issues/42728>
Expand Down
12 changes: 1 addition & 11 deletions docs/ReferenceGuides/UnderscoredAttributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,7 @@ Adding this attribute to a type leads to remarks being emitted for all methods.

## `@_backDeploy(before: ...)`

Causes the body of a function to be emitted into the module interface to be
available for emission into clients with deployment targets lower than the
ABI availability of the function. When the client's deployment target is
before the function's ABI availability, the compiler replaces calls to that
function with a call to a thunk that checks at runtime whether the original
library function is available. If the original is available then it is
called. Otherwise, the fallback copy of the function that was emitted into the
client is called instead.

For more details, see the [pitch thread](https://forums.swift.org/t/pitch-function-back-deployment/55769/)
in the forums.
The spelling of `@backDeployed(before:)` prior to the acceptance of [SE-0376](https://github.com/apple/swift-evolution/blob/main/proposals/0376-function-back-deployment.md).

## `@_borrowed`

Expand Down
19 changes: 8 additions & 11 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -2219,15 +2219,12 @@ class UnavailableFromAsyncAttr : public DeclAttribute {

/// The @_backDeploy(...) attribute, used to make function declarations available
/// for back deployment to older OSes via emission into the client binary.
class BackDeployAttr: public DeclAttribute {
class BackDeployedAttr : public DeclAttribute {
public:
BackDeployAttr(SourceLoc AtLoc, SourceRange Range,
PlatformKind Platform,
const llvm::VersionTuple Version,
bool Implicit)
: DeclAttribute(DAK_BackDeploy, AtLoc, Range, Implicit),
Platform(Platform),
Version(Version) {}
BackDeployedAttr(SourceLoc AtLoc, SourceRange Range, PlatformKind Platform,
const llvm::VersionTuple Version, bool Implicit)
: DeclAttribute(DAK_BackDeployed, AtLoc, Range, Implicit),
Platform(Platform), Version(Version) {}

/// The platform the symbol is available for back deployment on.
const PlatformKind Platform;
Expand All @@ -2239,7 +2236,7 @@ class BackDeployAttr: public DeclAttribute {
bool isActivePlatform(const ASTContext &ctx) const;

static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DAK_BackDeploy;
return DA->getKind() == DAK_BackDeployed;
}
};

Expand Down Expand Up @@ -2419,9 +2416,9 @@ class DeclAttributes {
/// otherwise.
const AvailableAttr *getNoAsync(const ASTContext &ctx) const;

/// Returns the \c @_backDeploy attribute that is active for the current
/// Returns the `@backDeployed` attribute that is active for the current
/// platform.
const BackDeployAttr *getBackDeploy(const ASTContext &ctx) const;
const BackDeployedAttr *getBackDeployed(const ASTContext &ctx) const;

SWIFT_DEBUG_DUMPER(dump(const Decl *D = nullptr));
void print(ASTPrinter &Printer, const PrintOptions &Options,
Expand Down
8 changes: 4 additions & 4 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -884,9 +884,9 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
Optional<llvm::VersionTuple> getIntroducedOSVersion(PlatformKind Kind) const;

/// Returns the OS version in which the decl became ABI as specified by the
/// @_backDeploy attribute.
/// @backDeployed attribute.
Optional<llvm::VersionTuple>
getBackDeployBeforeOSVersion(ASTContext &Ctx) const;
getBackDeployedBeforeOSVersion(ASTContext &Ctx) const;

/// Returns the starting location of the entire declaration.
SourceLoc getStartLoc() const { return getSourceRange().Start; }
Expand Down Expand Up @@ -6808,8 +6808,8 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
/// \return the synthesized thunk, or null if the base of the call has
/// diagnosed errors during type checking.
FuncDecl *getDistributedThunk() const;
/// Returns 'true' if the function has (or inherits) the @c @_backDeploy

/// Returns 'true' if the function has (or inherits) the `@backDeployed`
/// attribute.
bool isBackDeployed() const;

Expand Down
6 changes: 3 additions & 3 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -1593,11 +1593,11 @@ ERROR(originally_defined_in_need_nonempty_module_name,none,

// backDeploy
ERROR(attr_back_deploy_expected_before_label,none,
"expected 'before:' in '@_backDeploy' attribute", ())
"expected 'before:' in '@backDeployed' attribute", ())
ERROR(attr_back_deploy_expected_colon_after_before,none,
"expected ':' after 'before' in '@_backDeploy' attribute", ())
"expected ':' after 'before' in '@backDeployed' attribute", ())
ERROR(attr_back_deploy_missing_rparen,none,
"expected ')' in '@_backDeploy' argument list", ())
"expected ')' in '@backDeployed' argument list", ())

// convention
ERROR(convention_attribute_expected_lparen,none,
Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -6072,7 +6072,7 @@ ERROR(usable_from_inline_attr_in_protocol,none,
"an '@_alwaysEmitIntoClient' function|" \
"a default argument value|" \
"a property initializer in a '@frozen' type|" \
"a '@_backDeploy' function'}"
"a '@backDeployed' function'}"

#define DECL_OR_ACCESSOR "%select{%0|%0 for}"

Expand Down
7 changes: 4 additions & 3 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1093,9 +1093,10 @@ class Parser {
ParserResult<TransposeAttr> parseTransposeAttribute(SourceLoc AtLoc,
SourceLoc Loc);

/// Parse the @_backDeploy attribute.
bool parseBackDeployAttribute(DeclAttributes &Attributes, StringRef AttrName,
SourceLoc AtLoc, SourceLoc Loc);
/// Parse the @backDeployed attribute.
bool parseBackDeployedAttribute(DeclAttributes &Attributes,
StringRef AttrName, SourceLoc AtLoc,
SourceLoc Loc);

/// Parse the @_documentation attribute.
ParserResult<DocumentationAttr> parseDocumentationAttribute(SourceLoc AtLoc,
Expand Down
2 changes: 1 addition & 1 deletion include/swift/SIL/SILDeclRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ struct SILDeclRef {
bool isNoinline() const;
/// True if the function has __always inline attribute.
bool isAlwaysInline() const;
/// True if the function has the @_backDeploy attribute.
/// True if the function has the @backDeployed attribute.
bool isBackDeployed() const;

/// Return the expected linkage for a definition of this declaration.
Expand Down
41 changes: 22 additions & 19 deletions lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,23 +400,24 @@ const AvailableAttr *DeclAttributes::getNoAsync(const ASTContext &ctx) const {
return bestAttr;
}

const BackDeployAttr *
DeclAttributes::getBackDeploy(const ASTContext &ctx) const {
const BackDeployAttr *bestAttr = nullptr;
const BackDeployedAttr *
DeclAttributes::getBackDeployed(const ASTContext &ctx) const {
const BackDeployedAttr *bestAttr = nullptr;

for (auto attr : *this) {
auto *backDeployAttr = dyn_cast<BackDeployAttr>(attr);
if (!backDeployAttr)
auto *backDeployedAttr = dyn_cast<BackDeployedAttr>(attr);
if (!backDeployedAttr)
continue;

if (backDeployAttr->isInvalid() || !backDeployAttr->isActivePlatform(ctx))
if (backDeployedAttr->isInvalid() ||
!backDeployedAttr->isActivePlatform(ctx))
continue;

// We have an attribute that is active for the platform, but
// is it more specific than our current best?
if (!bestAttr || inheritsAvailabilityFromPlatform(backDeployAttr->Platform,
bestAttr->Platform)) {
bestAttr = backDeployAttr;
if (!bestAttr || inheritsAvailabilityFromPlatform(
backDeployedAttr->Platform, bestAttr->Platform)) {
bestAttr = backDeployedAttr;
}
}

Expand Down Expand Up @@ -547,13 +548,14 @@ static void printShortFormBackDeployed(ArrayRef<const DeclAttribute *> Attrs,
ASTPrinter &Printer,
const PrintOptions &Options) {
assert(!Attrs.empty());
// TODO: Print `@backDeployed` in swiftinterfaces (rdar://104920183)
Printer << "@_backDeploy(before: ";
bool isFirst = true;

for (auto *DA : Attrs) {
if (!isFirst)
Printer << ", ";
auto *attr = cast<BackDeployAttr>(DA);
auto *attr = cast<BackDeployedAttr>(DA);
Printer << platformString(attr->Platform) << " "
<< attr->Version.getAsString();
isFirst = false;
Expand Down Expand Up @@ -773,7 +775,7 @@ void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options,
AttributeVector shortAvailableAttributes;
const DeclAttribute *swiftVersionAvailableAttribute = nullptr;
const DeclAttribute *packageDescriptionVersionAvailableAttribute = nullptr;
AttributeVector backDeployAttributes;
AttributeVector backDeployedAttributes;
AttributeVector longAttributes;
AttributeVector attributes;
AttributeVector modifiers;
Expand Down Expand Up @@ -811,7 +813,7 @@ void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options,
}

AttributeVector &which = DA->isDeclModifier() ? modifiers :
isa<BackDeployAttr>(DA) ? backDeployAttributes :
isa<BackDeployedAttr>(DA) ? backDeployedAttributes :
isShortAvailable(DA) ? shortAvailableAttributes :
DA->isLongAttribute() ? longAttributes :
attributes;
Expand All @@ -824,8 +826,8 @@ void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options,
printShortFormAvailable(packageDescriptionVersionAvailableAttribute, Printer, Options);
if (!shortAvailableAttributes.empty())
printShortFormAvailable(shortAvailableAttributes, Printer, Options);
if (!backDeployAttributes.empty())
printShortFormBackDeployed(backDeployAttributes, Printer, Options);
if (!backDeployedAttributes.empty())
printShortFormBackDeployed(backDeployedAttributes, Printer, Options);

for (auto DA : longAttributes)
DA->print(Printer, Options, D);
Expand Down Expand Up @@ -1324,10 +1326,11 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
break;
}

case DAK_BackDeploy: {
case DAK_BackDeployed: {
// TODO: Print `@backDeployed` in swiftinterfaces (rdar://104920183)
Printer.printAttrName("@_backDeploy");
Printer << "(before: ";
auto Attr = cast<BackDeployAttr>(this);
auto Attr = cast<BackDeployedAttr>(this);
Printer << platformString(Attr->Platform) << " " <<
Attr->Version.getAsString();
Printer << ")";
Expand Down Expand Up @@ -1534,8 +1537,8 @@ StringRef DeclAttribute::getAttrName() const {
return "transpose";
case DAK_UnavailableFromAsync:
return "_unavailableFromAsync";
case DAK_BackDeploy:
return "_backDeploy";
case DAK_BackDeployed:
return "backDeployed";
case DAK_Expose:
return "_expose";
case DAK_Documentation:
Expand Down Expand Up @@ -1791,7 +1794,7 @@ bool AvailableAttr::isActivePlatform(const ASTContext &ctx) const {
return isPlatformActive(Platform, ctx.LangOpts);
}

bool BackDeployAttr::isActivePlatform(const ASTContext &ctx) const {
bool BackDeployedAttr::isActivePlatform(const ASTContext &ctx) const {
return isPlatformActive(Platform, ctx.LangOpts);
}

Expand Down
16 changes: 8 additions & 8 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -417,13 +417,13 @@ Decl::getIntroducedOSVersion(PlatformKind Kind) const {
}

Optional<llvm::VersionTuple>
Decl::getBackDeployBeforeOSVersion(ASTContext &Ctx) const {
if (auto *attr = getAttrs().getBackDeploy(Ctx))
Decl::getBackDeployedBeforeOSVersion(ASTContext &Ctx) const {
if (auto *attr = getAttrs().getBackDeployed(Ctx))
return attr->Version;

// Accessors may inherit `@_backDeploy`.
// Accessors may inherit `@backDeployed`.
if (auto *AD = dyn_cast<AccessorDecl>(this))
return AD->getStorage()->getBackDeployBeforeOSVersion(Ctx);
return AD->getStorage()->getBackDeployedBeforeOSVersion(Ctx);

return None;
}
Expand Down Expand Up @@ -971,8 +971,8 @@ AvailabilityContext Decl::getAvailabilityForLinkage() const {
ASTContext &ctx = getASTContext();

// When computing availability for linkage, use the "before" version from
// the @_backDeploy attribute, if present.
if (auto backDeployVersion = getBackDeployBeforeOSVersion(ctx))
// the @backDeployed attribute, if present.
if (auto backDeployVersion = getBackDeployedBeforeOSVersion(ctx))
return AvailabilityContext{VersionRange::allGTE(*backDeployVersion)};

auto containingContext =
Expand Down Expand Up @@ -8065,12 +8065,12 @@ bool AbstractFunctionDecl::isSendable() const {
}

bool AbstractFunctionDecl::isBackDeployed() const {
if (getAttrs().hasAttribute<BackDeployAttr>())
if (getAttrs().hasAttribute<BackDeployedAttr>())
return true;

// Property and subscript accessors inherit the attribute.
if (auto *AD = dyn_cast<AccessorDecl>(this)) {
if (AD->getStorage()->getAttrs().hasAttribute<BackDeployAttr>())
if (AD->getStorage()->getAttrs().hasAttribute<BackDeployedAttr>())
return true;
}

Expand Down
6 changes: 3 additions & 3 deletions lib/AST/DeclContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -466,13 +466,13 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator,
/*allowUsableFromInline=*/true};
}

if (AFD->getAttrs().hasAttribute<BackDeployAttr>()) {
if (AFD->getAttrs().hasAttribute<BackDeployedAttr>()) {
return {FragileFunctionKind::BackDeploy,
/*allowUsableFromInline=*/true};
}

// Property and subscript accessors inherit @_alwaysEmitIntoClient,
// @_backDeploy, and @inlinable from their storage declarations.
// @backDeployed, and @inlinable from their storage declarations.
if (auto accessor = dyn_cast<AccessorDecl>(AFD)) {
auto *storage = accessor->getStorage();
if (storage->getAttrs().getAttribute<InlinableAttr>()) {
Expand All @@ -483,7 +483,7 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator,
return {FragileFunctionKind::AlwaysEmitIntoClient,
/*allowUsableFromInline=*/true};
}
if (storage->getAttrs().hasAttribute<BackDeployAttr>()) {
if (storage->getAttrs().hasAttribute<BackDeployedAttr>()) {
return {FragileFunctionKind::BackDeploy,
/*allowUsableFromInline=*/true};
}
Expand Down
18 changes: 9 additions & 9 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1961,14 +1961,14 @@ ParserStatus Parser::parsePlatformVersionInList(StringRef AttrName,
return makeParserSuccess();
}

bool Parser::parseBackDeployAttribute(DeclAttributes &Attributes,
StringRef AttrName, SourceLoc AtLoc,
SourceLoc Loc) {
bool Parser::parseBackDeployedAttribute(DeclAttributes &Attributes,
StringRef AttrName, SourceLoc AtLoc,
SourceLoc Loc) {
std::string AtAttrName = (llvm::Twine("@") + AttrName).str();
auto LeftLoc = Tok.getLoc();
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AtAttrName,
DeclAttribute::isDeclModifier(DAK_BackDeploy));
DeclAttribute::isDeclModifier(DAK_BackDeployed));
return false;
}

Expand Down Expand Up @@ -2019,9 +2019,9 @@ bool Parser::parseBackDeployAttribute(DeclAttributes &Attributes,
assert(!PlatformAndVersions.empty());
auto AttrRange = SourceRange(Loc, Tok.getLoc());
for (auto &Item : PlatformAndVersions) {
Attributes.add(new (Context)
BackDeployAttr(AtLoc, AttrRange, Item.first, Item.second,
/*IsImplicit*/ false));
Attributes.add(new (Context) BackDeployedAttr(AtLoc, AttrRange, Item.first,
Item.second,
/*IsImplicit*/ false));
}
return true;
}
Expand Down Expand Up @@ -3366,8 +3366,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
message, AtLoc, SourceRange(Loc, Tok.getLoc()), false));
break;
}
case DAK_BackDeploy: {
if (!parseBackDeployAttribute(Attributes, AttrName, AtLoc, Loc))
case DAK_BackDeployed: {
if (!parseBackDeployedAttribute(Attributes, AttrName, AtLoc, Loc))
return false;
break;
}
Expand Down
Loading