Skip to content

Commit 967a8b4

Browse files
authored
Merge pull request #41271 from tshortli/introduce-backdeploy-attr
Introduce the @_backDeploy attribute
2 parents f1e602f + 2de28df commit 967a8b4

File tree

16 files changed

+404
-5
lines changed

16 files changed

+404
-5
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ Most notably, default argument expressions are implicitly
3939
`@_alwaysEmitIntoClient`, which means that adding a default argument to a
4040
function which did not have one previously does not break ABI.
4141

42+
## `@_backDeploy(availabilitySpec ...)`
43+
44+
Causes the body of a function to be emitted into the module interface to be
45+
available for inlining in clients with deployment targets lower than the formal
46+
availability of the function. When inlined, the body of the function is
47+
transformed such that it calls the library's copy of the function if it is
48+
available at runtime. Otherwise, the copy of the original function body is
49+
executed.
50+
4251
## `@_assemblyVision`
4352

4453
Forces emission of assembly vision remarks for a function or method, showing

include/swift/AST/Attr.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,12 @@ DECL_ATTR(exclusivity, Exclusivity,
713713
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
714714
128)
715715

716+
DECL_ATTR(_backDeploy, BackDeploy,
717+
OnAbstractFunction |
718+
AllowMultipleAttributes | LongAttribute | UserInaccessible |
719+
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove,
720+
129)
721+
716722
// If you're adding a new underscored attribute here, please document it in
717723
// docs/ReferenceGuides/UnderscoredAttributes.md.
718724

include/swift/AST/Attr.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2172,6 +2172,30 @@ class UnavailableFromAsyncAttr : public DeclAttribute {
21722172
}
21732173
};
21742174

