Skip to content

Commit 354f347

Browse files
committed
Add @_nonSendable decl attribute
This attribute creates an unavailable extension with a `Sendable` conformance so that the type is explicity marked as not being `Sendable`. We also fully suppress diagnostics about unavailable Sendable conformances in Swift 5 mode code. (This is not fully developed yet—it should return to being a warning in concurrent contexts.) The behavior when a @_nonSendable and a Sendable conformance are both on the same type is also not right yet.
1 parent 19f15ff commit 354f347

File tree

14 files changed

+184
-12
lines changed

14 files changed

+184
-12
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,22 @@ override.
376376
This attribute and the corresponding `-warn-implicit-overrides` flag are
377377
used when compiling the standard library and overlays.
378378

379+
## `@_nonSendable`
380+
381+
There is no clang attribute to add a Swift conformance to an imported type, but
382+
there *is* a clang attribute to add a Swift attribute to an imported type. So
383+
`@Sendable` (which is not normally allowed on types) is used from clang headers
384+
to indicate that an unconstrained, fully available `Sendable` conformance should
385+
be added to a given type, while `@_nonSendable` indicates that an unavailable
386+
`Sendable` conformance should be added to it.
387+
388+
`@_nonSendable` can have no options after it, in which case it "beats"
389+
`@Sendable` if both are applied to the same declaration, or it can have
390+
`(_assumed)` after it, in which case `@Sendable` "beats" it.
391+
`@_nonSendable(_assumed)` is intended to be used when mass-marking whole regions
392+
of a header as non-`Sendable` so that you can make spot exceptions with
393+
`@Sendable`.
394+
379395
## `@_objc_non_lazy_realization`
380396

381397
Marks a class as being non-lazily (i.e. eagerly) [realized](/docs/Lexicon.md#realization).

include/swift/AST/Attr.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,13 @@ SIMPLE_DECL_ATTR(_assemblyVision, EmitAssemblyVisionRemarks,
665665
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
666666
120)
667667

668+
DECL_ATTR(_nonSendable, NonSendable,
669+
OnNominalType |
670+
UserInaccessible | AllowMultipleAttributes |
671+
ABIStableToAdd | ABIBreakingToRemove |
672+
APIStableToAdd | APIBreakingToRemove,
673+
121)
674+
668675
// If you're adding a new underscored attribute here, please document it in
669676
// docs/ReferenceGuides/UnderscoredAttributes.md.
670677

include/swift/AST/Attr.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2030,6 +2030,37 @@ class TransposeAttr final
20302030
}
20312031
};
20322032

2033+
enum class NonSendableKind : uint8_t {
2034+
/// A plain '@_nonSendable' attribute. Should be applied directly to
2035+
/// particular declarations; overrides even an explicit 'Sendable'
2036+
/// conformance.
2037+
Specific,
2038+
2039+
/// A '@_nonSendable(_assumed)' attribute. Should be applied to large swaths
2040+
/// of declarations; does not override explicit 'Sendable' conformances.
2041+
Assumed
2042+
};
2043+
2044+
/// Marks a declaration as explicitly non-Sendable.
2045+
class NonSendableAttr : public DeclAttribute {
2046+
public:
2047+
NonSendableAttr(SourceLoc AtLoc, SourceRange Range,
2048+
NonSendableKind Specificity, bool Implicit = false)
2049+
: DeclAttribute(DAK_NonSendable, AtLoc, Range, Implicit),
2050+
Specificity(Specificity)
2051+
{}
2052+
2053+
NonSendableAttr(NonSendableKind Specificity, bool Implicit = false)
2054+
: NonSendableAttr(SourceLoc(), SourceRange(), Specificity, Implicit) {}
2055+
2056+
/// Was this '@_nonSendable(_assumed)'?
2057+
const NonSendableKind Specificity;
2058+
2059+
static bool classof(const DeclAttribute *DA) {
2060+
return DA->getKind() == DAK_NonSendable;
2061+
}
2062+
};
2063+
20332064
/// Attributes that may be applied to declarations.
20342065
class DeclAttributes {
20352066
/// Linked list of declaration attributes.

lib/AST/Attr.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,15 @@ StringRef DeclAttribute::getAttrName() const {
12261226
}
12271227
llvm_unreachable("Invalid inline kind");
12281228
}
1229+
case DAK_NonSendable: {
1230+
switch (cast<NonSendableAttr>(this)->Specificity) {
1231+
case NonSendableKind::Specific:
1232+
return "_nonSendable";
1233+
case NonSendableKind::Assumed:
1234+
return "_nonSendable(_assumed)";
1235+
}
1236+
llvm_unreachable("Invalid nonSendable kind");
1237+
}
12291238
case DAK_Optimize: {
12301239
switch (cast<OptimizeAttr>(this)->getMode()) {
12311240
case OptimizationMode::NoOptimization:

lib/Parse/ParseDecl.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,6 +2014,20 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
20142014
break;
20152015
}
20162016

2017+
case DAK_NonSendable: {
2018+
auto kind = parseSingleAttrOption<NonSendableKind>
2019+
(*this, Loc, AttrRange, AttrName, DK)
2020+
.when("_assumed", NonSendableKind::Assumed)
2021+
.whenOmitted(NonSendableKind::Specific);
2022+
if (!kind)
2023+
return false;
2024+
2025+
if (!DiscardAttribute)
2026+
Attributes.add(new (Context) NonSendableAttr(AtLoc, AttrRange, *kind));
2027+
2028+
break;
2029+
}
2030+
20172031
case DAK_AccessControl: {
20182032

20192033
// Diagnose using access control in a local scope, which isn't meaningful.

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
107107
IGNORED_ATTR(NoDerivative)
108108
IGNORED_ATTR(SpecializeExtension)
109109
IGNORED_ATTR(Sendable)
110+
IGNORED_ATTR(NonSendable)
110111
IGNORED_ATTR(AtRethrows)
111112
IGNORED_ATTR(AtReasync)
112113
IGNORED_ATTR(UnsafeSendable)

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2422,6 +2422,31 @@ bool swift::diagnoseExplicitUnavailability(const ValueDecl *D, SourceRange R,
24222422
});
24232423
}
24242424

