Skip to content
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

Introduce @_backDeploy attribute #41271

Merged
merged 5 commits into from
Feb 9, 2022
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
9 changes: 9 additions & 0 deletions docs/ReferenceGuides/UnderscoredAttributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ Most notably, default argument expressions are implicitly
`@_alwaysEmitIntoClient`, which means that adding a default argument to a
function which did not have one previously does not break ABI.

## `@_backDeploy(availabilitySpec ...)`

Causes the body of a function to be emitted into the module interface to be
available for inlining in clients with deployment targets lower than the formal
availability of the function. When inlined, the body of the function is
transformed such that it calls the library's copy of the function if it is
available at runtime. Otherwise, the copy of the original function body is
executed.

## `@_assemblyVision`

Forces emission of assembly vision remarks for a function or method, showing
Expand Down
6 changes: 6 additions & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,12 @@ DECL_ATTR(exclusivity, Exclusivity,
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
128)

DECL_ATTR(_backDeploy, BackDeploy,
OnAbstractFunction |
AllowMultipleAttributes | LongAttribute | UserInaccessible |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want AllowMultipleAttributes or is one attribute listing many platforms enough?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose to allow multiple attributes because currently this attribute explodes into one attribute per platform and version tuple when printed into the interface (following @_originallyDefinedIn as an archetype).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sounds good.

ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove,
129)

// If you're adding a new underscored attribute here, please document it in
// docs/ReferenceGuides/UnderscoredAttributes.md.

