Skip to content

Commit bd6490b

Browse files
committed
SIL: Introduce '@_alwaysEmitIntoClient' attribute for use by standard library
This is like '@inlinable', except that the symbol does not have a public entry point in the generated binary at all; it is deserialized and a copy is always emitted into the client binary, with shared linkage. Just like '@inlinable', if you apply this to an internal declaration it becomes '@usableFromInline' automatically. This uses the same mechanism as default arguments ever since Swift 4, so it should work reasonably well, but there are rough edges with diagnostics and such. Don't use this if you are not the standard library. Fixes <rdar://problem/33767512>, <https://bugs.swift.org/browse/SR-5646>.
1 parent c2f8622 commit bd6490b

13 files changed

+79
-11
lines changed

include/swift/AST/Attr.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,9 @@ DECL_ATTR(_private, PrivateImport,
390390
OnImport |
391391
UserInaccessible |
392392
NotSerialized, 82)
393+
SIMPLE_DECL_ATTR(_alwaysEmitIntoClient, AlwaysEmitIntoClient,
394+
OnVar | OnSubscript | OnAbstractFunction | UserInaccessible,
395+
83)
393396

394397
#undef TYPE_ATTR
395398
#undef DECL_ATTR_ALIAS

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4082,6 +4082,7 @@ ERROR(usable_from_inline_attr_in_protocol,none,
40824082
#define FRAGILE_FUNC_KIND \
40834083
"%select{a '@_transparent' function|" \
40844084
"an '@inlinable' function|" \
4085+
"an '@_alwaysEmitIntoClient' function|" \
40854086
"a default argument value|" \
40864087
"a property initializer in a '@_fixed_layout' type}"
40874088

@@ -4115,7 +4116,7 @@ NOTE(resilience_decl_declared_here,
41154116

41164117
ERROR(class_designated_init_inlinable_resilient,none,
41174118
"initializer for class %0 is "
4118-
"'%select{@_transparent|@inlinable|%error}1' and must "
4119+
"'%select{@_transparent|@inlinable|@_alwaysEmitIntoClient|%error}1' and must "
41194120
"delegate to another initializer", (Type, unsigned))
41204121

41214122
ERROR(attribute_invalid_on_stored_property,

include/swift/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5252
/// describe what change you made. The content of this comment isn't important;
5353
/// it just ensures a conflict if two people change the module format.
5454
/// Don't worry about adhering to the 80-column limit for this line.
55-
const uint16_t SWIFTMODULE_VERSION_MINOR = 473; // Last change: assign ownership qualifier
55+
const uint16_t SWIFTMODULE_VERSION_MINOR = 474; // Last change: @_alwaysEmitIntoClient
5656

5757
using DeclIDField = BCFixed<31>;
5858

lib/AST/Decl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2518,12 +2518,14 @@ bool ValueDecl::isUsableFromInline() const {
25182518
assert(getFormalAccess() == AccessLevel::Internal);
25192519

25202520
if (getAttrs().hasAttribute<UsableFromInlineAttr>() ||
2521+
getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>() ||
25212522
getAttrs().hasAttribute<InlinableAttr>())
25222523
return true;
25232524

25242525
if (auto *accessor = dyn_cast<AccessorDecl>(this)) {
25252526
auto *storage = accessor->getStorage();
25262527
if (storage->getAttrs().hasAttribute<UsableFromInlineAttr>() ||
2528+
storage->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>() ||
25272529
storage->getAttrs().hasAttribute<InlinableAttr>())
25282530
return true;
25292531
}

lib/AST/DeclContext.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -360,11 +360,18 @@ ResilienceExpansion DeclContext::getResilienceExpansion() const {
360360
if (AFD->getAttrs().hasAttribute<InlinableAttr>())
361361
return ResilienceExpansion::Minimal;
362362

363-
// If a property or subscript is @inlinable, the accessors are
364-
// @inlinable also.
365-
if (auto accessor = dyn_cast<AccessorDecl>(AFD))
366-
if (accessor->getStorage()->getAttrs().getAttribute<InlinableAttr>())
363+
if (AFD->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
364+
return ResilienceExpansion::Minimal;
365+
366+
// If a property or subscript is @inlinable or @_alwaysEmitIntoClient,
367+
// the accessors are @inlinable or @_alwaysEmitIntoClient also.
368+
if (auto accessor = dyn_cast<AccessorDecl>(AFD)) {
369+
auto *storage = accessor->getStorage();
370+
if (storage->getAttrs().getAttribute<InlinableAttr>())
367371
return ResilienceExpansion::Minimal;
372+
if (storage->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
373+
return ResilienceExpansion::Minimal;
374+
}
368375
}
369376
}
370377

lib/SIL/SILDeclRef.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,18 @@ SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const {
290290
/// or shared linkage.
291291
OnDemand,
292292
/// The declaration should never be made public.
293-
NeverPublic
293+
NeverPublic,
294+
/// The declaration should always be emitted into the client,
295+
AlwaysEmitIntoClient,
294296
};
295297
auto limit = Limit::None;
296298

299+
// @_alwaysEmitIntoClient declarations are like the default arguments of
300+
// public functions; they are roots for dead code elimination and have
301+
// serialized bodies, but no public symbol in the generated binary.
302+
if (d->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
303+
limit = Limit::AlwaysEmitIntoClient;
304+
297305
// ivar initializers and destroyers are completely contained within the class
298306
// from which they come, and never get seen externally.
299307
if (isIVarInitializerOrDestroyer()) {
@@ -369,6 +377,8 @@ SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const {
369377
return SILLinkage::Shared;
370378
if (limit == Limit::NeverPublic)
371379
return maybeAddExternal(SILLinkage::Hidden);
380+
if (limit == Limit::AlwaysEmitIntoClient)
381+
return maybeAddExternal(SILLinkage::PublicNonABI);
372382
return maybeAddExternal(SILLinkage::Public);
373383
}
374384
llvm_unreachable("unhandled access");
@@ -464,8 +474,8 @@ IsSerialized_t SILDeclRef::isSerialized() const {
464474

465475
auto *d = getDecl();
466476

467-
// Default argument generators are serialized if the function was
468-
// type-checked in Swift 4 mode.
477+
// Default argument generators are serialized if the containing
478+
// declaration is public.
469479
if (isDefaultArgGenerator()) {
470480
ResilienceExpansion expansion;
471481
if (auto *EED = dyn_cast<EnumElementDecl>(d)) {

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,21 @@ TypeChecker::getFragileFunctionKind(const DeclContext *DC) {
5858
return std::make_pair(FragileFunctionKind::Inlinable,
5959
/*treatUsableFromInlineAsPublic=*/true);
6060

61+
if (AFD->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
62+
return std::make_pair(FragileFunctionKind::AlwaysEmitIntoClient,
63+
/*treatUsableFromInlineAsPublic=*/true);
64+
6165
// If a property or subscript is @inlinable, the accessors are
6266
// @inlinable also.
63-
if (auto accessor = dyn_cast<AccessorDecl>(AFD))
64-
if (accessor->getStorage()->getAttrs().getAttribute<InlinableAttr>())
67+
if (auto accessor = dyn_cast<AccessorDecl>(AFD)) {
68+
auto *storage = accessor->getStorage();
69+
if (storage->getAttrs().getAttribute<InlinableAttr>())
6570
return std::make_pair(FragileFunctionKind::Inlinable,
6671
/*treatUsableFromInlineAsPublic=*/true);
72+
if (storage->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
73+
return std::make_pair(FragileFunctionKind::AlwaysEmitIntoClient,
74+
/*treatUsableFromInlineAsPublic=*/true);
75+
}
6776
}
6877
}
6978

lib/Sema/TypeCheckAttr.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class AttributeEarlyChecker : public AttributeVisitor<AttributeEarlyChecker> {
7272
bool visitDeclAttribute(DeclAttribute *A) = delete;
7373

7474
#define IGNORED_ATTR(X) void visit##X##Attr(X##Attr *) {}
75+
IGNORED_ATTR(AlwaysEmitIntoClient)
7576
IGNORED_ATTR(Available)
7677
IGNORED_ATTR(HasInitialValue)
7778
IGNORED_ATTR(CDecl)
@@ -799,6 +800,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
799800
void visit##CLASS##Attr(CLASS##Attr *) {}
800801

801802
IGNORED_ATTR(Alignment)
803+
IGNORED_ATTR(AlwaysEmitIntoClient)
802804
IGNORED_ATTR(Borrowed)
803805
IGNORED_ATTR(HasInitialValue)
804806
IGNORED_ATTR(ClangImporterSynthesizedType)

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,7 @@ namespace {
12461246

12471247
UNINTERESTING_ATTR(AccessControl)
12481248
UNINTERESTING_ATTR(Alignment)
1249+
UNINTERESTING_ATTR(AlwaysEmitIntoClient)
12491250
UNINTERESTING_ATTR(Borrowed)
12501251
UNINTERESTING_ATTR(CDecl)
12511252
UNINTERESTING_ATTR(Consuming)

lib/Sema/TypeChecker.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,6 +1925,7 @@ class TypeChecker final : public LazyResolver {
19251925
enum class FragileFunctionKind : unsigned {
19261926
Transparent,
19271927
Inlinable,
1928+
AlwaysEmitIntoClient,
19281929
DefaultArgument,
19291930
PropertyInitializer
19301931
};

0 commit comments

Comments
 (0)