2425+
static DiagnosticBehavior
2426+
behaviorLimitForExplicitUnavailability(const RootProtocolConformance *rootConf) {
2427+
auto protoDecl = rootConf->getProtocol();
2428+
2429+
// Soften errors about unavailable `Sendable` conformances depending on the
2430+
// concurrency checking mode
2431+
if (protoDecl->isSpecificProtocol(KnownProtocolKind::Sendable) ||
2432+
protoDecl->isSpecificProtocol(KnownProtocolKind::UnsafeSendable)) {
2433+
// TODO: Base this on concurrency checking mode from ExportContext so it
2434+
// detects when you're in concurrency code without -warn-concurrency.
2435+
auto &langOpts = protoDecl->getASTContext().LangOpts;
2436+
if (langOpts.isSwiftVersionAtLeast(6))
2437+
/* fall through */;
2438+
else if (!langOpts.WarnConcurrency)
2439+
// TODO: Needs more conditions--should only do this if we aren't in a
2440+
// concurrent context, and either the import or the declaration is
2441+
// @predatesConcurrency.
2442+
return DiagnosticBehavior::Ignore;
2443+
else
2444+
return DiagnosticBehavior::Warning;
2445+
}
2446+
2447+
return DiagnosticBehavior::Unspecified;
2448+
}
2449+
24252450
/// Emit a diagnostic for references to declarations that have been
24262451
/// marked as unavailable, either through "unavailable" or "obsoleted:".
24272452
bool swift::diagnoseExplicitUnavailability(SourceLoc loc,
@@ -2457,12 +2482,10 @@ bool swift::diagnoseExplicitUnavailability(SourceLoc loc,
24572482
// This was platform-specific; indicate the platform.
24582483
platform = attr->prettyPlatformString();
24592484
break;
2460-
} else if (rootConf->getProtocol()->isSpecificProtocol(
2461-
KnownProtocolKind::Sendable) &&
2462-
!ctx.LangOpts.isSwiftVersionAtLeast(6)) {
2485+
} else {
24632486
// Downgrade unavailable Sendable conformances to warnings prior to
24642487
// Swift 6.
2465-
behavior = DiagnosticBehavior::Warning;
2488+
behavior = behaviorLimitForExplicitUnavailability(rootConf);
24662489
}
24672490
LLVM_FALLTHROUGH;
24682491

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3933,22 +3933,57 @@ NormalProtocolConformance *GetImplicitSendableRequest::evaluate(
39333933
}
39343934

39353935
// Local function to form the implicit conformance.
3936-
auto formConformance = [&]() -> NormalProtocolConformance * {
3936+
auto formConformance = [&](const DeclAttribute *attrMakingUnavailable)
3937+
-> NormalProtocolConformance * {
39373938
ASTContext &ctx = nominal->getASTContext();
39383939
auto proto = ctx.getProtocol(KnownProtocolKind::Sendable);
39393940
if (!proto)
39403941
return nullptr;
39413942

3943+
DeclContext *conformanceDC = nominal;
3944+
if (attrMakingUnavailable) {
3945+
llvm::VersionTuple NoVersion;
3946+
auto attr = new (ctx) AvailableAttr(attrMakingUnavailable->AtLoc,
3947+
attrMakingUnavailable->Range,
3948+
PlatformKind::none, "", "", nullptr,
3949+
NoVersion, SourceRange(),
3950+
NoVersion, SourceRange(),
3951+
NoVersion, SourceRange(),
3952+
PlatformAgnosticAvailabilityKind::Unavailable,
3953+
false);
3954+
3955+
// Conformance availability is currently tied to the declaring extension.
3956+
// FIXME: This is a hack--we should give conformances real availability.
3957+
auto extension = ExtensionDecl::create(ctx, attrMakingUnavailable->AtLoc,
3958+
nullptr, {},
3959+
nominal->getModuleScopeContext(),
3960+
nullptr);
3961+
extension->setImplicit();
3962+
extension->getAttrs().add(attr);
3963+
3964+
ctx.evaluator.cacheOutput(ExtendedTypeRequest{extension},
3965+
nominal->getDeclaredType());
3966+
ctx.evaluator.cacheOutput(ExtendedNominalRequest{extension},
3967+
std::move(nominal));
3968+
nominal->addExtension(extension);
3969+
3970+
conformanceDC = extension;
3971+
}
3972+
39423973
auto conformance = ctx.getConformance(
39433974
nominal->getDeclaredInterfaceType(), proto, nominal->getLoc(),
3944-
nominal, ProtocolConformanceState::Complete, false);
3975+
conformanceDC, ProtocolConformanceState::Complete,
3976+
/*isUnchecked=*/attrMakingUnavailable != nullptr);
39453977
conformance->setSourceKindAndImplyingConformance(
39463978
ConformanceEntryKind::Synthesized, nullptr);
39473979

39483980
nominal->registerProtocolConformance(conformance, /*synthesized=*/true);
39493981
return conformance;
39503982
};
39513983

3984+
if (auto nonSendable = nominal->getAttrs().getAttribute<NonSendableAttr>())
3985+
return formConformance(nonSendable);
3986+
39523987
// A non-protocol type with a global actor is implicitly Sendable.
39533988
if (nominal->getGlobalActorAttr()) {
39543989
// If this is a class, check the superclass. We won't infer Sendable
@@ -3966,7 +4001,7 @@ NormalProtocolConformance *GetImplicitSendableRequest::evaluate(
39664001
}
39674002

39684003
// Form the implicit conformance to Sendable.
3969-
return formConformance();
4004+
return formConformance(nullptr);
39704005
}
39714006

39724007
// Only structs and enums can get implicit Sendable conformances by
@@ -3991,7 +4026,7 @@ NormalProtocolConformance *GetImplicitSendableRequest::evaluate(
39914026
nominal, nominal, SendableCheck::Implicit))
39924027
return nullptr;
39934028

3994-
return formConformance();
4029+
return formConformance(nullptr);
39954030
}
39964031

39974032
AnyFunctionType *swift::applyGlobalActorType(

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,6 +1545,7 @@ namespace {
15451545
UNINTERESTING_ATTR(GlobalActor)
15461546
UNINTERESTING_ATTR(Async)
15471547
UNINTERESTING_ATTR(Sendable)
1548+
UNINTERESTING_ATTR(NonSendable)
15481549

15491550
UNINTERESTING_ATTR(AtRethrows)
15501551
UNINTERESTING_ATTR(Marker)

lib/Serialization/Deserialization.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4404,6 +4404,14 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() {
44044404
break;
44054405
}
44064406

4407+
case decls_block::NonSendable_DECL_ATTR: {
4408+
unsigned kind;
4409+
serialization::decls_block::NonSendableDeclAttrLayout::readRecord(
4410+
scratch, kind);
4411+
Attr = new (ctx) NonSendableAttr((NonSendableKind)kind);
4412+
break;
4413+
}
4414+
44074415
case decls_block::Optimize_DECL_ATTR: {
44084416
unsigned kind;
44094417
serialization::decls_block::OptimizeDeclAttrLayout::readRecord(

0 commit comments

Comments
 (0)