2175+
/// The @_backDeploy(...) attribute, used to make function declarations available
2176+
/// for back deployment to older OSes via emission into the client binary.
2177+
class BackDeployAttr: public DeclAttribute {
2178+
public:
2179+
BackDeployAttr(SourceLoc AtLoc, SourceRange Range,
2180+
PlatformKind Platform,
2181+
const llvm::VersionTuple Version,
2182+
bool Implicit)
2183+
: DeclAttribute(DAK_BackDeploy, AtLoc, Range, Implicit),
2184+
Platform(Platform),
2185+
Version(Version) {}
2186+
2187+
/// The platform the symbol is available for back deployment on.
2188+
const PlatformKind Platform;
2189+
2190+
/// The earliest platform version that may use the back deployed implementation.
2191+
const llvm::VersionTuple Version;
2192+
2193+
static bool classof(const DeclAttribute *DA) {
2194+
return DA->getKind() == DAK_BackDeploy;
2195+
}
2196+
};
2197+
2198+
21752199
/// Attributes that may be applied to declarations.
21762200
class DeclAttributes {
21772201
/// Linked list of declaration attributes.

include/swift/AST/DiagnosticsParse.def

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,6 +1503,8 @@ WARNING(attr_availability_invalid_duplicate,none,
15031503
"'%0' argument has already been specified", (StringRef))
15041504
WARNING(attr_availability_unknown_platform,none,
15051505
"unknown platform '%0' for attribute '%1'", (StringRef, StringRef))
1506+
ERROR(attr_availability_expected_platform,none,
1507+
"expected platform in '%0' attribute", (StringRef))
15061508
ERROR(attr_availability_invalid_renamed,none,
15071509
"'renamed' argument of '%0' attribute must be an operator, identifier, "
15081510
"or full function name, optionally prefixed by a type name", (StringRef))
@@ -1531,6 +1533,15 @@ WARNING(attr_availability_nonspecific_platform_unexpected_version,none,
15311533
"unexpected version number in '%0' attribute for non-specific platform "
15321534
"'*'", (StringRef))
15331535

1536+
WARNING(attr_availability_wildcard_ignored,none,
1537+
"* as platform name has no effect in '%0' attribute", (StringRef))
1538+
1539+
ERROR(attr_availability_need_platform_version,none,
1540+
"expected at least one platform version in '%0' attribute", (StringRef))
1541+
1542+
WARNING(attr_availability_platform_version_major_minor_only,none,
1543+
"'%0' only uses major and minor version number", (StringRef))
1544+
15341545
// availability macro
15351546
ERROR(attr_availability_wildcard_in_macro, none,
15361547
"future platforms identified by '*' cannot be used in "
@@ -1548,12 +1559,15 @@ ERROR(attr_availability_duplicate,none,
15481559
(StringRef, StringRef))
15491560

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

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

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

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

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

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

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

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

1602+
// backDeploy
1603+
ERROR(attr_back_deploy_missing_rparen,none,
1604+
"expected ')' in '@_backDeploy' argument list", ())
1605+
15841606
// convention
15851607
ERROR(convention_attribute_expected_lparen,none,
15861608
"expected '(' after 'convention' attribute", ())

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1597,18 +1597,21 @@ WARNING(option_set_zero_constant,none,
15971597
NOTE(option_set_empty_set_init,none,
15981598
"use [] to silence this warning", ())
15991599

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

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

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

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

1614+
// FIXME(backDeploy): Refactor to share with back deployment attr
16121615
WARNING(originally_defined_in_on_non_public,
16131616
none, "@%0 does not have any effect on "
16141617
"%select{private|fileprivate|internal|%error|%error}1 declarations",

include/swift/Parse/Parser.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1846,6 +1846,16 @@ class Parser {
18461846
bool &DiscardAttribute, SourceRange &attrRange,
18471847
SourceLoc AtLoc, SourceLoc Loc,
18481848
llvm::function_ref<void(AvailableAttr *)> addAttribute);
1849+
1850+
using PlatformAndVersion = std::pair<PlatformKind, llvm::VersionTuple>;
1851+
1852+
/// Parse a platform and version tuple (e.g. "macOS 12.0") and append it to the
1853+
/// given vector. Wildcards ('*') parse successfully but are ignored. Assumes
1854+
/// that the tuples are part of a comma separated list ending with a trailing
1855+
/// ')'.
1856+
ParserStatus parsePlatformVersionInList(StringRef AttrName,
1857+
llvm::SmallVector<PlatformAndVersion, 4> &PlatformAndVersions);
1858+
18491859
//===--------------------------------------------------------------------===//
18501860
// Code completion second pass.
18511861

lib/AST/Attr.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,16 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
11881188
break;
11891189
}
11901190

1191+
case DAK_BackDeploy: {
1192+
Printer.printAttrName("@_backDeploy");
1193+
Printer << "(";
1194+
auto Attr = cast<BackDeployAttr>(this);
1195+
Printer << platformString(Attr->Platform) << " " <<
1196+
Attr->Version.getAsString();
1197+
Printer << ")";
1198+
break;
1199+
}
1200+
11911201
case DAK_Count:
11921202
llvm_unreachable("exceed declaration attribute kinds");
11931203

@@ -1300,9 +1310,8 @@ StringRef DeclAttribute::getAttrName() const {
13001310
return "exclusivity(checked)";
13011311
case ExclusivityAttr::Unchecked:
13021312
return "exclusivity(unchecked)";
1303-
default:
1304-
llvm_unreachable("Invalid optimization kind");
13051313
}
1314+
llvm_unreachable("Invalid optimization kind");
13061315
}
13071316
case DAK_Effects:
13081317
switch (cast<EffectsAttr>(this)->getKind()) {
@@ -1357,6 +1366,8 @@ StringRef DeclAttribute::getAttrName() const {
13571366
return "_typeSequence";
13581367
case DAK_UnavailableFromAsync:
13591368
return "_unavailableFromAsync";
1369+
case DAK_BackDeploy:
1370+
return "_backDeploy";
13601371
}
13611372
llvm_unreachable("bad DeclAttrKind");
13621373
}

lib/Parse/ParseDecl.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,6 +1738,64 @@ void Parser::parseAllAvailabilityMacroArguments() {
17381738
AvailabilityMacrosComputed = true;
17391739
}
17401740

1741+
ParserStatus Parser::parsePlatformVersionInList(StringRef AttrName,
1742+
llvm::SmallVector<PlatformAndVersion, 4> &PlatformAndVersions) {
1743+
// FIXME(backDeploy): Parse availability macros (e.g. SwiftStdlib: 5.1)
1744+
SyntaxParsingContext argumentContext(SyntaxContext,
1745+
SyntaxKind::AvailabilityVersionRestriction);
1746+
1747+
// Expect a possible platform name (e.g. 'macOS' or '*').
1748+
if (!Tok.isAny(tok::identifier, tok::oper_binary_spaced)) {
1749+
diagnose(Tok, diag::attr_availability_expected_platform, AttrName);
1750+
return makeParserError();
1751+
}
1752+
1753+
// Parse the platform name.
1754+
auto MaybePlatform = platformFromString(Tok.getText());
1755+
SourceLoc PlatformLoc = Tok.getLoc();
1756+
if (!MaybePlatform.hasValue()) {
1757+
diagnose(PlatformLoc, diag::attr_availability_unknown_platform,
1758+
Tok.getText(), AttrName);
1759+
return makeParserError();
1760+
}
1761+
consumeToken();
1762+
PlatformKind Platform = *MaybePlatform;
1763+
1764+
// Wildcards ('*') aren't supported in this kind of list. If this list
1765+
// entry is just a wildcard, skip it. Wildcards with a version are
1766+
// diagnosed below.
1767+
if (Platform == PlatformKind::none && Tok.isAny(tok::comma, tok::r_paren)) {
1768+
diagnose(PlatformLoc, diag::attr_availability_wildcard_ignored,
1769+
AttrName);
1770+
return makeParserSuccess();
1771+
}
1772+
1773+
// Parse version number.
1774+
llvm::VersionTuple VerTuple;
1775+
SourceRange VersionRange;
1776+
if (parseVersionTuple(VerTuple, VersionRange,
1777+
Diagnostic(diag::attr_availability_expected_version, AttrName))) {
1778+
return makeParserError();
1779+
}
1780+
1781+
// Diagnose specification of patch versions (e.g. '13.0.1').
1782+
if (VerTuple.getSubminor().hasValue() ||
1783+
VerTuple.getBuild().hasValue()) {
1784+
diagnose(VersionRange.Start,
1785+
diag::attr_availability_platform_version_major_minor_only,
1786+
AttrName);
1787+
}
1788+
1789+
// Wildcards ('*') aren't supported in this kind of list.
1790+
if (Platform == PlatformKind::none) {
1791+
diagnose(PlatformLoc, diag::attr_availability_wildcard_ignored,
1792+
AttrName);
1793+
} else {
1794+
PlatformAndVersions.emplace_back(Platform, VerTuple);
1795+
}
1796+
return makeParserSuccess();
1797+
}
1798+
17411799
/// Processes a parsed option name by attempting to match it to a list of
17421800
/// alternative name/value pairs provided by a chain of \c when() calls, ending
17431801
/// in either \c whenOmitted() if omitting the option is allowed, or
@@ -2855,6 +2913,47 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
28552913
message, AtLoc, SourceRange(Loc, Tok.getLoc()), false));
28562914
break;
28572915
}
2916+
case DAK_BackDeploy: {
2917+
auto LeftLoc = Tok.getLoc();
2918+
if (!consumeIf(tok::l_paren)) {
2919+
diagnose(Loc, diag::attr_expected_lparen, AttrName,
2920+
DeclAttribute::isDeclModifier(DK));
2921+
return false;
2922+
}
2923+
2924+
bool SuppressLaterDiags = false;
2925+
SourceLoc RightLoc;
2926+
llvm::SmallVector<PlatformAndVersion, 4> PlatformAndVersions;
2927+
StringRef AttrName = "@_backDeploy";
2928+
ParserStatus Status = parseList(tok::r_paren, LeftLoc, RightLoc, false,
2929+
diag::attr_back_deploy_missing_rparen,
2930+
SyntaxKind::Unknown, [&]() -> ParserStatus {
2931+
ParserStatus ListItemStatus =
2932+
parsePlatformVersionInList(AttrName, PlatformAndVersions);
2933+
if (ListItemStatus.isErrorOrHasCompletion())
2934+
SuppressLaterDiags = true;
2935+
return ListItemStatus;
2936+
});
2937+
2938+
if (Status.isErrorOrHasCompletion() || SuppressLaterDiags) {
2939+
return false;
2940+
}
2941+
2942+
if (PlatformAndVersions.empty()) {
2943+
diagnose(Loc, diag::attr_availability_need_platform_version, AttrName);
2944+
return false;
2945+
}
2946+
2947+
assert(!PlatformAndVersions.empty());
2948+
AttrRange = SourceRange(Loc, Tok.getLoc());
2949+
for (auto &Item: PlatformAndVersions) {
2950+
Attributes.add(new (Context) BackDeployAttr(AtLoc, AttrRange,
2951+
Item.first,
2952+
Item.second,
2953+
/*IsImplicit*/false));
2954+
}
2955+
break;
2956+
}
28582957
}
28592958

