Skip to content

Commit d66382d

Browse files
authored
Merge pull request swiftlang#36676 from DougGregor/unsafe-concurrency-attrs
[Concurrency] Introduce "unsafe" @sendable and @mainactor parameter attributes
2 parents 1e9ae59 + f02a01e commit d66382d

File tree

19 files changed

+344
-50
lines changed

19 files changed

+344
-50
lines changed

include/swift/AST/Attr.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,15 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(nonisolated, Nonisolated,
637637
APIBreakingToAdd | APIStableToRemove,
638638
112)
639639

640+
CONTEXTUAL_SIMPLE_DECL_ATTR(_unsafeSendable, UnsafeSendable,
641+
OnParam | UserInaccessible |
642+
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
643+
113)
644+
645+
CONTEXTUAL_SIMPLE_DECL_ATTR(_unsafeMainActor, UnsafeMainActor,
646+
OnParam | UserInaccessible |
647+
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove,
648+
114)
640649

641650
#undef TYPE_ATTR
642651
#undef DECL_ATTR_ALIAS

include/swift/AST/Decl.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6097,8 +6097,17 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
60976097
/// constructor.
60986098
bool hasDynamicSelfResult() const;
60996099

6100+
61006101
AbstractFunctionDecl *getAsyncAlternative() const;
61016102

6103+
/// Determine whether this function is implicitly known to have its
6104+
/// parameters of function type be @_unsafeSendable.
6105+
///
6106+
/// This hard-codes knowledge of a number of functions that will
6107+
/// eventually have @_unsafeSendable and, eventually, @Sendable,
6108+
/// on their parameters of function type.
6109+
bool hasKnownUnsafeSendableFunctionParams() const;
6110+
61026111
using DeclContext::operator new;
61036112
using Decl::getASTContext;
61046113
};

include/swift/AST/Expr.h

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3811,6 +3811,12 @@ class ClosureExpr : public AbstractClosureExpr {
38113811
SeparatelyTypeChecked,
38123812
};
38133813