Expand Down
24 changes: 24 additions & 0 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -2172,6 +2172,30 @@ 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 {
public:
BackDeployAttr(SourceLoc AtLoc, SourceRange Range,
PlatformKind Platform,
const llvm::VersionTuple Version,
bool Implicit)
: DeclAttribute(DAK_BackDeploy, AtLoc, Range, Implicit),
Platform(Platform),
Version(Version) {}

/// The platform the symbol is available for back deployment on.
const PlatformKind Platform;

/// The earliest platform version that may use the back deployed implementation.
const llvm::VersionTuple Version;

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


/// Attributes that may be applied to declarations.
class DeclAttributes {
/// Linked list of declaration attributes.
Expand Down
22 changes: 22 additions & 0 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -1503,6 +1503,8 @@ WARNING(attr_availability_invalid_duplicate,none,
"'%0' argument has already been specified", (StringRef))
WARNING(attr_availability_unknown_platform,none,
"unknown platform '%0' for attribute '%1'", (StringRef, StringRef))
ERROR(attr_availability_expected_platform,none,
"expected platform in '%0' attribute", (StringRef))
ERROR(attr_availability_invalid_renamed,none,
"'renamed' argument of '%0' attribute must be an operator, identifier, "
"or full function name, optionally prefixed by a type name", (StringRef))
Expand Down Expand Up @@ -1531,6 +1533,15 @@ WARNING(attr_availability_nonspecific_platform_unexpected_version,none,
"unexpected version number in '%0' attribute for non-specific platform "
"'*'", (StringRef))

WARNING(attr_availability_wildcard_ignored,none,
"* as platform name has no effect in '%0' attribute", (StringRef))

ERROR(attr_availability_need_platform_version,none,
"expected at least one platform version in '%0' attribute", (StringRef))

WARNING(attr_availability_platform_version_major_minor_only,none,
"'%0' only uses major and minor version number", (StringRef))

// availability macro
ERROR(attr_availability_wildcard_in_macro, none,
"future platforms identified by '*' cannot be used in "
Expand All @@ -1548,12 +1559,15 @@ ERROR(attr_availability_duplicate,none,
(StringRef, StringRef))

// originallyDefinedIn
// FIXME(backDeploy): Refactor to share with back deployment attr
ERROR(originally_defined_in_missing_rparen,none,
"expected ')' in @_originallyDefinedIn argument list", ())

// FIXME(backDeploy): Refactor to share with back deployment attr
ERROR(originally_defined_in_unrecognized_platform,none,
"unrecognized platform name in @_originallyDefinedIn argument list", ())

// FIXME: This is unused and can be removed
ERROR(originally_defined_in_unrecognized_property,none,
"unrecognized property in @_originallyDefinedIn argument list", ())

Expand All @@ -1564,15 +1578,19 @@ ERROR(originally_defined_in_need_original_module_name,none,
ERROR(originally_defined_in_need_nonempty_module_name,none,
"original module name cannot be empty in @_originallyDefinedIn", ())

// FIXME(backDeploy): Refactor to share with back deployment attr
ERROR(originally_defined_in_need_platform_version,none,
"expected at least one platform version in @_originallyDefinedIn", ())

// FIXME(backDeploy): Refactor to share with back deployment attr
WARNING(originally_defined_in_major_minor_only,none,
"@_originallyDefinedIn only uses major and minor version number", ())

// FIXME(backDeploy): Refactor to share with back deployment attr
WARNING(originally_defined_in_missing_platform_name,none,
"* as platform name has no effect", ())

// FIXME(backDeploy): Refactor to share with back deployment attr
WARNING(originally_defined_in_swift_version, none,
"Swift language version checks has no effect "
"in @_originallyDefinedIn", ())
Expand All @@ -1581,6 +1599,10 @@ WARNING(originally_defined_in_package_description, none,
"PackageDescription version checks has no effect "
"in @_originallyDefinedIn", ())

// backDeploy
ERROR(attr_back_deploy_missing_rparen,none,
"expected ')' in '@_backDeploy' argument list", ())

// convention
ERROR(convention_attribute_expected_lparen,none,
"expected '(' after 'convention' attribute", ())
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1597,18 +1597,21 @@ WARNING(option_set_zero_constant,none,
NOTE(option_set_empty_set_init,none,
"use [] to silence this warning", ())

// FIXME(backDeploy): Refactor to share with back deployment attr
ERROR(originally_defined_in_dupe_platform,none,
"duplicate version number for platform %0", (StringRef))

ERROR(originally_definedin_topleve_decl,none,
"@%0 is only applicable to top-level decl", (StringRef))

// FIXME(backDeploy): Refactor to share with back deployment attr
ERROR(originally_definedin_need_available,none,
"need @available attribute for @%0", (StringRef))

ERROR(originally_definedin_must_not_before_available_version,none,
"symbols are moved to the current module before they were available in the OSs", (StringRef))

// FIXME(backDeploy): Refactor to share with back deployment attr
WARNING(originally_defined_in_on_non_public,
none, "@%0 does not have any effect on "
"%select{private|fileprivate|internal|%error|%error}1 declarations",
Expand Down
10 changes: 10 additions & 0 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1846,6 +1846,16 @@ class Parser {
bool &DiscardAttribute, SourceRange &attrRange,
SourceLoc AtLoc, SourceLoc Loc,
llvm::function_ref<void(AvailableAttr *)> addAttribute);

using PlatformAndVersion = std::pair<PlatformKind, llvm::VersionTuple>;

/// Parse a platform and version tuple (e.g. "macOS 12.0") and append it to the
/// given vector. Wildcards ('*') parse successfully but are ignored. Assumes
/// that the tuples are part of a comma separated list ending with a trailing
/// ')'.
ParserStatus parsePlatformVersionInList(StringRef AttrName,
llvm::SmallVector<PlatformAndVersion, 4> &PlatformAndVersions);

//===--------------------------------------------------------------------===//
// Code completion second pass.

Expand Down
15 changes: 13 additions & 2 deletions lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,16 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
break;
}

case DAK_BackDeploy: {
Printer.printAttrName("@_backDeploy");
Printer << "(";
auto Attr = cast<BackDeployAttr>(this);
Printer << platformString(Attr->Platform) << " " <<
Attr->Version.getAsString();
Printer << ")";
break;
}

case DAK_Count:
llvm_unreachable("exceed declaration attribute kinds");

Expand Down Expand Up @@ -1300,9 +1310,8 @@ StringRef DeclAttribute::getAttrName() const {
return "exclusivity(checked)";
case ExclusivityAttr::Unchecked:
return "exclusivity(unchecked)";
default:
llvm_unreachable("Invalid optimization kind");
}
llvm_unreachable("Invalid optimization kind");
}
case DAK_Effects:
switch (cast<EffectsAttr>(this)->getKind()) {
Expand Down Expand Up @@ -1357,6 +1366,8 @@ StringRef DeclAttribute::getAttrName() const {
return "_typeSequence";
case DAK_UnavailableFromAsync:
return "_unavailableFromAsync";
case DAK_BackDeploy:
return "_backDeploy";
}
llvm_unreachable("bad DeclAttrKind");
}
Expand Down
99 changes: 99 additions & 0 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1738,6 +1738,64 @@ void Parser::parseAllAvailabilityMacroArguments() {
AvailabilityMacrosComputed = true;
}

ParserStatus Parser::parsePlatformVersionInList(StringRef AttrName,
llvm::SmallVector<PlatformAndVersion, 4> &PlatformAndVersions) {
// FIXME(backDeploy): Parse availability macros (e.g. SwiftStdlib: 5.1)
SyntaxParsingContext argumentContext(SyntaxContext,
SyntaxKind::AvailabilityVersionRestriction);

// Expect a possible platform name (e.g. 'macOS' or '*').
if (!Tok.isAny(tok::identifier, tok::oper_binary_spaced)) {
diagnose(Tok, diag::attr_availability_expected_platform, AttrName);
return makeParserError();
}

// Parse the platform name.
auto MaybePlatform = platformFromString(Tok.getText());
SourceLoc PlatformLoc = Tok.getLoc();
if (!MaybePlatform.hasValue()) {
diagnose(PlatformLoc, diag::attr_availability_unknown_platform,
Tok.getText(), AttrName);
return makeParserError();
}
consumeToken();
PlatformKind Platform = *MaybePlatform;

// Wildcards ('*') aren't supported in this kind of list. If this list
// entry is just a wildcard, skip it. Wildcards with a version are
// diagnosed below.
if (Platform == PlatformKind::none && Tok.isAny(tok::comma, tok::r_paren)) {
diagnose(PlatformLoc, diag::attr_availability_wildcard_ignored,
AttrName);
return makeParserSuccess();
}

// Parse version number.
llvm::VersionTuple VerTuple;
SourceRange VersionRange;
if (parseVersionTuple(VerTuple, VersionRange,
Diagnostic(diag::attr_availability_expected_version, AttrName))) {
return makeParserError();
}

// Diagnose specification of patch versions (e.g. '13.0.1').
if (VerTuple.getSubminor().hasValue() ||
VerTuple.getBuild().hasValue()) {
diagnose(VersionRange.Start,
diag::attr_availability_platform_version_major_minor_only,
AttrName);
}

// Wildcards ('*') aren't supported in this kind of list.
if (Platform == PlatformKind::none) {
diagnose(PlatformLoc, diag::attr_availability_wildcard_ignored,
AttrName);
} else {
PlatformAndVersions.emplace_back(Platform, VerTuple);
}
return makeParserSuccess();
}

/// Processes a parsed option name by attempting to match it to a list of
/// alternative name/value pairs provided by a chain of \c when() calls, ending
/// in either \c whenOmitted() if omitting the option is allowed, or
Expand Down Expand Up @@ -2855,6 +2913,47 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
message, AtLoc, SourceRange(Loc, Tok.getLoc()), false));
break;
}
case DAK_BackDeploy: {
auto LeftLoc = Tok.getLoc();
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}

bool SuppressLaterDiags = false;
SourceLoc RightLoc;
llvm::SmallVector<PlatformAndVersion, 4> PlatformAndVersions;
StringRef AttrName = "@_backDeploy";
ParserStatus Status = parseList(tok::r_paren, LeftLoc, RightLoc, false,
diag::attr_back_deploy_missing_rparen,
SyntaxKind::Unknown, [&]() -> ParserStatus {
ParserStatus ListItemStatus =
parsePlatformVersionInList(AttrName, PlatformAndVersions);
if (ListItemStatus.isErrorOrHasCompletion())
SuppressLaterDiags = true;
return ListItemStatus;
});

if (Status.isErrorOrHasCompletion() || SuppressLaterDiags) {
return false;
}

if (PlatformAndVersions.empty()) {
diagnose(Loc, diag::attr_availability_need_platform_version, AttrName);
return false;
}

assert(!PlatformAndVersions.empty());
AttrRange = SourceRange(Loc, Tok.getLoc());
for (auto &Item: PlatformAndVersions) {
Attributes.add(new (Context) BackDeployAttr(AtLoc, AttrRange,
Item.first,
Item.second,
/*IsImplicit*/false));
}
break;
}
}