28602959
if (DuplicateAttribute) {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
118118
IGNORED_ATTR(InheritActorContext)
119119
IGNORED_ATTR(Isolated)
120120
IGNORED_ATTR(Preconcurrency)
121+
IGNORED_ATTR(BackDeploy)
121122
#undef IGNORED_ATTR
122123

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

277278
void visitPrimaryAssociatedTypeAttr(PrimaryAssociatedTypeAttr *attr);
279+
280+
void checkBackDeployAttrs(Decl *D, ArrayRef<BackDeployAttr *> Attrs);
278281
};
279282

280283
} // end anonymous namespace
@@ -3460,6 +3463,11 @@ void AttributeChecker::checkOriginalDefinedInAttrs(Decl *D,
34603463
}
34613464
}
34623465

3466+
void AttributeChecker::checkBackDeployAttrs(Decl *D,
3467+
ArrayRef<BackDeployAttr *> Attrs) {
3468+
// FIXME(backDeploy): Diagnose incompatible uses of `@_backDeploy
3469+
}
3470+
34633471
Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type,
34643472
ReferenceOwnershipAttr *attr) {
34653473
auto &Diags = var->getASTContext().Diags;

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,6 +1567,8 @@ namespace {
15671567

15681568
UNINTERESTING_ATTR(PrimaryAssociatedType)
15691569

1570+
UNINTERESTING_ATTR(BackDeploy)
1571+
15701572
#undef UNINTERESTING_ATTR
15711573

15721574
void visitAvailableAttr(AvailableAttr *attr) {

0 commit comments

Comments
 (0)