3814+
/// Bits used to indicate contextual information that is concurrency-specific.
3815+
enum UnsafeConcurrencyBits {
3816+
Sendable = 1 << 0,
3817+
MainActor = 1 << 1
3818+
};
3819+
38143820
private:
38153821
/// The attributes attached to the closure.
38163822
DeclAttributes Attributes;
@@ -3825,7 +3831,10 @@ class ClosureExpr : public AbstractClosureExpr {
38253831
/// the CaptureListExpr which would normally maintain this sort of
38263832
/// information about captured variables), we need to have some way to access
38273833
/// this information directly on the ClosureExpr.
3828-
VarDecl * CapturedSelfDecl;
3834+
///
3835+
/// The integer indicates how the closure is contextually concurrent.
3836+
llvm::PointerIntPair<VarDecl *, 2, uint8_t>
3837+
CapturedSelfDeclAndUnsafeConcurrent;
38293838

38303839
/// The location of the "async", if present.
38313840
SourceLoc AsyncLoc;
@@ -3855,7 +3864,7 @@ class ClosureExpr : public AbstractClosureExpr {
38553864
: AbstractClosureExpr(ExprKind::Closure, Type(), /*Implicit=*/false,
38563865
discriminator, parent),
38573866
Attributes(attributes), BracketRange(bracketRange),
3858-
CapturedSelfDecl(capturedSelfDecl),
3867+
CapturedSelfDeclAndUnsafeConcurrent(capturedSelfDecl, 0),
38593868
AsyncLoc(asyncLoc), ThrowsLoc(throwsLoc), ArrowLoc(arrowLoc),
38603869
InLoc(inLoc),
38613870
ExplicitResultTypeAndBodyState(explicitResultType, BodyState::Parsed),
@@ -3961,7 +3970,27 @@ class ClosureExpr : public AbstractClosureExpr {
39613970

39623971
/// VarDecl captured by this closure under the literal name \c self , if any.
39633972
VarDecl *getCapturedSelfDecl() const {
3964-
return CapturedSelfDecl;
3973+
return CapturedSelfDeclAndUnsafeConcurrent.getPointer();
3974+
}
3975+
3976+
bool isUnsafeSendable() const {
3977+
return CapturedSelfDeclAndUnsafeConcurrent.getInt() &
3978+
UnsafeConcurrencyBits::Sendable;
3979+
}
3980+
3981+
bool isUnsafeMainActor() const {
3982+
return CapturedSelfDeclAndUnsafeConcurrent.getInt() &
3983+
UnsafeConcurrencyBits::MainActor;
3984+
}
3985+
3986+
void setUnsafeConcurrent(bool sendable, bool forMainActor) {
3987+
uint8_t bits = 0;
3988+
if (sendable)
3989+
bits |= UnsafeConcurrencyBits::Sendable;
3990+
if (forMainActor)
3991+
bits |= UnsafeConcurrencyBits::MainActor;
3992+
3993+
CapturedSelfDeclAndUnsafeConcurrent.setInt(bits);
39653994
}
39663995

39673996
/// Get the type checking state of this closure's body.

include/swift/AST/KnownSDKTypes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,6 @@ KNOWN_SDK_TYPE_DECL(ObjectiveC, ObjCBool, StructDecl, 0)
3838
// TODO(async): These might move to the stdlib module when concurrency is
3939
// standardized
4040
KNOWN_SDK_TYPE_DECL(Concurrency, UnsafeContinuation, NominalTypeDecl, 2)
41+
KNOWN_SDK_TYPE_DECL(Concurrency, MainActor, NominalTypeDecl, 0)
4142

4243
#undef KNOWN_SDK_TYPE_DECL

include/swift/AST/Types.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3350,6 +3350,8 @@ struct ParameterListInfo {
33503350
SmallBitVector defaultArguments;
33513351
SmallBitVector acceptsUnlabeledTrailingClosures;
33523352
SmallBitVector propertyWrappers;
3353+
SmallBitVector unsafeSendable;
3354+
SmallBitVector unsafeMainActor;
33533355

33543356
public:
33553357
ParameterListInfo() { }
@@ -3368,6 +3370,16 @@ struct ParameterListInfo {
33683370
/// property wrapper.
33693371
bool hasExternalPropertyWrapper(unsigned paramIdx) const;
33703372

3373+
/// Whether the given parameter is unsafe Sendable, meaning that
3374+
/// we will treat it as Sendable in a context that has adopted concurrency
3375+
/// features.
3376+
bool isUnsafeSendable(unsigned paramIdx) const;
3377+
3378+
/// Whether the given parameter is unsafe MainActor, meaning that
3379+
/// we will treat it as being part of the main actor but that it is not
3380+
/// part of the type system.
3381+
bool isUnsafeMainActor(unsigned paramIdx) const;
3382+
33713383
/// Retrieve the number of non-defaulted parameters.
33723384
unsigned numNonDefaultedParameters() const {
33733385
return defaultArguments.count();

lib/AST/Decl.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7331,6 +7331,27 @@ void AbstractFunctionDecl::addDerivativeFunctionConfiguration(
73317331
DerivativeFunctionConfigs->insert(config);
73327332
}
73337333

7334+
bool AbstractFunctionDecl::hasKnownUnsafeSendableFunctionParams() const {
7335+
auto nominal = getDeclContext()->getSelfNominalTypeDecl();
7336+
if (!nominal)
7337+
return false;
7338+
7339+
// DispatchQueue operations.
7340+
auto nominalName = nominal->getName().str();
7341+
if (nominalName == "DispatchQueue") {
7342+
auto name = getBaseName().userFacingName();
7343+
return llvm::StringSwitch<bool>(name)
7344+
.Case("sync", true)
7345+
.Case("async", true)
7346+
.Case("asyncAndWait", true)
7347+
.Case("asyncAfter", true)
7348+
.Case("concurrentPerform", true)
7349+
.Default(false);
7350+
}
7351+
7352+
return false;
7353+
}
7354+
73347355
void FuncDecl::setResultInterfaceType(Type type) {
73357356
getASTContext().evaluator.cacheOutput(ResultTypeRequest{this},
73367357
std::move(type));

lib/AST/Type.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,12 +935,36 @@ static bool allowsUnlabeledTrailingClosureParameter(const ParamDecl *param) {
935935
return paramType->is<AnyFunctionType>();
936936
}
937937

938+
/// Determine whether the parameter is contextually Sendable.
939+
static bool isParamUnsafeSendable(const ParamDecl *param) {
940+
// Check for @_unsafeSendable.
941+
if (param->getAttrs().hasAttribute<UnsafeSendableAttr>())
942+
return true;
943+
944+
// Check that the parameter is of function type.
945+
Type paramType = param->isVariadic() ? param->getVarargBaseTy()
946+
: param->getInterfaceType();
947+
paramType = paramType->getRValueType()->lookThroughAllOptionalTypes();
948+
if (!paramType->is<FunctionType>())
949+
return false;
950+
951+
// Check whether this function is known to have @_unsafeSendable function
952+
// parameters.
953+
auto func = dyn_cast<AbstractFunctionDecl>(param->getDeclContext());
954+
if (!func)
955+
return false;
956+
957+
return func->hasKnownUnsafeSendableFunctionParams();
958+
}
959+
938960
ParameterListInfo::ParameterListInfo(
939961
ArrayRef<AnyFunctionType::Param> params,
940962
const ValueDecl *paramOwner,
941963
bool skipCurriedSelf) {
942964
defaultArguments.resize(params.size());
943965
propertyWrappers.resize(params.size());
966+
unsafeSendable.resize(params.size());
967+
unsafeMainActor.resize(params.size());
944968

945969
// No parameter owner means no parameter list means no default arguments
946970
// - hand back the zeroed bitvector.
@@ -992,6 +1016,14 @@ ParameterListInfo::ParameterListInfo(
9921016
if (param->hasAttachedPropertyWrapper()) {
9931017
propertyWrappers.set(i);
9941018
}
1019+
1020+
if (isParamUnsafeSendable(param)) {
1021+
unsafeSendable.set(i);
1022+
}
1023+
1024+
if (param->getAttrs().hasAttribute<UnsafeMainActorAttr>()) {
1025+
unsafeMainActor.set(i);
1026+
}
9951027
}
9961028
}
9971029

@@ -1010,6 +1042,18 @@ bool ParameterListInfo::hasExternalPropertyWrapper(unsigned paramIdx) const {
10101042
return paramIdx < propertyWrappers.size() ? propertyWrappers[paramIdx] : false;
10111043
}
10121044

1045+
bool ParameterListInfo::isUnsafeSendable(unsigned paramIdx) const {
1046+
return paramIdx < unsafeSendable.size()
1047+
? unsafeSendable[paramIdx]
1048+
: false;
1049+
}
1050+
1051+
bool ParameterListInfo::isUnsafeMainActor(unsigned paramIdx) const {
1052+
return paramIdx < unsafeMainActor.size()
1053+
? unsafeMainActor[paramIdx]
1054+
: false;
1055+
}
1056+
10131057
/// Turn a param list into a symbolic and printable representation that does not
10141058
/// include the types, something like (_:, b:, c:)
10151059
std::string swift::getParamListAsString(ArrayRef<AnyFunctionType::Param> params) {

lib/ClangImporter/ImportDecl.cpp

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8289,35 +8289,6 @@ bool importer::isImportedAsStatic(const clang::OverloadedOperatorKind op) {
82898289
}
82908290
}
82918291

8292-
Type ClangImporter::Implementation::getMainActorType() {
8293-
if (MainActorType)
8294-
return *MainActorType;
8295-
8296-
auto finish = [&](Type type) -> Type {
8297-
MainActorType = type;
8298-
return type;
8299-
};
8300-
8301-
if (!SwiftContext.LangOpts.EnableExperimentalConcurrency) {
8302-
return finish(Type());
8303-
}
8304-
8305-
auto module = SwiftContext.getLoadedModule(SwiftContext.Id_Concurrency);
8306-
if (!module)
8307-
return finish(Type());
8308-
8309-
SmallVector<ValueDecl *, 1> decls;
8310-
module->lookupValue(
8311-
SwiftContext.getIdentifier("MainActor"),
8312-
NLKind::QualifiedLookup, decls);
8313-
for (auto decl : decls) {
8314-
if (auto typeDecl = dyn_cast<TypeDecl>(decl))
8315-
return finish(typeDecl->getDeclaredInterfaceType());
8316-
}
8317-
8318-
return finish(Type());
8319-
}
8320-
83218292
unsigned ClangImporter::Implementation::getClangSwiftAttrSourceBuffer(
83228293
StringRef attributeText) {
83238294
auto known = ClangSwiftAttrSourceBuffers.find(attributeText);
@@ -8529,7 +8500,7 @@ void ClangImporter::Implementation::importAttributes(
85298500
// point at which to do name lookup for imported entities.
85308501
if (auto isMainActor = isMainActorAttr(SwiftContext, swiftAttr)) {
85318502
bool isUnsafe = *isMainActor;
8532-
if (Type mainActorType = getMainActorType()) {
8503+
if (Type mainActorType = SwiftContext.getMainActorType()) {
85338504
auto typeExpr = TypeExpr::createImplicit(mainActorType, SwiftContext);
85348505
auto attr = CustomAttr::create(SwiftContext, SourceLoc(), typeExpr);
85358506
attr->setArgIsUnsafe(isUnsafe);

lib/ClangImporter/ImportType.cpp

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1711,7 +1711,8 @@ static Type applyToFunctionType(
17111711
}
17121712

17131713
Type ClangImporter::Implementation::applyParamAttributes(
1714-
const clang::ParmVarDecl *param, Type type) {
1714+
const clang::ParmVarDecl *param, Type type, bool &isUnsafeSendable,
1715+
bool &isUnsafeMainActor) {
17151716
if (!param->hasAttrs())
17161717
return type;
17171718

@@ -1731,7 +1732,7 @@ Type ClangImporter::Implementation::applyParamAttributes(
17311732

17321733
// Map the main-actor attribute.
17331734
if (isMainActorAttr(SwiftContext, swiftAttr)) {
1734-
if (Type mainActor = getMainActorType()) {
1735+
if (Type mainActor = SwiftContext.getMainActorType()) {
17351736
type = applyToFunctionType(type, [&](ASTExtInfo extInfo) {
17361737
return extInfo.withGlobalActor(mainActor);
17371738
});
@@ -1748,6 +1749,18 @@ Type ClangImporter::Implementation::applyParamAttributes(
17481749

17491750
continue;
17501751
}
1752+
1753+
// Map @_unsafeSendable.
1754+
if (swiftAttr->getAttribute() == "@_unsafeSendable") {
1755+
isUnsafeSendable = true;
1756+
continue;
1757+
}
1758+
1759+
// Map @_unsafeMainActor.
1760+
if (swiftAttr->getAttribute() == "@_unsafeMainActor") {
1761+
isUnsafeMainActor = true;
1762+
continue;
1763+
}
17511764
}
17521765

17531766
return type;
@@ -1928,7 +1941,10 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
19281941
}
19291942

19301943
// Apply attributes to the type.
1931-
swiftParamTy = applyParamAttributes(param, swiftParamTy);
1944+
bool isUnsafeSendable = false;
1945+
bool isUnsafeMainActor = false;
1946+
swiftParamTy = applyParamAttributes(
1947+
param, swiftParamTy, isUnsafeSendable, isUnsafeMainActor);
19321948

19331949
// Figure out the name for this parameter.
19341950
Identifier bodyName = importFullName(param, CurrentVersion)
@@ -1949,6 +1965,8 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
19491965
paramInfo->setSpecifier(ParamSpecifier::Default);
19501966
paramInfo->setInterfaceType(swiftParamTy);
19511967
recordImplicitUnwrapForDecl(paramInfo, isParamTypeImplicitlyUnwrapped);
1968+
recordUnsafeConcurrencyForDecl(
1969+
paramInfo, isUnsafeSendable, isUnsafeMainActor);
19521970
parameters.push_back(paramInfo);
19531971
++index;
19541972
}
@@ -2426,7 +2444,10 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
24262444
}
24272445

24282446
// Apply Clang attributes to the parameter type.
2429-
swiftParamTy = applyParamAttributes(param, swiftParamTy);
2447+
bool isUnsafeSendable = false;
2448+
bool isUnsafeMainActor = false;
2449+
swiftParamTy = applyParamAttributes(
2450+
param, swiftParamTy, isUnsafeSendable, isUnsafeMainActor);
24302451

24312452
// Figure out the name for this parameter.
24322453
Identifier bodyName = importFullName(param, CurrentVersion)
@@ -2450,6 +2471,8 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
24502471
paramInfo->setSpecifier(ParamSpecifier::Default);
24512472
paramInfo->setInterfaceType(swiftParamTy);
24522473
recordImplicitUnwrapForDecl(paramInfo, paramIsIUO);
2474+
recordUnsafeConcurrencyForDecl(
2475+
paramInfo, isUnsafeSendable, isUnsafeMainActor);
24532476

24542477
// Determine whether we have a default argument.
24552478
if (kind == SpecialMethodKind::Regular ||

0 commit comments

Comments
 (0)