if (DuplicateAttribute) {
Expand Down
8 changes: 8 additions & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
IGNORED_ATTR(InheritActorContext)
IGNORED_ATTR(Isolated)
IGNORED_ATTR(Preconcurrency)
IGNORED_ATTR(BackDeploy)
#undef IGNORED_ATTR

void visitAlignmentAttr(AlignmentAttr *attr) {
Expand Down Expand Up @@ -275,6 +276,8 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
void visitUnavailableFromAsyncAttr(UnavailableFromAsyncAttr *attr);

void visitPrimaryAssociatedTypeAttr(PrimaryAssociatedTypeAttr *attr);

void checkBackDeployAttrs(Decl *D, ArrayRef<BackDeployAttr *> Attrs);
};

} // end anonymous namespace
Expand Down Expand Up @@ -3460,6 +3463,11 @@ void AttributeChecker::checkOriginalDefinedInAttrs(Decl *D,
}
}

void AttributeChecker::checkBackDeployAttrs(Decl *D,
ArrayRef<BackDeployAttr *> Attrs) {
// FIXME(backDeploy): Diagnose incompatible uses of `@_backDeploy
}

Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type,
ReferenceOwnershipAttr *attr) {
auto &Diags = var->getASTContext().Diags;
Expand Down
2 changes: 2 additions & 0 deletions lib/Sema/TypeCheckDeclOverride.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1567,6 +1567,8 @@ namespace {

UNINTERESTING_ATTR(PrimaryAssociatedType)

UNINTERESTING_ATTR(BackDeploy)

#undef UNINTERESTING_ATTR

void visitAvailableAttr(AvailableAttr *attr) {
Expand Down
15 changes: 15 additions & 0 deletions lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4772,6 +4772,21 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() {
break;
}

case decls_block::BackDeploy_DECL_ATTR: {
bool isImplicit;
unsigned Platform;
DEF_VER_TUPLE_PIECES(Version);
serialization::decls_block::BackDeployDeclAttrLayout::readRecord(
scratch, isImplicit, LIST_VER_TUPLE_PIECES(Version), Platform);
llvm::VersionTuple Version;
DECODE_VER_TUPLE(Version)
Attr = new (ctx) BackDeployAttr(SourceLoc(), SourceRange(),
(PlatformKind)Platform,
Version,
isImplicit);
break;
}

#define SIMPLE_DECL_ATTR(NAME, CLASS, ...) \
case decls_block::CLASS##_DECL_ATTR: { \
bool isImplicit; \
Expand Down
Loading